diff options
| author | Jinyoung Park <jinyoungp@nvidia.com> | 2013-03-21 03:44:38 -0400 |
|---|---|---|
| committer | Nicolin Chen <nicolinc@nvidia.com> | 2017-08-14 21:38:52 -0400 |
| commit | 211c09b5d97072bea1e282bdd0ca236f8fbdee5b (patch) | |
| tree | dd6e9252ec7cd3a3ca47be9e31147666b6258891 | |
| parent | a80e0e397671343e1900eb265dbd281998f0bb3a (diff) | |
drivers: misc: therm_est: Add multiple trip points support
Replaced cdev_typa and trip_temp to struct thermal_trip_info to support
multiple trip points on therm_est.
And the struct thermal_trip_info has hysteresis for trip temp. So applied
hysteresis to trip temp.
Bug 1233363
Bug 1236444
Signed-off-by: Jinyoung Park <jinyoungp@nvidia.com>
Reviewed-on: http://git-master/r/211123
(cherry picked from commit d832906d904916a263c831d1bf55031070818991)
Change-Id: I6ce2806a323c25ec298291d1e4ee067c3adaebfa
Signed-off-by: Jinyoung Park <jinyoungp@nvidia.com>
Reviewed-on: http://git-master/r/215542
| -rw-r--r-- | drivers/misc/therm_est.c | 183 | ||||
| -rw-r--r-- | include/linux/therm_est.h | 13 |
2 files changed, 142 insertions, 54 deletions
diff --git a/drivers/misc/therm_est.c b/drivers/misc/therm_est.c index ab543bb7d..ef3399acf 100644 --- a/drivers/misc/therm_est.c +++ b/drivers/misc/therm_est.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * drivers/misc/therm_est.c | 2 | * drivers/misc/therm_est.c |
| 3 | * | 3 | * |
| 4 | * Copyright (c) 2010-2013 NVIDIA Corporation. All rights reserved. | 4 | * Copyright (c) 2010-2013, NVIDIA CORPORATION. All rights reserved. |
| 5 | * | 5 | * |
| 6 | * This software is licensed under the terms of the GNU General Public | 6 | * This software is licensed under the terms of the GNU General Public |
| 7 | * License version 2, as published by the Free Software Foundation, and | 7 | * License version 2, as published by the Free Software Foundation, and |
| @@ -34,32 +34,73 @@ | |||
| 34 | #include <linux/suspend.h> | 34 | #include <linux/suspend.h> |
| 35 | 35 | ||
| 36 | struct therm_estimator { | 36 | struct therm_estimator { |
| 37 | long cur_temp; | 37 | struct thermal_zone_device *thz; |
| 38 | long polling_period; | 38 | int num_trips; |
| 39 | struct thermal_trip_info *trips; | ||
| 40 | struct thermal_zone_params *tzp; | ||
| 41 | |||
| 39 | struct workqueue_struct *workqueue; | 42 | struct workqueue_struct *workqueue; |
| 40 | struct delayed_work therm_est_work; | 43 | struct delayed_work therm_est_work; |
| 41 | long toffset; | 44 | long cur_temp; |
| 45 | long low_limit; | ||
| 46 | long high_limit; | ||
| 42 | int ntemp; | 47 | int ntemp; |
| 43 | int ndevs; | 48 | long toffset; |
| 44 | struct therm_est_subdevice *devs; | 49 | long polling_period; |
| 45 | struct thermal_zone_device *thz; | ||
| 46 | char *cdev_type; | ||
| 47 | long trip_temp; | ||
| 48 | int tc1; | 50 | int tc1; |
| 49 | int tc2; | 51 | int tc2; |
| 52 | int ndevs; | ||
| 53 | struct therm_est_subdevice *devs; | ||
| 54 | |||
| 50 | #ifdef CONFIG_PM | 55 | #ifdef CONFIG_PM |
| 51 | struct notifier_block pm_nb; | 56 | struct notifier_block pm_nb; |
| 52 | #endif | 57 | #endif |
| 53 | }; | 58 | }; |
| 54 | 59 | ||
| 60 | static void therm_est_update_limits(struct therm_estimator *est) | ||
| 61 | { | ||
| 62 | const int MAX_HIGH_TEMP = 128000; | ||
| 63 | long low_temp = 0, high_temp = MAX_HIGH_TEMP; | ||
| 64 | long trip_temp, passive_low_temp = MAX_HIGH_TEMP; | ||
| 65 | enum thermal_trip_type trip_type; | ||
| 66 | struct thermal_trip_info *trip_state; | ||
| 67 | int i; | ||
| 68 | |||
| 69 | for (i = 0; i < est->num_trips; i++) { | ||
| 70 | trip_state = &est->trips[i]; | ||
| 71 | est->thz->ops->get_trip_temp(est->thz, i, &trip_temp); | ||
| 72 | est->thz->ops->get_trip_type(est->thz, i, &trip_type); | ||
| 73 | |||
| 74 | if (!trip_state->tripped) { /* not tripped? update high */ | ||
| 75 | if (trip_temp < high_temp) | ||
| 76 | high_temp = trip_temp; | ||
| 77 | } else { /* tripped? update low */ | ||
| 78 | if (trip_type != THERMAL_TRIP_PASSIVE) { | ||
| 79 | /* get highest ACTIVE */ | ||
| 80 | if (trip_temp > low_temp) | ||
| 81 | low_temp = trip_temp; | ||
| 82 | } else { | ||
| 83 | /* get lowest PASSIVE */ | ||
| 84 | if (trip_temp < passive_low_temp) | ||
| 85 | passive_low_temp = trip_temp; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | if (passive_low_temp != MAX_HIGH_TEMP) | ||
| 91 | low_temp = max(low_temp, passive_low_temp); | ||
| 92 | |||
| 93 | est->low_limit = low_temp; | ||
| 94 | est->high_limit = high_temp; | ||
| 95 | } | ||
| 96 | |||
| 55 | static void therm_est_work_func(struct work_struct *work) | 97 | static void therm_est_work_func(struct work_struct *work) |
| 56 | { | 98 | { |
| 57 | int i, j, index, sum = 0; | 99 | int i, j, index, sum = 0; |
| 58 | long temp; | 100 | long temp; |
| 59 | struct delayed_work *dwork = container_of (work, | 101 | struct delayed_work *dwork = container_of(work, |
| 60 | struct delayed_work, work); | 102 | struct delayed_work, work); |
| 61 | struct therm_estimator *est = container_of( | 103 | struct therm_estimator *est = container_of(dwork, |
| 62 | dwork, | ||
| 63 | struct therm_estimator, | 104 | struct therm_estimator, |
| 64 | therm_est_work); | 105 | therm_est_work); |
| 65 | 106 | ||
| @@ -78,12 +119,13 @@ static void therm_est_work_func(struct work_struct *work) | |||
| 78 | } | 119 | } |
| 79 | 120 | ||
| 80 | est->cur_temp = sum / 100 + est->toffset; | 121 | est->cur_temp = sum / 100 + est->toffset; |
| 81 | |||
| 82 | est->ntemp++; | 122 | est->ntemp++; |
| 83 | 123 | ||
| 84 | if (est->cur_temp >= est->trip_temp) | 124 | if (est->thz && ((est->cur_temp < est->low_limit) || |
| 85 | if (est->thz && !est->thz->passive) | 125 | (est->cur_temp >= est->high_limit))) { |
| 86 | thermal_zone_device_update(est->thz); | 126 | thermal_zone_device_update(est->thz); |
| 127 | therm_est_update_limits(est); | ||
| 128 | } | ||
| 87 | 129 | ||
| 88 | queue_delayed_work(est->workqueue, &est->therm_est_work, | 130 | queue_delayed_work(est->workqueue, &est->therm_est_work, |
| 89 | msecs_to_jiffies(est->polling_period)); | 131 | msecs_to_jiffies(est->polling_period)); |
| @@ -93,10 +135,18 @@ static int therm_est_bind(struct thermal_zone_device *thz, | |||
| 93 | struct thermal_cooling_device *cdev) | 135 | struct thermal_cooling_device *cdev) |
| 94 | { | 136 | { |
| 95 | struct therm_estimator *est = thz->devdata; | 137 | struct therm_estimator *est = thz->devdata; |
| 138 | struct thermal_trip_info *trip_state; | ||
| 139 | int i; | ||
| 96 | 140 | ||
| 97 | if (!strcmp(cdev->type, est->cdev_type)) | 141 | for (i = 0; i < est->num_trips; i++) { |
| 98 | thermal_zone_bind_cooling_device(thz, 0, cdev, | 142 | trip_state = &est->trips[i]; |
| 99 | THERMAL_NO_LIMIT, THERMAL_NO_LIMIT); | 143 | if (trip_state->cdev_type && |
| 144 | !strncmp(trip_state->cdev_type, cdev->type, | ||
| 145 | THERMAL_NAME_LENGTH)) | ||
| 146 | thermal_zone_bind_cooling_device(thz, i, cdev, | ||
| 147 | trip_state->upper, | ||
| 148 | trip_state->lower); | ||
| 149 | } | ||
| 100 | 150 | ||
| 101 | return 0; | 151 | return 0; |
| 102 | } | 152 | } |
| @@ -105,40 +155,59 @@ static int therm_est_unbind(struct thermal_zone_device *thz, | |||
| 105 | struct thermal_cooling_device *cdev) | 155 | struct thermal_cooling_device *cdev) |
| 106 | { | 156 | { |
| 107 | struct therm_estimator *est = thz->devdata; | 157 | struct therm_estimator *est = thz->devdata; |
| 158 | struct thermal_trip_info *trip_state; | ||
| 159 | int i; | ||
| 108 | 160 | ||
| 109 | if (!strcmp(cdev->type, est->cdev_type)) | 161 | for (i = 0; i < est->num_trips; i++) { |
| 110 | thermal_zone_unbind_cooling_device(thz, 0, cdev); | 162 | trip_state = &est->trips[i]; |
| 163 | if (trip_state->cdev_type && | ||
| 164 | !strncmp(trip_state->cdev_type, cdev->type, | ||
| 165 | THERMAL_NAME_LENGTH)) | ||
| 166 | thermal_zone_unbind_cooling_device(thz, i, cdev); | ||
| 167 | } | ||
| 111 | 168 | ||
| 112 | return 0; | 169 | return 0; |
| 113 | } | 170 | } |
| 114 | 171 | ||
| 115 | static int therm_est_get_trip_type(struct thermal_zone_device *thz, | 172 | static int therm_est_get_trip_type(struct thermal_zone_device *thz, |
| 116 | int trip, | 173 | int trip, enum thermal_trip_type *type) |
| 117 | enum thermal_trip_type *type) | ||
| 118 | { | 174 | { |
| 119 | *type = THERMAL_TRIP_PASSIVE; | 175 | struct therm_estimator *est = thz->devdata; |
| 176 | |||
| 177 | *type = est->trips[trip].trip_type; | ||
| 120 | return 0; | 178 | return 0; |
| 121 | } | 179 | } |
| 122 | 180 | ||
| 123 | static int therm_est_get_trip_temp(struct thermal_zone_device *thz, | 181 | static int therm_est_get_trip_temp(struct thermal_zone_device *thz, |
| 124 | int trip, | 182 | int trip, unsigned long *temp) |
| 125 | unsigned long *temp) | ||
| 126 | { | 183 | { |
| 127 | struct therm_estimator *est = thz->devdata; | 184 | struct therm_estimator *est = thz->devdata; |
| 185 | struct thermal_trip_info *trip_state = &est->trips[trip]; | ||
| 186 | unsigned long trip_temp, zone_temp; | ||
| 187 | |||
| 188 | trip_temp = trip_state->trip_temp; | ||
| 189 | zone_temp = thz->temperature; | ||
| 190 | |||
| 191 | if (zone_temp >= trip_temp) { | ||
| 192 | trip_temp -= trip_state->hysteresis; | ||
| 193 | trip_state->tripped = true; | ||
| 194 | } else if (trip_state->tripped) { | ||
| 195 | trip_temp -= trip_state->hysteresis; | ||
| 196 | if (zone_temp < trip_temp) | ||
| 197 | trip_state->tripped = false; | ||
| 198 | } | ||
| 128 | 199 | ||
| 129 | *temp = est->trip_temp; | 200 | *temp = trip_temp; |
| 130 | 201 | ||
| 131 | return 0; | 202 | return 0; |
| 132 | } | 203 | } |
| 133 | 204 | ||
| 134 | static int therm_est_set_trip_temp(struct thermal_zone_device *thz, | 205 | static int therm_est_set_trip_temp(struct thermal_zone_device *thz, |
| 135 | int trip, | 206 | int trip, unsigned long temp) |
| 136 | unsigned long temp) | ||
| 137 | { | 207 | { |
| 138 | struct therm_estimator *est = thz->devdata; | 208 | struct therm_estimator *est = thz->devdata; |
| 139 | 209 | ||
| 140 | est->trip_temp = temp; | 210 | est->trips[trip].trip_temp = temp; |
| 141 | |||
| 142 | return 0; | 211 | return 0; |
| 143 | } | 212 | } |
| 144 | 213 | ||
| @@ -146,29 +215,42 @@ static int therm_est_get_temp(struct thermal_zone_device *thz, | |||
| 146 | unsigned long *temp) | 215 | unsigned long *temp) |
| 147 | { | 216 | { |
| 148 | struct therm_estimator *est = thz->devdata; | 217 | struct therm_estimator *est = thz->devdata; |
| 218 | |||
| 149 | *temp = est->cur_temp; | 219 | *temp = est->cur_temp; |
| 150 | return 0; | 220 | return 0; |
| 151 | } | 221 | } |
| 152 | 222 | ||
| 153 | static int therm_est_get_trend(struct thermal_zone_device *thz, | 223 | static int therm_est_get_trend(struct thermal_zone_device *thz, |
| 154 | int trip, | 224 | int trip, enum thermal_trend *trend) |
| 155 | enum thermal_trend *trend) | ||
| 156 | { | 225 | { |
| 157 | struct therm_estimator *est = thz->devdata; | 226 | struct therm_estimator *est = thz->devdata; |
| 227 | struct thermal_trip_info *trip_state = &est->trips[trip]; | ||
| 228 | long trip_temp; | ||
| 158 | int new_trend; | 229 | int new_trend; |
| 159 | int cur_temp; | 230 | int cur_temp; |
| 160 | 231 | ||
| 232 | thz->ops->get_trip_temp(thz, trip, &trip_temp); | ||
| 233 | |||
| 161 | cur_temp = thz->temperature; | 234 | cur_temp = thz->temperature; |
| 162 | new_trend = (est->tc1 * (cur_temp - thz->last_temperature)) + | 235 | new_trend = (est->tc1 * (cur_temp - thz->last_temperature)) + |
| 163 | (est->tc2 * (cur_temp - est->trip_temp)); | 236 | (est->tc2 * (cur_temp - trip_temp)); |
| 164 | 237 | ||
| 165 | if (new_trend > 0) | 238 | switch (trip_state->trip_type) { |
| 239 | case THERMAL_TRIP_ACTIVE: | ||
| 240 | /* aggressive active cooling */ | ||
| 166 | *trend = THERMAL_TREND_RAISING; | 241 | *trend = THERMAL_TREND_RAISING; |
| 167 | else if (new_trend < 0) | 242 | break; |
| 168 | *trend = THERMAL_TREND_DROPPING; | 243 | case THERMAL_TRIP_PASSIVE: |
| 169 | else | 244 | if (new_trend > 0) |
| 170 | *trend = THERMAL_TREND_STABLE; | 245 | *trend = THERMAL_TREND_RAISING; |
| 171 | 246 | else if (new_trend < 0) | |
| 247 | *trend = THERMAL_TREND_DROPPING; | ||
| 248 | else | ||
| 249 | *trend = THERMAL_TREND_STABLE; | ||
| 250 | break; | ||
| 251 | default: | ||
| 252 | return -EINVAL; | ||
| 253 | } | ||
| 172 | return 0; | 254 | return 0; |
| 173 | } | 255 | } |
| 174 | 256 | ||
| @@ -390,6 +472,8 @@ static int therm_est_pm_notify(struct notifier_block *nb, | |||
| 390 | cancel_delayed_work_sync(&est->therm_est_work); | 472 | cancel_delayed_work_sync(&est->therm_est_work); |
| 391 | break; | 473 | break; |
| 392 | case PM_POST_SUSPEND: | 474 | case PM_POST_SUSPEND: |
| 475 | est->low_limit = 0; | ||
| 476 | est->high_limit = 0; | ||
| 393 | therm_est_init_history(est); | 477 | therm_est_init_history(est); |
| 394 | queue_delayed_work(est->workqueue, | 478 | queue_delayed_work(est->workqueue, |
| 395 | &est->therm_est_work, | 479 | &est->therm_est_work, |
| @@ -436,17 +520,18 @@ static int __devinit therm_est_probe(struct platform_device *pdev) | |||
| 436 | &est->therm_est_work, | 520 | &est->therm_est_work, |
| 437 | msecs_to_jiffies(est->polling_period)); | 521 | msecs_to_jiffies(est->polling_period)); |
| 438 | 522 | ||
| 439 | est->trip_temp = data->trip_temp; | 523 | est->num_trips = data->num_trips; |
| 440 | est->cdev_type = data->cdev_type; | 524 | est->trips = data->trips; |
| 525 | est->tzp = data->tzp; | ||
| 441 | 526 | ||
| 442 | est->thz = thermal_zone_device_register(dev_name(&pdev->dev), | 527 | est->thz = thermal_zone_device_register(dev_name(&pdev->dev), |
| 443 | 1, | 528 | est->num_trips, |
| 444 | 0x1, | 529 | (1 << est->num_trips) - 1, |
| 445 | est, | 530 | est, |
| 446 | &therm_est_ops, | 531 | &therm_est_ops, |
| 447 | NULL, | 532 | est->tzp, |
| 448 | data->passive_delay, | 533 | data->passive_delay, |
| 449 | 0); | 534 | 0); |
| 450 | if (IS_ERR_OR_NULL(est->thz)) | 535 | if (IS_ERR_OR_NULL(est->thz)) |
| 451 | goto err; | 536 | goto err; |
| 452 | 537 | ||
diff --git a/include/linux/therm_est.h b/include/linux/therm_est.h index 8877108af..f1ccbf0de 100644 --- a/include/linux/therm_est.h +++ b/include/linux/therm_est.h | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * include/linux/therm_est.h | 2 | * include/linux/therm_est.h |
| 3 | * | 3 | * |
| 4 | * Copyright (c) 2010-2012, NVIDIA Corporation. | 4 | * Copyright (c) 2010-2013, NVIDIA CORPORATION. All rights reserved. |
| 5 | * | 5 | * |
| 6 | * This software is licensed under the terms of the GNU General Public | 6 | * This software is licensed under the terms of the GNU General Public |
| 7 | * License version 2, as published by the Free Software Foundation, and | 7 | * License version 2, as published by the Free Software Foundation, and |
| @@ -18,6 +18,8 @@ | |||
| 18 | #define _LINUX_THERM_EST_H | 18 | #define _LINUX_THERM_EST_H |
| 19 | 19 | ||
| 20 | #include <linux/workqueue.h> | 20 | #include <linux/workqueue.h> |
| 21 | #include <linux/thermal.h> | ||
| 22 | #include <linux/platform_data/thermal_sensors.h> | ||
| 21 | 23 | ||
| 22 | #define HIST_LEN (20) | 24 | #define HIST_LEN (20) |
| 23 | 25 | ||
| @@ -31,17 +33,18 @@ struct therm_est_subdevice { | |||
| 31 | }; | 33 | }; |
| 32 | 34 | ||
| 33 | struct therm_est_data { | 35 | struct therm_est_data { |
| 34 | /* trip point info : there's only 1 trip point */ | 36 | /* trip point info */ |
| 35 | char *cdev_type; /* cooling device for this trip */ | 37 | int num_trips; |
| 36 | long trip_temp; | 38 | struct thermal_trip_info *trips; |
| 37 | 39 | ||
| 38 | /* zone parameters */ | 40 | /* zone parameters */ |
| 41 | struct thermal_zone_params *tzp; | ||
| 39 | long toffset; | 42 | long toffset; |
| 40 | long polling_period; | 43 | long polling_period; |
| 41 | int passive_delay; | 44 | int passive_delay; |
| 42 | int ndevs; | ||
| 43 | int tc1; | 45 | int tc1; |
| 44 | int tc2; | 46 | int tc2; |
| 47 | int ndevs; | ||
| 45 | struct therm_est_subdevice devs[]; | 48 | struct therm_est_subdevice devs[]; |
| 46 | }; | 49 | }; |
| 47 | 50 | ||
