diff options
author | Joshua Primero <jprimero@nvidia.com> | 2011-10-13 17:49:20 -0700 |
---|---|---|
committer | Varun Wadekar <vwadekar@nvidia.com> | 2011-12-08 17:03:38 +0530 |
commit | 0a892e234ce77f94973afbf2d00ea4239ee6b677 (patch) | |
tree | 60a1d541af0d8e03c93376f778caa1d9b71abf2d /arch/arm/mach-tegra/tegra3_thermal.c | |
parent | 99cd63f81e09e6dafcba8cb5e61a5b56b258fad5 (diff) |
arm: tegra: power: Tegra Thermal refactoring
Refactored Thermal module so that thermal device
drivers themselves are agnostic of the thermal
framework. Also separated throttle limit constraints
from EDP table.
Reviewed-on: http://git-master/r/57990
Reviewed-on: http://git-master/r/63338
Cherry-picked from 8d0610bdd03c3490b718f11bc2108f45cd868533.
Change-Id: I4f87889c9cdc88daac1e6173043bab1f2e7cebfd
Signed-off-by: Joshua Primero <jprimero@nvidia.com>
Reviewed-on: http://git-master/r/66551
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/tegra3_thermal.c')
-rw-r--r-- | arch/arm/mach-tegra/tegra3_thermal.c | 301 |
1 files changed, 163 insertions, 138 deletions
diff --git a/arch/arm/mach-tegra/tegra3_thermal.c b/arch/arm/mach-tegra/tegra3_thermal.c index 30f8d7d8d800..f6f3c40007e6 100644 --- a/arch/arm/mach-tegra/tegra3_thermal.c +++ b/arch/arm/mach-tegra/tegra3_thermal.c @@ -29,54 +29,35 @@ #include <mach/edp.h> #include <linux/slab.h> - #include "clock.h" #include "cpu-tegra.h" #include "dvfs.h" #define MAX_ZONES (16) -/* Thermal sysfs handles hysteresis */ -#ifndef CONFIG_TEGRA_THERMAL_SYSFS -#define ALERT_HYSTERESIS_THROTTLE 1 +struct tegra_thermal { + struct tegra_thermal_data data; + struct tegra_thermal_device *device; +#ifdef CONFIG_TEGRA_THERMAL_SYSFS + struct thermal_zone_device *thz; #endif +#ifdef CONFIG_TEGRA_EDP_LIMITS + int edp_thermal_zone_val; +#endif +}; -#define ALERT_HYSTERESIS_EDP 3 - -#define THROTTLING_LIMIT (85000) -#define MAX_LIMIT (90000) - -u8 thermal_zones[MAX_ZONES]; -int thermal_zones_sz; -static int edp_thermal_zone_val = -1; +static struct tegra_thermal thermal_state = { + .device = NULL, +#ifdef CONFIG_TEGRA_EDP_LIMITS + .edp_thermal_zone_val = -1, +#endif +}; #ifndef CONFIG_TEGRA_THERMAL_SYSFS static bool throttle_enb; struct mutex mutex; #endif - -int __init tegra_thermal_init() -{ - const struct tegra_edp_limits *z; - int zones_sz; - int i; - -#ifndef CONFIG_TEGRA_THERMAL_SYSFS - mutex_init(&mutex); -#endif - tegra_get_cpu_edp_limits(&z, &zones_sz); - zones_sz = min(zones_sz, MAX_ZONES); - - for (i = 0; i < zones_sz; i++) - thermal_zones[i] = z[i].temperature; - - thermal_zones_sz = zones_sz; - - return 0; -} - - #ifdef CONFIG_TEGRA_THERMAL_SYSFS static int tegra_thermal_zone_bind(struct thermal_zone_device *thermal, @@ -95,7 +76,7 @@ static int tegra_thermal_zone_get_temp(struct thermal_zone_device *thz, long *temp) { struct tegra_thermal *thermal = thz->devdata; - thermal->ops->get_temp(thermal->data, temp); + thermal->device->get_temp(thermal->device_client, temp); return 0; } @@ -121,7 +102,9 @@ static int tegra_thermal_zone_get_trip_temp(struct thermal_zone_device *thermal, if (trip != 0) return -EINVAL; - *temp = THROTTLING_LIMIT; + *temp = thermal->data.temp_throttle + + thermal->data.temp_offset - + thermal->device->offset; return 0; } @@ -135,61 +118,6 @@ static struct thermal_zone_device_ops tegra_thermal_zone_ops = { }; #endif - - -struct tegra_thermal -*tegra_thermal_register(void *data, struct tegra_thermal_ops *thermal_ops) -{ - long temp_milli; - struct tegra_thermal *thermal; -#ifdef CONFIG_THERMAL_SYSFS - struct thermal_zone_device *thz; -#endif - - thermal = kzalloc(sizeof(struct tegra_thermal), GFP_KERNEL); - if (!thermal) - return ERR_PTR(-ENOMEM); - - thermal->ops = thermal_ops; - thermal->data = data; - -#ifdef CONFIG_TEGRA_THERMAL_SYSFS - thz = thermal_zone_device_register("nct1008", - 1, /* trips */ - thermal, - &tegra_thermal_zone_ops, - 2, /* tc1 */ - 1, /* tc2 */ - 2000, /* passive delay */ - 0); /* polling delay */ - - if (IS_ERR(thz)) { - thz = NULL; - kfree(thermal); - return ERR_PTR(-ENODEV); - } - - thermal->thz = thz; -#endif - - thermal->ops->get_temp(thermal->data, &temp_milli); - tegra_edp_update_thermal_zone(MILLICELSIUS_TO_CELSIUS(temp_milli)); - - return thermal; -} - -int tegra_thermal_unregister(struct tegra_thermal *thermal) -{ -#ifdef CONFIG_TEGRA_THERMAL_SYSFS - if (thermal->thz) - thermal_zone_device_unregister(thermal->thz); -#endif - - kfree(thermal); - - return 0; -} - /* The thermal sysfs handles notifying the throttling * cooling device */ #ifndef CONFIG_TEGRA_THERMAL_SYSFS @@ -204,83 +132,180 @@ static void tegra_therm_throttle(bool enable) } #endif -int tegra_thermal_alert(struct tegra_thermal *thermal) +void tegra_thermal_alert(void *data) { + struct tegra_thermal *thermal = data; int err; - int hysteresis; - long temp, tzone1, tzone2; + long temp; + long lo_limit_throttle, hi_limit_throttle; + long lo_limit_edp = 0, hi_limit_edp = 0; + long tj_temp, tj_throttle_temp, tj_shutdown_temp; + int lo_limit_tj = 0, hi_limit_tj = 0; int lo_limit = 0, hi_limit = 0; - int nentries = thermal_zones_sz; + const struct tegra_edp_limits *z; + int zones_sz; int i; - err = thermal->ops->get_temp(thermal->data, &temp); + + if (thermal != &thermal_state) + BUG(); + +#ifdef CONFIG_TEGRA_THERMAL_SYSFS + if (thermal->thz) { + if (!thermal->thz->passive) + thermal_zone_device_update(thermal->thz); + } +#endif + + err = thermal->device->get_temp(thermal->device->data, &temp); if (err) { pr_err("%s: get temp fail(%d)", __func__, err); - return err; + return; } - hysteresis = ALERT_HYSTERESIS_EDP; - -#ifndef CONFIG_TEGRA_THERMAL_SYSFS - if (temp >= THROTTLING_LIMIT) { - /* start throttling */ - tegra_therm_throttle(true); - hysteresis = ALERT_HYSTERESIS_THROTTLE; - } else if (temp <= - (THROTTLING_LIMIT - - ALERT_HYSTERESIS_THROTTLE)) { - /* switch off throttling */ - tegra_therm_throttle(false); + tj_temp = temp + thermal->device->offset; + tj_throttle_temp = thermal->data.temp_throttle + + thermal->data.temp_offset; + tj_shutdown_temp = thermal->data.temp_shutdown + + thermal->data.temp_offset; + + if ((tegra_is_throttling() && + (tj_temp > + (tj_throttle_temp - thermal->data.hysteresis_throttle))) + || (tj_temp >= tj_throttle_temp)) { + lo_limit_throttle = tj_throttle_temp - + thermal->data.hysteresis_throttle; + hi_limit_throttle = tj_shutdown_temp; + } else { + lo_limit_throttle = 0; + hi_limit_throttle = tj_throttle_temp; } -#endif - if (temp < CELSIUS_TO_MILLICELSIUS(thermal_zones[0])) { - lo_limit = 0; - hi_limit = thermal_zones[0]; - } else if (temp >= - CELSIUS_TO_MILLICELSIUS(thermal_zones[nentries-1])) { - lo_limit = thermal_zones[nentries-1] - hysteresis; - hi_limit = MILLICELSIUS_TO_CELSIUS(MAX_LIMIT); +#ifdef CONFIG_TEGRA_EDP_LIMITS + tegra_get_cpu_edp_limits(&z, &zones_sz); + +/* edp table based off of tdiode measurements */ +#define EDP_TEMP(_index) ((z[_index].temperature * 1000)\ + + thermal->data.edp_offset) + if (tj_temp < EDP_TEMP(0)) { + lo_limit_edp = 0; + hi_limit_edp = EDP_TEMP(0); + } else if (tj_temp >= EDP_TEMP(zones_sz-1)) { + lo_limit_edp = EDP_TEMP(zones_sz-1) - + thermal->data.hysteresis_edp; + hi_limit_edp = tj_shutdown_temp; } else { - for (i = 0; (i + 1) < nentries; i++) { - tzone1 = thermal_zones[i]; - tzone2 = thermal_zones[i + 1]; - - if (temp >= CELSIUS_TO_MILLICELSIUS(tzone1) && - temp < CELSIUS_TO_MILLICELSIUS(tzone2)) { - lo_limit = tzone1 - hysteresis; - hi_limit = tzone2; + for (i = 0; (i + 1) < zones_sz; i++) { + if ((tj_temp >= EDP_TEMP(i)) && + (tj_temp < EDP_TEMP(i+1))) { + lo_limit_edp = EDP_TEMP(i) - + thermal->data.hysteresis_edp; + hi_limit_edp = EDP_TEMP(i+1); break; } } } +#undef EDP_TEMP +#else + lo_limit_edp = 0; + hi_limit_edp = tj_shutdown_temp; +#endif + + /* Get smallest window size */ + lo_limit_tj = max(lo_limit_throttle, lo_limit_edp); + hi_limit_tj = min(hi_limit_throttle, hi_limit_edp); + + /* Get corresponding device temps */ + lo_limit = lo_limit_tj ? (lo_limit_tj - thermal->device->offset) : 0; + hi_limit = hi_limit_tj ? (hi_limit_tj - thermal->device->offset) : 0; - err = thermal->ops->set_limits(thermal->data, lo_limit, hi_limit); + thermal->device->set_limits(thermal->device->data, lo_limit, hi_limit); - if (err) - return err; +#ifndef CONFIG_TEGRA_THERMAL_SYSFS + if (tj_temp >= tj_throttle_temp) { + /* start throttling */ + if (!tegra_is_throttling()) + tegra_therm_throttle(true); + } else if (tj_temp <= + (tj_throttle_temp - + thermal->data.hysteresis_throttle)) { + /* switch off throttling */ + if (tegra_is_throttling()) + tegra_therm_throttle(false); + } +#endif +#ifdef CONFIG_TEGRA_EDP_LIMITS /* inform edp governor */ - if (edp_thermal_zone_val != temp) - /* - * FIXME: Move this direct tegra_ function call to be called - * via a pointer in 'struct nct1008_data' (like 'alarm_fn') - */ - tegra_edp_update_thermal_zone(MILLICELSIUS_TO_CELSIUS(temp)); + if (thermal->edp_thermal_zone_val != tj_temp) + tegra_edp_update_thermal_zone( + (tj_temp - thermal->data.edp_offset)/1000); + + thermal->edp_thermal_zone_val = tj_temp; +#endif +} - edp_thermal_zone_val = temp; +int tegra_thermal_set_device(struct tegra_thermal_device *device) +{ +#ifdef CONFIG_THERMAL_SYSFS + struct thermal_zone_device *thz; +#endif + + /* only support one device */ + if (thermal_state.device) + return -EINVAL; + + thermal_state.device = device; #ifdef CONFIG_TEGRA_THERMAL_SYSFS - if (thermal->thz) { - if (!thermal->thz->passive) - thermal_zone_device_update(thermal->thz); + thz = thermal_zone_device_register("thermal", + 1, /* trips */ + &thermal_state, + &tegra_thermal_zone_ops, + 2, /* tc1 */ + 1, /* tc2 */ + 2000, /* passive delay */ + 0); /* polling delay */ + + if (IS_ERR(thz)) { + thz = NULL; + kfree(thermal); + return -ENODEV; } + + thermal_state.thz = thz; +#endif + + thermal_state.device->set_alert(thermal_state.device->data, + tegra_thermal_alert, + &thermal_state); + thermal_state.device->set_shutdown_temp(thermal_state.device->data, + thermal_state.data.temp_shutdown + + thermal_state.data.temp_offset - + thermal_state.device->offset); + /* initialize limits */ + tegra_thermal_alert(&thermal_state); + + return 0; +} + +int __init tegra_thermal_init(struct tegra_thermal_data *data) +{ +#ifndef CONFIG_TEGRA_THERMAL_SYSFS + mutex_init(&mutex); #endif + memcpy(&thermal_state.data, data, sizeof(struct tegra_thermal_data)); + return 0; } int tegra_thermal_exit(void) { +#ifdef CONFIG_TEGRA_THERMAL_SYSFS + if (thermal->thz) + thermal_zone_device_unregister(thermal->thz); +#endif + return 0; } |