diff options
author | Alex Frid <afrid@nvidia.com> | 2013-11-06 20:21:27 -0800 |
---|---|---|
committer | Yu-Huan Hsu <yhsu@nvidia.com> | 2013-11-26 15:44:06 -0800 |
commit | 54461f1cfde8dba9a73ee78ef9830ed887590971 (patch) | |
tree | bcc67951c806635d95f00708b98611a7e493be81 /arch | |
parent | 4368bddd02cc986e5c00ddb1a5fa7b18f7fe377d (diff) |
ARM: tegra: dvfs: Add core rail Vmax cooling device
Added cooling device to cap frequencies of core shared buses based on
core rail Vmax thermal profile. Thermal limits implementation is an
extension of the existing core voltage capping mechanism, combined with
core voltage override limits, and limits set from user space.
Bug 1413311
Change-Id: I65e8b885f49318020e20d25425e257b7f0b0f66e
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/335357
Reviewed-by: Sai Gurrappadi <sgurrappadi@nvidia.com>
Reviewed-by: Prashant Malani <pmalani@nvidia.com>
Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-tegra/dvfs.c | 67 | ||||
-rw-r--r-- | arch/arm/mach-tegra/dvfs.h | 5 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra_core_volt_cap.c | 33 |
3 files changed, 105 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c index bf61ddd514fd..e2be673ef52e 100644 --- a/arch/arm/mach-tegra/dvfs.c +++ b/arch/arm/mach-tegra/dvfs.c @@ -1419,6 +1419,13 @@ struct tegra_cooling_device *tegra_dvfs_get_cpu_vmin_cdev(void) return NULL; } +struct tegra_cooling_device *tegra_dvfs_get_core_vmax_cdev(void) +{ + if (tegra_core_rail) + return tegra_core_rail->vmax_cdev; + return NULL; +} + struct tegra_cooling_device *tegra_dvfs_get_core_vmin_cdev(void) { if (tegra_core_rail) @@ -1506,6 +1513,64 @@ static void tegra_dvfs_rail_register_vmin_cdev(struct dvfs_rail *rail) rail->vmin_cdev->cdev_type); } +/* + * Cooling device limits frequencies of the clocks in pll mode based on rail + * vmax thermal profile. Supported for core rail only, and applied only to + * shared buses selected by platform specific code. + */ +static int tegra_dvfs_rail_get_vmax_cdev_max_state( + struct thermal_cooling_device *cdev, unsigned long *max_state) +{ + struct dvfs_rail *rail = (struct dvfs_rail *)cdev->devdata; + *max_state = rail->vmax_cdev->trip_temperatures_num; + return 0; +} + +static int tegra_dvfs_rail_get_vmax_cdev_cur_state( + struct thermal_cooling_device *cdev, unsigned long *cur_state) +{ + struct dvfs_rail *rail = (struct dvfs_rail *)cdev->devdata; + *cur_state = rail->therm_cap_idx; + return 0; +} + +static int tegra_dvfs_rail_set_vmax_cdev_state( + struct thermal_cooling_device *cdev, unsigned long cur_state) +{ + struct dvfs_rail *rail = (struct dvfs_rail *)cdev->devdata; + int cur_cap = cur_state ? rail->therm_mv_caps[cur_state - 1] : 0; + + return tegra_dvfs_therm_vmax_core_cap_apply(&rail->therm_cap_idx, + cur_state, cur_cap); +} + +static struct thermal_cooling_device_ops tegra_dvfs_vmax_cooling_ops = { + .get_max_state = tegra_dvfs_rail_get_vmax_cdev_max_state, + .get_cur_state = tegra_dvfs_rail_get_vmax_cdev_cur_state, + .set_cur_state = tegra_dvfs_rail_set_vmax_cdev_state, +}; + +void tegra_dvfs_rail_register_vmax_cdev(struct dvfs_rail *rail) +{ + struct thermal_cooling_device *dev; + + if (!rail || !rail->vmax_cdev || (rail != tegra_core_rail)) + return; + + dev = thermal_cooling_device_register(rail->vmax_cdev->cdev_type, + (void *)rail, &tegra_dvfs_vmax_cooling_ops); + + if (IS_ERR_OR_NULL(dev) || list_empty(&dev->thermal_instances)) { + /* report error & set the most agressive caps */ + int cur_state = rail->vmax_cdev->trip_temperatures_num; + int cur_cap = rail->therm_mv_caps[cur_state - 1]; + tegra_dvfs_therm_vmax_core_cap_apply(&rail->therm_cap_idx, + cur_state, cur_cap); + pr_err("tegra cooling device %s failed to register\n", + rail->vmax_cdev->cdev_type); + } +} + /* Cooling device to scale voltage with temperature in pll mode */ static int tegra_dvfs_rail_get_vts_cdev_max_state( struct thermal_cooling_device *cdev, unsigned long *max_state) @@ -1566,6 +1631,8 @@ static void tegra_dvfs_rail_register_vts_cdev(struct dvfs_rail *rail) #else #define tegra_dvfs_rail_register_vmin_cdev(rail) +void tegra_dvfs_rail_register_vmax_cdev(struct dvfs_rail *rail) +{ } static inline void tegra_dvfs_rail_register_vts_cdev(struct dvfs_rail *rail) { make_safe_thermal_dvfs(rail); diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h index bcb1f62fc086..875b64f4828a 100644 --- a/arch/arm/mach-tegra/dvfs.h +++ b/arch/arm/mach-tegra/dvfs.h @@ -105,6 +105,7 @@ struct dvfs_rail { bool dfll_mode; bool dfll_mode_updating; int therm_floor_idx; + int therm_cap_idx; int therm_scale_idx; struct tegra_cooling_device *vmin_cdev; struct tegra_cooling_device *vmax_cdev; @@ -251,6 +252,8 @@ int tegra_dvfs_predict_millivolts_dfll(struct clk *c, unsigned long rate); const int *tegra_dvfs_get_millivolts_pll(struct dvfs *d); int tegra_dvfs_override_core_cap_apply(int level); +int tegra_dvfs_therm_vmax_core_cap_apply(int *cap_idx, int new_idx, int level); + int tegra_dvfs_alt_freqs_install(struct dvfs *d, unsigned long *alt_freqs); int tegra_dvfs_alt_freqs_set(struct dvfs *d, unsigned long *alt_freqs); int tegra_cpu_dvfs_alter(int edp_thermal_index, const cpumask_t *cpus, @@ -260,6 +263,7 @@ int tegra_dvfs_dfll_mode_clear(struct dvfs *d, unsigned long rate); struct tegra_cooling_device *tegra_dvfs_get_cpu_vmax_cdev(void); struct tegra_cooling_device *tegra_dvfs_get_cpu_vmin_cdev(void); +struct tegra_cooling_device *tegra_dvfs_get_core_vmax_cdev(void); struct tegra_cooling_device *tegra_dvfs_get_core_vmin_cdev(void); struct tegra_cooling_device *tegra_dvfs_get_gpu_vmin_cdev(void); struct tegra_cooling_device *tegra_dvfs_get_gpu_vts_cdev(void); @@ -274,6 +278,7 @@ int tegra_dvfs_rail_init_thermal_dvfs_trips( int tegra_dvfs_init_thermal_dvfs_voltages(int *millivolts, int *peak_millivolts, int freqs_num, int ranges_num, struct dvfs *d); int tegra_dvfs_rail_dfll_mode_set_cold(struct dvfs_rail *rail); +void tegra_dvfs_rail_register_vmax_cdev(struct dvfs_rail *rail); #ifdef CONFIG_TEGRA_VDD_CORE_OVERRIDE int tegra_dvfs_resolve_override(struct clk *c, unsigned long max_rate); diff --git a/arch/arm/mach-tegra/tegra_core_volt_cap.c b/arch/arm/mach-tegra/tegra_core_volt_cap.c index 9e19b9c32efd..dd009fd801f5 100644 --- a/arch/arm/mach-tegra/tegra_core_volt_cap.c +++ b/arch/arm/mach-tegra/tegra_core_volt_cap.c @@ -47,6 +47,7 @@ struct core_cap { static struct core_cap core_buses_cap; static struct core_cap override_core_cap; +static struct core_cap vmax_core_cap; static struct core_cap user_core_cap; static struct core_dvfs_cap_table *core_cap_table; @@ -105,6 +106,8 @@ static int core_cap_update(void) new_level = core_nominal_mv; if (override_core_cap.refcnt) new_level = min(new_level, override_core_cap.level); + if (vmax_core_cap.refcnt) + new_level = min(new_level, vmax_core_cap.level); if (user_core_cap.refcnt) new_level = min(new_level, user_core_cap.level); @@ -217,6 +220,36 @@ int tegra_dvfs_override_core_cap_apply(int level) return ret; } +int tegra_dvfs_therm_vmax_core_cap_apply(int *cap_idx, int new_idx, int level) +{ + int ret = 0; + + mutex_lock(&core_cap_lock); + if (*cap_idx == new_idx) + goto _out; + + *cap_idx = new_idx; + + if (level) { + if (!vmax_core_cap.refcnt) { + vmax_core_cap.level = level; + vmax_core_cap.refcnt = 1; + /* just report error (cannot revert temperature) */ + ret = core_cap_enable(true); + } else if (vmax_core_cap.level != level) { + vmax_core_cap.level = level; + /* just report error (cannot revert temperature) */ + ret = core_cap_update(); + } + } else if (vmax_core_cap.refcnt) { + vmax_core_cap.refcnt = 0; + core_cap_enable(false); + } +_out: + mutex_unlock(&core_cap_lock); + return ret; +} + static int __init init_core_cap_one(struct clk *c, unsigned long *freqs) { int i, v, next_v = 0; |