1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * of-thermal.c - Generic Thermal Management device tree support. 4 * 5 * Copyright (C) 2013 Texas Instruments 6 * Copyright (C) 2013 Eduardo Valentin <eduardo.valentin@ti.com> 7 */ 8 9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 10 11 #include <linux/err.h> 12 #include <linux/export.h> 13 #include <linux/of_device.h> 14 #include <linux/of_platform.h> 15 #include <linux/slab.h> 16 #include <linux/thermal.h> 17 #include <linux/types.h> 18 #include <linux/string.h> 19 20 #include "thermal_core.h" 21 22 /** 23 * of_thermal_get_ntrips - function to export number of available trip 24 * points. 25 * @tz: pointer to a thermal zone 26 * 27 * This function is a globally visible wrapper to get number of trip points 28 * stored in the local struct __thermal_zone 29 * 30 * Return: number of available trip points, -ENODEV when data not available 31 */ 32 int of_thermal_get_ntrips(struct thermal_zone_device *tz) 33 { 34 return tz->num_trips; 35 } 36 EXPORT_SYMBOL_GPL(of_thermal_get_ntrips); 37 38 /** 39 * of_thermal_is_trip_valid - function to check if trip point is valid 40 * 41 * @tz: pointer to a thermal zone 42 * @trip: trip point to evaluate 43 * 44 * This function is responsible for checking if passed trip point is valid 45 * 46 * Return: true if trip point is valid, false otherwise 47 */ 48 bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip) 49 { 50 if (trip >= tz->num_trips || trip < 0) 51 return false; 52 53 return true; 54 } 55 EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid); 56 57 /** 58 * of_thermal_get_trip_points - function to get access to a globally exported 59 * trip points 60 * 61 * @tz: pointer to a thermal zone 62 * 63 * This function provides a pointer to trip points table 64 * 65 * Return: pointer to trip points table, NULL otherwise 66 */ 67 const struct thermal_trip * 68 of_thermal_get_trip_points(struct thermal_zone_device *tz) 69 { 70 return tz->trips; 71 } 72 EXPORT_SYMBOL_GPL(of_thermal_get_trip_points); 73 74 static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip, 75 enum thermal_trip_type *type) 76 { 77 if (trip >= tz->num_trips || trip < 0) 78 return -EDOM; 79 80 *type = tz->trips[trip].type; 81 82 return 0; 83 } 84 85 static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip, 86 int *temp) 87 { 88 if (trip >= tz->num_trips || trip < 0) 89 return -EDOM; 90 91 *temp = tz->trips[trip].temperature; 92 93 return 0; 94 } 95 96 static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip, 97 int *hyst) 98 { 99 if (trip >= tz->num_trips || trip < 0) 100 return -EDOM; 101 102 *hyst = tz->trips[trip].hysteresis; 103 104 return 0; 105 } 106 107 static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip, 108 int hyst) 109 { 110 if (trip >= tz->num_trips || trip < 0) 111 return -EDOM; 112 113 /* thermal framework should take care of data->mask & (1 << trip) */ 114 tz->trips[trip].hysteresis = hyst; 115 116 return 0; 117 } 118 119 static int of_thermal_get_crit_temp(struct thermal_zone_device *tz, 120 int *temp) 121 { 122 int i; 123 124 for (i = 0; i < tz->num_trips; i++) 125 if (tz->trips[i].type == THERMAL_TRIP_CRITICAL) { 126 *temp = tz->trips[i].temperature; 127 return 0; 128 } 129 130 return -EINVAL; 131 } 132 133 /** 134 * thermal_zone_of_get_sensor_id - get sensor ID from a DT thermal zone 135 * @tz_np: a valid thermal zone device node. 136 * @sensor_np: a sensor node of a valid sensor device. 137 * @id: the sensor ID returned if success. 138 * 139 * This function will get sensor ID from a given thermal zone node and 140 * the sensor node must match the temperature provider @sensor_np. 141 * 142 * Return: 0 on success, proper error code otherwise. 143 */ 144 145 int thermal_zone_of_get_sensor_id(struct device_node *tz_np, 146 struct device_node *sensor_np, 147 u32 *id) 148 { 149 struct of_phandle_args sensor_specs; 150 int ret; 151 152 ret = of_parse_phandle_with_args(tz_np, 153 "thermal-sensors", 154 "#thermal-sensor-cells", 155 0, 156 &sensor_specs); 157 if (ret) 158 return ret; 159 160 if (sensor_specs.np != sensor_np) { 161 of_node_put(sensor_specs.np); 162 return -ENODEV; 163 } 164 165 if (sensor_specs.args_count > 1) 166 pr_warn("%pOFn: too many cells in sensor specifier %d\n", 167 sensor_specs.np, sensor_specs.args_count); 168 169 *id = sensor_specs.args_count ? sensor_specs.args[0] : 0; 170 171 of_node_put(sensor_specs.np); 172 173 return 0; 174 } 175 EXPORT_SYMBOL_GPL(thermal_zone_of_get_sensor_id); 176 177 /*** functions parsing device tree nodes ***/ 178 179 static int of_find_trip_id(struct device_node *np, struct device_node *trip) 180 { 181 struct device_node *trips; 182 struct device_node *t; 183 int i = 0; 184 185 trips = of_get_child_by_name(np, "trips"); 186 if (!trips) { 187 pr_err("Failed to find 'trips' node\n"); 188 return -EINVAL; 189 } 190 191 /* 192 * Find the trip id point associated with the cooling device map 193 */ 194 for_each_child_of_node(trips, t) { 195 196 if (t == trip) 197 goto out; 198 i++; 199 } 200 201 i = -ENXIO; 202 out: 203 of_node_put(trips); 204 205 return i; 206 } 207 208 /* 209 * It maps 'enum thermal_trip_type' found in include/linux/thermal.h 210 * into the device tree binding of 'trip', property type. 211 */ 212 static const char * const trip_types[] = { 213 [THERMAL_TRIP_ACTIVE] = "active", 214 [THERMAL_TRIP_PASSIVE] = "passive", 215 [THERMAL_TRIP_HOT] = "hot", 216 [THERMAL_TRIP_CRITICAL] = "critical", 217 }; 218 219 /** 220 * thermal_of_get_trip_type - Get phy mode for given device_node 221 * @np: Pointer to the given device_node 222 * @type: Pointer to resulting trip type 223 * 224 * The function gets trip type string from property 'type', 225 * and store its index in trip_types table in @type, 226 * 227 * Return: 0 on success, or errno in error case. 228 */ 229 static int thermal_of_get_trip_type(struct device_node *np, 230 enum thermal_trip_type *type) 231 { 232 const char *t; 233 int err, i; 234 235 err = of_property_read_string(np, "type", &t); 236 if (err < 0) 237 return err; 238 239 for (i = 0; i < ARRAY_SIZE(trip_types); i++) 240 if (!strcasecmp(t, trip_types[i])) { 241 *type = i; 242 return 0; 243 } 244 245 return -ENODEV; 246 } 247 248 static int thermal_of_populate_trip(struct device_node *np, 249 struct thermal_trip *trip) 250 { 251 int prop; 252 int ret; 253 254 ret = of_property_read_u32(np, "temperature", &prop); 255 if (ret < 0) { 256 pr_err("missing temperature property\n"); 257 return ret; 258 } 259 trip->temperature = prop; 260 261 ret = of_property_read_u32(np, "hysteresis", &prop); 262 if (ret < 0) { 263 pr_err("missing hysteresis property\n"); 264 return ret; 265 } 266 trip->hysteresis = prop; 267 268 ret = thermal_of_get_trip_type(np, &trip->type); 269 if (ret < 0) { 270 pr_err("wrong trip type property\n"); 271 return ret; 272 } 273 274 return 0; 275 } 276 277 static struct thermal_trip *thermal_of_trips_init(struct device_node *np, int *ntrips) 278 { 279 struct thermal_trip *tt; 280 struct device_node *trips, *trip; 281 int ret, count; 282 283 trips = of_get_child_by_name(np, "trips"); 284 if (!trips) { 285 pr_err("Failed to find 'trips' node\n"); 286 return ERR_PTR(-EINVAL); 287 } 288 289 count = of_get_child_count(trips); 290 if (!count) { 291 pr_err("No trip point defined\n"); 292 ret = -EINVAL; 293 goto out_of_node_put; 294 } 295 296 tt = kzalloc(sizeof(*tt) * count, GFP_KERNEL); 297 if (!tt) { 298 ret = -ENOMEM; 299 goto out_of_node_put; 300 } 301 302 *ntrips = count; 303 304 count = 0; 305 for_each_child_of_node(trips, trip) { 306 ret = thermal_of_populate_trip(trip, &tt[count++]); 307 if (ret) 308 goto out_kfree; 309 } 310 311 of_node_put(trips); 312 313 return tt; 314 315 out_kfree: 316 kfree(tt); 317 *ntrips = 0; 318 out_of_node_put: 319 of_node_put(trips); 320 321 return ERR_PTR(ret); 322 } 323 324 static struct device_node *of_thermal_zone_find(struct device_node *sensor, int id) 325 { 326 struct device_node *np, *tz; 327 struct of_phandle_args sensor_specs; 328 329 np = of_find_node_by_name(NULL, "thermal-zones"); 330 if (!np) { 331 pr_debug("No thermal zones description\n"); 332 return ERR_PTR(-ENODEV); 333 } 334 335 /* 336 * Search for each thermal zone, a defined sensor 337 * corresponding to the one passed as parameter 338 */ 339 for_each_available_child_of_node(np, tz) { 340 341 int count, i; 342 343 count = of_count_phandle_with_args(tz, "thermal-sensors", 344 "#thermal-sensor-cells"); 345 if (count <= 0) { 346 pr_err("%pOFn: missing thermal sensor\n", tz); 347 tz = ERR_PTR(-EINVAL); 348 goto out; 349 } 350 351 for (i = 0; i < count; i++) { 352 353 int ret; 354 355 ret = of_parse_phandle_with_args(tz, "thermal-sensors", 356 "#thermal-sensor-cells", 357 i, &sensor_specs); 358 if (ret < 0) { 359 pr_err("%pOFn: Failed to read thermal-sensors cells: %d\n", tz, ret); 360 tz = ERR_PTR(ret); 361 goto out; 362 } 363 364 if ((sensor == sensor_specs.np) && id == (sensor_specs.args_count ? 365 sensor_specs.args[0] : 0)) { 366 pr_debug("sensor %pOFn id=%d belongs to %pOFn\n", sensor, id, tz); 367 goto out; 368 } 369 } 370 } 371 tz = ERR_PTR(-ENODEV); 372 out: 373 of_node_put(np); 374 return tz; 375 } 376 377 static int thermal_of_monitor_init(struct device_node *np, int *delay, int *pdelay) 378 { 379 int ret; 380 381 ret = of_property_read_u32(np, "polling-delay-passive", pdelay); 382 if (ret < 0) { 383 pr_err("%pOFn: missing polling-delay-passive property\n", np); 384 return ret; 385 } 386 387 ret = of_property_read_u32(np, "polling-delay", delay); 388 if (ret < 0) { 389 pr_err("%pOFn: missing polling-delay property\n", np); 390 return ret; 391 } 392 393 return 0; 394 } 395 396 static struct thermal_zone_params *thermal_of_parameters_init(struct device_node *np) 397 { 398 struct thermal_zone_params *tzp; 399 int coef[2]; 400 int ncoef = ARRAY_SIZE(coef); 401 int prop, ret; 402 403 tzp = kzalloc(sizeof(*tzp), GFP_KERNEL); 404 if (!tzp) 405 return ERR_PTR(-ENOMEM); 406 407 tzp->no_hwmon = true; 408 409 if (!of_property_read_u32(np, "sustainable-power", &prop)) 410 tzp->sustainable_power = prop; 411 412 /* 413 * For now, the thermal framework supports only one sensor per 414 * thermal zone. Thus, we are considering only the first two 415 * values as slope and offset. 416 */ 417 ret = of_property_read_u32_array(np, "coefficients", coef, ncoef); 418 if (ret) { 419 coef[0] = 1; 420 coef[1] = 0; 421 } 422 423 tzp->slope = coef[0]; 424 tzp->offset = coef[1]; 425 426 return tzp; 427 } 428 429 static struct device_node *thermal_of_zone_get_by_name(struct thermal_zone_device *tz) 430 { 431 struct device_node *np, *tz_np; 432 433 np = of_find_node_by_name(NULL, "thermal-zones"); 434 if (!np) 435 return ERR_PTR(-ENODEV); 436 437 tz_np = of_get_child_by_name(np, tz->type); 438 439 of_node_put(np); 440 441 if (!tz_np) 442 return ERR_PTR(-ENODEV); 443 444 return tz_np; 445 } 446 447 static int __thermal_of_unbind(struct device_node *map_np, int index, int trip_id, 448 struct thermal_zone_device *tz, struct thermal_cooling_device *cdev) 449 { 450 struct of_phandle_args cooling_spec; 451 int ret; 452 453 ret = of_parse_phandle_with_args(map_np, "cooling-device", "#cooling-cells", 454 index, &cooling_spec); 455 456 of_node_put(cooling_spec.np); 457 458 if (ret < 0) { 459 pr_err("Invalid cooling-device entry\n"); 460 return ret; 461 } 462 463 if (cooling_spec.args_count < 2) { 464 pr_err("wrong reference to cooling device, missing limits\n"); 465 return -EINVAL; 466 } 467 468 if (cooling_spec.np != cdev->np) 469 return 0; 470 471 ret = thermal_zone_unbind_cooling_device(tz, trip_id, cdev); 472 if (ret) 473 pr_err("Failed to unbind '%s' with '%s': %d\n", tz->type, cdev->type, ret); 474 475 return ret; 476 } 477 478 static int __thermal_of_bind(struct device_node *map_np, int index, int trip_id, 479 struct thermal_zone_device *tz, struct thermal_cooling_device *cdev) 480 { 481 struct of_phandle_args cooling_spec; 482 int ret, weight = THERMAL_WEIGHT_DEFAULT; 483 484 of_property_read_u32(map_np, "contribution", &weight); 485 486 ret = of_parse_phandle_with_args(map_np, "cooling-device", "#cooling-cells", 487 index, &cooling_spec); 488 489 of_node_put(cooling_spec.np); 490 491 if (ret < 0) { 492 pr_err("Invalid cooling-device entry\n"); 493 return ret; 494 } 495 496 if (cooling_spec.args_count < 2) { 497 pr_err("wrong reference to cooling device, missing limits\n"); 498 return -EINVAL; 499 } 500 501 if (cooling_spec.np != cdev->np) 502 return 0; 503 504 ret = thermal_zone_bind_cooling_device(tz, trip_id, cdev, cooling_spec.args[1], 505 cooling_spec.args[0], 506 weight); 507 if (ret) 508 pr_err("Failed to bind '%s' with '%s': %d\n", tz->type, cdev->type, ret); 509 510 return ret; 511 } 512 513 static int thermal_of_for_each_cooling_device(struct device_node *tz_np, struct device_node *map_np, 514 struct thermal_zone_device *tz, struct thermal_cooling_device *cdev, 515 int (*action)(struct device_node *, int, int, 516 struct thermal_zone_device *, struct thermal_cooling_device *)) 517 { 518 struct device_node *tr_np; 519 int count, i, trip_id; 520 521 tr_np = of_parse_phandle(map_np, "trip", 0); 522 if (!tr_np) 523 return -ENODEV; 524 525 trip_id = of_find_trip_id(tz_np, tr_np); 526 if (trip_id < 0) 527 return trip_id; 528 529 count = of_count_phandle_with_args(map_np, "cooling-device", "#cooling-cells"); 530 if (count <= 0) { 531 pr_err("Add a cooling_device property with at least one device\n"); 532 return -ENOENT; 533 } 534 535 /* 536 * At this point, we don't want to bail out when there is an 537 * error, we will try to bind/unbind as many as possible 538 * cooling devices 539 */ 540 for (i = 0; i < count; i++) 541 action(map_np, i, trip_id, tz, cdev); 542 543 return 0; 544 } 545 546 static int thermal_of_for_each_cooling_maps(struct thermal_zone_device *tz, 547 struct thermal_cooling_device *cdev, 548 int (*action)(struct device_node *, int, int, 549 struct thermal_zone_device *, struct thermal_cooling_device *)) 550 { 551 struct device_node *tz_np, *cm_np, *child; 552 int ret = 0; 553 554 tz_np = thermal_of_zone_get_by_name(tz); 555 if (IS_ERR(tz_np)) { 556 pr_err("Failed to get node tz by name\n"); 557 return PTR_ERR(tz_np); 558 } 559 560 cm_np = of_get_child_by_name(tz_np, "cooling-maps"); 561 if (!cm_np) 562 goto out; 563 564 for_each_child_of_node(cm_np, child) { 565 ret = thermal_of_for_each_cooling_device(tz_np, child, tz, cdev, action); 566 if (ret) 567 break; 568 } 569 570 of_node_put(cm_np); 571 out: 572 of_node_put(tz_np); 573 574 return ret; 575 } 576 577 static int thermal_of_bind(struct thermal_zone_device *tz, 578 struct thermal_cooling_device *cdev) 579 { 580 return thermal_of_for_each_cooling_maps(tz, cdev, __thermal_of_bind); 581 } 582 583 static int thermal_of_unbind(struct thermal_zone_device *tz, 584 struct thermal_cooling_device *cdev) 585 { 586 return thermal_of_for_each_cooling_maps(tz, cdev, __thermal_of_unbind); 587 } 588 589 /** 590 * thermal_of_zone_unregister - Cleanup the specific allocated ressources 591 * 592 * This function disables the thermal zone and frees the different 593 * ressources allocated specific to the thermal OF. 594 * 595 * @tz: a pointer to the thermal zone structure 596 */ 597 void thermal_of_zone_unregister(struct thermal_zone_device *tz) 598 { 599 struct thermal_trip *trips = tz->trips; 600 struct thermal_zone_params *tzp = tz->tzp; 601 struct thermal_zone_device_ops *ops = tz->ops; 602 603 thermal_zone_device_disable(tz); 604 thermal_zone_device_unregister(tz); 605 kfree(trips); 606 kfree(tzp); 607 kfree(ops); 608 } 609 EXPORT_SYMBOL_GPL(thermal_of_zone_unregister); 610 611 /** 612 * thermal_of_zone_register - Register a thermal zone with device node 613 * sensor 614 * 615 * The thermal_of_zone_register() parses a device tree given a device 616 * node sensor and identifier. It searches for the thermal zone 617 * associated to the couple sensor/id and retrieves all the thermal 618 * zone properties and registers new thermal zone with those 619 * properties. 620 * 621 * @sensor: A device node pointer corresponding to the sensor in the device tree 622 * @id: An integer as sensor identifier 623 * @data: A private data to be stored in the thermal zone dedicated private area 624 * @ops: A set of thermal sensor ops 625 * 626 * Return: a valid thermal zone structure pointer on success. 627 * - EINVAL: if the device tree thermal description is malformed 628 * - ENOMEM: if one structure can not be allocated 629 * - Other negative errors are returned by the underlying called functions 630 */ 631 struct thermal_zone_device *thermal_of_zone_register(struct device_node *sensor, int id, void *data, 632 const struct thermal_zone_device_ops *ops) 633 { 634 struct thermal_zone_device *tz; 635 struct thermal_trip *trips; 636 struct thermal_zone_params *tzp; 637 struct thermal_zone_device_ops *of_ops; 638 struct device_node *np; 639 int delay, pdelay; 640 int ntrips, mask; 641 int ret; 642 643 of_ops = kmemdup(ops, sizeof(*ops), GFP_KERNEL); 644 if (!of_ops) 645 return ERR_PTR(-ENOMEM); 646 647 np = of_thermal_zone_find(sensor, id); 648 if (IS_ERR(np)) { 649 if (PTR_ERR(np) != -ENODEV) 650 pr_err("Failed to find thermal zone for %pOFn id=%d\n", sensor, id); 651 return ERR_CAST(np); 652 } 653 654 trips = thermal_of_trips_init(np, &ntrips); 655 if (IS_ERR(trips)) { 656 pr_err("Failed to find trip points for %pOFn id=%d\n", sensor, id); 657 return ERR_CAST(trips); 658 } 659 660 ret = thermal_of_monitor_init(np, &delay, &pdelay); 661 if (ret) { 662 pr_err("Failed to initialize monitoring delays from %pOFn\n", np); 663 goto out_kfree_trips; 664 } 665 666 tzp = thermal_of_parameters_init(np); 667 if (IS_ERR(tzp)) { 668 ret = PTR_ERR(tzp); 669 pr_err("Failed to initialize parameter from %pOFn: %d\n", np, ret); 670 goto out_kfree_trips; 671 } 672 673 of_ops->get_trip_type = of_ops->get_trip_type ? : of_thermal_get_trip_type; 674 of_ops->get_trip_temp = of_ops->get_trip_temp ? : of_thermal_get_trip_temp; 675 of_ops->get_trip_hyst = of_ops->get_trip_hyst ? : of_thermal_get_trip_hyst; 676 of_ops->set_trip_hyst = of_ops->set_trip_hyst ? : of_thermal_set_trip_hyst; 677 of_ops->get_crit_temp = of_ops->get_crit_temp ? : of_thermal_get_crit_temp; 678 of_ops->bind = thermal_of_bind; 679 of_ops->unbind = thermal_of_unbind; 680 681 mask = GENMASK_ULL((ntrips) - 1, 0); 682 683 tz = thermal_zone_device_register_with_trips(np->name, trips, ntrips, 684 mask, data, of_ops, tzp, 685 pdelay, delay); 686 if (IS_ERR(tz)) { 687 ret = PTR_ERR(tz); 688 pr_err("Failed to register thermal zone %pOFn: %d\n", np, ret); 689 goto out_kfree_tzp; 690 } 691 692 ret = thermal_zone_device_enable(tz); 693 if (ret) { 694 pr_err("Failed to enabled thermal zone '%s', id=%d: %d\n", 695 tz->type, tz->id, ret); 696 thermal_of_zone_unregister(tz); 697 return ERR_PTR(ret); 698 } 699 700 return tz; 701 702 out_kfree_tzp: 703 kfree(tzp); 704 out_kfree_trips: 705 kfree(trips); 706 707 return ERR_PTR(ret); 708 } 709 EXPORT_SYMBOL_GPL(thermal_of_zone_register); 710 711 static void devm_thermal_of_zone_release(struct device *dev, void *res) 712 { 713 thermal_of_zone_unregister(*(struct thermal_zone_device **)res); 714 } 715 716 static int devm_thermal_of_zone_match(struct device *dev, void *res, 717 void *data) 718 { 719 struct thermal_zone_device **r = res; 720 721 if (WARN_ON(!r || !*r)) 722 return 0; 723 724 return *r == data; 725 } 726 727 /** 728 * devm_thermal_of_zone_register - register a thermal tied with the sensor life cycle 729 * 730 * This function is the device version of the thermal_of_zone_register() function. 731 * 732 * @dev: a device structure pointer to sensor to be tied with the thermal zone OF life cycle 733 * @sensor_id: the sensor identifier 734 * @data: a pointer to a private data to be stored in the thermal zone 'devdata' field 735 * @ops: a pointer to the ops structure associated with the sensor 736 */ 737 struct thermal_zone_device *devm_thermal_of_zone_register(struct device *dev, int sensor_id, void *data, 738 const struct thermal_zone_device_ops *ops) 739 { 740 struct thermal_zone_device **ptr, *tzd; 741 742 ptr = devres_alloc(devm_thermal_of_zone_release, sizeof(*ptr), 743 GFP_KERNEL); 744 if (!ptr) 745 return ERR_PTR(-ENOMEM); 746 747 tzd = thermal_of_zone_register(dev->of_node, sensor_id, data, ops); 748 if (IS_ERR(tzd)) { 749 devres_free(ptr); 750 return tzd; 751 } 752 753 *ptr = tzd; 754 devres_add(dev, ptr); 755 756 return tzd; 757 } 758 EXPORT_SYMBOL_GPL(devm_thermal_of_zone_register); 759 760 /** 761 * devm_thermal_of_zone_unregister - Resource managed version of 762 * thermal_of_zone_unregister(). 763 * @dev: Device for which which resource was allocated. 764 * @tz: a pointer to struct thermal_zone where the sensor is registered. 765 * 766 * This function removes the sensor callbacks and private data from the 767 * thermal zone device registered with devm_thermal_zone_of_sensor_register() 768 * API. It will also silent the zone by remove the .get_temp() and .get_trend() 769 * thermal zone device callbacks. 770 * Normally this function will not need to be called and the resource 771 * management code will ensure that the resource is freed. 772 */ 773 void devm_thermal_of_zone_unregister(struct device *dev, struct thermal_zone_device *tz) 774 { 775 WARN_ON(devres_release(dev, devm_thermal_of_zone_release, 776 devm_thermal_of_zone_match, tz)); 777 } 778 EXPORT_SYMBOL_GPL(devm_thermal_of_zone_unregister); 779