diff options
| author | Hyungwoo Yang <hyungwooy@nvidia.com> | 2014-11-25 18:08:54 -0500 |
|---|---|---|
| committer | Nicolin Chen <nicolinc@nvidia.com> | 2017-08-14 21:38:52 -0400 |
| commit | dd6e683e308d39fc697cd58e854fdcd13100e57c (patch) | |
| tree | 82c79fce645c34a21c81baeb8efa5abfbabf38a6 | |
| parent | 303ebe41f25ac9f2e1aa1c5d1c6d0ac76d59adbd (diff) | |
tegra: therm_est: support multiple sets of coefficients
Edit original commit to use 'thermal_zone_of_device_ops'.
enable multlple sets of coefficients.
Bug 1582735
(cherry-picked from commit If0c31037fdf1d8004c9ee241e0387ceef1bb022f)
Signed-off-by: Hyungwoo Yang <hyungwooy@nvidia.com>
Reviewed-on: http://git-master/r/655573
Change-Id: If8ec11e80238f70227d473891076a04a8cd1301d
Signed-off-by: Srikar Srimath Tirumala <srikars@nvidia.com>
| -rw-r--r-- | Documentation/devicetree/bindings/misc/therm_est.txt | 46 | ||||
| -rw-r--r-- | drivers/misc/therm_est.c | 356 | ||||
| -rw-r--r-- | include/linux/therm_est.h | 24 |
3 files changed, 288 insertions, 138 deletions
diff --git a/Documentation/devicetree/bindings/misc/therm_est.txt b/Documentation/devicetree/bindings/misc/therm_est.txt index b0532c721..0dbcb147a 100644 --- a/Documentation/devicetree/bindings/misc/therm_est.txt +++ b/Documentation/devicetree/bindings/misc/therm_est.txt | |||
| @@ -2,34 +2,46 @@ Thermal estimator driver. | |||
| 2 | 2 | ||
| 3 | Properties : | 3 | Properties : |
| 4 | - compatible : Should contain "nvidia,therm-est". | 4 | - compatible : Should contain "nvidia,therm-est". |
| 5 | - toffset : Temperature offset for thermal estimation, in milli-celsius. | ||
| 6 | - polling-period : Polling wait times for thermal estimation, in milliseconds. | 5 | - polling-period : Polling wait times for thermal estimation, in milliseconds. |
| 7 | - tc1 : Coefficient 1 for thermal trend calculation. | 6 | - tc1 : Coefficient 1 for thermal trend calculation. |
| 8 | - tc2 : Coefficient 2 for thermal trend calculation. | 7 | - tc2 : Coefficient 2 for thermal trend calculation. |
| 9 | - node for subdev : Node for subdevice information. Required. | 8 | - node for subdev : Node for subdevice information. Required. |
| 10 | This node can be numerous. | ||
| 11 | 9 | ||
| 12 | Properties in subdevice node : Required. Can be numerous. | 10 | Properties in subdev node : Required. |
| 13 | - device_type : Should contain "therm-est-subdev". | 11 | - subdev_names : list of strings. It contains list of the name of the therm |
| 14 | - dev_data : Thermal zone device type for thermal estimation. | 12 | zones used for estimation. |
| 15 | - coeffs : An array of coefficients, the number of entries should be twenty. | 13 | - node for coeff_set : contains a set of zone specific coefficients+offset. |
| 14 | This node can be numerous. | ||
| 15 | |||
| 16 | Properties in coeff_set : Required. Can be numerous. | ||
| 17 | - toffset : Temperature offset for thermal estimation, in milli-celsius. | ||
| 18 | - coeffs : An array of coefficients, the number of entries should be twenty | ||
| 19 | per sub device. | ||
| 16 | 20 | ||
| 17 | Example: | 21 | Example: |
| 18 | 22 | ||
| 19 | therm_est { | 23 | therm_est_sensor { |
| 20 | device_type = "therm-est-subdev"; | 24 | device_type = "therm-est-subdev"; |
| 21 | toffset = <0>; | ||
| 22 | polling-period = <1100>; | 25 | polling-period = <1100>; |
| 23 | tc1 = <10>; | 26 | tc1 = <10>; |
| 24 | tc2 = <1>; | 27 | tc2 = <1>; |
| 25 | subdevs@0 { | 28 | subdev { |
| 26 | compatible = "nvidia,therm-est-subdev"; | 29 | subdev_names = "Tdiode_tegra", "Tboard_tegra"; |
| 27 | dev-data = "nct_ext"; | 30 | |
| 28 | coeffs = "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; | 31 | coeff_set@0 { |
| 29 | }; | 32 | toffset = <9793>; |
| 30 | subdevs@1 { | 33 | coeffs = < |
| 31 | compatible = "nvidia,therm-est-subdev"; | 34 | 2 1 1 1 1 1 1 1 1 1 1 0 1 1 0 0 0 0 (-1) (-7) |
| 32 | dev-data = "nct_int"; | 35 | (-11) (-7) (-5) (-3) (-3) (-2) (-1) 0 0 0 1 1 1 2 2 3 4 6 11 18 |
| 33 | coeffs = "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; | 36 | >; |
| 37 | }; | ||
| 38 | |||
| 39 | coeff_set@1 { | ||
| 40 | toffset = <708>; | ||
| 41 | coeffs = < | ||
| 42 | -1 -1 1 1 1 1 1 1 1 1 1 0 1 1 0 0 0 (-1) (-3) (-9) | ||
| 43 | (-6) (-4) (-1) 0 0 0 0 0 0 0 1 1 1 2 2 5 7 10 17 20 | ||
| 44 | >; | ||
| 45 | }; | ||
| 34 | }; | 46 | }; |
| 35 | }; | 47 | }; |
diff --git a/drivers/misc/therm_est.c b/drivers/misc/therm_est.c index 32ddd7fac..156fab120 100644 --- a/drivers/misc/therm_est.c +++ b/drivers/misc/therm_est.c | |||
| @@ -44,14 +44,11 @@ struct therm_estimator { | |||
| 44 | long cur_temp; | 44 | long cur_temp; |
| 45 | long low_limit; | 45 | long low_limit; |
| 46 | long high_limit; | 46 | long high_limit; |
| 47 | int ntemp; | ||
| 48 | long toffset; | ||
| 49 | long polling_period; | 47 | long polling_period; |
| 50 | int polling_enabled; | 48 | int polling_enabled; |
| 51 | int tc1; | 49 | int tc1; |
| 52 | int tc2; | 50 | int tc2; |
| 53 | int ndevs; | 51 | struct therm_est_subdevice *subdevice; |
| 54 | struct therm_est_subdevice *devs; | ||
| 55 | bool *tripped_info; | 52 | bool *tripped_info; |
| 56 | 53 | ||
| 57 | int use_activator; | 54 | int use_activator; |
| @@ -123,30 +120,37 @@ static void therm_est_update_limits(struct therm_estimator *est) | |||
| 123 | 120 | ||
| 124 | static void therm_est_work_func(struct work_struct *work) | 121 | static void therm_est_work_func(struct work_struct *work) |
| 125 | { | 122 | { |
| 126 | int i, j, index, sum = 0; | ||
| 127 | long temp; | ||
| 128 | struct delayed_work *dwork = container_of(work, | 123 | struct delayed_work *dwork = container_of(work, |
| 129 | struct delayed_work, work); | 124 | struct delayed_work, work); |
| 130 | struct therm_estimator *est = container_of(dwork, | 125 | struct therm_estimator *est = container_of(dwork, |
| 131 | struct therm_estimator, | 126 | struct therm_estimator, |
| 132 | therm_est_work); | 127 | therm_est_work); |
| 128 | struct therm_est_subdevice *subdevice; | ||
| 129 | struct therm_est_coeffs *coeffs_set; | ||
| 130 | long *thz_coeffs, *hist; | ||
| 131 | long temp; | ||
| 132 | int i, j, index, sum = 0; | ||
| 133 | 133 | ||
| 134 | for (i = 0; i < est->ndevs; i++) { | 134 | subdevice = est->subdevice; |
| 135 | if (therm_est_subdev_get_temp(est->devs[i].sub_thz, &temp)) | 135 | coeffs_set = &subdevice->coeffs_set[subdevice->active_coeffs]; |
| 136 | |||
| 137 | subdevice->ntemp = (subdevice->ntemp + 1) % HIST_LEN; | ||
| 138 | for (i = 0; i < subdevice->num_devs; i++) { | ||
| 139 | if (therm_est_subdev_get_temp(subdevice->sub_thz[i].thz, &temp)) | ||
| 136 | continue; | 140 | continue; |
| 137 | est->devs[i].hist[(est->ntemp % HIST_LEN)] = temp; | 141 | subdevice->sub_thz[i].hist[subdevice->ntemp] = temp; |
| 138 | } | 142 | } |
| 139 | 143 | ||
| 140 | for (i = 0; i < est->ndevs; i++) { | 144 | for (i = 0; i < subdevice->num_devs; i++) { |
| 145 | hist = subdevice->sub_thz[i].hist; | ||
| 146 | thz_coeffs = coeffs_set->coeffs[i]; | ||
| 141 | for (j = 0; j < HIST_LEN; j++) { | 147 | for (j = 0; j < HIST_LEN; j++) { |
| 142 | index = (est->ntemp - j + HIST_LEN) % HIST_LEN; | 148 | index = (subdevice->ntemp - j + HIST_LEN) % HIST_LEN; |
| 143 | sum += est->devs[i].hist[index] * | 149 | sum += hist[index] * thz_coeffs[j]; |
| 144 | est->devs[i].coeffs[j]; | ||
| 145 | } | 150 | } |
| 146 | } | 151 | } |
| 147 | 152 | ||
| 148 | est->cur_temp = sum / 100 + est->toffset; | 153 | est->cur_temp = sum / 100 + coeffs_set->toffset; |
| 149 | est->ntemp++; | ||
| 150 | 154 | ||
| 151 | if (est->thz && ((est->cur_temp < est->low_limit) || | 155 | if (est->thz && ((est->cur_temp < est->low_limit) || |
| 152 | (est->cur_temp >= est->high_limit))) { | 156 | (est->cur_temp >= est->high_limit))) { |
| @@ -194,18 +198,17 @@ static int therm_est_trip_update(void *of_data, int trip) | |||
| 194 | 198 | ||
| 195 | static int therm_est_init_history(struct therm_estimator *est) | 199 | static int therm_est_init_history(struct therm_estimator *est) |
| 196 | { | 200 | { |
| 197 | int i, j; | 201 | struct therm_est_sub_thz *sub_thz; |
| 198 | struct therm_est_subdevice *dev; | ||
| 199 | long temp; | 202 | long temp; |
| 203 | int i, j; | ||
| 200 | 204 | ||
| 201 | for (i = 0; i < est->ndevs; i++) { | 205 | sub_thz = est->subdevice->sub_thz; |
| 202 | dev = &est->devs[i]; | 206 | for (i = 0; i < est->subdevice->num_devs; i++) { |
| 203 | 207 | if (therm_est_subdev_get_temp(sub_thz[i].thz, &temp)) | |
| 204 | if (therm_est_subdev_get_temp(dev->sub_thz, &temp)) | ||
| 205 | return -EINVAL; | 208 | return -EINVAL; |
| 206 | 209 | ||
| 207 | for (j = 0; j < HIST_LEN; j++) | 210 | for (j = 0; j < HIST_LEN; j++) |
| 208 | dev->hist[j] = temp; | 211 | sub_thz[i].hist[j] = temp; |
| 209 | } | 212 | } |
| 210 | 213 | ||
| 211 | return 0; | 214 | return 0; |
| @@ -230,25 +233,57 @@ static int therm_est_polling(struct therm_estimator *est, | |||
| 230 | return 0; | 233 | return 0; |
| 231 | } | 234 | } |
| 232 | 235 | ||
| 236 | static int switch_active_coeffs(struct therm_estimator *est, int active_coeffs) | ||
| 237 | { | ||
| 238 | struct therm_est_subdevice *subdevice = est->subdevice; | ||
| 239 | |||
| 240 | if (active_coeffs < 0 || active_coeffs >= subdevice->num_coeffs) | ||
| 241 | return -EINVAL; | ||
| 242 | |||
| 243 | subdevice->active_coeffs = active_coeffs; | ||
| 244 | |||
| 245 | return 0; | ||
| 246 | } | ||
| 247 | |||
| 233 | static ssize_t show_coeff(struct device *dev, | 248 | static ssize_t show_coeff(struct device *dev, |
| 234 | struct device_attribute *da, | 249 | struct device_attribute *da, |
| 235 | char *buf) | 250 | char *buf) |
| 236 | { | 251 | { |
| 237 | struct therm_estimator *est = dev_get_drvdata(dev); | 252 | struct therm_estimator *est = dev_get_drvdata(dev); |
| 238 | ssize_t len, total_len = 0; | 253 | struct therm_est_subdevice *subdevice = est->subdevice; |
| 239 | int i, j; | 254 | struct therm_est_coeffs *coeffs_set; |
| 240 | for (i = 0; i < est->ndevs; i++) { | 255 | long *coeffs; |
| 241 | len = snprintf(buf + total_len, | 256 | ssize_t total_len = 0; |
| 242 | PAGE_SIZE - total_len, "[%d]", i); | 257 | int i, j, k; |
| 243 | total_len += len; | 258 | |
| 244 | for (j = 0; j < HIST_LEN; j++) { | 259 | total_len += snprintf(buf + total_len, PAGE_SIZE - total_len, |
| 245 | len = snprintf(buf + total_len, | 260 | "Total %02d set(s) are available\n\n", |
| 246 | PAGE_SIZE - total_len, " %ld", | 261 | subdevice->num_coeffs); |
| 247 | est->devs[i].coeffs[j]); | 262 | |
| 248 | total_len += len; | 263 | for (i = 0; i < subdevice->num_coeffs; i++) { |
| 264 | total_len += snprintf(buf + total_len, PAGE_SIZE - total_len, | ||
| 265 | "- SET %02d - %s\n", i, | ||
| 266 | (i == subdevice->active_coeffs)?"active":""); | ||
| 267 | |||
| 268 | coeffs_set = subdevice->coeffs_set + i; | ||
| 269 | for (j = 0; j < subdevice->num_devs; j++) { | ||
| 270 | total_len += snprintf(buf + total_len, | ||
| 271 | PAGE_SIZE - total_len, | ||
| 272 | "%s : ", | ||
| 273 | subdevice->sub_thz[j].thz->type); | ||
| 274 | coeffs = coeffs_set->coeffs[j]; | ||
| 275 | for (k = 0; k < HIST_LEN; k++) { | ||
| 276 | total_len += snprintf(buf + total_len, | ||
| 277 | PAGE_SIZE - total_len, " %ld", | ||
| 278 | coeffs[k]); | ||
| 279 | } | ||
| 280 | total_len += snprintf(buf + total_len, | ||
| 281 | PAGE_SIZE - total_len, "\n"); | ||
| 249 | } | 282 | } |
| 250 | len = snprintf(buf + total_len, PAGE_SIZE - total_len, "\n"); | 283 | total_len += snprintf(buf + total_len, |
| 251 | total_len += len; | 284 | PAGE_SIZE - total_len, |
| 285 | "toffset : %ld\n\n", | ||
| 286 | coeffs_set->toffset); | ||
| 252 | } | 287 | } |
| 253 | return strlen(buf); | 288 | return strlen(buf); |
| 254 | } | 289 | } |
| @@ -258,15 +293,15 @@ static ssize_t set_coeff(struct device *dev, | |||
| 258 | const char *buf, size_t count) | 293 | const char *buf, size_t count) |
| 259 | { | 294 | { |
| 260 | struct therm_estimator *est = dev_get_drvdata(dev); | 295 | struct therm_estimator *est = dev_get_drvdata(dev); |
| 261 | int devid, scount; | 296 | int coeffs_index, dev_index, scount; |
| 262 | long coeff[20]; | 297 | long coeff[20]; |
| 263 | 298 | ||
| 264 | if (HIST_LEN > 20) | 299 | if (HIST_LEN > 20) |
| 265 | return -EINVAL; | 300 | return -EINVAL; |
| 266 | 301 | ||
| 267 | scount = sscanf(buf, "[%d] %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld " \ | 302 | scount = sscanf(buf, "[%d][%d] %ld %ld %ld %ld %ld %ld %ld %ld %ld " \ |
| 268 | "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", | 303 | "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", |
| 269 | &devid, | 304 | &coeffs_index, &dev_index, |
| 270 | &coeff[0], | 305 | &coeff[0], |
| 271 | &coeff[1], | 306 | &coeff[1], |
| 272 | &coeff[2], | 307 | &coeff[2], |
| @@ -288,14 +323,19 @@ static ssize_t set_coeff(struct device *dev, | |||
| 288 | &coeff[18], | 323 | &coeff[18], |
| 289 | &coeff[19]); | 324 | &coeff[19]); |
| 290 | 325 | ||
| 291 | if (scount != HIST_LEN + 1) | 326 | if (scount != HIST_LEN + 2) |
| 292 | return -1; | 327 | return -1; |
| 293 | 328 | ||
| 294 | if (devid < 0 || devid >= est->ndevs) | 329 | if (dev_index < 0 || dev_index >= est->subdevice->num_devs) |
| 330 | return -EINVAL; | ||
| 331 | |||
| 332 | if (coeffs_index < 0 || coeffs_index >= est->subdevice->num_coeffs) | ||
| 295 | return -EINVAL; | 333 | return -EINVAL; |
| 296 | 334 | ||
| 297 | /* This has obvious locking issues but don't worry about it */ | 335 | /* This has obvious locking issues but don't worry about it */ |
| 298 | memcpy(est->devs[devid].coeffs, coeff, sizeof(coeff[0]) * HIST_LEN); | 336 | memcpy(est->subdevice->coeffs_set[coeffs_index].coeffs[dev_index], |
| 337 | coeff, | ||
| 338 | sizeof(coeff[0]) * HIST_LEN); | ||
| 299 | 339 | ||
| 300 | return count; | 340 | return count; |
| 301 | } | 341 | } |
| @@ -304,9 +344,7 @@ static ssize_t show_offset(struct device *dev, | |||
| 304 | struct device_attribute *da, | 344 | struct device_attribute *da, |
| 305 | char *buf) | 345 | char *buf) |
| 306 | { | 346 | { |
| 307 | struct therm_estimator *est = dev_get_drvdata(dev); | 347 | return show_coeff(dev, da, buf); |
| 308 | snprintf(buf, PAGE_SIZE, "%ld\n", est->toffset); | ||
| 309 | return strlen(buf); | ||
| 310 | } | 348 | } |
| 311 | 349 | ||
| 312 | static ssize_t set_offset(struct device *dev, | 350 | static ssize_t set_offset(struct device *dev, |
| @@ -314,12 +352,68 @@ static ssize_t set_offset(struct device *dev, | |||
| 314 | const char *buf, size_t count) | 352 | const char *buf, size_t count) |
| 315 | { | 353 | { |
| 316 | struct therm_estimator *est = dev_get_drvdata(dev); | 354 | struct therm_estimator *est = dev_get_drvdata(dev); |
| 317 | int offset; | 355 | int offset, scount, coeffs_index; |
| 318 | 356 | ||
| 319 | if (kstrtoint(buf, 0, &offset)) | 357 | scount = sscanf(buf, "[%d] %d", &coeffs_index, &offset); |
| 358 | if (scount != 2) | ||
| 320 | return -EINVAL; | 359 | return -EINVAL; |
| 321 | 360 | ||
| 322 | est->toffset = offset; | 361 | if (coeffs_index < 0 || coeffs_index >= est->subdevice->num_coeffs) |
| 362 | return -EINVAL; | ||
| 363 | |||
| 364 | est->subdevice->coeffs_set[coeffs_index].toffset = offset; | ||
| 365 | |||
| 366 | return count; | ||
| 367 | } | ||
| 368 | |||
| 369 | static ssize_t show_active_coeffs(struct device *dev, | ||
| 370 | struct device_attribute *da, | ||
| 371 | char *buf) | ||
| 372 | { | ||
| 373 | struct therm_estimator *est = dev_get_drvdata(dev); | ||
| 374 | struct therm_est_subdevice *subdevice = est->subdevice; | ||
| 375 | struct therm_est_coeffs *coeffs_set; | ||
| 376 | long *coeffs; | ||
| 377 | ssize_t total_len = 0; | ||
| 378 | int i, j; | ||
| 379 | |||
| 380 | total_len += snprintf(buf + total_len, PAGE_SIZE - total_len, | ||
| 381 | "There are %d set(s) and the active set is [%02d]\n\n", | ||
| 382 | subdevice->num_coeffs, subdevice->active_coeffs); | ||
| 383 | |||
| 384 | coeffs_set = subdevice->coeffs_set + subdevice->active_coeffs; | ||
| 385 | for (i = 0; i < subdevice->num_devs; i++) { | ||
| 386 | total_len += snprintf(buf + total_len, PAGE_SIZE - total_len, | ||
| 387 | "%s : ", subdevice->sub_thz[i].thz->type); | ||
| 388 | coeffs = coeffs_set->coeffs[i]; | ||
| 389 | for (j = 0; j < HIST_LEN; j++) { | ||
| 390 | total_len += snprintf(buf + total_len, | ||
| 391 | PAGE_SIZE - total_len, " %ld", | ||
| 392 | coeffs[j]); | ||
| 393 | } | ||
| 394 | total_len += snprintf(buf + total_len, | ||
| 395 | PAGE_SIZE - total_len, "\n"); | ||
| 396 | } | ||
| 397 | total_len += snprintf(buf + total_len, PAGE_SIZE - total_len, | ||
| 398 | "toffset : %ld\n", | ||
| 399 | coeffs_set->toffset); | ||
| 400 | |||
| 401 | return strlen(buf); | ||
| 402 | } | ||
| 403 | |||
| 404 | static ssize_t set_active_coeffs(struct device *dev, | ||
| 405 | struct device_attribute *da, | ||
| 406 | const char *buf, size_t count) | ||
| 407 | { | ||
| 408 | struct therm_estimator *est = dev_get_drvdata(dev); | ||
| 409 | int active_coeffs, ret; | ||
| 410 | |||
| 411 | if (kstrtoint(buf, 0, &active_coeffs)) | ||
| 412 | return -EINVAL; | ||
| 413 | |||
| 414 | ret = switch_active_coeffs(est, active_coeffs); | ||
| 415 | if (ret) | ||
| 416 | return ret; | ||
| 323 | 417 | ||
| 324 | return count; | 418 | return count; |
| 325 | } | 419 | } |
| @@ -329,19 +423,22 @@ static ssize_t show_temps(struct device *dev, | |||
| 329 | char *buf) | 423 | char *buf) |
| 330 | { | 424 | { |
| 331 | struct therm_estimator *est = dev_get_drvdata(dev); | 425 | struct therm_estimator *est = dev_get_drvdata(dev); |
| 426 | struct therm_est_subdevice *subdevice = est->subdevice; | ||
| 427 | long *hist; | ||
| 332 | ssize_t total_len = 0; | 428 | ssize_t total_len = 0; |
| 333 | int i, j; | 429 | int i, j; |
| 334 | int index; | 430 | int index; |
| 335 | 431 | ||
| 336 | /* This has obvious locking issues but don't worry about it */ | 432 | /* This has obvious locking issues but don't worry about it */ |
| 337 | for (i = 0; i < est->ndevs; i++) { | 433 | for (i = 0; i < subdevice->num_devs; i++) { |
| 338 | total_len += snprintf(buf + total_len, | 434 | total_len += snprintf(buf + total_len, |
| 339 | PAGE_SIZE - total_len, "[%d]", i); | 435 | PAGE_SIZE - total_len, "[%d]", i); |
| 436 | hist = subdevice->sub_thz[i].hist; | ||
| 340 | for (j = 0; j < HIST_LEN; j++) { | 437 | for (j = 0; j < HIST_LEN; j++) { |
| 341 | index = (est->ntemp - j + HIST_LEN) % HIST_LEN; | 438 | index = (subdevice->ntemp - j + HIST_LEN) % HIST_LEN; |
| 342 | total_len += snprintf(buf + total_len, | 439 | total_len += snprintf(buf + total_len, |
| 343 | PAGE_SIZE - total_len, " %ld", | 440 | PAGE_SIZE - total_len, " %ld", |
| 344 | est->devs[i].hist[index]); | 441 | hist[index]); |
| 345 | } | 442 | } |
| 346 | total_len += snprintf(buf + total_len, | 443 | total_len += snprintf(buf + total_len, |
| 347 | PAGE_SIZE - total_len, "\n"); | 444 | PAGE_SIZE - total_len, "\n"); |
| @@ -400,6 +497,8 @@ static ssize_t set_tc2(struct device *dev, | |||
| 400 | static struct sensor_device_attribute therm_est_nodes[] = { | 497 | static struct sensor_device_attribute therm_est_nodes[] = { |
| 401 | SENSOR_ATTR(coeff, S_IRUGO | S_IWUSR, show_coeff, set_coeff, 0), | 498 | SENSOR_ATTR(coeff, S_IRUGO | S_IWUSR, show_coeff, set_coeff, 0), |
| 402 | SENSOR_ATTR(offset, S_IRUGO | S_IWUSR, show_offset, set_offset, 0), | 499 | SENSOR_ATTR(offset, S_IRUGO | S_IWUSR, show_offset, set_offset, 0), |
| 500 | SENSOR_ATTR(active_coeffs, S_IRUGO | S_IWUSR, show_active_coeffs, | ||
| 501 | set_active_coeffs, 0), | ||
| 403 | SENSOR_ATTR(tc1, S_IRUGO | S_IWUSR, show_tc1, set_tc1, 0), | 502 | SENSOR_ATTR(tc1, S_IRUGO | S_IWUSR, show_tc1, set_tc1, 0), |
| 404 | SENSOR_ATTR(tc2, S_IRUGO | S_IWUSR, show_tc2, set_tc2, 0), | 503 | SENSOR_ATTR(tc2, S_IRUGO | S_IWUSR, show_tc2, set_tc2, 0), |
| 405 | SENSOR_ATTR(temps, S_IRUGO, show_temps, 0, 0), | 504 | SENSOR_ATTR(temps, S_IRUGO, show_temps, 0, 0), |
| @@ -485,57 +584,110 @@ struct thermal_cooling_device *thermal_est_activation_device_register( | |||
| 485 | return cdev; | 584 | return cdev; |
| 486 | } | 585 | } |
| 487 | 586 | ||
| 488 | static int __parse_dt_subdev(struct device_node *np, | 587 | static int therm_est_get_subdev(struct device *dev, |
| 489 | struct therm_est_subdevice *subdev) | 588 | struct device_node *subdev_np, |
| 589 | struct therm_est_subdevice *subdevice) | ||
| 490 | { | 590 | { |
| 491 | const char *str; | 591 | struct thermal_zone_device *thz; |
| 492 | char *sbegin; | 592 | struct device_node *coeffs_np; |
| 493 | int i = 0; | 593 | char *thz_name; |
| 494 | int ret; | 594 | u32 *values; |
| 595 | long *coeffs; | ||
| 596 | int num_subdevs; | ||
| 597 | int num_coeffs; | ||
| 598 | int i, j, ret = 0; | ||
| 599 | s32 val; | ||
| 600 | |||
| 601 | num_subdevs = of_property_count_strings(subdev_np, "subdev_names"); | ||
| 602 | if (num_subdevs == 0) | ||
| 603 | return -ENOENT; | ||
| 604 | |||
| 605 | num_coeffs = of_get_child_count(subdev_np); | ||
| 606 | if (num_coeffs == 0) | ||
| 607 | return -ENOENT; | ||
| 608 | |||
| 609 | subdevice->sub_thz = devm_kzalloc(dev, | ||
| 610 | sizeof(*subdevice->sub_thz) * num_subdevs, GFP_KERNEL); | ||
| 611 | if (!subdevice->sub_thz) | ||
| 612 | return -ENOMEM; | ||
| 495 | 613 | ||
| 496 | subdev->dev_data = (void *)of_get_property(np, "dev-data", NULL); | 614 | subdevice->coeffs_set = devm_kzalloc(dev, |
| 497 | if (!subdev->dev_data) | 615 | sizeof(*subdevice->coeffs_set) * num_coeffs, |
| 498 | return -ENODATA; | 616 | GFP_KERNEL); |
| 617 | if (!subdevice->coeffs_set) | ||
| 618 | return -ENOMEM; | ||
| 499 | 619 | ||
| 500 | ret = of_property_read_string(np, "coeffs", &str); | 620 | for (i = 0; i < num_coeffs; i++) { |
| 501 | if (ret < 0) | 621 | subdevice->coeffs_set[i].coeffs = devm_kzalloc(dev, |
| 502 | return ret; | 622 | sizeof(*subdevice->coeffs_set->coeffs) * num_subdevs, |
| 623 | GFP_KERNEL); | ||
| 624 | if (!subdevice->coeffs_set[i].coeffs) | ||
| 625 | return -ENOMEM; | ||
| 626 | } | ||
| 503 | 627 | ||
| 504 | while (str && (i < HIST_LEN)) { | 628 | for (i = 0; i < num_subdevs; i++) { |
| 505 | str = skip_spaces(str); | 629 | ret = of_property_read_string_index(subdev_np, |
| 506 | sbegin = strsep((char **)&str, " "); | 630 | "subdev_names", |
| 507 | if (!sbegin || (kstrtol((const char *)sbegin, 10, | 631 | i, (const char**)&thz_name); |
| 508 | &subdev->coeffs[i++]) < 0)) | 632 | if (ret) |
| 509 | break; | 633 | return -EINVAL; |
| 634 | |||
| 635 | thz = thermal_zone_device_find(thz_name, | ||
| 636 | therm_est_subdev_match); | ||
| 637 | if (!thz) | ||
| 638 | return -EINVAL; | ||
| 639 | subdevice->sub_thz[i].thz = thz; | ||
| 510 | } | 640 | } |
| 511 | if (i != HIST_LEN) | ||
| 512 | return -EINVAL; | ||
| 513 | 641 | ||
| 514 | return 0; | 642 | values = kzalloc(sizeof(u32) * num_subdevs * HIST_LEN, GFP_KERNEL); |
| 643 | if (!values) | ||
| 644 | return -ENOMEM; | ||
| 645 | i = 0; | ||
| 646 | for_each_child_of_node(subdev_np, coeffs_np) { | ||
| 647 | ret = of_property_read_s32(coeffs_np, "toffset", &val); | ||
| 648 | if (ret) | ||
| 649 | goto err; | ||
| 650 | |||
| 651 | subdevice->coeffs_set[i].toffset = val; | ||
| 652 | ret = of_property_read_u32_array(coeffs_np, "coeffs", values, | ||
| 653 | num_subdevs * HIST_LEN); | ||
| 654 | if (ret) | ||
| 655 | goto err; | ||
| 656 | |||
| 657 | coeffs = (long*)subdevice->coeffs_set[i].coeffs; | ||
| 658 | for (j = 0; j < num_subdevs * HIST_LEN; j++) { | ||
| 659 | val = values[j]; | ||
| 660 | coeffs[j] = (s32)((val & 0x80000000U) ? | ||
| 661 | -((val ^ 0xFFFFFFFFU) + 1) : val); | ||
| 662 | } | ||
| 663 | |||
| 664 | i++; | ||
| 665 | } | ||
| 666 | of_node_put(coeffs_np); | ||
| 667 | |||
| 668 | subdevice->num_coeffs = num_coeffs; | ||
| 669 | subdevice->num_devs = num_subdevs; | ||
| 670 | |||
| 671 | err: | ||
| 672 | kfree(values); | ||
| 673 | return ret; | ||
| 515 | } | 674 | } |
| 516 | 675 | ||
| 517 | static struct therm_est_data *therm_est_get_pdata(struct device *dev) | 676 | static struct therm_est_data *therm_est_get_pdata(struct device *dev) |
| 518 | { | 677 | { |
| 678 | struct device_node *np, *subdev_np; | ||
| 519 | struct therm_est_data *data; | 679 | struct therm_est_data *data; |
| 520 | struct device_node *np; | ||
| 521 | struct device_node *ch; | ||
| 522 | u32 val; | 680 | u32 val; |
| 523 | int num_subdev; | ||
| 524 | int ret; | 681 | int ret; |
| 525 | 682 | ||
| 526 | np = dev->of_node; | 683 | np = dev->of_node; |
| 527 | if (!np) | 684 | if (!np) |
| 528 | return dev->platform_data; | 685 | return ERR_PTR(-ENOENT); |
| 529 | 686 | ||
| 530 | data = devm_kzalloc(dev, sizeof(struct therm_est_data), GFP_KERNEL); | 687 | data = devm_kzalloc(dev, sizeof(struct therm_est_data), GFP_KERNEL); |
| 531 | if (!data) | 688 | if (!data) |
| 532 | return ERR_PTR(-ENOMEM); | 689 | return ERR_PTR(-ENOMEM); |
| 533 | 690 | ||
| 534 | ret = of_property_read_u32(np, "toffset", &val); | ||
| 535 | if (ret < 0) | ||
| 536 | return ERR_PTR(ret); | ||
| 537 | data->toffset = val; | ||
| 538 | |||
| 539 | ret = of_property_read_u32(np, "polling-period", &val); | 691 | ret = of_property_read_u32(np, "polling-period", &val); |
| 540 | if (ret < 0) | 692 | if (ret < 0) |
| 541 | return ERR_PTR(ret); | 693 | return ERR_PTR(ret); |
| @@ -556,30 +708,15 @@ static struct therm_est_data *therm_est_get_pdata(struct device *dev) | |||
| 556 | return ERR_PTR(ret); | 708 | return ERR_PTR(ret); |
| 557 | data->use_activator = val; | 709 | data->use_activator = val; |
| 558 | 710 | ||
| 559 | num_subdev = 0; | 711 | subdev_np = of_get_child_by_name(np, "subdev"); |
| 560 | ch = np; | 712 | if (!subdev_np) |
| 561 | while ((ch = of_find_node_by_type(ch, "therm-est-subdev"))) | ||
| 562 | num_subdev++; | ||
| 563 | |||
| 564 | /* subdevices are must required data. */ | ||
| 565 | if (num_subdev == 0) | ||
| 566 | return ERR_PTR(-ENOENT); | 713 | return ERR_PTR(-ENOENT); |
| 567 | 714 | ||
| 568 | data->devs = devm_kzalloc(dev, | 715 | ret = therm_est_get_subdev(dev, subdev_np, &data->subdevice); |
| 569 | sizeof(struct therm_est_subdevice) * num_subdev, | 716 | if (ret) |
| 570 | GFP_KERNEL); | 717 | return ERR_PTR(ret); |
| 571 | if (!data->devs) | ||
| 572 | return ERR_PTR(-ENOMEM); | ||
| 573 | |||
| 574 | num_subdev = 0; | ||
| 575 | ch = np; | ||
| 576 | while ((ch = of_find_node_by_type(ch, "therm-est-subdev"))) { | ||
| 577 | ret = __parse_dt_subdev(ch, &data->devs[num_subdev++]); | ||
| 578 | if (ret < 0) | ||
| 579 | return ERR_PTR(ret); | ||
| 580 | } | ||
| 581 | 718 | ||
| 582 | data->ndevs = num_subdev; | 719 | of_node_put(subdev_np); |
| 583 | 720 | ||
| 584 | return data; | 721 | return data; |
| 585 | } | 722 | } |
| @@ -595,7 +732,6 @@ static int therm_est_probe(struct platform_device *pdev) | |||
| 595 | int i, ret; | 732 | int i, ret; |
| 596 | struct therm_estimator *est; | 733 | struct therm_estimator *est; |
| 597 | struct therm_est_data *data; | 734 | struct therm_est_data *data; |
| 598 | struct thermal_zone_device *thz; | ||
| 599 | 735 | ||
| 600 | est = kzalloc(sizeof(struct therm_estimator), GFP_KERNEL); | 736 | est = kzalloc(sizeof(struct therm_estimator), GFP_KERNEL); |
| 601 | if (IS_ERR_OR_NULL(est)) | 737 | if (IS_ERR_OR_NULL(est)) |
| @@ -604,22 +740,14 @@ static int therm_est_probe(struct platform_device *pdev) | |||
| 604 | platform_set_drvdata(pdev, est); | 740 | platform_set_drvdata(pdev, est); |
| 605 | 741 | ||
| 606 | data = pdev->dev.platform_data; | 742 | data = pdev->dev.platform_data; |
| 607 | if (!data) | 743 | if (!data) { |
| 608 | data = therm_est_get_pdata(&pdev->dev); | 744 | data = therm_est_get_pdata(&pdev->dev); |
| 609 | 745 | if (!data) | |
| 610 | for (i = 0; i < data->ndevs; i++) { | ||
| 611 | thz = thermal_zone_device_find(data->devs[i].dev_data, | ||
| 612 | therm_est_subdev_match); | ||
| 613 | if (!thz) | ||
| 614 | goto err; | 746 | goto err; |
| 615 | data->devs[i].sub_thz = thz; | ||
| 616 | } | 747 | } |
| 617 | 748 | ||
| 618 | est->devs = data->devs; | 749 | est->subdevice = &data->subdevice; |
| 619 | est->ndevs = data->ndevs; | ||
| 620 | est->toffset = data->toffset; | ||
| 621 | est->polling_period = data->polling_period; | 750 | est->polling_period = data->polling_period; |
| 622 | est->polling_enabled = 0; /* By default polling is switched off */ | ||
| 623 | est->tc1 = data->tc1; | 751 | est->tc1 = data->tc1; |
| 624 | est->tc2 = data->tc2; | 752 | est->tc2 = data->tc2; |
| 625 | est->cur_temp = DEFAULT_TSKIN; | 753 | est->cur_temp = DEFAULT_TSKIN; |
diff --git a/include/linux/therm_est.h b/include/linux/therm_est.h index 9630bd046..a67739483 100644 --- a/include/linux/therm_est.h +++ b/include/linux/therm_est.h | |||
| @@ -26,13 +26,25 @@ | |||
| 26 | #define MAX_ACTIVE_STATES 10 | 26 | #define MAX_ACTIVE_STATES 10 |
| 27 | #define MAX_TIMER_TRIPS 10 | 27 | #define MAX_TIMER_TRIPS 10 |
| 28 | 28 | ||
| 29 | struct therm_est_subdevice { | 29 | struct therm_est_sub_thz { |
| 30 | void *dev_data; | 30 | struct thermal_zone_device *thz; |
| 31 | struct thermal_zone_device *sub_thz; | ||
| 32 | long coeffs[HIST_LEN]; | ||
| 33 | long hist[HIST_LEN]; | 31 | long hist[HIST_LEN]; |
| 34 | }; | 32 | }; |
| 35 | 33 | ||
| 34 | struct therm_est_coeffs { | ||
| 35 | long toffset; | ||
| 36 | long (*coeffs)[HIST_LEN]; | ||
| 37 | }; | ||
| 38 | |||
| 39 | struct therm_est_subdevice { | ||
| 40 | struct therm_est_sub_thz *sub_thz; | ||
| 41 | struct therm_est_coeffs *coeffs_set; | ||
| 42 | int num_devs; | ||
| 43 | int num_coeffs; | ||
| 44 | int active_coeffs; | ||
| 45 | int ntemp; | ||
| 46 | }; | ||
| 47 | |||
| 36 | /* | 48 | /* |
| 37 | * Timer trip provides a way to change trip temp dynamically based on timestamp | 49 | * Timer trip provides a way to change trip temp dynamically based on timestamp |
| 38 | * when the trip is enabled. | 50 | * when the trip is enabled. |
| @@ -74,13 +86,11 @@ struct therm_est_data { | |||
| 74 | 86 | ||
| 75 | /* zone parameters */ | 87 | /* zone parameters */ |
| 76 | struct thermal_zone_params *tzp; | 88 | struct thermal_zone_params *tzp; |
| 77 | long toffset; | ||
| 78 | long polling_period; | 89 | long polling_period; |
| 79 | int passive_delay; | 90 | int passive_delay; |
| 80 | int tc1; | 91 | int tc1; |
| 81 | int tc2; | 92 | int tc2; |
| 82 | int ndevs; | 93 | struct therm_est_subdevice subdevice; |
| 83 | struct therm_est_subdevice *devs; | ||
| 84 | int use_activator; | 94 | int use_activator; |
| 85 | }; | 95 | }; |
| 86 | 96 | ||
