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