Commit 7c5b2759 authored by Zhang Rui's avatar Zhang Rui

Merge branches 'thermal-core', 'thermal-intel' and 'thermal-soc' into next

...@@ -12,6 +12,11 @@ Required properties: ...@@ -12,6 +12,11 @@ Required properties:
- resets : Must contain an entry for each entry in reset-names. - resets : Must contain an entry for each entry in reset-names.
See ../reset/reset.txt for details. See ../reset/reset.txt for details.
- reset-names : Must include the name "tsadc-apb". - reset-names : Must include the name "tsadc-apb".
- pinctrl-names : The pin control state names;
- pinctrl-0 : The "init" pinctrl state, it will be set before device probe.
- pinctrl-1 : The "default" pinctrl state, it will be set after reset the
TSADC controller.
- pinctrl-2 : The "sleep" pinctrl state, it will be in for suspend.
- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description. - #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description.
- rockchip,hw-tshut-temp : The hardware-controlled shutdown temperature value. - rockchip,hw-tshut-temp : The hardware-controlled shutdown temperature value.
- rockchip,hw-tshut-mode : The hardware-controlled shutdown mode 0:CRU 1:GPIO. - rockchip,hw-tshut-mode : The hardware-controlled shutdown mode 0:CRU 1:GPIO.
...@@ -27,8 +32,10 @@ tsadc: tsadc@ff280000 { ...@@ -27,8 +32,10 @@ tsadc: tsadc@ff280000 {
clock-names = "tsadc", "apb_pclk"; clock-names = "tsadc", "apb_pclk";
resets = <&cru SRST_TSADC>; resets = <&cru SRST_TSADC>;
reset-names = "tsadc-apb"; reset-names = "tsadc-apb";
pinctrl-names = "default"; pinctrl-names = "init", "default", "sleep";
pinctrl-0 = <&otp_out>; pinctrl-0 = <&otp_gpio>;
pinctrl-1 = <&otp_out>;
pinctrl-2 = <&otp_gpio>;
#thermal-sensor-cells = <1>; #thermal-sensor-cells = <1>;
rockchip,hw-tshut-temp = <95000>; rockchip,hw-tshut-temp = <95000>;
rockchip,hw-tshut-mode = <0>; rockchip,hw-tshut-mode = <0>;
......
...@@ -10,6 +10,8 @@ to the silicon temperature. ...@@ -10,6 +10,8 @@ to the silicon temperature.
Required properties: Required properties:
- compatible : Should be: - compatible : Should be:
- "ti,omap34xx-bandgap" : for OMAP34xx bandgap
- "ti,omap36xx-bandgap" : for OMAP36xx bandgap
- "ti,omap4430-bandgap" : for OMAP4430 bandgap - "ti,omap4430-bandgap" : for OMAP4430 bandgap
- "ti,omap4460-bandgap" : for OMAP4460 bandgap - "ti,omap4460-bandgap" : for OMAP4460 bandgap
- "ti,omap4470-bandgap" : for OMAP4470 bandgap - "ti,omap4470-bandgap" : for OMAP4470 bandgap
...@@ -25,6 +27,18 @@ to each bandgap version, because the mapping may change from ...@@ -25,6 +27,18 @@ to each bandgap version, because the mapping may change from
soc to soc, apart of depending on available features. soc to soc, apart of depending on available features.
Example: Example:
OMAP34xx:
bandgap {
reg = <0x48002524 0x4>;
compatible = "ti,omap34xx-bandgap";
};
OMAP36xx:
bandgap {
reg = <0x48002524 0x4>;
compatible = "ti,omap36xx-bandgap";
};
OMAP4430: OMAP4430:
bandgap { bandgap {
reg = <0x4a002260 0x4 0x4a00232C 0x4>; reg = <0x4a002260 0x4 0x4a00232C 0x4>;
......
...@@ -217,7 +217,7 @@ static struct device_opp *_find_device_opp(struct device *dev) ...@@ -217,7 +217,7 @@ static struct device_opp *_find_device_opp(struct device *dev)
} }
/** /**
* dev_pm_opp_get_voltage() - Gets the voltage corresponding to an available opp * dev_pm_opp_get_voltage() - Gets the voltage corresponding to an opp
* @opp: opp for which voltage has to be returned for * @opp: opp for which voltage has to be returned for
* *
* Return: voltage in micro volt corresponding to the opp, else * Return: voltage in micro volt corresponding to the opp, else
...@@ -239,7 +239,7 @@ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) ...@@ -239,7 +239,7 @@ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
opp_rcu_lockdep_assert(); opp_rcu_lockdep_assert();
tmp_opp = rcu_dereference(opp); tmp_opp = rcu_dereference(opp);
if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) if (IS_ERR_OR_NULL(tmp_opp))
pr_err("%s: Invalid parameters\n", __func__); pr_err("%s: Invalid parameters\n", __func__);
else else
v = tmp_opp->u_volt; v = tmp_opp->u_volt;
......
...@@ -147,6 +147,20 @@ config CLOCK_THERMAL ...@@ -147,6 +147,20 @@ config CLOCK_THERMAL
device that is configured to use this cooling mechanism will be device that is configured to use this cooling mechanism will be
controlled to reduce clock frequency whenever temperature is high. controlled to reduce clock frequency whenever temperature is high.
config DEVFREQ_THERMAL
bool "Generic device cooling support"
depends on PM_DEVFREQ
depends on PM_OPP
help
This implements the generic devfreq cooling mechanism through
frequency reduction for devices using devfreq.
This will throttle the device by limiting the maximum allowed DVFS
frequency corresponding to the cooling level.
In order to use the power extensions of the cooling device,
devfreq should use the simple_ondemand governor.
If you want this support, you should say Y here. If you want this support, you should say Y here.
config THERMAL_EMULATION config THERMAL_EMULATION
...@@ -275,6 +289,7 @@ config X86_PKG_TEMP_THERMAL ...@@ -275,6 +289,7 @@ config X86_PKG_TEMP_THERMAL
tristate "X86 package temperature thermal driver" tristate "X86 package temperature thermal driver"
depends on X86_THERMAL_VECTOR depends on X86_THERMAL_VECTOR
select THERMAL_GOV_USER_SPACE select THERMAL_GOV_USER_SPACE
select THERMAL_WRITABLE_TRIPS
default m default m
help help
Enable this to register CPU digital sensor for package temperature as Enable this to register CPU digital sensor for package temperature as
...@@ -296,6 +311,7 @@ config INTEL_SOC_DTS_THERMAL ...@@ -296,6 +311,7 @@ config INTEL_SOC_DTS_THERMAL
tristate "Intel SoCs DTS thermal driver" tristate "Intel SoCs DTS thermal driver"
depends on X86 depends on X86
select INTEL_SOC_DTS_IOSF_CORE select INTEL_SOC_DTS_IOSF_CORE
select THERMAL_WRITABLE_TRIPS
help help
Enable this to register Intel SoCs (e.g. Bay Trail) platform digital Enable this to register Intel SoCs (e.g. Bay Trail) platform digital
temperature sensor (DTS). These SoCs have two additional DTSs in temperature sensor (DTS). These SoCs have two additional DTSs in
...@@ -322,6 +338,7 @@ config INT340X_THERMAL ...@@ -322,6 +338,7 @@ config INT340X_THERMAL
select ACPI_THERMAL_REL select ACPI_THERMAL_REL
select ACPI_FAN select ACPI_FAN
select INTEL_SOC_DTS_IOSF_CORE select INTEL_SOC_DTS_IOSF_CORE
select THERMAL_WRITABLE_TRIPS
help help
Newer laptops and tablets that use ACPI may have thermal sensors and Newer laptops and tablets that use ACPI may have thermal sensors and
other devices with thermal control capabilities outside the core other devices with thermal control capabilities outside the core
......
...@@ -22,6 +22,9 @@ thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o ...@@ -22,6 +22,9 @@ thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
# clock cooling # clock cooling
thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o
# devfreq cooling
thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
# platform thermal drivers # platform thermal drivers
obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
......
...@@ -224,9 +224,9 @@ static const struct armada_thermal_data armada380_data = { ...@@ -224,9 +224,9 @@ static const struct armada_thermal_data armada380_data = {
.is_valid_shift = 10, .is_valid_shift = 10,
.temp_shift = 0, .temp_shift = 0,
.temp_mask = 0x3ff, .temp_mask = 0x3ff,
.coef_b = 2931108200UL, .coef_b = 1172499100UL,
.coef_m = 5000000UL, .coef_m = 2000096UL,
.coef_div = 10502, .coef_div = 4201,
.inverted = true, .inverted = true,
}; };
......
/*
* devfreq_cooling: Thermal cooling device implementation for devices using
* devfreq
*
* Copyright (C) 2014-2015 ARM Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* TODO:
* - If OPPs are added or removed after devfreq cooling has
* registered, the devfreq cooling won't react to it.
*/
#include <linux/devfreq.h>
#include <linux/devfreq_cooling.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/pm_opp.h>
#include <linux/thermal.h>
#include <trace/events/thermal.h>
static DEFINE_MUTEX(devfreq_lock);
static DEFINE_IDR(devfreq_idr);
/**
* struct devfreq_cooling_device - Devfreq cooling device
* @id: unique integer value corresponding to each
* devfreq_cooling_device registered.
* @cdev: Pointer to associated thermal cooling device.
* @devfreq: Pointer to associated devfreq device.
* @cooling_state: Current cooling state.
* @power_table: Pointer to table with maximum power draw for each
* cooling state. State is the index into the table, and
* the power is in mW.
* @freq_table: Pointer to a table with the frequencies sorted in descending
* order. You can index the table by cooling device state
* @freq_table_size: Size of the @freq_table and @power_table
* @power_ops: Pointer to devfreq_cooling_power, used to generate the
* @power_table.
*/
struct devfreq_cooling_device {
int id;
struct thermal_cooling_device *cdev;
struct devfreq *devfreq;
unsigned long cooling_state;
u32 *power_table;
u32 *freq_table;
size_t freq_table_size;
struct devfreq_cooling_power *power_ops;
};
/**
* get_idr - function to get a unique id.
* @idr: struct idr * handle used to create a id.
* @id: int * value generated by this function.
*
* This function will populate @id with an unique
* id, using the idr API.
*
* Return: 0 on success, an error code on failure.
*/
static int get_idr(struct idr *idr, int *id)
{
int ret;
mutex_lock(&devfreq_lock);
ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL);
mutex_unlock(&devfreq_lock);
if (unlikely(ret < 0))
return ret;
*id = ret;
return 0;
}
/**
* release_idr - function to free the unique id.
* @idr: struct idr * handle used for creating the id.
* @id: int value representing the unique id.
*/
static void release_idr(struct idr *idr, int id)
{
mutex_lock(&devfreq_lock);
idr_remove(idr, id);
mutex_unlock(&devfreq_lock);
}
/**
* partition_enable_opps() - disable all opps above a given state
* @dfc: Pointer to devfreq we are operating on
* @cdev_state: cooling device state we're setting
*
* Go through the OPPs of the device, enabling all OPPs until
* @cdev_state and disabling those frequencies above it.
*/
static int partition_enable_opps(struct devfreq_cooling_device *dfc,
unsigned long cdev_state)
{
int i;
struct device *dev = dfc->devfreq->dev.parent;
for (i = 0; i < dfc->freq_table_size; i++) {
struct dev_pm_opp *opp;
int ret = 0;
unsigned int freq = dfc->freq_table[i];
bool want_enable = i >= cdev_state ? true : false;
rcu_read_lock();
opp = dev_pm_opp_find_freq_exact(dev, freq, !want_enable);
rcu_read_unlock();
if (PTR_ERR(opp) == -ERANGE)
continue;
else if (IS_ERR(opp))
return PTR_ERR(opp);
if (want_enable)
ret = dev_pm_opp_enable(dev, freq);
else
ret = dev_pm_opp_disable(dev, freq);
if (ret)
return ret;
}
return 0;
}
static int devfreq_cooling_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct devfreq_cooling_device *dfc = cdev->devdata;
*state = dfc->freq_table_size - 1;
return 0;
}
static int devfreq_cooling_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct devfreq_cooling_device *dfc = cdev->devdata;
*state = dfc->cooling_state;
return 0;
}
static int devfreq_cooling_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
struct devfreq_cooling_device *dfc = cdev->devdata;
struct devfreq *df = dfc->devfreq;
struct device *dev = df->dev.parent;
int ret;
if (state == dfc->cooling_state)
return 0;
dev_dbg(dev, "Setting cooling state %lu\n", state);
if (state >= dfc->freq_table_size)
return -EINVAL;
ret = partition_enable_opps(dfc, state);
if (ret)
return ret;
dfc->cooling_state = state;
return 0;
}
/**
* freq_get_state() - get the cooling state corresponding to a frequency
* @dfc: Pointer to devfreq cooling device
* @freq: frequency in Hz
*
* Return: the cooling state associated with the @freq, or
* THERMAL_CSTATE_INVALID if it wasn't found.
*/
static unsigned long
freq_get_state(struct devfreq_cooling_device *dfc, unsigned long freq)
{
int i;
for (i = 0; i < dfc->freq_table_size; i++) {
if (dfc->freq_table[i] == freq)
return i;
}
return THERMAL_CSTATE_INVALID;
}
/**
* get_static_power() - calculate the static power
* @dfc: Pointer to devfreq cooling device
* @freq: Frequency in Hz
*
* Calculate the static power in milliwatts using the supplied
* get_static_power(). The current voltage is calculated using the
* OPP library. If no get_static_power() was supplied, assume the
* static power is negligible.
*/
static unsigned long
get_static_power(struct devfreq_cooling_device *dfc, unsigned long freq)
{
struct devfreq *df = dfc->devfreq;
struct device *dev = df->dev.parent;
unsigned long voltage;
struct dev_pm_opp *opp;
if (!dfc->power_ops->get_static_power)
return 0;
rcu_read_lock();
opp = dev_pm_opp_find_freq_exact(dev, freq, true);
if (IS_ERR(opp) && (PTR_ERR(opp) == -ERANGE))
opp = dev_pm_opp_find_freq_exact(dev, freq, false);
voltage = dev_pm_opp_get_voltage(opp) / 1000; /* mV */
rcu_read_unlock();
if (voltage == 0) {
dev_warn_ratelimited(dev,
"Failed to get voltage for frequency %lu: %ld\n",
freq, IS_ERR(opp) ? PTR_ERR(opp) : 0);
return 0;
}
return dfc->power_ops->get_static_power(voltage);
}
/**
* get_dynamic_power - calculate the dynamic power
* @dfc: Pointer to devfreq cooling device
* @freq: Frequency in Hz
* @voltage: Voltage in millivolts
*
* Calculate the dynamic power in milliwatts consumed by the device at
* frequency @freq and voltage @voltage. If the get_dynamic_power()
* was supplied as part of the devfreq_cooling_power struct, then that
* function is used. Otherwise, a simple power model (Pdyn = Coeff *
* Voltage^2 * Frequency) is used.
*/
static unsigned long
get_dynamic_power(struct devfreq_cooling_device *dfc, unsigned long freq,
unsigned long voltage)
{
u64 power;
u32 freq_mhz;
struct devfreq_cooling_power *dfc_power = dfc->power_ops;
if (dfc_power->get_dynamic_power)
return dfc_power->get_dynamic_power(freq, voltage);
freq_mhz = freq / 1000000;
power = (u64)dfc_power->dyn_power_coeff * freq_mhz * voltage * voltage;
do_div(power, 1000000000);
return power;
}
static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cdev,
struct thermal_zone_device *tz,
u32 *power)
{
struct devfreq_cooling_device *dfc = cdev->devdata;
struct devfreq *df = dfc->devfreq;
struct devfreq_dev_status *status = &df->last_status;
unsigned long state;
unsigned long freq = status->current_frequency;
u32 dyn_power, static_power;
/* Get dynamic power for state */
state = freq_get_state(dfc, freq);
if (state == THERMAL_CSTATE_INVALID)
return -EAGAIN;
dyn_power = dfc->power_table[state];
/* Scale dynamic power for utilization */
dyn_power = (dyn_power * status->busy_time) / status->total_time;
/* Get static power */
static_power = get_static_power(dfc, freq);
trace_thermal_power_devfreq_get_power(cdev, status, freq, dyn_power,
static_power);
*power = dyn_power + static_power;
return 0;
}
static int devfreq_cooling_state2power(struct thermal_cooling_device *cdev,
struct thermal_zone_device *tz,
unsigned long state,
u32 *power)
{
struct devfreq_cooling_device *dfc = cdev->devdata;
unsigned long freq;
u32 static_power;
if (state < 0 || state >= dfc->freq_table_size)
return -EINVAL;
freq = dfc->freq_table[state];
static_power = get_static_power(dfc, freq);
*power = dfc->power_table[state] + static_power;
return 0;
}
static int devfreq_cooling_power2state(struct thermal_cooling_device *cdev,
struct thermal_zone_device *tz,
u32 power, unsigned long *state)
{
struct devfreq_cooling_device *dfc = cdev->devdata;
struct devfreq *df = dfc->devfreq;
struct devfreq_dev_status *status = &df->last_status;
unsigned long freq = status->current_frequency;
unsigned long busy_time;
s32 dyn_power;
u32 static_power;
int i;
static_power = get_static_power(dfc, freq);
dyn_power = power - static_power;
dyn_power = dyn_power > 0 ? dyn_power : 0;
/* Scale dynamic power for utilization */
busy_time = status->busy_time ?: 1;
dyn_power = (dyn_power * status->total_time) / busy_time;
/*
* Find the first cooling state that is within the power
* budget for dynamic power.
*/
for (i = 0; i < dfc->freq_table_size - 1; i++)
if (dyn_power >= dfc->power_table[i])
break;
*state = i;
trace_thermal_power_devfreq_limit(cdev, freq, *state, power);
return 0;
}
static struct thermal_cooling_device_ops devfreq_cooling_ops = {
.get_max_state = devfreq_cooling_get_max_state,
.get_cur_state = devfreq_cooling_get_cur_state,
.set_cur_state = devfreq_cooling_set_cur_state,
};
/**
* devfreq_cooling_gen_tables() - Generate power and freq tables.
* @dfc: Pointer to devfreq cooling device.
*
* Generate power and frequency tables: the power table hold the
* device's maximum power usage at each cooling state (OPP). The
* static and dynamic power using the appropriate voltage and
* frequency for the state, is acquired from the struct
* devfreq_cooling_power, and summed to make the maximum power draw.
*
* The frequency table holds the frequencies in descending order.
* That way its indexed by cooling device state.
*
* The tables are malloced, and pointers put in dfc. They must be
* freed when unregistering the devfreq cooling device.
*
* Return: 0 on success, negative error code on failure.
*/
static int devfreq_cooling_gen_tables(struct devfreq_cooling_device *dfc)
{
struct devfreq *df = dfc->devfreq;
struct device *dev = df->dev.parent;
int ret, num_opps;
unsigned long freq;
u32 *power_table = NULL;
u32 *freq_table;
int i;
num_opps = dev_pm_opp_get_opp_count(dev);
if (dfc->power_ops) {
power_table = kcalloc(num_opps, sizeof(*power_table),
GFP_KERNEL);
if (!power_table)
return -ENOMEM;
}
freq_table = kcalloc(num_opps, sizeof(*freq_table),
GFP_KERNEL);
if (!freq_table) {
ret = -ENOMEM;
goto free_power_table;
}
for (i = 0, freq = ULONG_MAX; i < num_opps; i++, freq--) {
unsigned long power_dyn, voltage;
struct dev_pm_opp *opp;
rcu_read_lock();
opp = dev_pm_opp_find_freq_floor(dev, &freq);
if (IS_ERR(opp)) {
rcu_read_unlock();
ret = PTR_ERR(opp);
goto free_tables;
}
voltage = dev_pm_opp_get_voltage(opp) / 1000; /* mV */
rcu_read_unlock();
if (dfc->power_ops) {
power_dyn = get_dynamic_power(dfc, freq, voltage);
dev_dbg(dev, "Dynamic power table: %lu MHz @ %lu mV: %lu = %lu mW\n",
freq / 1000000, voltage, power_dyn, power_dyn);
power_table[i] = power_dyn;
}
freq_table[i] = freq;
}
if (dfc->power_ops)
dfc->power_table = power_table;
dfc->freq_table = freq_table;
dfc->freq_table_size = num_opps;
return 0;
free_tables:
kfree(freq_table);
free_power_table:
kfree(power_table);
return ret;
}
/**
* of_devfreq_cooling_register_power() - Register devfreq cooling device,
* with OF and power information.
* @np: Pointer to OF device_node.
* @df: Pointer to devfreq device.
* @dfc_power: Pointer to devfreq_cooling_power.
*
* Register a devfreq cooling device. The available OPPs must be
* registered on the device.
*
* If @dfc_power is provided, the cooling device is registered with the
* power extensions. For the power extensions to work correctly,
* devfreq should use the simple_ondemand governor, other governors
* are not currently supported.
*/
struct thermal_cooling_device *
of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
struct devfreq_cooling_power *dfc_power)
{
struct thermal_cooling_device *cdev;
struct devfreq_cooling_device *dfc;
char dev_name[THERMAL_NAME_LENGTH];
int err;
dfc = kzalloc(sizeof(*dfc), GFP_KERNEL);
if (!dfc)
return ERR_PTR(-ENOMEM);
dfc->devfreq = df;
if (dfc_power) {
dfc->power_ops = dfc_power;
devfreq_cooling_ops.get_requested_power =
devfreq_cooling_get_requested_power;
devfreq_cooling_ops.state2power = devfreq_cooling_state2power;
devfreq_cooling_ops.power2state = devfreq_cooling_power2state;
}
err = devfreq_cooling_gen_tables(dfc);
if (err)
goto free_dfc;
err = get_idr(&devfreq_idr, &dfc->id);
if (err)
goto free_tables;
snprintf(dev_name, sizeof(dev_name), "thermal-devfreq-%d", dfc->id);
cdev = thermal_of_cooling_device_register(np, dev_name, dfc,
&devfreq_cooling_ops);
if (IS_ERR(cdev)) {
err = PTR_ERR(cdev);
dev_err(df->dev.parent,
"Failed to register devfreq cooling device (%d)\n",
err);
goto release_idr;
}
dfc->cdev = cdev;
return cdev;
release_idr:
release_idr(&devfreq_idr, dfc->id);
free_tables:
kfree(dfc->power_table);
kfree(dfc->freq_table);
free_dfc:
kfree(dfc);
return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(of_devfreq_cooling_register_power);
/**
* of_devfreq_cooling_register() - Register devfreq cooling device,
* with OF information.
* @np: Pointer to OF device_node.
* @df: Pointer to devfreq device.
*/
struct thermal_cooling_device *
of_devfreq_cooling_register(struct device_node *np, struct devfreq *df)
{
return of_devfreq_cooling_register_power(np, df, NULL);
}
EXPORT_SYMBOL_GPL(of_devfreq_cooling_register);
/**
* devfreq_cooling_register() - Register devfreq cooling device.
* @df: Pointer to devfreq device.
*/
struct thermal_cooling_device *devfreq_cooling_register(struct devfreq *df)
{
return of_devfreq_cooling_register(NULL, df);
}
EXPORT_SYMBOL_GPL(devfreq_cooling_register);
/**
* devfreq_cooling_unregister() - Unregister devfreq cooling device.
* @dfc: Pointer to devfreq cooling device to unregister.
*/
void devfreq_cooling_unregister(struct thermal_cooling_device *cdev)
{
struct devfreq_cooling_device *dfc;
if (!cdev)
return;
dfc = cdev->devdata;
thermal_cooling_device_unregister(dfc->cdev);
release_idr(&devfreq_idr, dfc->id);
kfree(dfc->power_table);
kfree(dfc->freq_table);
kfree(dfc);
}
EXPORT_SYMBOL_GPL(devfreq_cooling_unregister);
...@@ -288,7 +288,7 @@ static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip, ...@@ -288,7 +288,7 @@ static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip,
if (trip == IMX_TRIP_CRITICAL) if (trip == IMX_TRIP_CRITICAL)
return -EPERM; return -EPERM;
if (temp > IMX_TEMP_PASSIVE) if (temp < 0 || temp > IMX_TEMP_PASSIVE)
return -EINVAL; return -EINVAL;
data->temp_passive = temp; data->temp_passive = temp;
...@@ -487,14 +487,6 @@ static int imx_thermal_probe(struct platform_device *pdev) ...@@ -487,14 +487,6 @@ static int imx_thermal_probe(struct platform_device *pdev)
if (data->irq < 0) if (data->irq < 0)
return data->irq; return data->irq;
ret = devm_request_threaded_irq(&pdev->dev, data->irq,
imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread,
0, "imx_thermal", data);
if (ret < 0) {
dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret);
return ret;
}
platform_set_drvdata(pdev, data); platform_set_drvdata(pdev, data);
ret = imx_get_sensor_data(pdev); ret = imx_get_sensor_data(pdev);
...@@ -571,6 +563,17 @@ static int imx_thermal_probe(struct platform_device *pdev) ...@@ -571,6 +563,17 @@ static int imx_thermal_probe(struct platform_device *pdev)
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP); regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
ret = devm_request_threaded_irq(&pdev->dev, data->irq,
imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread,
0, "imx_thermal", data);
if (ret < 0) {
dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret);
clk_disable_unprepare(data->thermal_clk);
thermal_zone_device_unregister(data->tz);
cpufreq_cooling_unregister(data->cdev);
return ret;
}
data->irq_enabled = true; data->irq_enabled = true;
data->mode = THERMAL_DEVICE_ENABLED; data->mode = THERMAL_DEVICE_ENABLED;
......
...@@ -106,16 +106,14 @@ struct rockchip_thermal_data { ...@@ -106,16 +106,14 @@ struct rockchip_thermal_data {
#define TSADCV2_AUTO_PERIOD_HT 0x6c #define TSADCV2_AUTO_PERIOD_HT 0x6c
#define TSADCV2_AUTO_EN BIT(0) #define TSADCV2_AUTO_EN BIT(0)
#define TSADCV2_AUTO_DISABLE ~BIT(0)
#define TSADCV2_AUTO_SRC_EN(chn) BIT(4 + (chn)) #define TSADCV2_AUTO_SRC_EN(chn) BIT(4 + (chn))
#define TSADCV2_AUTO_TSHUT_POLARITY_HIGH BIT(8) #define TSADCV2_AUTO_TSHUT_POLARITY_HIGH BIT(8)
#define TSADCV2_AUTO_TSHUT_POLARITY_LOW ~BIT(8)
#define TSADCV2_INT_SRC_EN(chn) BIT(chn) #define TSADCV2_INT_SRC_EN(chn) BIT(chn)
#define TSADCV2_SHUT_2GPIO_SRC_EN(chn) BIT(4 + (chn)) #define TSADCV2_SHUT_2GPIO_SRC_EN(chn) BIT(4 + (chn))
#define TSADCV2_SHUT_2CRU_SRC_EN(chn) BIT(8 + (chn)) #define TSADCV2_SHUT_2CRU_SRC_EN(chn) BIT(8 + (chn))
#define TSADCV2_INT_PD_CLEAR ~BIT(8) #define TSADCV2_INT_PD_CLEAR_MASK ~BIT(8)
#define TSADCV2_DATA_MASK 0xfff #define TSADCV2_DATA_MASK 0xfff
#define TSADCV2_HIGHT_INT_DEBOUNCE_COUNT 4 #define TSADCV2_HIGHT_INT_DEBOUNCE_COUNT 4
...@@ -124,7 +122,7 @@ struct rockchip_thermal_data { ...@@ -124,7 +122,7 @@ struct rockchip_thermal_data {
#define TSADCV2_AUTO_PERIOD_HT_TIME 50 /* msec */ #define TSADCV2_AUTO_PERIOD_HT_TIME 50 /* msec */
struct tsadc_table { struct tsadc_table {
unsigned long code; u32 code;
long temp; long temp;
}; };
...@@ -164,7 +162,6 @@ static const struct tsadc_table v2_code_table[] = { ...@@ -164,7 +162,6 @@ static const struct tsadc_table v2_code_table[] = {
{3452, 115000}, {3452, 115000},
{3437, 120000}, {3437, 120000},
{3421, 125000}, {3421, 125000},
{0, 125000},
}; };
static u32 rk_tsadcv2_temp_to_code(long temp) static u32 rk_tsadcv2_temp_to_code(long temp)
...@@ -191,19 +188,21 @@ static u32 rk_tsadcv2_temp_to_code(long temp) ...@@ -191,19 +188,21 @@ static u32 rk_tsadcv2_temp_to_code(long temp)
return 0; return 0;
} }
static int rk_tsadcv2_code_to_temp(u32 code) static int rk_tsadcv2_code_to_temp(u32 code, int *temp)
{ {
unsigned int low = 0; unsigned int low = 1;
unsigned int high = ARRAY_SIZE(v2_code_table) - 1; unsigned int high = ARRAY_SIZE(v2_code_table) - 1;
unsigned int mid = (low + high) / 2; unsigned int mid = (low + high) / 2;
unsigned int num; unsigned int num;
unsigned long denom; unsigned long denom;
/* Invalid code, return -EAGAIN */ BUILD_BUG_ON(ARRAY_SIZE(v2_code_table) < 2);
if (code > TSADCV2_DATA_MASK)
return -EAGAIN;
while (low <= high && mid) { code &= TSADCV2_DATA_MASK;
if (code < v2_code_table[high].code)
return -EAGAIN; /* Incorrect reading */
while (low <= high) {
if (code >= v2_code_table[mid].code && if (code >= v2_code_table[mid].code &&
code < v2_code_table[mid - 1].code) code < v2_code_table[mid - 1].code)
break; break;
...@@ -223,7 +222,9 @@ static int rk_tsadcv2_code_to_temp(u32 code) ...@@ -223,7 +222,9 @@ static int rk_tsadcv2_code_to_temp(u32 code)
num = v2_code_table[mid].temp - v2_code_table[mid - 1].temp; num = v2_code_table[mid].temp - v2_code_table[mid - 1].temp;
num *= v2_code_table[mid - 1].code - code; num *= v2_code_table[mid - 1].code - code;
denom = v2_code_table[mid - 1].code - v2_code_table[mid].code; denom = v2_code_table[mid - 1].code - v2_code_table[mid].code;
return v2_code_table[mid - 1].temp + (num / denom); *temp = v2_code_table[mid - 1].temp + (num / denom);
return 0;
} }
/** /**
...@@ -241,10 +242,10 @@ static void rk_tsadcv2_initialize(void __iomem *regs, ...@@ -241,10 +242,10 @@ static void rk_tsadcv2_initialize(void __iomem *regs,
enum tshut_polarity tshut_polarity) enum tshut_polarity tshut_polarity)
{ {
if (tshut_polarity == TSHUT_HIGH_ACTIVE) if (tshut_polarity == TSHUT_HIGH_ACTIVE)
writel_relaxed(0 | (TSADCV2_AUTO_TSHUT_POLARITY_HIGH), writel_relaxed(0U | TSADCV2_AUTO_TSHUT_POLARITY_HIGH,
regs + TSADCV2_AUTO_CON); regs + TSADCV2_AUTO_CON);
else else
writel_relaxed(0 | (TSADCV2_AUTO_TSHUT_POLARITY_LOW), writel_relaxed(0U & ~TSADCV2_AUTO_TSHUT_POLARITY_HIGH,
regs + TSADCV2_AUTO_CON); regs + TSADCV2_AUTO_CON);
writel_relaxed(TSADCV2_AUTO_PERIOD_TIME, regs + TSADCV2_AUTO_PERIOD); writel_relaxed(TSADCV2_AUTO_PERIOD_TIME, regs + TSADCV2_AUTO_PERIOD);
...@@ -261,7 +262,7 @@ static void rk_tsadcv2_irq_ack(void __iomem *regs) ...@@ -261,7 +262,7 @@ static void rk_tsadcv2_irq_ack(void __iomem *regs)
u32 val; u32 val;
val = readl_relaxed(regs + TSADCV2_INT_PD); val = readl_relaxed(regs + TSADCV2_INT_PD);
writel_relaxed(val & TSADCV2_INT_PD_CLEAR, regs + TSADCV2_INT_PD); writel_relaxed(val & TSADCV2_INT_PD_CLEAR_MASK, regs + TSADCV2_INT_PD);
} }
static void rk_tsadcv2_control(void __iomem *regs, bool enable) static void rk_tsadcv2_control(void __iomem *regs, bool enable)
...@@ -281,14 +282,9 @@ static int rk_tsadcv2_get_temp(int chn, void __iomem *regs, int *temp) ...@@ -281,14 +282,9 @@ static int rk_tsadcv2_get_temp(int chn, void __iomem *regs, int *temp)
{ {
u32 val; u32 val;
/* the A/D value of the channel last conversion need some time */
val = readl_relaxed(regs + TSADCV2_DATA(chn)); val = readl_relaxed(regs + TSADCV2_DATA(chn));
if (val == 0)
return -EAGAIN;
*temp = rk_tsadcv2_code_to_temp(val); return rk_tsadcv2_code_to_temp(val, temp);
return 0;
} }
static void rk_tsadcv2_tshut_temp(int chn, void __iomem *regs, long temp) static void rk_tsadcv2_tshut_temp(int chn, void __iomem *regs, long temp)
...@@ -642,6 +638,8 @@ static int __maybe_unused rockchip_thermal_suspend(struct device *dev) ...@@ -642,6 +638,8 @@ static int __maybe_unused rockchip_thermal_suspend(struct device *dev)
clk_disable(thermal->pclk); clk_disable(thermal->pclk);
clk_disable(thermal->clk); clk_disable(thermal->clk);
pinctrl_pm_select_sleep_state(dev);
return 0; return 0;
} }
...@@ -678,6 +676,8 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev) ...@@ -678,6 +676,8 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev)
for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++) for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++)
rockchip_thermal_toggle_sensor(&thermal->sensors[i], true); rockchip_thermal_toggle_sensor(&thermal->sensors[i], true);
pinctrl_pm_select_default_state(dev);
return 0; return 0;
} }
......
...@@ -548,7 +548,7 @@ static int exynos5433_tmu_initialize(struct platform_device *pdev) ...@@ -548,7 +548,7 @@ static int exynos5433_tmu_initialize(struct platform_device *pdev)
default: default:
pdata->cal_type = TYPE_ONE_POINT_TRIMMING; pdata->cal_type = TYPE_ONE_POINT_TRIMMING;
break; break;
}; }
dev_info(&pdev->dev, "Calibration type is %d-point calibration\n", dev_info(&pdev->dev, "Calibration type is %d-point calibration\n",
cal_type ? 2 : 1); cal_type ? 2 : 1);
...@@ -608,7 +608,7 @@ static int exynos5440_tmu_initialize(struct platform_device *pdev) ...@@ -608,7 +608,7 @@ static int exynos5440_tmu_initialize(struct platform_device *pdev)
{ {
struct exynos_tmu_data *data = platform_get_drvdata(pdev); struct exynos_tmu_data *data = platform_get_drvdata(pdev);
unsigned int trim_info = 0, con, rising_threshold; unsigned int trim_info = 0, con, rising_threshold;
int ret = 0, threshold_code; int threshold_code;
int crit_temp = 0; int crit_temp = 0;
/* /*
...@@ -651,7 +651,8 @@ static int exynos5440_tmu_initialize(struct platform_device *pdev) ...@@ -651,7 +651,8 @@ static int exynos5440_tmu_initialize(struct platform_device *pdev)
/* Clear the PMIN in the common TMU register */ /* Clear the PMIN in the common TMU register */
if (!data->id) if (!data->id)
writel(0, data->base_second + EXYNOS5440_TMU_PMIN); writel(0, data->base_second + EXYNOS5440_TMU_PMIN);
return ret;
return 0;
} }
static int exynos7_tmu_initialize(struct platform_device *pdev) static int exynos7_tmu_initialize(struct platform_device *pdev)
...@@ -1168,27 +1169,10 @@ static int exynos_map_dt_data(struct platform_device *pdev) ...@@ -1168,27 +1169,10 @@ static int exynos_map_dt_data(struct platform_device *pdev)
struct exynos_tmu_data *data = platform_get_drvdata(pdev); struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct exynos_tmu_platform_data *pdata; struct exynos_tmu_platform_data *pdata;
struct resource res; struct resource res;
int ret;
if (!data || !pdev->dev.of_node) if (!data || !pdev->dev.of_node)
return -ENODEV; return -ENODEV;
/*
* Try enabling the regulator if found
* TODO: Add regulator as an SOC feature, so that regulator enable
* is a compulsory call.
*/
data->regulator = devm_regulator_get(&pdev->dev, "vtmu");
if (!IS_ERR(data->regulator)) {
ret = regulator_enable(data->regulator);
if (ret) {
dev_err(&pdev->dev, "failed to enable vtmu\n");
return ret;
}
} else {
dev_info(&pdev->dev, "Regulator node (vtmu) not found\n");
}
data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl"); data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl");
if (data->id < 0) if (data->id < 0)
data->id = 0; data->id = 0;
...@@ -1306,12 +1290,22 @@ static int exynos_tmu_probe(struct platform_device *pdev) ...@@ -1306,12 +1290,22 @@ static int exynos_tmu_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data); platform_set_drvdata(pdev, data);
mutex_init(&data->lock); mutex_init(&data->lock);
data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data, /*
&exynos_sensor_ops); * Try enabling the regulator if found
if (IS_ERR(data->tzd)) { * TODO: Add regulator as an SOC feature, so that regulator enable
pr_err("thermal: tz: %p ERROR\n", data->tzd); * is a compulsory call.
return PTR_ERR(data->tzd); */
data->regulator = devm_regulator_get(&pdev->dev, "vtmu");
if (!IS_ERR(data->regulator)) {
ret = regulator_enable(data->regulator);
if (ret) {
dev_err(&pdev->dev, "failed to enable vtmu\n");
return ret;
}
} else {
dev_info(&pdev->dev, "Regulator node (vtmu) not found\n");
} }
ret = exynos_map_dt_data(pdev); ret = exynos_map_dt_data(pdev);
if (ret) if (ret)
goto err_sensor; goto err_sensor;
...@@ -1363,23 +1357,38 @@ static int exynos_tmu_probe(struct platform_device *pdev) ...@@ -1363,23 +1357,38 @@ static int exynos_tmu_probe(struct platform_device *pdev)
break; break;
default: default:
break; break;
}; }
/*
* data->tzd must be registered before calling exynos_tmu_initialize(),
* requesting irq and calling exynos_tmu_control().
*/
data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data,
&exynos_sensor_ops);
if (IS_ERR(data->tzd)) {
ret = PTR_ERR(data->tzd);
dev_err(&pdev->dev, "Failed to register sensor: %d\n", ret);
goto err_sclk;
}
ret = exynos_tmu_initialize(pdev); ret = exynos_tmu_initialize(pdev);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Failed to initialize TMU\n"); dev_err(&pdev->dev, "Failed to initialize TMU\n");
goto err_sclk; goto err_thermal;
} }
ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq,
IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data); IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
goto err_sclk; goto err_thermal;
} }
exynos_tmu_control(pdev, true); exynos_tmu_control(pdev, true);
return 0; return 0;
err_thermal:
thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd);
err_sclk: err_sclk:
clk_disable_unprepare(data->sclk); clk_disable_unprepare(data->sclk);
err_clk: err_clk:
...@@ -1388,9 +1397,8 @@ static int exynos_tmu_probe(struct platform_device *pdev) ...@@ -1388,9 +1397,8 @@ static int exynos_tmu_probe(struct platform_device *pdev)
if (!IS_ERR(data->clk_sec)) if (!IS_ERR(data->clk_sec))
clk_unprepare(data->clk_sec); clk_unprepare(data->clk_sec);
err_sensor: err_sensor:
if (!IS_ERR_OR_NULL(data->regulator)) if (!IS_ERR(data->regulator))
regulator_disable(data->regulator); regulator_disable(data->regulator);
thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd);
return ret; return ret;
} }
......
...@@ -19,6 +19,21 @@ config TI_THERMAL ...@@ -19,6 +19,21 @@ config TI_THERMAL
This includes trip points definitions, extrapolation rules and This includes trip points definitions, extrapolation rules and
CPU cooling device bindings. CPU cooling device bindings.
config OMAP3_THERMAL
bool "Texas Instruments OMAP3 thermal support"
depends on TI_SOC_THERMAL
depends on ARCH_OMAP3 || COMPILE_TEST
help
If you say yes here you get thermal support for the Texas Instruments
OMAP3 SoC family. The current chips supported are:
- OMAP3430
OMAP3 chips normally don't need thermal management, and sensors in
this generation are not accurate, nor they are very close to
the important hotspots.
Say 'N' here.
config OMAP4_THERMAL config OMAP4_THERMAL
bool "Texas Instruments OMAP4 thermal support" bool "Texas Instruments OMAP4 thermal support"
depends on TI_SOC_THERMAL depends on TI_SOC_THERMAL
......
...@@ -2,5 +2,6 @@ obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal.o ...@@ -2,5 +2,6 @@ obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal.o
ti-soc-thermal-y := ti-bandgap.o ti-soc-thermal-y := ti-bandgap.o
ti-soc-thermal-$(CONFIG_TI_THERMAL) += ti-thermal-common.o ti-soc-thermal-$(CONFIG_TI_THERMAL) += ti-thermal-common.o
ti-soc-thermal-$(CONFIG_DRA752_THERMAL) += dra752-thermal-data.o ti-soc-thermal-$(CONFIG_DRA752_THERMAL) += dra752-thermal-data.o
ti-soc-thermal-$(CONFIG_OMAP3_THERMAL) += omap3-thermal-data.o
ti-soc-thermal-$(CONFIG_OMAP4_THERMAL) += omap4-thermal-data.o ti-soc-thermal-$(CONFIG_OMAP4_THERMAL) += omap4-thermal-data.o
ti-soc-thermal-$(CONFIG_OMAP5_THERMAL) += omap5-thermal-data.o ti-soc-thermal-$(CONFIG_OMAP5_THERMAL) += omap5-thermal-data.o
/*
* OMAP3 thermal driver.
*
* Copyright (C) 2011-2012 Texas Instruments Inc.
* Copyright (C) 2014 Pavel Machek <pavel@ucw.cz>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Note
* http://www.ti.com/lit/er/sprz278f/sprz278f.pdf "Advisory
* 3.1.1.186 MMC OCP Clock Not Gated When Thermal Sensor Is Used"
*
* Also TI says:
* Just be careful when you try to make thermal policy like decisions
* based on this sensor. Placement of the sensor w.r.t the actual logic
* generating heat has to be a factor as well. If you are just looking
* for an approximation temperature (thermometerish kind), you might be
* ok with this. I am not sure we'd find any TI data around this.. just a
* heads up.
*/
#include "ti-thermal.h"
#include "ti-bandgap.h"
/*
* OMAP34XX has one instance of thermal sensor for MPU
* need to describe the individual bit fields
*/
static struct temp_sensor_registers
omap34xx_mpu_temp_sensor_registers = {
.temp_sensor_ctrl = 0,
.bgap_soc_mask = BIT(8),
.bgap_eocz_mask = BIT(7),
.bgap_dtemp_mask = 0x7f,
.bgap_mode_ctrl = 0,
.mode_ctrl_mask = BIT(9),
};
/* Thresholds and limits for OMAP34XX MPU temperature sensor */
static struct temp_sensor_data omap34xx_mpu_temp_sensor_data = {
.min_freq = 32768,
.max_freq = 32768,
.max_temp = 125000,
.min_temp = -40000,
.hyst_val = 5000,
};
/*
* Temperature values in milli degree celsius
*/
static const int
omap34xx_adc_to_temp[128] = {
-40000, -40000, -40000, -40000, -40000, -39000, -38000, -36000,
-34000, -32000, -31000, -29000, -28000, -26000, -25000, -24000,
-22000, -21000, -19000, -18000, -17000, -15000, -14000, -12000,
-11000, -9000, -8000, -7000, -5000, -4000, -2000, -1000, 0000,
1000, 3000, 4000, 5000, 7000, 8000, 10000, 11000, 13000, 14000,
15000, 17000, 18000, 20000, 21000, 22000, 24000, 25000, 27000,
28000, 30000, 31000, 32000, 34000, 35000, 37000, 38000, 39000,
41000, 42000, 44000, 45000, 47000, 48000, 49000, 51000, 52000,
53000, 55000, 56000, 58000, 59000, 60000, 62000, 63000, 65000,
66000, 67000, 69000, 70000, 72000, 73000, 74000, 76000, 77000,
79000, 80000, 81000, 83000, 84000, 85000, 87000, 88000, 89000,
91000, 92000, 94000, 95000, 96000, 98000, 99000, 100000,
102000, 103000, 105000, 106000, 107000, 109000, 110000, 111000,
113000, 114000, 116000, 117000, 118000, 120000, 121000, 122000,
124000, 124000, 125000, 125000, 125000, 125000, 125000
};
/* OMAP34XX data */
const struct ti_bandgap_data omap34xx_data = {
.features = TI_BANDGAP_FEATURE_CLK_CTRL | TI_BANDGAP_FEATURE_UNRELIABLE,
.fclock_name = "ts_fck",
.div_ck_name = "ts_fck",
.conv_table = omap34xx_adc_to_temp,
.adc_start_val = 0,
.adc_end_val = 127,
.expose_sensor = ti_thermal_expose_sensor,
.remove_sensor = ti_thermal_remove_sensor,
.sensors = {
{
.registers = &omap34xx_mpu_temp_sensor_registers,
.ts_data = &omap34xx_mpu_temp_sensor_data,
.domain = "cpu",
.slope = 0,
.constant = 20000,
.slope_pcb = 0,
.constant_pcb = 20000,
.register_cooling = NULL,
.unregister_cooling = NULL,
},
},
.sensor_count = 1,
};
/*
* OMAP36XX has one instance of thermal sensor for MPU
* need to describe the individual bit fields
*/
static struct temp_sensor_registers
omap36xx_mpu_temp_sensor_registers = {
.temp_sensor_ctrl = 0,
.bgap_soc_mask = BIT(9),
.bgap_eocz_mask = BIT(8),
.bgap_dtemp_mask = 0xFF,
.bgap_mode_ctrl = 0,
.mode_ctrl_mask = BIT(10),
};
/* Thresholds and limits for OMAP36XX MPU temperature sensor */
static struct temp_sensor_data omap36xx_mpu_temp_sensor_data = {
.min_freq = 32768,
.max_freq = 32768,
.max_temp = 125000,
.min_temp = -40000,
.hyst_val = 5000,
};
/*
* Temperature values in milli degree celsius
*/
static const int
omap36xx_adc_to_temp[128] = {
-40000, -40000, -40000, -40000, -40000, -40000, -40000, -40000,
-40000, -40000, -40000, -40000, -40000, -38000, -35000, -34000,
-32000, -30000, -28000, -26000, -24000, -22000, -20000, -18500,
-17000, -15000, -13500, -12000, -10000, -8000, -6500, -5000, -3500,
-1500, 0, 2000, 3500, 5000, 6500, 8500, 10000, 12000, 13500,
15000, 17000, 19000, 21000, 23000, 25000, 27000, 28500, 30000,
32000, 33500, 35000, 37000, 38500, 40000, 42000, 43500, 45000,
47000, 48500, 50000, 52000, 53500, 55000, 57000, 58500, 60000,
62000, 64000, 66000, 68000, 70000, 71500, 73500, 75000, 77000,
78500, 80000, 82000, 83500, 85000, 87000, 88500, 90000, 92000,
93500, 95000, 97000, 98500, 100000, 102000, 103500, 105000, 107000,
109000, 111000, 113000, 115000, 117000, 118500, 120000, 122000,
123500, 125000, 125000, 125000, 125000, 125000, 125000, 125000,
125000, 125000, 125000, 125000, 125000, 125000, 125000, 125000,
125000, 125000, 125000, 125000, 125000, 125000, 125000
};
/* OMAP36XX data */
const struct ti_bandgap_data omap36xx_data = {
.features = TI_BANDGAP_FEATURE_CLK_CTRL | TI_BANDGAP_FEATURE_UNRELIABLE,
.fclock_name = "ts_fck",
.div_ck_name = "ts_fck",
.conv_table = omap36xx_adc_to_temp,
.adc_start_val = 0,
.adc_end_val = 127,
.expose_sensor = ti_thermal_expose_sensor,
.remove_sensor = ti_thermal_remove_sensor,
.sensors = {
{
.registers = &omap36xx_mpu_temp_sensor_registers,
.ts_data = &omap36xx_mpu_temp_sensor_data,
.domain = "cpu",
.slope = 0,
.constant = 20000,
.slope_pcb = 0,
.constant_pcb = 20000,
.register_cooling = NULL,
.unregister_cooling = NULL,
},
},
.sensor_count = 1,
};
...@@ -1274,6 +1274,10 @@ int ti_bandgap_probe(struct platform_device *pdev) ...@@ -1274,6 +1274,10 @@ int ti_bandgap_probe(struct platform_device *pdev)
} }
bgp->dev = &pdev->dev; bgp->dev = &pdev->dev;
if (TI_BANDGAP_HAS(bgp, UNRELIABLE))
dev_warn(&pdev->dev,
"This OMAP thermal sensor is unreliable. You've been warned\n");
if (TI_BANDGAP_HAS(bgp, TSHUT)) { if (TI_BANDGAP_HAS(bgp, TSHUT)) {
ret = ti_bandgap_tshut_init(bgp, pdev); ret = ti_bandgap_tshut_init(bgp, pdev);
if (ret) { if (ret) {
...@@ -1579,6 +1583,16 @@ static SIMPLE_DEV_PM_OPS(ti_bandgap_dev_pm_ops, ti_bandgap_suspend, ...@@ -1579,6 +1583,16 @@ static SIMPLE_DEV_PM_OPS(ti_bandgap_dev_pm_ops, ti_bandgap_suspend,
#endif #endif
static const struct of_device_id of_ti_bandgap_match[] = { static const struct of_device_id of_ti_bandgap_match[] = {
#ifdef CONFIG_OMAP3_THERMAL
{
.compatible = "ti,omap34xx-bandgap",
.data = (void *)&omap34xx_data,
},
{
.compatible = "ti,omap36xx-bandgap",
.data = (void *)&omap36xx_data,
},
#endif
#ifdef CONFIG_OMAP4_THERMAL #ifdef CONFIG_OMAP4_THERMAL
{ {
.compatible = "ti,omap4430-bandgap", .compatible = "ti,omap4430-bandgap",
......
...@@ -322,6 +322,8 @@ struct ti_temp_sensor { ...@@ -322,6 +322,8 @@ struct ti_temp_sensor {
* has Errata 814 * has Errata 814
* TI_BANDGAP_FEATURE_ERRATA_813 - used to workaorund when the bandgap device * TI_BANDGAP_FEATURE_ERRATA_813 - used to workaorund when the bandgap device
* has Errata 813 * has Errata 813
* TI_BANDGAP_FEATURE_UNRELIABLE - used when the sensor readings are too
* inaccurate.
* TI_BANDGAP_HAS(b, f) - macro to check if a bandgap device is capable of a * TI_BANDGAP_HAS(b, f) - macro to check if a bandgap device is capable of a
* specific feature (above) or not. Return non-zero, if yes. * specific feature (above) or not. Return non-zero, if yes.
*/ */
...@@ -337,6 +339,7 @@ struct ti_temp_sensor { ...@@ -337,6 +339,7 @@ struct ti_temp_sensor {
#define TI_BANDGAP_FEATURE_HISTORY_BUFFER BIT(9) #define TI_BANDGAP_FEATURE_HISTORY_BUFFER BIT(9)
#define TI_BANDGAP_FEATURE_ERRATA_814 BIT(10) #define TI_BANDGAP_FEATURE_ERRATA_814 BIT(10)
#define TI_BANDGAP_FEATURE_ERRATA_813 BIT(11) #define TI_BANDGAP_FEATURE_ERRATA_813 BIT(11)
#define TI_BANDGAP_FEATURE_UNRELIABLE BIT(12)
#define TI_BANDGAP_HAS(b, f) \ #define TI_BANDGAP_HAS(b, f) \
((b)->conf->features & TI_BANDGAP_FEATURE_ ## f) ((b)->conf->features & TI_BANDGAP_FEATURE_ ## f)
...@@ -390,6 +393,14 @@ int ti_bandgap_set_sensor_data(struct ti_bandgap *bgp, int id, void *data); ...@@ -390,6 +393,14 @@ int ti_bandgap_set_sensor_data(struct ti_bandgap *bgp, int id, void *data);
void *ti_bandgap_get_sensor_data(struct ti_bandgap *bgp, int id); void *ti_bandgap_get_sensor_data(struct ti_bandgap *bgp, int id);
int ti_bandgap_get_trend(struct ti_bandgap *bgp, int id, int *trend); int ti_bandgap_get_trend(struct ti_bandgap *bgp, int id, int *trend);
#ifdef CONFIG_OMAP3_THERMAL
extern const struct ti_bandgap_data omap34xx_data;
extern const struct ti_bandgap_data omap36xx_data;
#else
#define omap34xx_data NULL
#define omap36xx_data NULL
#endif
#ifdef CONFIG_OMAP4_THERMAL #ifdef CONFIG_OMAP4_THERMAL
extern const struct ti_bandgap_data omap4430_data; extern const struct ti_bandgap_data omap4430_data;
extern const struct ti_bandgap_data omap4460_data; extern const struct ti_bandgap_data omap4460_data;
......
/*
* devfreq_cooling: Thermal cooling device implementation for devices using
* devfreq
*
* Copyright (C) 2014-2015 ARM Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __DEVFREQ_COOLING_H__
#define __DEVFREQ_COOLING_H__
#include <linux/devfreq.h>
#include <linux/thermal.h>
#ifdef CONFIG_DEVFREQ_THERMAL
/**
* struct devfreq_cooling_power - Devfreq cooling power ops
* @get_static_power: Take voltage, in mV, and return the static power
* in mW. If NULL, the static power is assumed
* to be 0.
* @get_dynamic_power: Take voltage, in mV, and frequency, in HZ, and
* return the dynamic power draw in mW. If NULL,
* a simple power model is used.
* @dyn_power_coeff: Coefficient for the simple dynamic power model in
* mW/(MHz mV mV).
* If get_dynamic_power() is NULL, then the
* dynamic power is calculated as
* @dyn_power_coeff * frequency * voltage^2
*/
struct devfreq_cooling_power {
unsigned long (*get_static_power)(unsigned long voltage);
unsigned long (*get_dynamic_power)(unsigned long freq,
unsigned long voltage);
unsigned long dyn_power_coeff;
};
struct thermal_cooling_device *
of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
struct devfreq_cooling_power *dfc_power);
struct thermal_cooling_device *
of_devfreq_cooling_register(struct device_node *np, struct devfreq *df);
struct thermal_cooling_device *devfreq_cooling_register(struct devfreq *df);
void devfreq_cooling_unregister(struct thermal_cooling_device *dfc);
#else /* !CONFIG_DEVFREQ_THERMAL */
struct thermal_cooling_device *
of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
struct devfreq_cooling_power *dfc_power)
{
return ERR_PTR(-EINVAL);
}
static inline struct thermal_cooling_device *
of_devfreq_cooling_register(struct device_node *np, struct devfreq *df)
{
return ERR_PTR(-EINVAL);
}
static inline struct thermal_cooling_device *
devfreq_cooling_register(struct devfreq *df)
{
return ERR_PTR(-EINVAL);
}
static inline void
devfreq_cooling_unregister(struct thermal_cooling_device *dfc)
{
}
#endif /* CONFIG_DEVFREQ_THERMAL */
#endif /* __DEVFREQ_COOLING_H__ */
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#if !defined(_TRACE_THERMAL_H) || defined(TRACE_HEADER_MULTI_READ) #if !defined(_TRACE_THERMAL_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_THERMAL_H #define _TRACE_THERMAL_H
#include <linux/devfreq.h>
#include <linux/thermal.h> #include <linux/thermal.h>
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
...@@ -135,6 +136,58 @@ TRACE_EVENT(thermal_power_cpu_limit, ...@@ -135,6 +136,58 @@ TRACE_EVENT(thermal_power_cpu_limit,
__entry->power) __entry->power)
); );
TRACE_EVENT(thermal_power_devfreq_get_power,
TP_PROTO(struct thermal_cooling_device *cdev,
struct devfreq_dev_status *status, unsigned long freq,
u32 dynamic_power, u32 static_power),
TP_ARGS(cdev, status, freq, dynamic_power, static_power),
TP_STRUCT__entry(
__string(type, cdev->type )
__field(unsigned long, freq )
__field(u32, load )
__field(u32, dynamic_power )
__field(u32, static_power )
),
TP_fast_assign(
__assign_str(type, cdev->type);
__entry->freq = freq;
__entry->load = (100 * status->busy_time) / status->total_time;
__entry->dynamic_power = dynamic_power;
__entry->static_power = static_power;
),
TP_printk("type=%s freq=%lu load=%u dynamic_power=%u static_power=%u",
__get_str(type), __entry->freq,
__entry->load, __entry->dynamic_power, __entry->static_power)
);
TRACE_EVENT(thermal_power_devfreq_limit,
TP_PROTO(struct thermal_cooling_device *cdev, unsigned long freq,
unsigned long cdev_state, u32 power),
TP_ARGS(cdev, freq, cdev_state, power),
TP_STRUCT__entry(
__string(type, cdev->type)
__field(unsigned int, freq )
__field(unsigned long, cdev_state)
__field(u32, power )
),
TP_fast_assign(
__assign_str(type, cdev->type);
__entry->freq = freq;
__entry->cdev_state = cdev_state;
__entry->power = power;
),
TP_printk("type=%s freq=%u cdev_state=%lu power=%u",
__get_str(type), __entry->freq, __entry->cdev_state,
__entry->power)
);
#endif /* _TRACE_THERMAL_H */ #endif /* _TRACE_THERMAL_H */
/* This part must be outside protection */ /* This part must be outside protection */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment