thermal_of.c 18 KB

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