1*0d80ee71Skettenis /* $OpenBSD: ofw_thermal.c,v 1.10 2024/07/01 14:13:43 kettenis Exp $ */
2b4264e16Skettenis /*
3b4264e16Skettenis * Copyright (c) 2019 Mark Kettenis
4b4264e16Skettenis *
5b4264e16Skettenis * Permission to use, copy, modify, and distribute this software for any
6b4264e16Skettenis * purpose with or without fee is hereby granted, provided that the above
7b4264e16Skettenis * copyright notice and this permission notice appear in all copies.
8b4264e16Skettenis *
9b4264e16Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10b4264e16Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11b4264e16Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12b4264e16Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13b4264e16Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14b4264e16Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15b4264e16Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16b4264e16Skettenis */
17b4264e16Skettenis
18dfd7428cSdlg #include "kstat.h"
19dfd7428cSdlg
20b4264e16Skettenis #include <sys/types.h>
21b4264e16Skettenis #include <sys/systm.h>
22b4264e16Skettenis #include <sys/malloc.h>
23b4264e16Skettenis #include <sys/stdint.h>
24b4264e16Skettenis #include <sys/task.h>
25b4264e16Skettenis #include <sys/timeout.h>
26dfd7428cSdlg #include <sys/sched.h>
27dfd7428cSdlg #include <sys/kstat.h>
28b4264e16Skettenis
29b4264e16Skettenis #include <machine/bus.h>
30b4264e16Skettenis
31b4264e16Skettenis #include <dev/ofw/openfirm.h>
32b4264e16Skettenis #include <dev/ofw/ofw_thermal.h>
33b4264e16Skettenis
34b4264e16Skettenis LIST_HEAD(, thermal_sensor) thermal_sensors =
35b4264e16Skettenis LIST_HEAD_INITIALIZER(thermal_sensors);
36b4264e16Skettenis
37b4264e16Skettenis LIST_HEAD(, cooling_device) cooling_devices =
38b4264e16Skettenis LIST_HEAD_INITIALIZER(cooling_devices);
39b4264e16Skettenis
40b4264e16Skettenis struct taskq *tztq;
41b4264e16Skettenis
42b4264e16Skettenis struct trippoint {
43dfd7428cSdlg int tp_node;
44b4264e16Skettenis int32_t tp_temperature;
45b4264e16Skettenis uint32_t tp_hysteresis;
46b4264e16Skettenis int tp_type;
47b4264e16Skettenis uint32_t tp_phandle;
48b4264e16Skettenis };
49b4264e16Skettenis
50b4264e16Skettenis #define THERMAL_NONE 0
51b4264e16Skettenis #define THERMAL_ACTIVE 1
52b4264e16Skettenis #define THERMAL_PASSIVE 2
53b4264e16Skettenis #define THERMAL_HOT 3
54b4264e16Skettenis #define THERMAL_CRITICAL 4
55b4264e16Skettenis
56dfd7428cSdlg static const char *trip_types[] = {
57dfd7428cSdlg [THERMAL_NONE] = "none",
58dfd7428cSdlg [THERMAL_ACTIVE] = "active",
59dfd7428cSdlg [THERMAL_PASSIVE] = "passive",
60dfd7428cSdlg [THERMAL_HOT] = "hot",
61dfd7428cSdlg [THERMAL_CRITICAL] = "critical",
62dfd7428cSdlg };
63dfd7428cSdlg
64b4264e16Skettenis struct cmap {
65b4264e16Skettenis uint32_t *cm_cdev;
66b4264e16Skettenis uint32_t *cm_cdevend;
67b4264e16Skettenis uint32_t cm_trip;
68b4264e16Skettenis };
69b4264e16Skettenis
70b4264e16Skettenis struct cdev {
71b4264e16Skettenis uint32_t cd_phandle;
72b4264e16Skettenis int32_t cd_level;
73b4264e16Skettenis int cd_active;
74b4264e16Skettenis LIST_ENTRY(cdev) cd_list;
75b4264e16Skettenis };
76b4264e16Skettenis
77b4264e16Skettenis struct thermal_zone {
78b4264e16Skettenis int tz_node;
79b4264e16Skettenis char tz_name[64];
80b4264e16Skettenis struct task tz_poll_task;
81b4264e16Skettenis struct timeout tz_poll_to;
82b4264e16Skettenis uint32_t *tz_sensors;
83b4264e16Skettenis uint32_t tz_polling_delay;
84b4264e16Skettenis uint32_t tz_polling_delay_passive;
85d89d806fSkettenis LIST_ENTRY(thermal_zone) tz_list;
86b4264e16Skettenis
87b4264e16Skettenis struct trippoint *tz_trips;
88b4264e16Skettenis int tz_ntrips;
89b4264e16Skettenis struct trippoint *tz_tp;
90b4264e16Skettenis
91b4264e16Skettenis struct cmap *tz_cmaps;
92b4264e16Skettenis int tz_ncmaps;
93b4264e16Skettenis struct cmap *tz_cm;
94b4264e16Skettenis
95b4264e16Skettenis LIST_HEAD(, cdev) tz_cdevs;
96b4264e16Skettenis
97b4264e16Skettenis int32_t tz_temperature;
98dfd7428cSdlg
99dfd7428cSdlg struct rwlock tz_lock;
100dfd7428cSdlg struct kstat *tz_kstat;
101b4264e16Skettenis };
102b4264e16Skettenis
103dfd7428cSdlg #if NKSTAT > 0
104dfd7428cSdlg static void thermal_zone_kstat_attach(struct thermal_zone *);
105dfd7428cSdlg static void thermal_zone_kstat_update(struct thermal_zone *);
106dfd7428cSdlg #endif /* NKSTAT > 0 */
107dfd7428cSdlg
108d89d806fSkettenis LIST_HEAD(, thermal_zone) thermal_zones =
109d89d806fSkettenis LIST_HEAD_INITIALIZER(thermal_zones);
110d89d806fSkettenis
111b4264e16Skettenis void
thermal_sensor_register(struct thermal_sensor * ts)112b4264e16Skettenis thermal_sensor_register(struct thermal_sensor *ts)
113b4264e16Skettenis {
114b4264e16Skettenis ts->ts_cells = OF_getpropint(ts->ts_node, "#thermal-sensor-cells", 0);
115b4264e16Skettenis ts->ts_phandle = OF_getpropint(ts->ts_node, "phandle", 0);
116b4264e16Skettenis if (ts->ts_phandle == 0)
117b4264e16Skettenis return;
118b4264e16Skettenis
119b4264e16Skettenis LIST_INSERT_HEAD(&thermal_sensors, ts, ts_list);
120b4264e16Skettenis }
121b4264e16Skettenis
122b4264e16Skettenis void
thermal_sensor_update(struct thermal_sensor * ts,uint32_t * cells)123d89d806fSkettenis thermal_sensor_update(struct thermal_sensor *ts, uint32_t *cells)
124d89d806fSkettenis {
125d89d806fSkettenis struct thermal_zone *tz;
126d89d806fSkettenis
127d89d806fSkettenis LIST_FOREACH(tz, &thermal_zones, tz_list) {
128d89d806fSkettenis if (tz->tz_sensors[0] == ts->ts_phandle &&
129d89d806fSkettenis memcmp(&tz->tz_sensors[1], cells,
130d89d806fSkettenis ts->ts_cells * sizeof(uint32_t)) == 0)
131d89d806fSkettenis task_add(tztq, &tz->tz_poll_task);
132d89d806fSkettenis }
133d89d806fSkettenis }
134d89d806fSkettenis
135d89d806fSkettenis void
cooling_device_register(struct cooling_device * cd)136b4264e16Skettenis cooling_device_register(struct cooling_device *cd)
137b4264e16Skettenis {
138b4264e16Skettenis cd->cd_cells = OF_getpropint(cd->cd_node, "#cooling-cells", 0);
139b4264e16Skettenis cd->cd_phandle = OF_getpropint(cd->cd_node, "phandle", 0);
140b4264e16Skettenis if (cd->cd_phandle == 0)
141b4264e16Skettenis return;
142b4264e16Skettenis
143b4264e16Skettenis LIST_INSERT_HEAD(&cooling_devices, cd, cd_list);
144b4264e16Skettenis }
145b4264e16Skettenis
146b4264e16Skettenis int32_t
thermal_get_temperature_cells(uint32_t * cells)147b4264e16Skettenis thermal_get_temperature_cells(uint32_t *cells)
148b4264e16Skettenis {
149b4264e16Skettenis struct thermal_sensor *ts;
150b4264e16Skettenis uint32_t phandle = cells[0];
151b4264e16Skettenis
152b4264e16Skettenis LIST_FOREACH(ts, &thermal_sensors, ts_list) {
153b4264e16Skettenis if (ts->ts_phandle == phandle)
154b4264e16Skettenis break;
155b4264e16Skettenis }
156b4264e16Skettenis
157b4264e16Skettenis if (ts && ts->ts_get_temperature)
158b4264e16Skettenis return ts->ts_get_temperature(ts->ts_cookie, &cells[1]);
159b4264e16Skettenis
160b4264e16Skettenis return THERMAL_SENSOR_MAX;
161b4264e16Skettenis }
162b4264e16Skettenis
16368910761Skettenis int
thermal_set_limit_cells(uint32_t * cells,uint32_t temp)16468910761Skettenis thermal_set_limit_cells(uint32_t *cells, uint32_t temp)
16568910761Skettenis {
16668910761Skettenis struct thermal_sensor *ts;
16768910761Skettenis uint32_t phandle = cells[0];
16868910761Skettenis
16968910761Skettenis LIST_FOREACH(ts, &thermal_sensors, ts_list) {
17068910761Skettenis if (ts->ts_phandle == phandle)
17168910761Skettenis break;
17268910761Skettenis }
17368910761Skettenis
17468910761Skettenis if (ts && ts->ts_set_limit)
17568910761Skettenis return ts->ts_set_limit(ts->ts_cookie, &cells[1], temp);
17668910761Skettenis
17768910761Skettenis return ENXIO;
17868910761Skettenis }
17968910761Skettenis
180b4264e16Skettenis void
thermal_zone_poll_timeout(void * arg)181b4264e16Skettenis thermal_zone_poll_timeout(void *arg)
182b4264e16Skettenis {
183b4264e16Skettenis struct thermal_zone *tz = arg;
184b4264e16Skettenis
185b4264e16Skettenis task_add(tztq, &tz->tz_poll_task);
186b4264e16Skettenis }
187b4264e16Skettenis
188b4264e16Skettenis uint32_t *
cdev_next_cdev(uint32_t * cells)189b4264e16Skettenis cdev_next_cdev(uint32_t *cells)
190b4264e16Skettenis {
191b4264e16Skettenis uint32_t phandle = cells[0];
192b4264e16Skettenis int node, ncells;
193b4264e16Skettenis
194b4264e16Skettenis node = OF_getnodebyphandle(phandle);
195b4264e16Skettenis if (node == 0)
196b4264e16Skettenis return NULL;
197b4264e16Skettenis
198b4264e16Skettenis ncells = OF_getpropint(node, "#cooling-cells", 2);
199b4264e16Skettenis return cells + ncells + 1;
200b4264e16Skettenis }
201b4264e16Skettenis
202b4264e16Skettenis uint32_t
cdev_get_level(uint32_t * cells)203b4264e16Skettenis cdev_get_level(uint32_t *cells)
204b4264e16Skettenis {
205b4264e16Skettenis struct cooling_device *cd;
206b4264e16Skettenis uint32_t phandle = cells[0];
207b4264e16Skettenis
208b4264e16Skettenis LIST_FOREACH(cd, &cooling_devices, cd_list) {
209b4264e16Skettenis if (cd->cd_phandle == phandle)
210b4264e16Skettenis break;
211b4264e16Skettenis }
212b4264e16Skettenis
213b4264e16Skettenis if (cd && cd->cd_get_level)
214b4264e16Skettenis return cd->cd_get_level(cd->cd_cookie, &cells[1]);
215b4264e16Skettenis
216b4264e16Skettenis return 0;
217b4264e16Skettenis }
218b4264e16Skettenis
219b4264e16Skettenis void
cdev_set_level(uint32_t * cells,uint32_t level)220b4264e16Skettenis cdev_set_level(uint32_t *cells, uint32_t level)
221b4264e16Skettenis {
222b4264e16Skettenis struct cooling_device *cd;
223b4264e16Skettenis uint32_t phandle = cells[0];
224b4264e16Skettenis
225b4264e16Skettenis LIST_FOREACH(cd, &cooling_devices, cd_list) {
226b4264e16Skettenis if (cd->cd_phandle == phandle)
227b4264e16Skettenis break;
228b4264e16Skettenis }
229b4264e16Skettenis
230b4264e16Skettenis if (cd && cd->cd_set_level)
231b4264e16Skettenis cd->cd_set_level(cd->cd_cookie, &cells[1], level);
232b4264e16Skettenis }
233b4264e16Skettenis
234b4264e16Skettenis
235b4264e16Skettenis void
cmap_deactivate(struct thermal_zone * tz,struct cmap * cm)236b4264e16Skettenis cmap_deactivate(struct thermal_zone *tz, struct cmap *cm)
237b4264e16Skettenis {
238b4264e16Skettenis struct cdev *cd;
239b4264e16Skettenis uint32_t *cdev;
240b4264e16Skettenis
241b4264e16Skettenis if (cm == NULL)
242b4264e16Skettenis return;
243b4264e16Skettenis
244b4264e16Skettenis cdev = cm->cm_cdev;
245b4264e16Skettenis while (cdev && cdev < cm->cm_cdevend) {
246b4264e16Skettenis LIST_FOREACH(cd, &tz->tz_cdevs, cd_list) {
247b4264e16Skettenis if (cd->cd_phandle == cdev[0])
248b4264e16Skettenis break;
249b4264e16Skettenis }
250b4264e16Skettenis KASSERT(cd != NULL);
251b4264e16Skettenis cd->cd_active = 0;
252b4264e16Skettenis cdev = cdev_next_cdev(cdev);
253b4264e16Skettenis }
254b4264e16Skettenis }
255b4264e16Skettenis
256b4264e16Skettenis void
cmap_activate(struct thermal_zone * tz,struct cmap * cm,int32_t delta)257b4264e16Skettenis cmap_activate(struct thermal_zone *tz, struct cmap *cm, int32_t delta)
258b4264e16Skettenis {
259b4264e16Skettenis struct cdev *cd;
260b4264e16Skettenis uint32_t *cdev;
261b4264e16Skettenis int32_t min, max;
262b4264e16Skettenis
263b4264e16Skettenis if (cm == NULL)
264b4264e16Skettenis return;
265b4264e16Skettenis
266b4264e16Skettenis cdev = cm->cm_cdev;
267b4264e16Skettenis while (cdev && cdev < cm->cm_cdevend) {
268b4264e16Skettenis LIST_FOREACH(cd, &tz->tz_cdevs, cd_list) {
269b4264e16Skettenis if (cd->cd_phandle == cdev[0])
270b4264e16Skettenis break;
271b4264e16Skettenis }
272b4264e16Skettenis KASSERT(cd != NULL);
273b4264e16Skettenis
274b4264e16Skettenis min = (cdev[1] == THERMAL_NO_LIMIT) ? 0 : cdev[1];
275b4264e16Skettenis max = (cdev[2] == THERMAL_NO_LIMIT) ? INT32_MAX : cdev[2];
276b4264e16Skettenis
277b4264e16Skettenis cd->cd_active = 1;
278b4264e16Skettenis cd->cd_level = cdev_get_level(cdev) + delta;
279b4264e16Skettenis cd->cd_level = MAX(cd->cd_level, min);
280b4264e16Skettenis cd->cd_level = MIN(cd->cd_level, max);
281b4264e16Skettenis cdev_set_level(cdev, cd->cd_level);
282b4264e16Skettenis cdev = cdev_next_cdev(cdev);
283b4264e16Skettenis }
284b4264e16Skettenis }
285b4264e16Skettenis
286b4264e16Skettenis void
cmap_finish(struct thermal_zone * tz)287b4264e16Skettenis cmap_finish(struct thermal_zone *tz)
288b4264e16Skettenis {
289b4264e16Skettenis struct cdev *cd;
290b4264e16Skettenis
291b4264e16Skettenis LIST_FOREACH(cd, &tz->tz_cdevs, cd_list) {
292b4264e16Skettenis if (cd->cd_active == 0 && cd->cd_level != 0) {
293b4264e16Skettenis cdev_set_level(&cd->cd_phandle, 0);
294b4264e16Skettenis cd->cd_level = 0;
295b4264e16Skettenis }
296b4264e16Skettenis }
297b4264e16Skettenis }
298b4264e16Skettenis
299b4264e16Skettenis void
thermal_zone_poll(void * arg)300b4264e16Skettenis thermal_zone_poll(void *arg)
301b4264e16Skettenis {
302b4264e16Skettenis struct thermal_zone *tz = arg;
303b4264e16Skettenis struct trippoint *tp, *newtp;
304b4264e16Skettenis struct cmap *cm, *newcm;
305b4264e16Skettenis uint32_t polling_delay;
306b4264e16Skettenis int32_t temp, delta;
307b4264e16Skettenis int i;
308b4264e16Skettenis
309*0d80ee71Skettenis tp = tz->tz_trips;
310b4264e16Skettenis temp = thermal_get_temperature_cells(tz->tz_sensors);
311b4264e16Skettenis if (temp == THERMAL_SENSOR_MAX)
31216138881Skettenis goto out;
313b4264e16Skettenis
314b4264e16Skettenis newtp = NULL;
315b4264e16Skettenis for (i = 0; i < tz->tz_ntrips; i++) {
316b4264e16Skettenis if (temp < tp->tp_temperature && tp != tz->tz_tp)
317b4264e16Skettenis break;
318b4264e16Skettenis if (temp < tp->tp_temperature - tp->tp_hysteresis)
319b4264e16Skettenis break;
320b4264e16Skettenis newtp = tp++;
321b4264e16Skettenis }
322b4264e16Skettenis
323b4264e16Skettenis /* Short circuit if we didn't hit a trip point. */
324b4264e16Skettenis if (newtp == NULL && tz->tz_tp == NULL)
325b4264e16Skettenis goto out;
326b4264e16Skettenis
327b4264e16Skettenis /*
328b4264e16Skettenis * If the current temperature is above the trip temperature:
329b4264e16Skettenis * - increase the cooling level if the temperature is rising
330b4264e16Skettenis * - do nothing if the temperature is falling
331b4264e16Skettenis * If the current temperature is below the trip temperature:
332b4264e16Skettenis * - do nothing if the temperature is rising
333b4264e16Skettenis * - decrease the cooling level if the temperature is falling
334b4264e16Skettenis */
335b4264e16Skettenis delta = 0;
33616138881Skettenis if (newtp && tz->tz_temperature != THERMAL_SENSOR_MAX) {
337b4264e16Skettenis if (temp >= newtp->tp_temperature) {
338b4264e16Skettenis if (temp > tz->tz_temperature)
339b4264e16Skettenis delta = 1;
340b4264e16Skettenis } else {
341b4264e16Skettenis if (temp < tz->tz_temperature)
342b4264e16Skettenis delta = -1;
343b4264e16Skettenis }
344b4264e16Skettenis }
345b4264e16Skettenis
346b4264e16Skettenis newcm = NULL;
347b4264e16Skettenis cm = tz->tz_cmaps;
348b4264e16Skettenis for (i = 0; i < tz->tz_ncmaps; i++) {
349b4264e16Skettenis if (newtp && cm->cm_trip == newtp->tp_phandle) {
350b4264e16Skettenis newcm = cm;
351b4264e16Skettenis break;
352b4264e16Skettenis }
353b4264e16Skettenis cm++;
354b4264e16Skettenis }
355b4264e16Skettenis
356b4264e16Skettenis cmap_deactivate(tz, tz->tz_cm);
357b4264e16Skettenis cmap_activate(tz, newcm, delta);
358b4264e16Skettenis cmap_finish(tz);
359b4264e16Skettenis
360b4264e16Skettenis tz->tz_tp = newtp;
361b4264e16Skettenis tz->tz_cm = newcm;
362b4264e16Skettenis
363b4264e16Skettenis out:
364b4264e16Skettenis tz->tz_temperature = temp;
365dfd7428cSdlg #if NKSTAT > 0
366dfd7428cSdlg thermal_zone_kstat_update(tz);
367dfd7428cSdlg #endif
368b4264e16Skettenis if (tz->tz_tp && tz->tz_tp->tp_type == THERMAL_PASSIVE)
369b4264e16Skettenis polling_delay = tz->tz_polling_delay_passive;
370b4264e16Skettenis else
371b4264e16Skettenis polling_delay = tz->tz_polling_delay;
37268910761Skettenis
37368910761Skettenis if (polling_delay > 0)
374b4264e16Skettenis timeout_add_msec(&tz->tz_poll_to, polling_delay);
375*0d80ee71Skettenis else if (tp)
37668910761Skettenis thermal_set_limit_cells(tz->tz_sensors, tp->tp_temperature);
377b4264e16Skettenis }
378b4264e16Skettenis
379dfd7428cSdlg static int
thermal_zone_triptype(const char * prop)380dfd7428cSdlg thermal_zone_triptype(const char *prop)
381dfd7428cSdlg {
382dfd7428cSdlg size_t i;
383dfd7428cSdlg
384dfd7428cSdlg for (i = 0; i < nitems(trip_types); i++) {
385dfd7428cSdlg const char *name = trip_types[i];
386dfd7428cSdlg if (name == NULL)
387dfd7428cSdlg continue;
388dfd7428cSdlg
389dfd7428cSdlg if (strcmp(name, prop) == 0)
390dfd7428cSdlg return (i);
391dfd7428cSdlg }
392dfd7428cSdlg
393dfd7428cSdlg return (THERMAL_NONE);
394dfd7428cSdlg }
395dfd7428cSdlg
396b4264e16Skettenis void
thermal_zone_init(int node)397b4264e16Skettenis thermal_zone_init(int node)
398b4264e16Skettenis {
399b4264e16Skettenis struct thermal_zone *tz;
400b4264e16Skettenis struct trippoint *tp;
401b4264e16Skettenis struct cmap *cm;
402b4264e16Skettenis struct cdev *cd;
403b4264e16Skettenis int len, i;
404b4264e16Skettenis
405b4264e16Skettenis len = OF_getproplen(node, "thermal-sensors");
406b4264e16Skettenis if (len <= 0)
407b4264e16Skettenis return;
408b4264e16Skettenis
409fc1dafb7Skettenis if (OF_getnodebyname(node, "trips") == 0)
410fc1dafb7Skettenis return;
411fc1dafb7Skettenis if (OF_getnodebyname(node, "cooling-maps") == 0)
412fc1dafb7Skettenis return;
413fc1dafb7Skettenis
414b4264e16Skettenis tz = malloc(sizeof(struct thermal_zone), M_DEVBUF, M_ZERO | M_WAITOK);
415b4264e16Skettenis tz->tz_node = node;
416dfd7428cSdlg rw_init(&tz->tz_lock, "tzlk");
417b4264e16Skettenis
418b4264e16Skettenis OF_getprop(node, "name", &tz->tz_name, sizeof(tz->tz_name));
419b4264e16Skettenis tz->tz_name[sizeof(tz->tz_name) - 1] = 0;
420b4264e16Skettenis tz->tz_sensors = malloc(len, M_DEVBUF, M_WAITOK);
421b4264e16Skettenis OF_getpropintarray(node, "thermal-sensors", tz->tz_sensors, len);
422b4264e16Skettenis tz->tz_polling_delay = OF_getpropint(node, "polling-delay", 0);
423b4264e16Skettenis tz->tz_polling_delay_passive =
424b4264e16Skettenis OF_getpropint(node, "polling-delay-passive", tz->tz_polling_delay);
425b4264e16Skettenis
426b4264e16Skettenis task_set(&tz->tz_poll_task, thermal_zone_poll, tz);
427b4264e16Skettenis timeout_set(&tz->tz_poll_to, thermal_zone_poll_timeout, tz);
428b4264e16Skettenis
429b4264e16Skettenis /*
430b4264e16Skettenis * Trip points for this thermal zone.
431b4264e16Skettenis */
432b4264e16Skettenis node = OF_getnodebyname(tz->tz_node, "trips");
433b4264e16Skettenis for (node = OF_child(node); node != 0; node = OF_peer(node))
434b4264e16Skettenis tz->tz_ntrips++;
435b4264e16Skettenis
436b4264e16Skettenis tz->tz_trips = mallocarray(tz->tz_ntrips, sizeof(struct trippoint),
437b4264e16Skettenis M_DEVBUF, M_ZERO | M_WAITOK);
438b4264e16Skettenis
439b4264e16Skettenis node = OF_getnodebyname(tz->tz_node, "trips");
440b4264e16Skettenis for (node = OF_child(node); node != 0; node = OF_peer(node)) {
441b4264e16Skettenis char type[32] = "none";
442fe6c337eSpatrick int32_t temp;
443b4264e16Skettenis
444fe6c337eSpatrick temp = OF_getpropint(node, "temperature", THERMAL_SENSOR_MAX);
445fe6c337eSpatrick
446fe6c337eSpatrick /* Sorted insertion, since tree might not be */
447fe6c337eSpatrick for (i = 0; i < tz->tz_ntrips; i++) {
448fe6c337eSpatrick /* No trip point should be 0 degC, take it */
449fe6c337eSpatrick if (tz->tz_trips[i].tp_temperature == 0)
450fe6c337eSpatrick break;
451fe6c337eSpatrick /* We should be bigger than the one before us */
452fe6c337eSpatrick if (tz->tz_trips[i].tp_temperature < temp)
453fe6c337eSpatrick continue;
454fe6c337eSpatrick /* Free current slot */
455fe6c337eSpatrick memmove(&tz->tz_trips[i + 1], &tz->tz_trips[i],
456fe6c337eSpatrick (tz->tz_ntrips - (i + 1)) * sizeof(*tp));
457fe6c337eSpatrick break;
458fe6c337eSpatrick }
459fe6c337eSpatrick tp = &tz->tz_trips[i];
460dfd7428cSdlg tp->tp_node = node;
461fe6c337eSpatrick tp->tp_temperature = temp;
462b4264e16Skettenis tp->tp_hysteresis = OF_getpropint(node, "hysteresis", 0);
463b4264e16Skettenis OF_getprop(node, "type", type, sizeof(type));
464dfd7428cSdlg tp->tp_type = thermal_zone_triptype(type);
465b4264e16Skettenis tp->tp_phandle = OF_getpropint(node, "phandle", 0);
466b4264e16Skettenis tp++;
467b4264e16Skettenis }
468b4264e16Skettenis
469b4264e16Skettenis /*
470b4264e16Skettenis * Cooling maps for this thermal zone.
471b4264e16Skettenis */
472b4264e16Skettenis node = OF_getnodebyname(tz->tz_node, "cooling-maps");
473b4264e16Skettenis for (node = OF_child(node); node != 0; node = OF_peer(node))
474b4264e16Skettenis tz->tz_ncmaps++;
475b4264e16Skettenis
476b4264e16Skettenis tz->tz_cmaps = mallocarray(tz->tz_ncmaps, sizeof(struct cmap),
477b4264e16Skettenis M_DEVBUF, M_ZERO | M_WAITOK);
478b4264e16Skettenis cm = tz->tz_cmaps;
479b4264e16Skettenis
480b4264e16Skettenis node = OF_getnodebyname(tz->tz_node, "cooling-maps");
481b4264e16Skettenis for (node = OF_child(node); node != 0; node = OF_peer(node)) {
482b4264e16Skettenis len = OF_getproplen(node, "cooling-device");
483b4264e16Skettenis if (len <= 0)
484b4264e16Skettenis continue;
485b4264e16Skettenis cm->cm_cdev = malloc(len, M_DEVBUF, M_ZERO | M_WAITOK);
486b4264e16Skettenis OF_getpropintarray(node, "cooling-device", cm->cm_cdev, len);
487b4264e16Skettenis cm->cm_cdevend = cm->cm_cdev + len / sizeof(uint32_t);
488b4264e16Skettenis cm->cm_trip = OF_getpropint(node, "trip", 0);
489b4264e16Skettenis cm++;
490b4264e16Skettenis }
491b4264e16Skettenis
492b4264e16Skettenis /*
493b4264e16Skettenis * Create a list of all the possible cooling devices from the
494b4264e16Skettenis * cooling maps for this thermal zone, and initialize their
495b4264e16Skettenis * state.
496b4264e16Skettenis */
497b4264e16Skettenis LIST_INIT(&tz->tz_cdevs);
498b4264e16Skettenis cm = tz->tz_cmaps;
499b4264e16Skettenis for (i = 0; i < tz->tz_ncmaps; i++) {
500b4264e16Skettenis uint32_t *cdev;
501b4264e16Skettenis
502b4264e16Skettenis cdev = cm->cm_cdev;
503b4264e16Skettenis while (cdev && cdev < cm->cm_cdevend) {
504b4264e16Skettenis LIST_FOREACH(cd, &tz->tz_cdevs, cd_list) {
505b4264e16Skettenis if (cd->cd_phandle == cdev[0])
506b4264e16Skettenis break;
507b4264e16Skettenis }
508b4264e16Skettenis if (cd == NULL) {
509b4264e16Skettenis cd = malloc(sizeof(struct cdev), M_DEVBUF,
510b4264e16Skettenis M_ZERO | M_WAITOK);
511b4264e16Skettenis cd->cd_phandle = cdev[0];
512b4264e16Skettenis cd->cd_level = 0;
513b4264e16Skettenis cd->cd_active = 0;
514b4264e16Skettenis LIST_INSERT_HEAD(&tz->tz_cdevs, cd, cd_list);
515b4264e16Skettenis }
516b4264e16Skettenis cdev = cdev_next_cdev(cdev);
517b4264e16Skettenis }
518b4264e16Skettenis cm++;
519b4264e16Skettenis }
520b4264e16Skettenis
521d89d806fSkettenis LIST_INSERT_HEAD(&thermal_zones, tz, tz_list);
522dfd7428cSdlg
523dfd7428cSdlg #if NKSTAT > 0
524dfd7428cSdlg thermal_zone_kstat_attach(tz);
525dfd7428cSdlg #endif
52668910761Skettenis
52768910761Skettenis /* Poll once to get things going. */
52868910761Skettenis thermal_zone_poll(tz);
529b4264e16Skettenis }
530b4264e16Skettenis
531b4264e16Skettenis void
thermal_init(void)532b4264e16Skettenis thermal_init(void)
533b4264e16Skettenis {
534b4264e16Skettenis int node = OF_finddevice("/thermal-zones");
535b4264e16Skettenis
53693d59f3fSkettenis if (node == -1)
537b4264e16Skettenis return;
538b4264e16Skettenis
539951085b7Skettenis tztq = taskq_create("tztq", 1, IPL_SOFTCLOCK, 0);
540b4264e16Skettenis
541b4264e16Skettenis for (node = OF_child(node); node != 0; node = OF_peer(node))
542b4264e16Skettenis thermal_zone_init(node);
543b4264e16Skettenis }
544dfd7428cSdlg
545dfd7428cSdlg #if NKSTAT > 0
546dfd7428cSdlg
547dfd7428cSdlg static const char *
thermal_zone_tripname(int type)548dfd7428cSdlg thermal_zone_tripname(int type)
549dfd7428cSdlg {
550dfd7428cSdlg if (type >= nitems(trip_types))
551dfd7428cSdlg return (NULL);
552dfd7428cSdlg
553dfd7428cSdlg return (trip_types[type]);
554dfd7428cSdlg }
555dfd7428cSdlg
556dfd7428cSdlg struct thermal_zone_kstats {
557dfd7428cSdlg struct kstat_kv tzk_name; /* istr could be short */
558dfd7428cSdlg struct kstat_kv tzk_temp;
559dfd7428cSdlg struct kstat_kv tzk_tp;
560dfd7428cSdlg struct kstat_kv tzk_tp_type;
561dfd7428cSdlg struct kstat_kv tzk_cooling;
562dfd7428cSdlg };
563dfd7428cSdlg
564dfd7428cSdlg static void
thermal_zone_kstat_update(struct thermal_zone * tz)565dfd7428cSdlg thermal_zone_kstat_update(struct thermal_zone *tz)
566dfd7428cSdlg {
567dfd7428cSdlg struct kstat *ks = tz->tz_kstat;
568dfd7428cSdlg struct thermal_zone_kstats *tzk;
569dfd7428cSdlg
570dfd7428cSdlg if (ks == NULL)
571dfd7428cSdlg return;
572dfd7428cSdlg
573dfd7428cSdlg tzk = ks->ks_data;
574dfd7428cSdlg
575dfd7428cSdlg rw_enter_write(&tz->tz_lock);
576dfd7428cSdlg if (tz->tz_temperature == THERMAL_SENSOR_MAX)
577dfd7428cSdlg tzk->tzk_temp.kv_type = KSTAT_KV_T_NULL;
578dfd7428cSdlg else {
579dfd7428cSdlg tzk->tzk_temp.kv_type = KSTAT_KV_T_TEMP;
580dfd7428cSdlg kstat_kv_temp(&tzk->tzk_temp) = 273150000 +
581dfd7428cSdlg 1000 * tz->tz_temperature;
582dfd7428cSdlg }
583dfd7428cSdlg
584dfd7428cSdlg if (tz->tz_tp == NULL) {
585dfd7428cSdlg kstat_kv_u32(&tzk->tzk_tp) = 0;
586dfd7428cSdlg strlcpy(kstat_kv_istr(&tzk->tzk_tp_type), "none",
587dfd7428cSdlg sizeof(kstat_kv_istr(&tzk->tzk_tp_type)));
588dfd7428cSdlg } else {
589dfd7428cSdlg int triptype = tz->tz_tp->tp_type;
590dfd7428cSdlg const char *tripname = thermal_zone_tripname(triptype);
591dfd7428cSdlg
592dfd7428cSdlg kstat_kv_u32(&tzk->tzk_tp) = tz->tz_tp->tp_node;
593dfd7428cSdlg
594dfd7428cSdlg if (tripname == NULL) {
595dfd7428cSdlg snprintf(kstat_kv_istr(&tzk->tzk_tp_type),
596dfd7428cSdlg sizeof(kstat_kv_istr(&tzk->tzk_tp_type)),
597dfd7428cSdlg "%u", triptype);
598dfd7428cSdlg } else {
599dfd7428cSdlg strlcpy(kstat_kv_istr(&tzk->tzk_tp_type), tripname,
600dfd7428cSdlg sizeof(kstat_kv_istr(&tzk->tzk_tp_type)));
601dfd7428cSdlg }
602dfd7428cSdlg }
603dfd7428cSdlg
604dfd7428cSdlg kstat_kv_bool(&tzk->tzk_cooling) = (tz->tz_cm != NULL);
605dfd7428cSdlg
606dfd7428cSdlg getnanouptime(&ks->ks_updated);
607dfd7428cSdlg rw_exit_write(&tz->tz_lock);
608dfd7428cSdlg }
609dfd7428cSdlg
610dfd7428cSdlg static void
thermal_zone_kstat_attach(struct thermal_zone * tz)611dfd7428cSdlg thermal_zone_kstat_attach(struct thermal_zone *tz)
612dfd7428cSdlg {
613dfd7428cSdlg struct kstat *ks;
614dfd7428cSdlg struct thermal_zone_kstats *tzk;
615dfd7428cSdlg static unsigned int unit = 0;
616dfd7428cSdlg
617dfd7428cSdlg ks = kstat_create("dt", 0, "thermal-zone", unit++, KSTAT_T_KV, 0);
618dfd7428cSdlg if (ks == NULL) {
619dfd7428cSdlg printf("unable to create thermal-zone kstats for %s",
620dfd7428cSdlg tz->tz_name);
621dfd7428cSdlg return;
622dfd7428cSdlg }
623dfd7428cSdlg
624dfd7428cSdlg tzk = malloc(sizeof(*tzk), M_DEVBUF, M_WAITOK|M_ZERO);
625dfd7428cSdlg
626dfd7428cSdlg kstat_kv_init(&tzk->tzk_name, "name", KSTAT_KV_T_ISTR);
627dfd7428cSdlg strlcpy(kstat_kv_istr(&tzk->tzk_name), tz->tz_name,
628dfd7428cSdlg sizeof(kstat_kv_istr(&tzk->tzk_name)));
629dfd7428cSdlg kstat_kv_init(&tzk->tzk_temp, "temperature", KSTAT_KV_T_NULL);
630dfd7428cSdlg
631dfd7428cSdlg /* XXX dt node is not be the most useful info here. */
632dfd7428cSdlg kstat_kv_init(&tzk->tzk_tp, "trip-point-node", KSTAT_KV_T_UINT32);
633dfd7428cSdlg kstat_kv_init(&tzk->tzk_tp_type, "trip-type", KSTAT_KV_T_ISTR);
634dfd7428cSdlg strlcpy(kstat_kv_istr(&tzk->tzk_tp_type), "unknown",
635dfd7428cSdlg sizeof(kstat_kv_istr(&tzk->tzk_tp_type)));
636dfd7428cSdlg
637dfd7428cSdlg kstat_kv_init(&tzk->tzk_cooling, "active-cooling", KSTAT_KV_T_BOOL);
638dfd7428cSdlg kstat_kv_bool(&tzk->tzk_cooling) = 0;
639dfd7428cSdlg
640dfd7428cSdlg ks->ks_softc = tz;
641dfd7428cSdlg ks->ks_data = tzk;
642dfd7428cSdlg ks->ks_datalen = sizeof(*tzk);
643dfd7428cSdlg ks->ks_read = kstat_read_nop;
644dfd7428cSdlg kstat_set_rlock(ks, &tz->tz_lock);
645dfd7428cSdlg
646dfd7428cSdlg tz->tz_kstat = ks;
647dfd7428cSdlg kstat_install(ks);
648dfd7428cSdlg }
649dfd7428cSdlg #endif /* NKSTAT > 0 */
650