Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'thermal-core'

Merge thermal core updates for 6.12 which, among other things, rework
the thermal driver interface for binding cooling devices to thermal
zones and add a thermal core testing module:

- Update some thermal drivers to eliminate thermal_zone_get_trip()
calls from them and get rid of that function (Rafael Wysocki).

- Update the thermal sysfs code to store trip point attributes in trip
descriptors and get to trip points via attribute pointers (Rafael
Wysocki).

- Move the computation of the low and high boundaries for
thermal_zone_set_trips() to __thermal_zone_device_update() (Daniel
Lezcano).

- Introduce a debugfs-based facility for thermal core testing (Rafael
Wysocki).

- Replace the thermal zone .bind() and .unbind() callbacks for binding
cooling devices to thermal zones with one .should_bind() callback
used for deciding whether or not a given cooling devices should be
bound to a given trip point in a given thermal zone (Rafael Wysocki).

- Eliminate code that has no more users after the other changes, drop
some redundant checks from the thermal core and clean it up (Rafael
Wysocki).

- Fix rounding of delay jiffies in the thermal core (Rafael Wysocki).

* thermal-core: (31 commits)
thermal: core: Drop tz field from struct thermal_instance
thermal: core: Drop redundant checks from thermal_bind_cdev_to_trip()
thermal: core: Rename cdev-to-thermal-zone bind/unbind functions
thermal: core: Fix rounding of delay jiffies
thermal: core: Clean up trip bind/unbind functions
thermal: core: Drop unused bind/unbind functions and callbacks
thermal/of: Use the .should_bind() thermal zone callback
thermal: imx: Use the .should_bind() thermal zone callback
mlxsw: core_thermal: Use the .should_bind() thermal zone callback
platform/x86: acerhdf: Use the .should_bind() thermal zone callback
thermal: core: Unexport thermal_bind_cdev_to_trip() and thermal_unbind_cdev_from_trip()
thermal: ACPI: Use the .should_bind() thermal zone callback
thermal: core: Introduce .should_bind() thermal zone callback
thermal: core: Move thermal zone locking out of bind/unbind functions
thermal: sysfs: Use the dev argument in instance-related show/store
thermal: core: Drop redundant thermal instance checks
thermal: core: Rearrange checks in thermal_bind_cdev_to_trip()
thermal: core: Fold two functions into their respective callers
thermal: Introduce a debugfs-based testing facility
thermal/core: Compute low and high boundaries in thermal_zone_device_update()
...

+1075 -818
+4 -64
Documentation/driver-api/thermal/sysfs-api.rst
··· 58 58 ops: 59 59 thermal zone device call-backs. 60 60 61 - .bind: 62 - bind the thermal zone device with a thermal cooling device. 63 - .unbind: 64 - unbind the thermal zone device with a thermal cooling device. 61 + .should_bind: 62 + check whether or not a given cooling device should be bound to 63 + a given trip point in this thermal zone. 65 64 .get_temp: 66 65 get the current temperature of the thermal zone. 67 66 .set_trips: ··· 245 246 It deletes the corresponding entry from /sys/class/thermal folder and 246 247 unbinds itself from all the thermal zone devices using it. 247 248 248 - 1.3 interface for binding a thermal zone device with a thermal cooling device 249 - ----------------------------------------------------------------------------- 250 - 251 - :: 252 - 253 - int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, 254 - int trip, struct thermal_cooling_device *cdev, 255 - unsigned long upper, unsigned long lower, unsigned int weight); 256 - 257 - This interface function binds a thermal cooling device to a particular trip 258 - point of a thermal zone device. 259 - 260 - This function is usually called in the thermal zone device .bind callback. 261 - 262 - tz: 263 - the thermal zone device 264 - cdev: 265 - thermal cooling device 266 - trip: 267 - indicates which trip point in this thermal zone the cooling device 268 - is associated with. 269 - upper: 270 - the Maximum cooling state for this trip point. 271 - THERMAL_NO_LIMIT means no upper limit, 272 - and the cooling device can be in max_state. 273 - lower: 274 - the Minimum cooling state can be used for this trip point. 275 - THERMAL_NO_LIMIT means no lower limit, 276 - and the cooling device can be in cooling state 0. 277 - weight: 278 - the influence of this cooling device in this thermal 279 - zone. See 1.4.1 below for more information. 280 - 281 - :: 282 - 283 - int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, 284 - int trip, struct thermal_cooling_device *cdev); 285 - 286 - This interface function unbinds a thermal cooling device from a particular 287 - trip point of a thermal zone device. This function is usually called in 288 - the thermal zone device .unbind callback. 289 - 290 - tz: 291 - the thermal zone device 292 - cdev: 293 - thermal cooling device 294 - trip: 295 - indicates which trip point in this thermal zone the cooling device 296 - is associated with. 297 - 298 249 1.4 Thermal Zone Parameters 299 250 --------------------------- 300 251 ··· 315 366 316 367 Then next two dynamic attributes are created/removed in pairs. They represent 317 368 the relationship between a thermal zone and its associated cooling device. 318 - They are created/removed for each successful execution of 319 - thermal_zone_bind_cooling_device/thermal_zone_unbind_cooling_device. 320 369 321 370 :: 322 371 ··· 406 459 framework calculated the trend by comparing the previous and the current 407 460 temperature values. 408 461 409 - 4.2. get_thermal_instance 410 - ------------------------- 411 - 412 - This function returns the thermal_instance corresponding to a given 413 - {thermal_zone, cooling_device, trip_point} combination. Returns NULL 414 - if such an instance does not exist. 415 - 416 - 4.3. thermal_cdev_update 462 + 4.2. thermal_cdev_update 417 463 ------------------------ 418 464 419 465 This function serves as an arbitrator to set the state of a cooling
+9 -55
drivers/acpi/thermal.c
··· 558 558 thermal_zone_device_critical(thermal); 559 559 } 560 560 561 - struct acpi_thermal_bind_data { 562 - struct thermal_zone_device *thermal; 563 - struct thermal_cooling_device *cdev; 564 - bool bind; 565 - }; 566 - 567 - static int bind_unbind_cdev_cb(struct thermal_trip *trip, void *arg) 561 + static bool acpi_thermal_should_bind_cdev(struct thermal_zone_device *thermal, 562 + const struct thermal_trip *trip, 563 + struct thermal_cooling_device *cdev, 564 + struct cooling_spec *c) 568 565 { 569 566 struct acpi_thermal_trip *acpi_trip = trip->priv; 570 - struct acpi_thermal_bind_data *bd = arg; 571 - struct thermal_zone_device *thermal = bd->thermal; 572 - struct thermal_cooling_device *cdev = bd->cdev; 573 567 struct acpi_device *cdev_adev = cdev->devdata; 574 568 int i; 575 569 576 570 /* Skip critical and hot trips. */ 577 571 if (!acpi_trip) 578 - return 0; 572 + return false; 579 573 580 574 for (i = 0; i < acpi_trip->devices.count; i++) { 581 575 acpi_handle handle = acpi_trip->devices.handles[i]; 582 - struct acpi_device *adev = acpi_fetch_acpi_dev(handle); 583 576 584 - if (adev != cdev_adev) 585 - continue; 586 - 587 - if (bd->bind) { 588 - int ret; 589 - 590 - ret = thermal_bind_cdev_to_trip(thermal, trip, cdev, 591 - THERMAL_NO_LIMIT, 592 - THERMAL_NO_LIMIT, 593 - THERMAL_WEIGHT_DEFAULT); 594 - if (ret) 595 - return ret; 596 - } else { 597 - thermal_unbind_cdev_from_trip(thermal, trip, cdev); 598 - } 577 + if (acpi_fetch_acpi_dev(handle) == cdev_adev) 578 + return true; 599 579 } 600 580 601 - return 0; 602 - } 603 - 604 - static int acpi_thermal_bind_unbind_cdev(struct thermal_zone_device *thermal, 605 - struct thermal_cooling_device *cdev, 606 - bool bind) 607 - { 608 - struct acpi_thermal_bind_data bd = { 609 - .thermal = thermal, .cdev = cdev, .bind = bind 610 - }; 611 - 612 - return for_each_thermal_trip(thermal, bind_unbind_cdev_cb, &bd); 613 - } 614 - 615 - static int 616 - acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal, 617 - struct thermal_cooling_device *cdev) 618 - { 619 - return acpi_thermal_bind_unbind_cdev(thermal, cdev, true); 620 - } 621 - 622 - static int 623 - acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal, 624 - struct thermal_cooling_device *cdev) 625 - { 626 - return acpi_thermal_bind_unbind_cdev(thermal, cdev, false); 581 + return false; 627 582 } 628 583 629 584 static const struct thermal_zone_device_ops acpi_thermal_zone_ops = { 630 - .bind = acpi_thermal_bind_cooling_device, 631 - .unbind = acpi_thermal_unbind_cooling_device, 585 + .should_bind = acpi_thermal_should_bind_cdev, 632 586 .get_temp = thermal_get_temp, 633 587 .get_trend = thermal_get_trend, 634 588 .hot = acpi_thermal_zone_device_hot,
+31 -84
drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
··· 165 165 return -ENODEV; 166 166 } 167 167 168 - static int mlxsw_thermal_bind(struct thermal_zone_device *tzdev, 169 - struct thermal_cooling_device *cdev) 168 + static bool mlxsw_thermal_should_bind(struct thermal_zone_device *tzdev, 169 + const struct thermal_trip *trip, 170 + struct thermal_cooling_device *cdev, 171 + struct cooling_spec *c) 170 172 { 171 173 struct mlxsw_thermal *thermal = thermal_zone_device_priv(tzdev); 172 - struct device *dev = thermal->bus_info->dev; 173 - int i, err; 174 + const struct mlxsw_cooling_states *state = trip->priv; 174 175 175 176 /* If the cooling device is one of ours bind it */ 176 177 if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0) 177 - return 0; 178 + return false; 178 179 179 - for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) { 180 - const struct mlxsw_cooling_states *state = &thermal->cooling_states[i]; 180 + c->upper = state->max_state; 181 + c->lower = state->min_state; 181 182 182 - err = thermal_zone_bind_cooling_device(tzdev, i, cdev, 183 - state->max_state, 184 - state->min_state, 185 - THERMAL_WEIGHT_DEFAULT); 186 - if (err < 0) { 187 - dev_err(dev, "Failed to bind cooling device to trip %d\n", i); 188 - return err; 189 - } 190 - } 191 - return 0; 192 - } 193 - 194 - static int mlxsw_thermal_unbind(struct thermal_zone_device *tzdev, 195 - struct thermal_cooling_device *cdev) 196 - { 197 - struct mlxsw_thermal *thermal = thermal_zone_device_priv(tzdev); 198 - struct device *dev = thermal->bus_info->dev; 199 - int i; 200 - int err; 201 - 202 - /* If the cooling device is our one unbind it */ 203 - if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0) 204 - return 0; 205 - 206 - for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) { 207 - err = thermal_zone_unbind_cooling_device(tzdev, i, cdev); 208 - if (err < 0) { 209 - dev_err(dev, "Failed to unbind cooling device\n"); 210 - return err; 211 - } 212 - } 213 - return 0; 183 + return true; 214 184 } 215 185 216 186 static int mlxsw_thermal_get_temp(struct thermal_zone_device *tzdev, ··· 210 240 }; 211 241 212 242 static struct thermal_zone_device_ops mlxsw_thermal_ops = { 213 - .bind = mlxsw_thermal_bind, 214 - .unbind = mlxsw_thermal_unbind, 243 + .should_bind = mlxsw_thermal_should_bind, 215 244 .get_temp = mlxsw_thermal_get_temp, 216 245 }; 217 246 218 - static int mlxsw_thermal_module_bind(struct thermal_zone_device *tzdev, 219 - struct thermal_cooling_device *cdev) 247 + static bool mlxsw_thermal_module_should_bind(struct thermal_zone_device *tzdev, 248 + const struct thermal_trip *trip, 249 + struct thermal_cooling_device *cdev, 250 + struct cooling_spec *c) 220 251 { 221 252 struct mlxsw_thermal_module *tz = thermal_zone_device_priv(tzdev); 253 + const struct mlxsw_cooling_states *state = trip->priv; 222 254 struct mlxsw_thermal *thermal = tz->parent; 223 - int i, j, err; 224 255 225 256 /* If the cooling device is one of ours bind it */ 226 257 if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0) 227 - return 0; 258 + return false; 228 259 229 - for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) { 230 - const struct mlxsw_cooling_states *state = &tz->cooling_states[i]; 260 + c->upper = state->max_state; 261 + c->lower = state->min_state; 231 262 232 - err = thermal_zone_bind_cooling_device(tzdev, i, cdev, 233 - state->max_state, 234 - state->min_state, 235 - THERMAL_WEIGHT_DEFAULT); 236 - if (err < 0) 237 - goto err_thermal_zone_bind_cooling_device; 238 - } 239 - return 0; 240 - 241 - err_thermal_zone_bind_cooling_device: 242 - for (j = i - 1; j >= 0; j--) 243 - thermal_zone_unbind_cooling_device(tzdev, j, cdev); 244 - return err; 245 - } 246 - 247 - static int mlxsw_thermal_module_unbind(struct thermal_zone_device *tzdev, 248 - struct thermal_cooling_device *cdev) 249 - { 250 - struct mlxsw_thermal_module *tz = thermal_zone_device_priv(tzdev); 251 - struct mlxsw_thermal *thermal = tz->parent; 252 - int i; 253 - int err; 254 - 255 - /* If the cooling device is one of ours unbind it */ 256 - if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0) 257 - return 0; 258 - 259 - for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) { 260 - err = thermal_zone_unbind_cooling_device(tzdev, i, cdev); 261 - WARN_ON(err); 262 - } 263 - return err; 263 + return true; 264 264 } 265 265 266 266 static int mlxsw_thermal_module_temp_get(struct thermal_zone_device *tzdev, ··· 253 313 } 254 314 255 315 static struct thermal_zone_device_ops mlxsw_thermal_module_ops = { 256 - .bind = mlxsw_thermal_module_bind, 257 - .unbind = mlxsw_thermal_module_unbind, 316 + .should_bind = mlxsw_thermal_module_should_bind, 258 317 .get_temp = mlxsw_thermal_module_temp_get, 259 318 }; 260 319 ··· 281 342 } 282 343 283 344 static struct thermal_zone_device_ops mlxsw_thermal_gearbox_ops = { 284 - .bind = mlxsw_thermal_module_bind, 285 - .unbind = mlxsw_thermal_module_unbind, 345 + .should_bind = mlxsw_thermal_module_should_bind, 286 346 .get_temp = mlxsw_thermal_gearbox_temp_get, 287 347 }; 288 348 ··· 389 451 struct mlxsw_thermal_area *area, u8 module) 390 452 { 391 453 struct mlxsw_thermal_module *module_tz; 454 + int i; 392 455 393 456 module_tz = &area->tz_module_arr[module]; 394 457 /* Skip if parent is already set (case of port split). */ ··· 404 465 sizeof(thermal->trips)); 405 466 memcpy(module_tz->cooling_states, default_cooling_states, 406 467 sizeof(thermal->cooling_states)); 468 + for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) 469 + module_tz->trips[i].priv = &module_tz->cooling_states[i]; 407 470 } 408 471 409 472 static void mlxsw_thermal_module_fini(struct mlxsw_thermal_module *module_tz) ··· 520 579 struct mlxsw_thermal_module *gearbox_tz; 521 580 char mgpir_pl[MLXSW_REG_MGPIR_LEN]; 522 581 u8 gbox_num; 523 - int i; 582 + int i, j; 524 583 int err; 525 584 526 585 mlxsw_reg_mgpir_pack(mgpir_pl, area->slot_index); ··· 547 606 sizeof(thermal->trips)); 548 607 memcpy(gearbox_tz->cooling_states, default_cooling_states, 549 608 sizeof(thermal->cooling_states)); 609 + for (j = 0; j < MLXSW_THERMAL_NUM_TRIPS; j++) 610 + gearbox_tz->trips[j].priv = &gearbox_tz->cooling_states[j]; 611 + 550 612 gearbox_tz->module = i; 551 613 gearbox_tz->parent = thermal; 552 614 gearbox_tz->slot_index = area->slot_index; ··· 666 722 thermal->bus_info = bus_info; 667 723 memcpy(thermal->trips, default_thermal_trips, sizeof(thermal->trips)); 668 724 memcpy(thermal->cooling_states, default_cooling_states, sizeof(thermal->cooling_states)); 725 + for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) 726 + thermal->trips[i].priv = &thermal->cooling_states[i]; 727 + 669 728 thermal->line_cards[0].slot_index = 0; 670 729 671 730 err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfcr), mfcr_pl);
+6 -27
drivers/platform/x86/acerhdf.c
··· 378 378 return 0; 379 379 } 380 380 381 - static int acerhdf_bind(struct thermal_zone_device *thermal, 382 - struct thermal_cooling_device *cdev) 381 + static bool acerhdf_should_bind(struct thermal_zone_device *thermal, 382 + const struct thermal_trip *trip, 383 + struct thermal_cooling_device *cdev, 384 + struct cooling_spec *c) 383 385 { 384 386 /* if the cooling device is the one from acerhdf bind it */ 385 - if (cdev != cl_dev) 386 - return 0; 387 - 388 - if (thermal_zone_bind_cooling_device(thermal, 0, cdev, 389 - THERMAL_NO_LIMIT, THERMAL_NO_LIMIT, 390 - THERMAL_WEIGHT_DEFAULT)) { 391 - pr_err("error binding cooling dev\n"); 392 - return -EINVAL; 393 - } 394 - return 0; 395 - } 396 - 397 - static int acerhdf_unbind(struct thermal_zone_device *thermal, 398 - struct thermal_cooling_device *cdev) 399 - { 400 - if (cdev != cl_dev) 401 - return 0; 402 - 403 - if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) { 404 - pr_err("error unbinding cooling dev\n"); 405 - return -EINVAL; 406 - } 407 - return 0; 387 + return cdev == cl_dev && trip->type == THERMAL_TRIP_ACTIVE; 408 388 } 409 389 410 390 static inline void acerhdf_revert_to_bios_mode(void) ··· 427 447 428 448 /* bind callback functions to thermalzone */ 429 449 static struct thermal_zone_device_ops acerhdf_dev_ops = { 430 - .bind = acerhdf_bind, 431 - .unbind = acerhdf_unbind, 450 + .should_bind = acerhdf_should_bind, 432 451 .get_temp = acerhdf_get_ec_temp, 433 452 .change_mode = acerhdf_change_mode, 434 453 .get_crit_temp = acerhdf_get_crit_temp,
+9
drivers/thermal/Kconfig
··· 40 40 Say Y to allow the thermal subsystem to collect diagnostic 41 41 information that can be accessed via debugfs. 42 42 43 + config THERMAL_CORE_TESTING 44 + tristate "Thermal core testing facility" 45 + depends on DEBUG_FS 46 + help 47 + Say Y to add a debugfs-based thermal core testing facility. 48 + It allows test thermal zones to be created and populated 49 + with trip points in order to exercise the thermal core 50 + functionality in a controlled way. 51 + 43 52 config THERMAL_EMERGENCY_POWEROFF_DELAY_MS 44 53 int "Emergency poweroff delay in milli-seconds" 45 54 default 0
+1
drivers/thermal/Makefile
··· 63 63 obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o 64 64 obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL) += khadas_mcu_fan.o 65 65 obj-$(CONFIG_LOONGSON2_THERMAL) += loongson2_thermal.o 66 + obj-$(CONFIG_THERMAL_CORE_TESTING) += testing/
+3 -4
drivers/thermal/broadcom/bcm2835_thermal.c
··· 208 208 */ 209 209 val = readl(data->regs + BCM2835_TS_TSENSCTL); 210 210 if (!(val & BCM2835_TS_TSENSCTL_RSTB)) { 211 - struct thermal_trip trip; 212 - int offset, slope; 211 + int offset, slope, crit_temp; 213 212 214 213 slope = thermal_zone_get_slope(tz); 215 214 offset = thermal_zone_get_offset(tz); ··· 216 217 * For now we deal only with critical, otherwise 217 218 * would need to iterate 218 219 */ 219 - err = thermal_zone_get_trip(tz, 0, &trip); 220 + err = thermal_zone_get_crit_temp(tz, &crit_temp); 220 221 if (err < 0) { 221 222 dev_err(dev, "Not able to read trip_temp: %d\n", err); 222 223 return err; ··· 231 232 val |= (0xFE << BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT); 232 233 233 234 /* trip_adc value from info */ 234 - val |= bcm2835_thermal_temp2adc(trip.temperature, 235 + val |= bcm2835_thermal_temp2adc(crit_temp, 235 236 offset, 236 237 slope) 237 238 << BCM2835_TS_TSENSCTL_THOLD_SHIFT;
+14 -11
drivers/thermal/hisi_thermal.c
··· 465 465 return IRQ_HANDLED; 466 466 } 467 467 468 + static int hisi_trip_walk_cb(struct thermal_trip *trip, void *arg) 469 + { 470 + struct hisi_thermal_sensor *sensor = arg; 471 + 472 + if (trip->type != THERMAL_TRIP_PASSIVE) 473 + return 0; 474 + 475 + sensor->thres_temp = trip->temperature; 476 + /* Return nonzero to terminate the search. */ 477 + return 1; 478 + } 479 + 468 480 static int hisi_thermal_register_sensor(struct platform_device *pdev, 469 481 struct hisi_thermal_sensor *sensor) 470 482 { 471 - int ret, i; 472 - struct thermal_trip trip; 483 + int ret; 473 484 474 485 sensor->tzd = devm_thermal_of_zone_register(&pdev->dev, 475 486 sensor->id, sensor, ··· 493 482 return ret; 494 483 } 495 484 496 - for (i = 0; i < thermal_zone_get_num_trips(sensor->tzd); i++) { 497 - 498 - thermal_zone_get_trip(sensor->tzd, i, &trip); 499 - 500 - if (trip.type == THERMAL_TRIP_PASSIVE) { 501 - sensor->thres_temp = trip.temperature; 502 - break; 503 - } 504 - } 485 + thermal_zone_for_each_trip(sensor->tzd, hisi_trip_walk_cb, sensor); 505 486 506 487 return 0; 507 488 }
+6 -14
drivers/thermal/imx_thermal.c
··· 353 353 return 0; 354 354 } 355 355 356 - static int imx_bind(struct thermal_zone_device *tz, 357 - struct thermal_cooling_device *cdev) 356 + static bool imx_should_bind(struct thermal_zone_device *tz, 357 + const struct thermal_trip *trip, 358 + struct thermal_cooling_device *cdev, 359 + struct cooling_spec *c) 358 360 { 359 - return thermal_zone_bind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev, 360 - THERMAL_NO_LIMIT, 361 - THERMAL_NO_LIMIT, 362 - THERMAL_WEIGHT_DEFAULT); 363 - } 364 - 365 - static int imx_unbind(struct thermal_zone_device *tz, 366 - struct thermal_cooling_device *cdev) 367 - { 368 - return thermal_zone_unbind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev); 361 + return trip->type == THERMAL_TRIP_PASSIVE; 369 362 } 370 363 371 364 static struct thermal_zone_device_ops imx_tz_ops = { 372 - .bind = imx_bind, 373 - .unbind = imx_unbind, 365 + .should_bind = imx_should_bind, 374 366 .get_temp = imx_get_temp, 375 367 .change_mode = imx_change_mode, 376 368 .set_trip_temp = imx_set_trip_temp,
+3 -19
drivers/thermal/qcom/qcom-spmi-temp-alarm.c
··· 291 291 return IRQ_HANDLED; 292 292 } 293 293 294 - static int qpnp_tm_get_critical_trip_temp(struct qpnp_tm_chip *chip) 295 - { 296 - struct thermal_trip trip; 297 - int i, ret; 298 - 299 - for (i = 0; i < thermal_zone_get_num_trips(chip->tz_dev); i++) { 300 - 301 - ret = thermal_zone_get_trip(chip->tz_dev, i, &trip); 302 - if (ret) 303 - continue; 304 - 305 - if (trip.type == THERMAL_TRIP_CRITICAL) 306 - return trip.temperature; 307 - } 308 - 309 - return THERMAL_TEMP_INVALID; 310 - } 311 - 312 294 /* 313 295 * This function initializes the internal temp value based on only the 314 296 * current thermal stage and threshold. Setup threshold control and ··· 325 343 326 344 mutex_unlock(&chip->lock); 327 345 328 - crit_temp = qpnp_tm_get_critical_trip_temp(chip); 346 + ret = thermal_zone_get_crit_temp(chip->tz_dev, &crit_temp); 347 + if (ret) 348 + crit_temp = THERMAL_TEMP_INVALID; 329 349 330 350 mutex_lock(&chip->lock); 331 351
+1 -5
drivers/thermal/renesas/rcar_gen3_thermal.c
··· 563 563 if (ret) 564 564 goto error_unregister; 565 565 566 - ret = thermal_zone_get_num_trips(tsc->zone); 567 - if (ret < 0) 568 - goto error_unregister; 569 - 570 - dev_info(dev, "Sensor %u: Loaded %d trip points\n", i, ret); 566 + dev_info(dev, "Sensor %u: Loaded\n", i); 571 567 } 572 568 573 569 if (!priv->num_tscs) {
+19 -17
drivers/thermal/tegra/soctherm.c
··· 682 682 .set_trips = tegra_thermctl_set_trips, 683 683 }; 684 684 685 - static int get_hot_temp(struct thermal_zone_device *tz, int *trip_id, int *temp) 685 + static int get_hot_trip_cb(struct thermal_trip *trip, void *arg) 686 686 { 687 - int i, ret; 688 - struct thermal_trip trip; 687 + const struct thermal_trip **trip_ret = arg; 689 688 690 - for (i = 0; i < thermal_zone_get_num_trips(tz); i++) { 689 + if (trip->type != THERMAL_TRIP_HOT) 690 + return 0; 691 691 692 - ret = thermal_zone_get_trip(tz, i, &trip); 693 - if (ret) 694 - return -EINVAL; 692 + *trip_ret = trip; 693 + /* Return nonzero to terminate the search. */ 694 + return 1; 695 + } 695 696 696 - if (trip.type == THERMAL_TRIP_HOT) { 697 - *trip_id = i; 698 - return 0; 699 - } 700 - } 697 + static const struct thermal_trip *get_hot_trip(struct thermal_zone_device *tz) 698 + { 699 + const struct thermal_trip *trip = NULL; 701 700 702 - return -EINVAL; 701 + thermal_zone_for_each_trip(tz, get_hot_trip_cb, &trip); 702 + 703 + return trip; 703 704 } 704 705 705 706 /** ··· 732 731 struct thermal_zone_device *tz) 733 732 { 734 733 struct tegra_soctherm *ts = dev_get_drvdata(dev); 734 + const struct thermal_trip *hot_trip; 735 735 struct soctherm_throt_cfg *stc; 736 - int i, trip, temperature, ret; 736 + int i, temperature, ret; 737 737 738 738 /* Get thermtrips. If missing, try to get critical trips. */ 739 739 temperature = tsensor_group_thermtrip_get(ts, sg->id); ··· 751 749 dev_info(dev, "thermtrip: will shut down when %s reaches %d mC\n", 752 750 sg->name, temperature); 753 751 754 - ret = get_hot_temp(tz, &trip, &temperature); 755 - if (ret) { 752 + hot_trip = get_hot_trip(tz); 753 + if (!hot_trip) { 756 754 dev_info(dev, "throttrip: %s: missing hot temperature\n", 757 755 sg->name); 758 756 return 0; ··· 765 763 continue; 766 764 767 765 cdev = ts->throt_cfgs[i].cdev; 768 - if (get_thermal_instance(tz, cdev, trip)) 766 + if (thermal_trip_is_bound_to_cdev(tz, hot_trip, cdev)) 769 767 stc = find_throttle_cfg_by_name(ts, cdev->type); 770 768 else 771 769 continue;
+32 -27
drivers/thermal/tegra/tegra30-tsensor.c
··· 303 303 return 0; 304 304 } 305 305 306 - static void tegra_tsensor_get_hw_channel_trips(struct thermal_zone_device *tzd, 307 - int *hot_trip, int *crit_trip) 308 - { 309 - unsigned int i; 306 + struct trip_temps { 307 + int hot_trip; 308 + int crit_trip; 309 + }; 310 310 311 + static int tegra_tsensor_get_trips_cb(struct thermal_trip *trip, void *arg) 312 + { 313 + struct trip_temps *temps = arg; 314 + 315 + if (trip->type == THERMAL_TRIP_HOT) 316 + temps->hot_trip = trip->temperature; 317 + else if (trip->type == THERMAL_TRIP_CRITICAL) 318 + temps->crit_trip = trip->temperature; 319 + 320 + return 0; 321 + } 322 + 323 + static void tegra_tsensor_get_hw_channel_trips(struct thermal_zone_device *tzd, 324 + struct trip_temps *temps) 325 + { 311 326 /* 312 327 * 90C is the maximal critical temperature of all Tegra30 SoC variants, 313 328 * use it for the default trip if unspecified in a device-tree. 314 329 */ 315 - *hot_trip = 85000; 316 - *crit_trip = 90000; 330 + temps->hot_trip = 85000; 331 + temps->crit_trip = 90000; 317 332 318 - for (i = 0; i < thermal_zone_get_num_trips(tzd); i++) { 319 - 320 - struct thermal_trip trip; 321 - 322 - thermal_zone_get_trip(tzd, i, &trip); 323 - 324 - if (trip.type == THERMAL_TRIP_HOT) 325 - *hot_trip = trip.temperature; 326 - 327 - if (trip.type == THERMAL_TRIP_CRITICAL) 328 - *crit_trip = trip.temperature; 329 - } 333 + thermal_zone_for_each_trip(tzd, tegra_tsensor_get_trips_cb, temps); 330 334 331 335 /* clamp hardware trips to the calibration limits */ 332 - *hot_trip = clamp(*hot_trip, 25000, 90000); 336 + temps->hot_trip = clamp(temps->hot_trip, 25000, 90000); 333 337 334 338 /* 335 339 * Kernel will perform a normal system shut down if it will ··· 342 338 * shut down gracefully before sending signal to the Power 343 339 * Management controller. 344 340 */ 345 - *crit_trip = clamp(*crit_trip + 5000, 25000, 90000); 341 + temps->crit_trip = clamp(temps->crit_trip + 5000, 25000, 90000); 346 342 } 347 343 348 344 static int tegra_tsensor_enable_hw_channel(const struct tegra_tsensor *ts, ··· 350 346 { 351 347 const struct tegra_tsensor_channel *tsc = &ts->ch[id]; 352 348 struct thermal_zone_device *tzd = tsc->tzd; 353 - int err, hot_trip = 0, crit_trip = 0; 349 + struct trip_temps temps = { 0 }; 350 + int err; 354 351 u32 val; 355 352 356 353 if (!tzd) { ··· 362 357 return 0; 363 358 } 364 359 365 - tegra_tsensor_get_hw_channel_trips(tzd, &hot_trip, &crit_trip); 360 + tegra_tsensor_get_hw_channel_trips(tzd, &temps); 366 361 367 362 dev_info_once(ts->dev, "ch%u: PMC emergency shutdown trip set to %dC\n", 368 - id, DIV_ROUND_CLOSEST(crit_trip, 1000)); 363 + id, DIV_ROUND_CLOSEST(temps.crit_trip, 1000)); 369 364 370 - hot_trip = tegra_tsensor_temp_to_counter(ts, hot_trip); 371 - crit_trip = tegra_tsensor_temp_to_counter(ts, crit_trip); 365 + temps.hot_trip = tegra_tsensor_temp_to_counter(ts, temps.hot_trip); 366 + temps.crit_trip = tegra_tsensor_temp_to_counter(ts, temps.crit_trip); 372 367 373 368 /* program LEVEL2 counter threshold */ 374 369 val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG1); 375 370 val &= ~TSENSOR_SENSOR0_CONFIG1_TH2; 376 - val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG1_TH2, hot_trip); 371 + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG1_TH2, temps.hot_trip); 377 372 writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG1); 378 373 379 374 /* program LEVEL3 counter threshold */ 380 375 val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG2); 381 376 val &= ~TSENSOR_SENSOR0_CONFIG2_TH3; 382 - val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG2_TH3, crit_trip); 377 + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG2_TH3, temps.crit_trip); 383 378 writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG2); 384 379 385 380 /*
+7
drivers/thermal/testing/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # 3 + # Thermal core testing facility. 4 + 5 + obj-$(CONFIG_THERMAL_CORE_TESTING) += thermal-testing.o 6 + 7 + thermal-testing-y := command.o zone.o
+221
drivers/thermal/testing/command.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright 2024, Intel Corporation 4 + * 5 + * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> 6 + * 7 + * Thermal subsystem testing facility. 8 + * 9 + * This facility allows the thermal core functionality to be exercised in a 10 + * controlled way in order to verify its behavior. 11 + * 12 + * It resides in the "thermal-testing" directory under the debugfs root and 13 + * starts with a single file called "command" which can be written a string 14 + * representing a thermal testing facility command. 15 + * 16 + * The currently supported commands are listed in the tt_commands enum below. 17 + * 18 + * The "addtz" command causes a new test thermal zone template to be created, 19 + * for example: 20 + * 21 + * # echo addtz > /sys/kernel/debug/thermal-testing/command 22 + * 23 + * That template will be represented as a subdirectory in the "thermal-testing" 24 + * directory, for example 25 + * 26 + * # ls /sys/kernel/debug/thermal-testing/ 27 + * command tz0 28 + * 29 + * The thermal zone template can be populated with trip points with the help of 30 + * the "tzaddtrip" command, for example: 31 + * 32 + * # echo tzaddtrip:0 > /sys/kernel/debug/thermal-testing/command 33 + * 34 + * which causes a trip point template to be added to the test thermal zone 35 + * template 0 (represented by the tz0 subdirectory in "thermal-testing"). 36 + * 37 + * # ls /sys/kernel/debug/thermal-testing/tz0 38 + * init_temp temp trip_0_temp trip_0_hyst 39 + * 40 + * The temperature of a trip point template is initially THERMAL_TEMP_INVALID 41 + * and its hysteresis is initially 0. They can be adjusted by writing to the 42 + * "trip_x_temp" and "trip_x_hyst" files correspoinding to that trip point 43 + * template, respectively. 44 + * 45 + * The initial temperature of a thermal zone based on a template can be set by 46 + * writing to the "init_temp" file in its directory under "thermal-testing", for 47 + * example: 48 + * 49 + * echo 50000 > /sys/kernel/debug/thermal-testing/tz0/init_temp 50 + * 51 + * When ready, "tzreg" command can be used for registering and enabling a 52 + * thermal zone based on a given template with the thermal core, for example 53 + * 54 + * # echo tzreg:0 > /sys/kernel/debug/thermal-testing/command 55 + * 56 + * In this case, test thermal zone template 0 is used for registering a new 57 + * thermal zone and the set of trip point templates associated with it is used 58 + * for populating the new thermal zone's trip points table. The type of the new 59 + * thermal zone is "test_tz". 60 + * 61 + * The temperature and hysteresis of all of the trip points in that new thermal 62 + * zone are adjustable via sysfs, so they can be updated at any time. 63 + * 64 + * The current temperature of the new thermal zone can be set by writing to the 65 + * "temp" file in the corresponding thermal zone template's directory under 66 + * "thermal-testing", for example 67 + * 68 + * echo 10000 > /sys/kernel/debug/thermal-testing/tz0/temp 69 + * 70 + * which will also trigger a temperature update for this zone in the thermal 71 + * core, including checking its trip points, sending notifications to user space 72 + * if any of them have been crossed and so on. 73 + * 74 + * When it is not needed any more, a test thermal zone template can be deleted 75 + * with the help of the "deltz" command, for example 76 + * 77 + * # echo deltz:0 > /sys/kernel/debug/thermal-testing/command 78 + * 79 + * which will also unregister the thermal zone based on it, if present. 80 + */ 81 + 82 + #define pr_fmt(fmt) "thermal-testing: " fmt 83 + 84 + #include <linux/debugfs.h> 85 + #include <linux/module.h> 86 + 87 + #include "thermal_testing.h" 88 + 89 + struct dentry *d_testing; 90 + 91 + #define TT_COMMAND_SIZE 16 92 + 93 + enum tt_commands { 94 + TT_CMD_ADDTZ, 95 + TT_CMD_DELTZ, 96 + TT_CMD_TZADDTRIP, 97 + TT_CMD_TZREG, 98 + TT_CMD_TZUNREG, 99 + }; 100 + 101 + static const char *tt_command_strings[] = { 102 + [TT_CMD_ADDTZ] = "addtz", 103 + [TT_CMD_DELTZ] = "deltz", 104 + [TT_CMD_TZADDTRIP] = "tzaddtrip", 105 + [TT_CMD_TZREG] = "tzreg", 106 + [TT_CMD_TZUNREG] = "tzunreg", 107 + }; 108 + 109 + static int tt_command_exec(int index, const char *arg) 110 + { 111 + int ret; 112 + 113 + switch (index) { 114 + case TT_CMD_ADDTZ: 115 + ret = tt_add_tz(); 116 + break; 117 + 118 + case TT_CMD_DELTZ: 119 + ret = tt_del_tz(arg); 120 + break; 121 + 122 + case TT_CMD_TZADDTRIP: 123 + ret = tt_zone_add_trip(arg); 124 + break; 125 + 126 + case TT_CMD_TZREG: 127 + ret = tt_zone_reg(arg); 128 + break; 129 + 130 + case TT_CMD_TZUNREG: 131 + ret = tt_zone_unreg(arg); 132 + break; 133 + 134 + default: 135 + ret = -EINVAL; 136 + break; 137 + } 138 + 139 + return ret; 140 + } 141 + 142 + static ssize_t tt_command_process(struct dentry *dentry, const char __user *user_buf, 143 + size_t count) 144 + { 145 + char *buf __free(kfree); 146 + char *arg; 147 + int i; 148 + 149 + buf = kmalloc(count + 1, GFP_KERNEL); 150 + if (!buf) 151 + return -ENOMEM; 152 + 153 + if (copy_from_user(buf, user_buf, count)) 154 + return -EFAULT; 155 + 156 + buf[count] = '\0'; 157 + strim(buf); 158 + 159 + arg = strstr(buf, ":"); 160 + if (arg) { 161 + *arg = '\0'; 162 + arg++; 163 + } 164 + 165 + for (i = 0; i < ARRAY_SIZE(tt_command_strings); i++) { 166 + if (!strcmp(buf, tt_command_strings[i])) 167 + return tt_command_exec(i, arg); 168 + } 169 + 170 + return -EINVAL; 171 + } 172 + 173 + static ssize_t tt_command_write(struct file *file, const char __user *user_buf, 174 + size_t count, loff_t *ppos) 175 + { 176 + struct dentry *dentry = file->f_path.dentry; 177 + ssize_t ret; 178 + 179 + if (*ppos) 180 + return -EINVAL; 181 + 182 + if (count + 1 > TT_COMMAND_SIZE) 183 + return -E2BIG; 184 + 185 + ret = debugfs_file_get(dentry); 186 + if (unlikely(ret)) 187 + return ret; 188 + 189 + ret = tt_command_process(dentry, user_buf, count); 190 + if (ret) 191 + return ret; 192 + 193 + return count; 194 + } 195 + 196 + static const struct file_operations tt_command_fops = { 197 + .write = tt_command_write, 198 + .open = simple_open, 199 + .llseek = default_llseek, 200 + }; 201 + 202 + static int __init thermal_testing_init(void) 203 + { 204 + d_testing = debugfs_create_dir("thermal-testing", NULL); 205 + if (!IS_ERR(d_testing)) 206 + debugfs_create_file("command", 0200, d_testing, NULL, 207 + &tt_command_fops); 208 + 209 + return 0; 210 + } 211 + module_init(thermal_testing_init); 212 + 213 + static void __exit thermal_testing_exit(void) 214 + { 215 + debugfs_remove(d_testing); 216 + tt_zone_cleanup(); 217 + } 218 + module_exit(thermal_testing_exit); 219 + 220 + MODULE_DESCRIPTION("Thermal core testing facility"); 221 + MODULE_LICENSE("GPL v2");
+11
drivers/thermal/testing/thermal_testing.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + extern struct dentry *d_testing; 4 + 5 + int tt_add_tz(void); 6 + int tt_del_tz(const char *arg); 7 + int tt_zone_add_trip(const char *arg); 8 + int tt_zone_reg(const char *arg); 9 + int tt_zone_unreg(const char *arg); 10 + 11 + void tt_zone_cleanup(void);
+468
drivers/thermal/testing/zone.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright 2024, Intel Corporation 4 + * 5 + * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> 6 + * 7 + * Thermal zone tempalates handling for thermal core testing. 8 + */ 9 + 10 + #define pr_fmt(fmt) "thermal-testing: " fmt 11 + 12 + #include <linux/debugfs.h> 13 + #include <linux/idr.h> 14 + #include <linux/list.h> 15 + #include <linux/thermal.h> 16 + #include <linux/workqueue.h> 17 + 18 + #include "thermal_testing.h" 19 + 20 + #define TT_MAX_FILE_NAME_LENGTH 16 21 + 22 + /** 23 + * struct tt_thermal_zone - Testing thermal zone template 24 + * 25 + * Represents a template of a thermal zone that can be used for registering 26 + * a test thermal zone with the thermal core. 27 + * 28 + * @list_node: Node in the list of all testing thermal zone templates. 29 + * @trips: List of trip point templates for this thermal zone template. 30 + * @d_tt_zone: Directory in debugfs representing this template. 31 + * @tz: Test thermal zone based on this template, if present. 32 + * @lock: Mutex for synchronizing changes of this template. 33 + * @ida: IDA for trip point IDs. 34 + * @id: The ID of this template for the debugfs interface. 35 + * @temp: Temperature value. 36 + * @tz_temp: Current thermal zone temperature (after registration). 37 + * @num_trips: Number of trip points in the @trips list. 38 + * @refcount: Reference counter for usage and removal synchronization. 39 + */ 40 + struct tt_thermal_zone { 41 + struct list_head list_node; 42 + struct list_head trips; 43 + struct dentry *d_tt_zone; 44 + struct thermal_zone_device *tz; 45 + struct mutex lock; 46 + struct ida ida; 47 + int id; 48 + int temp; 49 + int tz_temp; 50 + unsigned int num_trips; 51 + unsigned int refcount; 52 + }; 53 + 54 + DEFINE_GUARD(tt_zone, struct tt_thermal_zone *, mutex_lock(&_T->lock), mutex_unlock(&_T->lock)) 55 + 56 + /** 57 + * struct tt_trip - Testing trip point template 58 + * 59 + * Represents a template of a trip point to be used for populating a trip point 60 + * during the registration of a thermal zone based on a given zone template. 61 + * 62 + * @list_node: Node in the list of all trip templates in the zone template. 63 + * @trip: Trip point data to use for thernal zone registration. 64 + * @id: The ID of this trip template for the debugfs interface. 65 + */ 66 + struct tt_trip { 67 + struct list_head list_node; 68 + struct thermal_trip trip; 69 + int id; 70 + }; 71 + 72 + /* 73 + * It is both questionable and potentially problematic from the sychnronization 74 + * perspective to attempt to manipulate debugfs from within a debugfs file 75 + * "write" operation, so auxiliary work items are used for that. The majority 76 + * of zone-related command functions have a part that runs from a workqueue and 77 + * make changes in debugs, among other things. 78 + */ 79 + struct tt_work { 80 + struct work_struct work; 81 + struct tt_thermal_zone *tt_zone; 82 + struct tt_trip *tt_trip; 83 + }; 84 + 85 + static inline struct tt_work *tt_work_of_work(struct work_struct *work) 86 + { 87 + return container_of(work, struct tt_work, work); 88 + } 89 + 90 + static LIST_HEAD(tt_thermal_zones); 91 + static DEFINE_IDA(tt_thermal_zones_ida); 92 + static DEFINE_MUTEX(tt_thermal_zones_lock); 93 + 94 + static int tt_int_get(void *data, u64 *val) 95 + { 96 + *val = *(int *)data; 97 + return 0; 98 + } 99 + static int tt_int_set(void *data, u64 val) 100 + { 101 + if ((int)val < THERMAL_TEMP_INVALID) 102 + return -EINVAL; 103 + 104 + *(int *)data = val; 105 + return 0; 106 + } 107 + DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(tt_int_attr, tt_int_get, tt_int_set, "%lld\n"); 108 + DEFINE_DEBUGFS_ATTRIBUTE(tt_unsigned_int_attr, tt_int_get, tt_int_set, "%llu\n"); 109 + 110 + static int tt_zone_tz_temp_get(void *data, u64 *val) 111 + { 112 + struct tt_thermal_zone *tt_zone = data; 113 + 114 + guard(tt_zone)(tt_zone); 115 + 116 + if (!tt_zone->tz) 117 + return -EBUSY; 118 + 119 + *val = tt_zone->tz_temp; 120 + 121 + return 0; 122 + } 123 + static int tt_zone_tz_temp_set(void *data, u64 val) 124 + { 125 + struct tt_thermal_zone *tt_zone = data; 126 + 127 + guard(tt_zone)(tt_zone); 128 + 129 + if (!tt_zone->tz) 130 + return -EBUSY; 131 + 132 + WRITE_ONCE(tt_zone->tz_temp, val); 133 + thermal_zone_device_update(tt_zone->tz, THERMAL_EVENT_TEMP_SAMPLE); 134 + 135 + return 0; 136 + } 137 + DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(tt_zone_tz_temp_attr, tt_zone_tz_temp_get, 138 + tt_zone_tz_temp_set, "%lld\n"); 139 + 140 + static void tt_zone_free_trips(struct tt_thermal_zone *tt_zone) 141 + { 142 + struct tt_trip *tt_trip, *aux; 143 + 144 + list_for_each_entry_safe(tt_trip, aux, &tt_zone->trips, list_node) { 145 + list_del(&tt_trip->list_node); 146 + ida_free(&tt_zone->ida, tt_trip->id); 147 + kfree(tt_trip); 148 + } 149 + } 150 + 151 + static void tt_zone_free(struct tt_thermal_zone *tt_zone) 152 + { 153 + tt_zone_free_trips(tt_zone); 154 + ida_free(&tt_thermal_zones_ida, tt_zone->id); 155 + ida_destroy(&tt_zone->ida); 156 + kfree(tt_zone); 157 + } 158 + 159 + static void tt_add_tz_work_fn(struct work_struct *work) 160 + { 161 + struct tt_work *tt_work = tt_work_of_work(work); 162 + struct tt_thermal_zone *tt_zone = tt_work->tt_zone; 163 + char f_name[TT_MAX_FILE_NAME_LENGTH]; 164 + 165 + kfree(tt_work); 166 + 167 + snprintf(f_name, TT_MAX_FILE_NAME_LENGTH, "tz%d", tt_zone->id); 168 + tt_zone->d_tt_zone = debugfs_create_dir(f_name, d_testing); 169 + if (IS_ERR(tt_zone->d_tt_zone)) { 170 + tt_zone_free(tt_zone); 171 + return; 172 + } 173 + 174 + debugfs_create_file_unsafe("temp", 0600, tt_zone->d_tt_zone, tt_zone, 175 + &tt_zone_tz_temp_attr); 176 + 177 + debugfs_create_file_unsafe("init_temp", 0600, tt_zone->d_tt_zone, 178 + &tt_zone->temp, &tt_int_attr); 179 + 180 + guard(mutex)(&tt_thermal_zones_lock); 181 + 182 + list_add_tail(&tt_zone->list_node, &tt_thermal_zones); 183 + } 184 + 185 + int tt_add_tz(void) 186 + { 187 + struct tt_thermal_zone *tt_zone __free(kfree); 188 + struct tt_work *tt_work __free(kfree); 189 + int ret; 190 + 191 + tt_zone = kzalloc(sizeof(*tt_zone), GFP_KERNEL); 192 + if (!tt_zone) 193 + return -ENOMEM; 194 + 195 + tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); 196 + if (!tt_work) 197 + return -ENOMEM; 198 + 199 + INIT_LIST_HEAD(&tt_zone->trips); 200 + mutex_init(&tt_zone->lock); 201 + ida_init(&tt_zone->ida); 202 + tt_zone->temp = THERMAL_TEMP_INVALID; 203 + 204 + ret = ida_alloc(&tt_thermal_zones_ida, GFP_KERNEL); 205 + if (ret < 0) 206 + return ret; 207 + 208 + tt_zone->id = ret; 209 + 210 + INIT_WORK(&tt_work->work, tt_add_tz_work_fn); 211 + tt_work->tt_zone = no_free_ptr(tt_zone); 212 + schedule_work(&(no_free_ptr(tt_work)->work)); 213 + 214 + return 0; 215 + } 216 + 217 + static void tt_del_tz_work_fn(struct work_struct *work) 218 + { 219 + struct tt_work *tt_work = tt_work_of_work(work); 220 + struct tt_thermal_zone *tt_zone = tt_work->tt_zone; 221 + 222 + kfree(tt_work); 223 + 224 + debugfs_remove(tt_zone->d_tt_zone); 225 + tt_zone_free(tt_zone); 226 + } 227 + 228 + static void tt_zone_unregister_tz(struct tt_thermal_zone *tt_zone) 229 + { 230 + guard(tt_zone)(tt_zone); 231 + 232 + if (tt_zone->tz) { 233 + thermal_zone_device_unregister(tt_zone->tz); 234 + tt_zone->tz = NULL; 235 + } 236 + } 237 + 238 + int tt_del_tz(const char *arg) 239 + { 240 + struct tt_work *tt_work __free(kfree); 241 + struct tt_thermal_zone *tt_zone, *aux; 242 + int ret; 243 + int id; 244 + 245 + ret = sscanf(arg, "%d", &id); 246 + if (ret != 1) 247 + return -EINVAL; 248 + 249 + tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); 250 + if (!tt_work) 251 + return -ENOMEM; 252 + 253 + guard(mutex)(&tt_thermal_zones_lock); 254 + 255 + ret = -EINVAL; 256 + list_for_each_entry_safe(tt_zone, aux, &tt_thermal_zones, list_node) { 257 + if (tt_zone->id == id) { 258 + if (tt_zone->refcount) { 259 + ret = -EBUSY; 260 + } else { 261 + list_del(&tt_zone->list_node); 262 + ret = 0; 263 + } 264 + break; 265 + } 266 + } 267 + 268 + if (ret) 269 + return ret; 270 + 271 + tt_zone_unregister_tz(tt_zone); 272 + 273 + INIT_WORK(&tt_work->work, tt_del_tz_work_fn); 274 + tt_work->tt_zone = tt_zone; 275 + schedule_work(&(no_free_ptr(tt_work)->work)); 276 + 277 + return 0; 278 + } 279 + 280 + static struct tt_thermal_zone *tt_get_tt_zone(const char *arg) 281 + { 282 + struct tt_thermal_zone *tt_zone; 283 + int ret, id; 284 + 285 + ret = sscanf(arg, "%d", &id); 286 + if (ret != 1) 287 + return ERR_PTR(-EINVAL); 288 + 289 + guard(mutex)(&tt_thermal_zones_lock); 290 + 291 + ret = -EINVAL; 292 + list_for_each_entry(tt_zone, &tt_thermal_zones, list_node) { 293 + if (tt_zone->id == id) { 294 + tt_zone->refcount++; 295 + ret = 0; 296 + break; 297 + } 298 + } 299 + 300 + if (ret) 301 + return ERR_PTR(ret); 302 + 303 + return tt_zone; 304 + } 305 + 306 + static void tt_put_tt_zone(struct tt_thermal_zone *tt_zone) 307 + { 308 + guard(mutex)(&tt_thermal_zones_lock); 309 + 310 + tt_zone->refcount--; 311 + } 312 + 313 + static void tt_zone_add_trip_work_fn(struct work_struct *work) 314 + { 315 + struct tt_work *tt_work = tt_work_of_work(work); 316 + struct tt_thermal_zone *tt_zone = tt_work->tt_zone; 317 + struct tt_trip *tt_trip = tt_work->tt_trip; 318 + char d_name[TT_MAX_FILE_NAME_LENGTH]; 319 + 320 + kfree(tt_work); 321 + 322 + snprintf(d_name, TT_MAX_FILE_NAME_LENGTH, "trip_%d_temp", tt_trip->id); 323 + debugfs_create_file_unsafe(d_name, 0600, tt_zone->d_tt_zone, 324 + &tt_trip->trip.temperature, &tt_int_attr); 325 + 326 + snprintf(d_name, TT_MAX_FILE_NAME_LENGTH, "trip_%d_hyst", tt_trip->id); 327 + debugfs_create_file_unsafe(d_name, 0600, tt_zone->d_tt_zone, 328 + &tt_trip->trip.hysteresis, &tt_unsigned_int_attr); 329 + 330 + tt_put_tt_zone(tt_zone); 331 + } 332 + 333 + int tt_zone_add_trip(const char *arg) 334 + { 335 + struct tt_work *tt_work __free(kfree); 336 + struct tt_trip *tt_trip __free(kfree); 337 + struct tt_thermal_zone *tt_zone; 338 + int id; 339 + 340 + tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); 341 + if (!tt_work) 342 + return -ENOMEM; 343 + 344 + tt_trip = kzalloc(sizeof(*tt_trip), GFP_KERNEL); 345 + if (!tt_trip) 346 + return -ENOMEM; 347 + 348 + tt_zone = tt_get_tt_zone(arg); 349 + if (IS_ERR(tt_zone)) 350 + return PTR_ERR(tt_zone); 351 + 352 + id = ida_alloc(&tt_zone->ida, GFP_KERNEL); 353 + if (id < 0) { 354 + tt_put_tt_zone(tt_zone); 355 + return id; 356 + } 357 + 358 + tt_trip->trip.type = THERMAL_TRIP_ACTIVE; 359 + tt_trip->trip.temperature = THERMAL_TEMP_INVALID; 360 + tt_trip->trip.flags = THERMAL_TRIP_FLAG_RW; 361 + tt_trip->id = id; 362 + 363 + guard(tt_zone)(tt_zone); 364 + 365 + list_add_tail(&tt_trip->list_node, &tt_zone->trips); 366 + tt_zone->num_trips++; 367 + 368 + INIT_WORK(&tt_work->work, tt_zone_add_trip_work_fn); 369 + tt_work->tt_zone = tt_zone; 370 + tt_work->tt_trip = no_free_ptr(tt_trip); 371 + schedule_work(&(no_free_ptr(tt_work)->work)); 372 + 373 + return 0; 374 + } 375 + 376 + static int tt_zone_get_temp(struct thermal_zone_device *tz, int *temp) 377 + { 378 + struct tt_thermal_zone *tt_zone = thermal_zone_device_priv(tz); 379 + 380 + *temp = READ_ONCE(tt_zone->tz_temp); 381 + 382 + if (*temp < THERMAL_TEMP_INVALID) 383 + return -ENODATA; 384 + 385 + return 0; 386 + } 387 + 388 + static struct thermal_zone_device_ops tt_zone_ops = { 389 + .get_temp = tt_zone_get_temp, 390 + }; 391 + 392 + static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone) 393 + { 394 + struct thermal_trip *trips __free(kfree); 395 + struct thermal_zone_device *tz; 396 + struct tt_trip *tt_trip; 397 + int i; 398 + 399 + guard(tt_zone)(tt_zone); 400 + 401 + if (tt_zone->tz) 402 + return -EINVAL; 403 + 404 + trips = kcalloc(tt_zone->num_trips, sizeof(*trips), GFP_KERNEL); 405 + if (!trips) 406 + return -ENOMEM; 407 + 408 + i = 0; 409 + list_for_each_entry(tt_trip, &tt_zone->trips, list_node) 410 + trips[i++] = tt_trip->trip; 411 + 412 + tt_zone->tz_temp = tt_zone->temp; 413 + 414 + tz = thermal_zone_device_register_with_trips("test_tz", trips, i, tt_zone, 415 + &tt_zone_ops, NULL, 0, 0); 416 + if (IS_ERR(tz)) 417 + return PTR_ERR(tz); 418 + 419 + tt_zone->tz = tz; 420 + 421 + thermal_zone_device_enable(tz); 422 + 423 + return 0; 424 + } 425 + 426 + int tt_zone_reg(const char *arg) 427 + { 428 + struct tt_thermal_zone *tt_zone; 429 + int ret; 430 + 431 + tt_zone = tt_get_tt_zone(arg); 432 + if (IS_ERR(tt_zone)) 433 + return PTR_ERR(tt_zone); 434 + 435 + ret = tt_zone_register_tz(tt_zone); 436 + 437 + tt_put_tt_zone(tt_zone); 438 + 439 + return ret; 440 + } 441 + 442 + int tt_zone_unreg(const char *arg) 443 + { 444 + struct tt_thermal_zone *tt_zone; 445 + 446 + tt_zone = tt_get_tt_zone(arg); 447 + if (IS_ERR(tt_zone)) 448 + return PTR_ERR(tt_zone); 449 + 450 + tt_zone_unregister_tz(tt_zone); 451 + 452 + tt_put_tt_zone(tt_zone); 453 + 454 + return 0; 455 + } 456 + 457 + void tt_zone_cleanup(void) 458 + { 459 + struct tt_thermal_zone *tt_zone, *aux; 460 + 461 + list_for_each_entry_safe(tt_zone, aux, &tt_thermal_zones, list_node) { 462 + tt_zone_unregister_tz(tt_zone); 463 + 464 + list_del(&tt_zone->list_node); 465 + 466 + tt_zone_free(tt_zone); 467 + } 468 + }
+93 -130
drivers/thermal/thermal_core.c
··· 323 323 static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, 324 324 unsigned long delay) 325 325 { 326 - if (delay) 327 - mod_delayed_work(system_freezable_power_efficient_wq, 328 - &tz->poll_queue, delay); 329 - else 326 + if (!delay) { 330 327 cancel_delayed_work(&tz->poll_queue); 328 + return; 329 + } 330 + 331 + if (delay > HZ) 332 + delay = round_jiffies_relative(delay); 333 + 334 + mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, delay); 331 335 } 332 336 333 337 static void thermal_zone_recheck(struct thermal_zone_device *tz, int error) ··· 551 547 struct thermal_trip_desc *td; 552 548 LIST_HEAD(way_down_list); 553 549 LIST_HEAD(way_up_list); 550 + int low = -INT_MAX, high = INT_MAX; 554 551 int temp, ret; 555 552 556 553 if (tz->suspended) ··· 585 580 586 581 tz->notify_event = event; 587 582 588 - for_each_trip_desc(tz, td) 583 + for_each_trip_desc(tz, td) { 589 584 handle_thermal_trip(tz, td, &way_up_list, &way_down_list); 590 585 591 - thermal_zone_set_trips(tz); 586 + if (td->threshold <= tz->temperature && td->threshold > low) 587 + low = td->threshold; 588 + 589 + if (td->threshold >= tz->temperature && td->threshold < high) 590 + high = td->threshold; 591 + } 592 + 593 + thermal_zone_set_trips(tz, low, high); 592 594 593 595 list_sort(NULL, &way_up_list, thermal_trip_notify_cmp); 594 596 list_for_each_entry(td, &way_up_list, notify_list_node) ··· 769 757 * @tz: pointer to struct thermal_zone_device 770 758 * @trip: trip point the cooling devices is associated with in this zone. 771 759 * @cdev: pointer to struct thermal_cooling_device 772 - * @upper: the Maximum cooling state for this trip point. 773 - * THERMAL_NO_LIMIT means no upper limit, 774 - * and the cooling device can be in max_state. 775 - * @lower: the Minimum cooling state can be used for this trip point. 776 - * THERMAL_NO_LIMIT means no lower limit, 777 - * and the cooling device can be in cooling state 0. 778 - * @weight: The weight of the cooling device to be bound to the 779 - * thermal zone. Use THERMAL_WEIGHT_DEFAULT for the 780 - * default value 760 + * @cool_spec: cooling specification for @trip and @cdev 781 761 * 782 762 * This interface function bind a thermal cooling device to the certain trip 783 763 * point of a thermal zone device. ··· 777 773 * 778 774 * Return: 0 on success, the proper error value otherwise. 779 775 */ 780 - int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, 776 + static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, 781 777 const struct thermal_trip *trip, 782 778 struct thermal_cooling_device *cdev, 783 - unsigned long upper, unsigned long lower, 784 - unsigned int weight) 779 + struct cooling_spec *cool_spec) 785 780 { 786 781 struct thermal_instance *dev; 787 782 struct thermal_instance *pos; 788 - struct thermal_zone_device *pos1; 789 - struct thermal_cooling_device *pos2; 790 783 bool upper_no_limit; 791 784 int result; 792 785 793 - list_for_each_entry(pos1, &thermal_tz_list, node) { 794 - if (pos1 == tz) 795 - break; 796 - } 797 - list_for_each_entry(pos2, &thermal_cdev_list, node) { 798 - if (pos2 == cdev) 799 - break; 800 - } 801 - 802 - if (tz != pos1 || cdev != pos2) 803 - return -EINVAL; 804 - 805 786 /* lower default 0, upper default max_state */ 806 - lower = lower == THERMAL_NO_LIMIT ? 0 : lower; 787 + if (cool_spec->lower == THERMAL_NO_LIMIT) 788 + cool_spec->lower = 0; 807 789 808 - if (upper == THERMAL_NO_LIMIT) { 809 - upper = cdev->max_state; 790 + if (cool_spec->upper == THERMAL_NO_LIMIT) { 791 + cool_spec->upper = cdev->max_state; 810 792 upper_no_limit = true; 811 793 } else { 812 794 upper_no_limit = false; 813 795 } 814 796 815 - if (lower > upper || upper > cdev->max_state) 797 + if (cool_spec->lower > cool_spec->upper || cool_spec->upper > cdev->max_state) 816 798 return -EINVAL; 817 799 818 800 dev = kzalloc(sizeof(*dev), GFP_KERNEL); 819 801 if (!dev) 820 802 return -ENOMEM; 821 - dev->tz = tz; 803 + 822 804 dev->cdev = cdev; 823 805 dev->trip = trip; 824 - dev->upper = upper; 806 + dev->upper = cool_spec->upper; 825 807 dev->upper_no_limit = upper_no_limit; 826 - dev->lower = lower; 808 + dev->lower = cool_spec->lower; 827 809 dev->target = THERMAL_NO_TARGET; 828 - dev->weight = weight; 810 + dev->weight = cool_spec->weight; 829 811 830 812 result = ida_alloc(&tz->ida, GFP_KERNEL); 831 813 if (result < 0) ··· 845 855 if (result) 846 856 goto remove_trip_file; 847 857 848 - mutex_lock(&tz->lock); 849 858 mutex_lock(&cdev->lock); 850 859 list_for_each_entry(pos, &tz->thermal_instances, tz_node) 851 - if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { 860 + if (pos->trip == trip && pos->cdev == cdev) { 852 861 result = -EEXIST; 853 862 break; 854 863 } ··· 859 870 thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV); 860 871 } 861 872 mutex_unlock(&cdev->lock); 862 - mutex_unlock(&tz->lock); 863 873 864 874 if (!result) 865 875 return 0; ··· 874 886 kfree(dev); 875 887 return result; 876 888 } 877 - EXPORT_SYMBOL_GPL(thermal_bind_cdev_to_trip); 878 - 879 - int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, 880 - int trip_index, 881 - struct thermal_cooling_device *cdev, 882 - unsigned long upper, unsigned long lower, 883 - unsigned int weight) 884 - { 885 - if (trip_index < 0 || trip_index >= tz->num_trips) 886 - return -EINVAL; 887 - 888 - return thermal_bind_cdev_to_trip(tz, &tz->trips[trip_index].trip, cdev, 889 - upper, lower, weight); 890 - } 891 - EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device); 892 889 893 890 /** 894 891 * thermal_unbind_cdev_from_trip - unbind a cooling device from a thermal zone. ··· 884 911 * This interface function unbind a thermal cooling device from the certain 885 912 * trip point of a thermal zone device. 886 913 * This function is usually called in the thermal zone device .unbind callback. 887 - * 888 - * Return: 0 on success, the proper error value otherwise. 889 914 */ 890 - int thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, 891 - const struct thermal_trip *trip, 892 - struct thermal_cooling_device *cdev) 915 + static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, 916 + const struct thermal_trip *trip, 917 + struct thermal_cooling_device *cdev) 893 918 { 894 919 struct thermal_instance *pos, *next; 895 920 896 - mutex_lock(&tz->lock); 921 + lockdep_assert_held(&tz->lock); 922 + 897 923 mutex_lock(&cdev->lock); 898 924 list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) { 899 - if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { 925 + if (pos->trip == trip && pos->cdev == cdev) { 900 926 list_del(&pos->tz_node); 901 927 list_del(&pos->cdev_node); 902 928 903 929 thermal_governor_update_tz(tz, THERMAL_TZ_UNBIND_CDEV); 904 930 905 931 mutex_unlock(&cdev->lock); 906 - mutex_unlock(&tz->lock); 907 932 goto unbind; 908 933 } 909 934 } 910 935 mutex_unlock(&cdev->lock); 911 - mutex_unlock(&tz->lock); 912 936 913 - return -ENODEV; 937 + return; 914 938 915 939 unbind: 916 940 device_remove_file(&tz->device, &pos->weight_attr); ··· 915 945 sysfs_remove_link(&tz->device.kobj, pos->name); 916 946 ida_free(&tz->ida, pos->id); 917 947 kfree(pos); 918 - return 0; 919 948 } 920 - EXPORT_SYMBOL_GPL(thermal_unbind_cdev_from_trip); 921 - 922 - int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, 923 - int trip_index, 924 - struct thermal_cooling_device *cdev) 925 - { 926 - if (trip_index < 0 || trip_index >= tz->num_trips) 927 - return -EINVAL; 928 - 929 - return thermal_unbind_cdev_from_trip(tz, &tz->trips[trip_index].trip, cdev); 930 - } 931 - EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device); 932 949 933 950 static void thermal_release(struct device *dev) 934 951 { ··· 942 985 943 986 static inline 944 987 void print_bind_err_msg(struct thermal_zone_device *tz, 988 + const struct thermal_trip *trip, 945 989 struct thermal_cooling_device *cdev, int ret) 946 990 { 947 - dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n", 948 - tz->type, cdev->type, ret); 991 + dev_err(&tz->device, "binding cdev %s to trip %d failed: %d\n", 992 + cdev->type, thermal_zone_trip_id(tz, trip), ret); 949 993 } 950 994 951 - static void bind_cdev(struct thermal_cooling_device *cdev) 995 + static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, 996 + struct thermal_cooling_device *cdev) 952 997 { 953 - int ret; 954 - struct thermal_zone_device *pos = NULL; 998 + struct thermal_trip_desc *td; 955 999 956 - list_for_each_entry(pos, &thermal_tz_list, node) { 957 - if (pos->ops.bind) { 958 - ret = pos->ops.bind(pos, cdev); 959 - if (ret) 960 - print_bind_err_msg(pos, cdev, ret); 961 - } 1000 + if (!tz->ops.should_bind) 1001 + return; 1002 + 1003 + mutex_lock(&tz->lock); 1004 + 1005 + for_each_trip_desc(tz, td) { 1006 + struct thermal_trip *trip = &td->trip; 1007 + struct cooling_spec c = { 1008 + .upper = THERMAL_NO_LIMIT, 1009 + .lower = THERMAL_NO_LIMIT, 1010 + .weight = THERMAL_WEIGHT_DEFAULT 1011 + }; 1012 + int ret; 1013 + 1014 + if (!tz->ops.should_bind(tz, trip, cdev, &c)) 1015 + continue; 1016 + 1017 + ret = thermal_bind_cdev_to_trip(tz, trip, cdev, &c); 1018 + if (ret) 1019 + print_bind_err_msg(tz, trip, cdev, ret); 962 1020 } 1021 + 1022 + mutex_unlock(&tz->lock); 963 1023 } 964 1024 965 1025 /** ··· 1074 1100 list_add(&cdev->node, &thermal_cdev_list); 1075 1101 1076 1102 /* Update binding information for 'this' new cdev */ 1077 - bind_cdev(cdev); 1103 + list_for_each_entry(pos, &thermal_tz_list, node) 1104 + thermal_zone_cdev_bind(pos, cdev); 1078 1105 1079 1106 list_for_each_entry(pos, &thermal_tz_list, node) 1080 1107 if (atomic_cmpxchg(&pos->need_update, 1, 0)) ··· 1276 1301 } 1277 1302 EXPORT_SYMBOL_GPL(thermal_cooling_device_update); 1278 1303 1304 + static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz, 1305 + struct thermal_cooling_device *cdev) 1306 + { 1307 + struct thermal_trip_desc *td; 1308 + 1309 + mutex_lock(&tz->lock); 1310 + 1311 + for_each_trip_desc(tz, td) 1312 + thermal_unbind_cdev_from_trip(tz, &td->trip, cdev); 1313 + 1314 + mutex_unlock(&tz->lock); 1315 + } 1316 + 1279 1317 /** 1280 1318 * thermal_cooling_device_unregister - removes a thermal cooling device 1281 1319 * @cdev: the thermal cooling device to remove. ··· 1315 1327 list_del(&cdev->node); 1316 1328 1317 1329 /* Unbind all thermal zones associated with 'this' cdev */ 1318 - list_for_each_entry(tz, &thermal_tz_list, node) { 1319 - if (tz->ops.unbind) 1320 - tz->ops.unbind(tz, cdev); 1321 - } 1330 + list_for_each_entry(tz, &thermal_tz_list, node) 1331 + thermal_zone_cdev_unbind(tz, cdev); 1322 1332 1323 1333 mutex_unlock(&thermal_list_lock); 1324 1334 1325 1335 device_unregister(&cdev->device); 1326 1336 } 1327 1337 EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); 1328 - 1329 - static void bind_tz(struct thermal_zone_device *tz) 1330 - { 1331 - int ret; 1332 - struct thermal_cooling_device *pos = NULL; 1333 - 1334 - if (!tz->ops.bind) 1335 - return; 1336 - 1337 - mutex_lock(&thermal_list_lock); 1338 - 1339 - list_for_each_entry(pos, &thermal_cdev_list, node) { 1340 - ret = tz->ops.bind(tz, pos); 1341 - if (ret) 1342 - print_bind_err_msg(tz, pos, ret); 1343 - } 1344 - 1345 - mutex_unlock(&thermal_list_lock); 1346 - } 1347 - 1348 - static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms) 1349 - { 1350 - *delay_jiffies = msecs_to_jiffies(delay_ms); 1351 - if (delay_ms > 1000) 1352 - *delay_jiffies = round_jiffies(*delay_jiffies); 1353 - } 1354 1338 1355 1339 int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) 1356 1340 { ··· 1384 1424 unsigned int polling_delay) 1385 1425 { 1386 1426 const struct thermal_trip *trip = trips; 1427 + struct thermal_cooling_device *cdev; 1387 1428 struct thermal_zone_device *tz; 1388 1429 struct thermal_trip_desc *td; 1389 1430 int id; ··· 1408 1447 } 1409 1448 1410 1449 if (!ops || !ops->get_temp) { 1411 - pr_err("Thermal zone device ops not defined\n"); 1450 + pr_err("Thermal zone device ops not defined or invalid\n"); 1412 1451 return ERR_PTR(-EINVAL); 1413 1452 } 1414 1453 ··· 1470 1509 td->threshold = INT_MAX; 1471 1510 } 1472 1511 1473 - thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay); 1474 - thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay); 1512 + tz->polling_delay_jiffies = msecs_to_jiffies(polling_delay); 1513 + tz->passive_delay_jiffies = msecs_to_jiffies(passive_delay); 1475 1514 tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY; 1476 1515 1477 1516 /* sys I/F */ ··· 1515 1554 } 1516 1555 1517 1556 mutex_lock(&thermal_list_lock); 1557 + 1518 1558 mutex_lock(&tz->lock); 1519 1559 list_add_tail(&tz->node, &thermal_tz_list); 1520 1560 mutex_unlock(&tz->lock); 1521 - mutex_unlock(&thermal_list_lock); 1522 1561 1523 1562 /* Bind cooling devices for this zone */ 1524 - bind_tz(tz); 1563 + list_for_each_entry(cdev, &thermal_cdev_list, node) 1564 + thermal_zone_cdev_bind(tz, cdev); 1565 + 1566 + mutex_unlock(&thermal_list_lock); 1525 1567 1526 1568 thermal_zone_device_init(tz); 1527 1569 /* Update the new thermal zone and mark it as already updated. */ ··· 1616 1652 1617 1653 /* Unbind all cdevs associated with 'this' thermal zone */ 1618 1654 list_for_each_entry(cdev, &thermal_cdev_list, node) 1619 - if (tz->ops.unbind) 1620 - tz->ops.unbind(tz, cdev); 1655 + thermal_zone_cdev_unbind(tz, cdev); 1621 1656 1622 1657 mutex_unlock(&thermal_list_lock); 1623 1658
+15 -20
drivers/thermal/thermal_core.h
··· 15 15 #include "thermal_netlink.h" 16 16 #include "thermal_debugfs.h" 17 17 18 + struct thermal_attr { 19 + struct device_attribute attr; 20 + char name[THERMAL_NAME_LENGTH]; 21 + }; 22 + 23 + struct thermal_trip_attrs { 24 + struct thermal_attr type; 25 + struct thermal_attr temp; 26 + struct thermal_attr hyst; 27 + }; 28 + 18 29 struct thermal_trip_desc { 19 30 struct thermal_trip trip; 31 + struct thermal_trip_attrs trip_attrs; 20 32 struct list_head notify_list_node; 21 33 int notify_temp; 22 34 int threshold; ··· 68 56 * @device: &struct device for this thermal zone 69 57 * @removal: removal completion 70 58 * @resume: resume completion 71 - * @trip_temp_attrs: attributes for trip points for sysfs: trip temperature 72 - * @trip_type_attrs: attributes for trip points for sysfs: trip type 73 - * @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis 74 59 * @mode: current mode of this thermal zone 75 60 * @devdata: private pointer for device private data 76 61 * @num_trips: number of trip points the thermal zone supports ··· 111 102 struct completion removal; 112 103 struct completion resume; 113 104 struct attribute_group trips_attribute_group; 114 - struct thermal_attr *trip_temp_attrs; 115 - struct thermal_attr *trip_type_attrs; 116 - struct thermal_attr *trip_hyst_attrs; 117 105 enum thermal_device_mode mode; 118 106 void *devdata; 119 107 int num_trips; ··· 194 188 195 189 struct thermal_zone_device *thermal_zone_get_by_id(int id); 196 190 197 - struct thermal_attr { 198 - struct device_attribute attr; 199 - char name[THERMAL_NAME_LENGTH]; 200 - }; 201 - 202 191 static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev) 203 192 { 204 193 return cdev->ops->get_requested_power && cdev->ops->state2power && ··· 205 204 206 205 int get_tz_trend(struct thermal_zone_device *tz, const struct thermal_trip *trip); 207 206 208 - struct thermal_instance * 209 - get_thermal_instance(struct thermal_zone_device *tz, 210 - struct thermal_cooling_device *cdev, 211 - int trip); 212 - 213 207 /* 214 208 * This structure is used to describe the behavior of 215 209 * a certain cooling device on a certain trip point ··· 213 217 struct thermal_instance { 214 218 int id; 215 219 char name[THERMAL_NAME_LENGTH]; 216 - struct thermal_zone_device *tz; 217 220 struct thermal_cooling_device *cdev; 218 221 const struct thermal_trip *trip; 219 222 bool initialized; ··· 254 259 255 260 const char *thermal_trip_type_name(enum thermal_trip_type trip_type); 256 261 257 - void thermal_zone_set_trips(struct thermal_zone_device *tz); 262 + void thermal_zone_set_trips(struct thermal_zone_device *tz, int low, int high); 258 263 int thermal_zone_trip_id(const struct thermal_zone_device *tz, 259 264 const struct thermal_trip *trip); 260 - void thermal_zone_trip_updated(struct thermal_zone_device *tz, 261 - const struct thermal_trip *trip); 262 265 int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp); 263 266 void thermal_zone_trip_down(struct thermal_zone_device *tz, 264 267 const struct thermal_trip *trip); 268 + void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, 269 + struct thermal_trip *trip, int hyst); 265 270 266 271 /* sysfs I/F */ 267 272 int thermal_zone_create_device_groups(struct thermal_zone_device *tz);
+6 -26
drivers/thermal/thermal_helpers.c
··· 39 39 return trend; 40 40 } 41 41 42 - static struct thermal_instance *get_instance(struct thermal_zone_device *tz, 43 - struct thermal_cooling_device *cdev, 44 - const struct thermal_trip *trip) 42 + static bool thermal_instance_present(struct thermal_zone_device *tz, 43 + struct thermal_cooling_device *cdev, 44 + const struct thermal_trip *trip) 45 45 { 46 46 struct thermal_instance *ti; 47 47 48 48 list_for_each_entry(ti, &tz->thermal_instances, tz_node) { 49 49 if (ti->trip == trip && ti->cdev == cdev) 50 - return ti; 50 + return true; 51 51 } 52 52 53 - return NULL; 53 + return false; 54 54 } 55 55 56 56 bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz, ··· 62 62 mutex_lock(&tz->lock); 63 63 mutex_lock(&cdev->lock); 64 64 65 - ret = !!get_instance(tz, cdev, trip); 65 + ret = thermal_instance_present(tz, cdev, trip); 66 66 67 67 mutex_unlock(&cdev->lock); 68 68 mutex_unlock(&tz->lock); ··· 70 70 return ret; 71 71 } 72 72 EXPORT_SYMBOL_GPL(thermal_trip_is_bound_to_cdev); 73 - 74 - struct thermal_instance * 75 - get_thermal_instance(struct thermal_zone_device *tz, 76 - struct thermal_cooling_device *cdev, int trip_index) 77 - { 78 - struct thermal_instance *ti; 79 - 80 - mutex_lock(&tz->lock); 81 - mutex_lock(&cdev->lock); 82 - 83 - ti = get_instance(tz, cdev, &tz->trips[trip_index].trip); 84 - 85 - mutex_unlock(&cdev->lock); 86 - mutex_unlock(&tz->lock); 87 - 88 - return ti; 89 - } 90 - EXPORT_SYMBOL(get_thermal_instance); 91 73 92 74 /** 93 75 * __thermal_zone_get_temp() - returns the temperature of a thermal zone ··· 181 199 182 200 /* Make sure cdev enters the deepest cooling state */ 183 201 list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { 184 - dev_dbg(&cdev->device, "zone%d->target=%lu\n", 185 - instance->tz->id, instance->target); 186 202 if (instance->target == THERMAL_NO_TARGET) 187 203 continue; 188 204 if (instance->target > target)
+40 -131
drivers/thermal/thermal_of.c
··· 20 20 21 21 /*** functions parsing device tree nodes ***/ 22 22 23 - static int of_find_trip_id(struct device_node *np, struct device_node *trip) 24 - { 25 - struct device_node *trips; 26 - struct device_node *t; 27 - int i = 0; 28 - 29 - trips = of_get_child_by_name(np, "trips"); 30 - if (!trips) { 31 - pr_err("Failed to find 'trips' node\n"); 32 - return -EINVAL; 33 - } 34 - 35 - /* 36 - * Find the trip id point associated with the cooling device map 37 - */ 38 - for_each_child_of_node(trips, t) { 39 - 40 - if (t == trip) { 41 - of_node_put(t); 42 - goto out; 43 - } 44 - i++; 45 - } 46 - 47 - i = -ENXIO; 48 - out: 49 - of_node_put(trips); 50 - 51 - return i; 52 - } 53 - 54 23 /* 55 24 * It maps 'enum thermal_trip_type' found in include/linux/thermal.h 56 25 * into the device tree binding of 'trip', property type. ··· 87 118 } 88 119 89 120 trip->flags = THERMAL_TRIP_FLAG_RW_TEMP; 121 + 122 + trip->priv = np; 90 123 91 124 return 0; 92 125 } ··· 262 291 return tz_np; 263 292 } 264 293 265 - static int __thermal_of_unbind(struct device_node *map_np, int index, int trip_id, 266 - struct thermal_zone_device *tz, struct thermal_cooling_device *cdev) 267 - { 268 - struct of_phandle_args cooling_spec; 269 - int ret; 270 - 271 - ret = of_parse_phandle_with_args(map_np, "cooling-device", "#cooling-cells", 272 - index, &cooling_spec); 273 - 274 - if (ret < 0) { 275 - pr_err("Invalid cooling-device entry\n"); 276 - return ret; 277 - } 278 - 279 - of_node_put(cooling_spec.np); 280 - 281 - if (cooling_spec.args_count < 2) { 282 - pr_err("wrong reference to cooling device, missing limits\n"); 283 - return -EINVAL; 284 - } 285 - 286 - if (cooling_spec.np != cdev->np) 287 - return 0; 288 - 289 - ret = thermal_zone_unbind_cooling_device(tz, trip_id, cdev); 290 - if (ret) 291 - pr_err("Failed to unbind '%s' with '%s': %d\n", tz->type, cdev->type, ret); 292 - 293 - return ret; 294 - } 295 - 296 - static int __thermal_of_bind(struct device_node *map_np, int index, int trip_id, 297 - struct thermal_zone_device *tz, struct thermal_cooling_device *cdev) 294 + static bool thermal_of_get_cooling_spec(struct device_node *map_np, int index, 295 + struct thermal_cooling_device *cdev, 296 + struct cooling_spec *c) 298 297 { 299 298 struct of_phandle_args cooling_spec; 300 299 int ret, weight = THERMAL_WEIGHT_DEFAULT; ··· 276 335 277 336 if (ret < 0) { 278 337 pr_err("Invalid cooling-device entry\n"); 279 - return ret; 338 + return false; 280 339 } 281 340 282 341 of_node_put(cooling_spec.np); 283 342 284 343 if (cooling_spec.args_count < 2) { 285 344 pr_err("wrong reference to cooling device, missing limits\n"); 286 - return -EINVAL; 345 + return false; 287 346 } 288 347 289 348 if (cooling_spec.np != cdev->np) 290 - return 0; 349 + return false; 291 350 292 - ret = thermal_zone_bind_cooling_device(tz, trip_id, cdev, cooling_spec.args[1], 293 - cooling_spec.args[0], 294 - weight); 295 - if (ret) 296 - pr_err("Failed to bind '%s' with '%s': %d\n", tz->type, cdev->type, ret); 351 + c->lower = cooling_spec.args[0]; 352 + c->upper = cooling_spec.args[1]; 353 + c->weight = weight; 297 354 298 - return ret; 355 + return true; 299 356 } 300 357 301 - static int thermal_of_for_each_cooling_device(struct device_node *tz_np, struct device_node *map_np, 302 - struct thermal_zone_device *tz, struct thermal_cooling_device *cdev, 303 - int (*action)(struct device_node *, int, int, 304 - struct thermal_zone_device *, struct thermal_cooling_device *)) 305 - { 306 - struct device_node *tr_np; 307 - int count, i, trip_id; 308 - 309 - tr_np = of_parse_phandle(map_np, "trip", 0); 310 - if (!tr_np) 311 - return -ENODEV; 312 - 313 - trip_id = of_find_trip_id(tz_np, tr_np); 314 - if (trip_id < 0) 315 - return trip_id; 316 - 317 - count = of_count_phandle_with_args(map_np, "cooling-device", "#cooling-cells"); 318 - if (count <= 0) { 319 - pr_err("Add a cooling_device property with at least one device\n"); 320 - return -ENOENT; 321 - } 322 - 323 - /* 324 - * At this point, we don't want to bail out when there is an 325 - * error, we will try to bind/unbind as many as possible 326 - * cooling devices 327 - */ 328 - for (i = 0; i < count; i++) 329 - action(map_np, i, trip_id, tz, cdev); 330 - 331 - return 0; 332 - } 333 - 334 - static int thermal_of_for_each_cooling_maps(struct thermal_zone_device *tz, 335 - struct thermal_cooling_device *cdev, 336 - int (*action)(struct device_node *, int, int, 337 - struct thermal_zone_device *, struct thermal_cooling_device *)) 358 + static bool thermal_of_should_bind(struct thermal_zone_device *tz, 359 + const struct thermal_trip *trip, 360 + struct thermal_cooling_device *cdev, 361 + struct cooling_spec *c) 338 362 { 339 363 struct device_node *tz_np, *cm_np, *child; 340 - int ret = 0; 364 + bool result = false; 341 365 342 366 tz_np = thermal_of_zone_get_by_name(tz); 343 367 if (IS_ERR(tz_np)) { 344 368 pr_err("Failed to get node tz by name\n"); 345 - return PTR_ERR(tz_np); 369 + return false; 346 370 } 347 371 348 372 cm_np = of_get_child_by_name(tz_np, "cooling-maps"); 349 373 if (!cm_np) 350 374 goto out; 351 375 376 + /* Look up the trip and the cdev in the cooling maps. */ 352 377 for_each_child_of_node(cm_np, child) { 353 - ret = thermal_of_for_each_cooling_device(tz_np, child, tz, cdev, action); 354 - if (ret) { 355 - of_node_put(child); 356 - break; 378 + struct device_node *tr_np; 379 + int count, i; 380 + 381 + tr_np = of_parse_phandle(child, "trip", 0); 382 + if (tr_np != trip->priv) 383 + continue; 384 + 385 + /* The trip has been found, look up the cdev. */ 386 + count = of_count_phandle_with_args(child, "cooling-device", "#cooling-cells"); 387 + if (count <= 0) 388 + pr_err("Add a cooling_device property with at least one device\n"); 389 + 390 + for (i = 0; i < count; i++) { 391 + result = thermal_of_get_cooling_spec(child, i, cdev, c); 392 + if (result) 393 + break; 357 394 } 395 + 396 + of_node_put(child); 397 + break; 358 398 } 359 399 360 400 of_node_put(cm_np); 361 401 out: 362 402 of_node_put(tz_np); 363 403 364 - return ret; 365 - } 366 - 367 - static int thermal_of_bind(struct thermal_zone_device *tz, 368 - struct thermal_cooling_device *cdev) 369 - { 370 - return thermal_of_for_each_cooling_maps(tz, cdev, __thermal_of_bind); 371 - } 372 - 373 - static int thermal_of_unbind(struct thermal_zone_device *tz, 374 - struct thermal_cooling_device *cdev) 375 - { 376 - return thermal_of_for_each_cooling_maps(tz, cdev, __thermal_of_unbind); 404 + return result; 377 405 } 378 406 379 407 /** ··· 414 504 415 505 thermal_of_parameters_init(np, &tzp); 416 506 417 - of_ops.bind = thermal_of_bind; 418 - of_ops.unbind = thermal_of_unbind; 507 + of_ops.should_bind = thermal_of_should_bind; 419 508 420 509 ret = of_property_read_string(np, "critical-action", &action); 421 510 if (!ret)
+62 -114
drivers/thermal/thermal_sysfs.c
··· 12 12 13 13 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 14 14 15 + #include <linux/container_of.h> 15 16 #include <linux/sysfs.h> 16 17 #include <linux/device.h> 17 18 #include <linux/err.h> ··· 79 78 return count; 80 79 } 81 80 81 + #define thermal_trip_of_attr(_ptr_, _attr_) \ 82 + ({ \ 83 + struct thermal_trip_desc *td; \ 84 + \ 85 + td = container_of(_ptr_, struct thermal_trip_desc, \ 86 + trip_attrs._attr_.attr); \ 87 + &td->trip; \ 88 + }) 89 + 82 90 static ssize_t 83 91 trip_point_type_show(struct device *dev, struct device_attribute *attr, 84 92 char *buf) 85 93 { 86 - struct thermal_zone_device *tz = to_thermal_zone(dev); 87 - int trip_id; 94 + struct thermal_trip *trip = thermal_trip_of_attr(attr, type); 88 95 89 - if (sscanf(attr->attr.name, "trip_point_%d_type", &trip_id) != 1) 90 - return -EINVAL; 91 - 92 - return sprintf(buf, "%s\n", thermal_trip_type_name(tz->trips[trip_id].trip.type)); 96 + return sprintf(buf, "%s\n", thermal_trip_type_name(trip->type)); 93 97 } 94 98 95 99 static ssize_t 96 100 trip_point_temp_store(struct device *dev, struct device_attribute *attr, 97 101 const char *buf, size_t count) 98 102 { 103 + struct thermal_trip *trip = thermal_trip_of_attr(attr, temp); 99 104 struct thermal_zone_device *tz = to_thermal_zone(dev); 100 - struct thermal_trip *trip; 101 - int trip_id, ret; 102 - int temp; 105 + int ret, temp; 103 106 104 107 ret = kstrtoint(buf, 10, &temp); 105 108 if (ret) 106 109 return -EINVAL; 107 110 108 - if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1) 109 - return -EINVAL; 110 - 111 111 mutex_lock(&tz->lock); 112 - 113 - trip = &tz->trips[trip_id].trip; 114 112 115 113 if (temp != trip->temperature) { 116 114 if (tz->ops.set_trip_temp) { ··· 133 133 trip_point_temp_show(struct device *dev, struct device_attribute *attr, 134 134 char *buf) 135 135 { 136 - struct thermal_zone_device *tz = to_thermal_zone(dev); 137 - int trip_id; 136 + struct thermal_trip *trip = thermal_trip_of_attr(attr, temp); 138 137 139 - if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1) 140 - return -EINVAL; 141 - 142 - return sprintf(buf, "%d\n", READ_ONCE(tz->trips[trip_id].trip.temperature)); 138 + return sprintf(buf, "%d\n", READ_ONCE(trip->temperature)); 143 139 } 144 140 145 141 static ssize_t 146 142 trip_point_hyst_store(struct device *dev, struct device_attribute *attr, 147 143 const char *buf, size_t count) 148 144 { 145 + struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst); 149 146 struct thermal_zone_device *tz = to_thermal_zone(dev); 150 - struct thermal_trip *trip; 151 - int trip_id, ret; 152 - int hyst; 147 + int ret, hyst; 153 148 154 149 ret = kstrtoint(buf, 10, &hyst); 155 150 if (ret || hyst < 0) 156 151 return -EINVAL; 157 152 158 - if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1) 159 - return -EINVAL; 160 - 161 153 mutex_lock(&tz->lock); 162 154 163 - trip = &tz->trips[trip_id].trip; 164 - 165 155 if (hyst != trip->hysteresis) { 166 - WRITE_ONCE(trip->hysteresis, hyst); 156 + thermal_zone_set_trip_hyst(tz, trip, hyst); 167 157 168 - thermal_zone_trip_updated(tz, trip); 158 + __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); 169 159 } 170 160 171 161 mutex_unlock(&tz->lock); ··· 167 177 trip_point_hyst_show(struct device *dev, struct device_attribute *attr, 168 178 char *buf) 169 179 { 170 - struct thermal_zone_device *tz = to_thermal_zone(dev); 171 - int trip_id; 180 + struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst); 172 181 173 - if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1) 174 - return -EINVAL; 175 - 176 - return sprintf(buf, "%d\n", READ_ONCE(tz->trips[trip_id].trip.hysteresis)); 182 + return sprintf(buf, "%d\n", READ_ONCE(trip->hysteresis)); 177 183 } 178 184 179 185 static ssize_t ··· 368 382 */ 369 383 static int create_trip_attrs(struct thermal_zone_device *tz) 370 384 { 371 - const struct thermal_trip_desc *td; 385 + struct thermal_trip_desc *td; 372 386 struct attribute **attrs; 373 - 374 - /* This function works only for zones with at least one trip */ 375 - if (tz->num_trips <= 0) 376 - return -EINVAL; 377 - 378 - tz->trip_type_attrs = kcalloc(tz->num_trips, sizeof(*tz->trip_type_attrs), 379 - GFP_KERNEL); 380 - if (!tz->trip_type_attrs) 381 - return -ENOMEM; 382 - 383 - tz->trip_temp_attrs = kcalloc(tz->num_trips, sizeof(*tz->trip_temp_attrs), 384 - GFP_KERNEL); 385 - if (!tz->trip_temp_attrs) { 386 - kfree(tz->trip_type_attrs); 387 - return -ENOMEM; 388 - } 389 - 390 - tz->trip_hyst_attrs = kcalloc(tz->num_trips, 391 - sizeof(*tz->trip_hyst_attrs), 392 - GFP_KERNEL); 393 - if (!tz->trip_hyst_attrs) { 394 - kfree(tz->trip_type_attrs); 395 - kfree(tz->trip_temp_attrs); 396 - return -ENOMEM; 397 - } 387 + int i; 398 388 399 389 attrs = kcalloc(tz->num_trips * 3 + 1, sizeof(*attrs), GFP_KERNEL); 400 - if (!attrs) { 401 - kfree(tz->trip_type_attrs); 402 - kfree(tz->trip_temp_attrs); 403 - kfree(tz->trip_hyst_attrs); 390 + if (!attrs) 404 391 return -ENOMEM; 405 - } 406 392 393 + i = 0; 407 394 for_each_trip_desc(tz, td) { 408 - int indx = thermal_zone_trip_id(tz, &td->trip); 395 + struct thermal_trip_attrs *trip_attrs = &td->trip_attrs; 409 396 410 397 /* create trip type attribute */ 411 - snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH, 412 - "trip_point_%d_type", indx); 398 + snprintf(trip_attrs->type.name, THERMAL_NAME_LENGTH, 399 + "trip_point_%d_type", i); 413 400 414 - sysfs_attr_init(&tz->trip_type_attrs[indx].attr.attr); 415 - tz->trip_type_attrs[indx].attr.attr.name = 416 - tz->trip_type_attrs[indx].name; 417 - tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO; 418 - tz->trip_type_attrs[indx].attr.show = trip_point_type_show; 419 - attrs[indx] = &tz->trip_type_attrs[indx].attr.attr; 401 + sysfs_attr_init(&trip_attrs->type.attr.attr); 402 + trip_attrs->type.attr.attr.name = trip_attrs->type.name; 403 + trip_attrs->type.attr.attr.mode = S_IRUGO; 404 + trip_attrs->type.attr.show = trip_point_type_show; 405 + attrs[i] = &trip_attrs->type.attr.attr; 420 406 421 407 /* create trip temp attribute */ 422 - snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH, 423 - "trip_point_%d_temp", indx); 408 + snprintf(trip_attrs->temp.name, THERMAL_NAME_LENGTH, 409 + "trip_point_%d_temp", i); 424 410 425 - sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr); 426 - tz->trip_temp_attrs[indx].attr.attr.name = 427 - tz->trip_temp_attrs[indx].name; 428 - tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO; 429 - tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show; 411 + sysfs_attr_init(&trip_attrs->temp.attr.attr); 412 + trip_attrs->temp.attr.attr.name = trip_attrs->temp.name; 413 + trip_attrs->temp.attr.attr.mode = S_IRUGO; 414 + trip_attrs->temp.attr.show = trip_point_temp_show; 430 415 if (td->trip.flags & THERMAL_TRIP_FLAG_RW_TEMP) { 431 - tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR; 432 - tz->trip_temp_attrs[indx].attr.store = 433 - trip_point_temp_store; 416 + trip_attrs->temp.attr.attr.mode |= S_IWUSR; 417 + trip_attrs->temp.attr.store = trip_point_temp_store; 434 418 } 435 - attrs[indx + tz->num_trips] = &tz->trip_temp_attrs[indx].attr.attr; 419 + attrs[i + tz->num_trips] = &trip_attrs->temp.attr.attr; 436 420 437 - snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH, 438 - "trip_point_%d_hyst", indx); 421 + snprintf(trip_attrs->hyst.name, THERMAL_NAME_LENGTH, 422 + "trip_point_%d_hyst", i); 439 423 440 - sysfs_attr_init(&tz->trip_hyst_attrs[indx].attr.attr); 441 - tz->trip_hyst_attrs[indx].attr.attr.name = 442 - tz->trip_hyst_attrs[indx].name; 443 - tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO; 444 - tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show; 424 + sysfs_attr_init(&trip_attrs->hyst.attr.attr); 425 + trip_attrs->hyst.attr.attr.name = trip_attrs->hyst.name; 426 + trip_attrs->hyst.attr.attr.mode = S_IRUGO; 427 + trip_attrs->hyst.attr.show = trip_point_hyst_show; 445 428 if (td->trip.flags & THERMAL_TRIP_FLAG_RW_HYST) { 446 - tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR; 447 - tz->trip_hyst_attrs[indx].attr.store = 448 - trip_point_hyst_store; 429 + trip_attrs->hyst.attr.attr.mode |= S_IWUSR; 430 + trip_attrs->hyst.attr.store = trip_point_hyst_store; 449 431 } 450 - attrs[indx + tz->num_trips * 2] = 451 - &tz->trip_hyst_attrs[indx].attr.attr; 432 + attrs[i + 2 * tz->num_trips] = &trip_attrs->hyst.attr.attr; 433 + i++; 452 434 } 453 435 attrs[tz->num_trips * 3] = NULL; 454 436 ··· 433 479 */ 434 480 static void destroy_trip_attrs(struct thermal_zone_device *tz) 435 481 { 436 - if (!tz) 437 - return; 438 - 439 - kfree(tz->trip_type_attrs); 440 - kfree(tz->trip_temp_attrs); 441 - kfree(tz->trip_hyst_attrs); 442 - kfree(tz->trips_attribute_group.attrs); 482 + if (tz) 483 + kfree(tz->trips_attribute_group.attrs); 443 484 } 444 485 445 486 int thermal_zone_create_device_groups(struct thermal_zone_device *tz) ··· 836 887 ssize_t 837 888 trip_point_show(struct device *dev, struct device_attribute *attr, char *buf) 838 889 { 890 + struct thermal_zone_device *tz = to_thermal_zone(dev); 839 891 struct thermal_instance *instance; 840 892 841 - instance = 842 - container_of(attr, struct thermal_instance, attr); 893 + instance = container_of(attr, struct thermal_instance, attr); 843 894 844 - return sprintf(buf, "%d\n", 845 - thermal_zone_trip_id(instance->tz, instance->trip)); 895 + return sprintf(buf, "%d\n", thermal_zone_trip_id(tz, instance->trip)); 846 896 } 847 897 848 898 ssize_t ··· 857 909 ssize_t weight_store(struct device *dev, struct device_attribute *attr, 858 910 const char *buf, size_t count) 859 911 { 912 + struct thermal_zone_device *tz = to_thermal_zone(dev); 860 913 struct thermal_instance *instance; 861 914 int ret, weight; 862 915 ··· 868 919 instance = container_of(attr, struct thermal_instance, weight_attr); 869 920 870 921 /* Don't race with governors using the 'weight' value */ 871 - mutex_lock(&instance->tz->lock); 922 + mutex_lock(&tz->lock); 872 923 873 924 instance->weight = weight; 874 925 875 - thermal_governor_update_tz(instance->tz, 876 - THERMAL_INSTANCE_WEIGHT_CHANGED); 926 + thermal_governor_update_tz(tz, THERMAL_INSTANCE_WEIGHT_CHANGED); 877 927 878 - mutex_unlock(&instance->tz->lock); 928 + mutex_unlock(&tz->lock); 879 929 880 930 return count; 881 931 }
+4 -49
drivers/thermal/thermal_trip.c
··· 55 55 } 56 56 EXPORT_SYMBOL_GPL(thermal_zone_for_each_trip); 57 57 58 - int thermal_zone_get_num_trips(struct thermal_zone_device *tz) 58 + void thermal_zone_set_trips(struct thermal_zone_device *tz, int low, int high) 59 59 { 60 - return tz->num_trips; 61 - } 62 - EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips); 63 - 64 - /** 65 - * thermal_zone_set_trips - Computes the next trip points for the driver 66 - * @tz: a pointer to a thermal zone device structure 67 - * 68 - * The function computes the next temperature boundaries by browsing 69 - * the trip points. The result is the closer low and high trip points 70 - * to the current temperature. These values are passed to the backend 71 - * driver to let it set its own notification mechanism (usually an 72 - * interrupt). 73 - * 74 - * This function must be called with tz->lock held. Both tz and tz->ops 75 - * must be valid pointers. 76 - * 77 - * It does not return a value 78 - */ 79 - void thermal_zone_set_trips(struct thermal_zone_device *tz) 80 - { 81 - const struct thermal_trip_desc *td; 82 - int low = -INT_MAX, high = INT_MAX; 83 60 int ret; 84 61 85 62 lockdep_assert_held(&tz->lock); 86 63 87 64 if (!tz->ops.set_trips) 88 65 return; 89 - 90 - for_each_trip_desc(tz, td) { 91 - if (td->threshold <= tz->temperature && td->threshold > low) 92 - low = td->threshold; 93 - 94 - if (td->threshold >= tz->temperature && td->threshold < high) 95 - high = td->threshold; 96 - } 97 66 98 67 /* No need to change trip points */ 99 68 if (tz->prev_low_trip == low && tz->prev_high_trip == high) ··· 83 114 dev_err(&tz->device, "Failed to set trips: %d\n", ret); 84 115 } 85 116 86 - int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, 87 - struct thermal_trip *trip) 88 - { 89 - if (!tz || !trip || trip_id < 0 || trip_id >= tz->num_trips) 90 - return -EINVAL; 91 - 92 - mutex_lock(&tz->lock); 93 - *trip = tz->trips[trip_id].trip; 94 - mutex_unlock(&tz->lock); 95 - 96 - return 0; 97 - } 98 - EXPORT_SYMBOL_GPL(thermal_zone_get_trip); 99 - 100 117 int thermal_zone_trip_id(const struct thermal_zone_device *tz, 101 118 const struct thermal_trip *trip) 102 119 { ··· 93 138 return trip_to_trip_desc(trip) - tz->trips; 94 139 } 95 140 96 - void thermal_zone_trip_updated(struct thermal_zone_device *tz, 97 - const struct thermal_trip *trip) 141 + void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, 142 + struct thermal_trip *trip, int hyst) 98 143 { 144 + WRITE_ONCE(trip->hysteresis, hyst); 99 145 thermal_notify_tz_trip_change(tz, trip); 100 - __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); 101 146 } 102 147 103 148 void thermal_zone_set_trip_temp(struct thermal_zone_device *tz,
+10 -21
include/linux/thermal.h
··· 85 85 86 86 struct thermal_zone_device; 87 87 88 + struct cooling_spec { 89 + unsigned long upper; /* Highest cooling state */ 90 + unsigned long lower; /* Lowest cooling state */ 91 + unsigned int weight; /* Cooling device weight */ 92 + }; 93 + 88 94 struct thermal_zone_device_ops { 89 - int (*bind) (struct thermal_zone_device *, 90 - struct thermal_cooling_device *); 91 - int (*unbind) (struct thermal_zone_device *, 92 - struct thermal_cooling_device *); 95 + bool (*should_bind) (struct thermal_zone_device *, 96 + const struct thermal_trip *, 97 + struct thermal_cooling_device *, 98 + struct cooling_spec *); 93 99 int (*get_temp) (struct thermal_zone_device *, int *); 94 100 int (*set_trips) (struct thermal_zone_device *, int, int); 95 101 int (*change_mode) (struct thermal_zone_device *, ··· 209 203 } 210 204 #endif 211 205 212 - int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, 213 - struct thermal_trip *trip); 214 206 int for_each_thermal_trip(struct thermal_zone_device *tz, 215 207 int (*cb)(struct thermal_trip *, void *), 216 208 void *data); 217 209 int thermal_zone_for_each_trip(struct thermal_zone_device *tz, 218 210 int (*cb)(struct thermal_trip *, void *), 219 211 void *data); 220 - int thermal_zone_get_num_trips(struct thermal_zone_device *tz); 221 212 void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, 222 213 struct thermal_trip *trip, int temp); 223 214 ··· 243 240 int thermal_zone_device_id(struct thermal_zone_device *tzd); 244 241 struct device *thermal_zone_device(struct thermal_zone_device *tzd); 245 242 246 - int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, 247 - const struct thermal_trip *trip, 248 - struct thermal_cooling_device *cdev, 249 - unsigned long upper, unsigned long lower, 250 - unsigned int weight); 251 - int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int, 252 - struct thermal_cooling_device *, 253 - unsigned long, unsigned long, 254 - unsigned int); 255 - int thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, 256 - const struct thermal_trip *trip, 257 - struct thermal_cooling_device *cdev); 258 - int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int, 259 - struct thermal_cooling_device *); 260 243 void thermal_zone_device_update(struct thermal_zone_device *, 261 244 enum thermal_notify_event); 262 245