xref: /openbsd-src/sys/arch/arm64/dev/aplsmc.c (revision eb720dca6d18a593706022e078b8b06158dd1b74)
1 /*	$OpenBSD: aplsmc.c,v 1.28 2024/11/04 09:33:16 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
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 <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/proc.h>
22 #include <sys/sensors.h>
23 #include <sys/signalvar.h>
24 
25 #include <machine/apmvar.h>
26 #include <machine/bus.h>
27 #include <machine/fdt.h>
28 
29 #include <dev/clock_subr.h>
30 #include <dev/ofw/openfirm.h>
31 #include <dev/ofw/ofw_gpio.h>
32 #include <dev/ofw/ofw_misc.h>
33 #include <dev/ofw/fdt.h>
34 
35 #include <arm64/dev/aplmbox.h>
36 #include <arm64/dev/rtkit.h>
37 
38 #include "apm.h"
39 
40 extern int lid_action;
41 extern void (*simplefb_burn_hook)(u_int);
42 
43 extern void (*cpuresetfn)(void);
44 extern void (*powerdownfn)(void);
45 
46 extern int (*hw_battery_setchargemode)(int);
47 extern int (*hw_battery_setchargestart)(int);
48 extern int (*hw_battery_setchargestop)(int);
49 extern int hw_battery_chargemode;
50 extern int hw_battery_chargestart;
51 extern int hw_battery_chargestop;
52 
53 /* SMC mailbox endpoint */
54 #define SMC_EP			32
55 
56 /* SMC commands */
57 #define SMC_CMD(d)		((d) & 0xff)
58 #define SMC_READ_KEY		0x10
59 #define SMC_WRITE_KEY		0x11
60 #define SMC_GET_KEY_BY_INDEX	0x12
61 #define SMC_GET_KEY_INFO	0x13
62 #define SMC_GET_SRAM_ADDR	0x17
63 #define  SMC_SRAM_SIZE		0x4000
64 #define SMC_NOTIFICATION	0x18
65 
66 /* SMC errors */
67 #define SMC_ERROR(d)		((d) & 0xff)
68 #define SMC_OK			0x00
69 #define SMC_KEYNOTFOUND		0x84
70 
71 /* SMC keys */
72 #define SMC_KEY(s)	((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3])
73 
74 struct smc_key_info {
75 	uint8_t		size;
76 	uint8_t		type[4];
77 	uint8_t		flags;
78 };
79 
80 /* SMC GPIO commands */
81 #define SMC_GPIO_CMD_OUTPUT	(0x01 << 24)
82 
83 /* RTC related constants */
84 #define RTC_OFFSET_LEN		6
85 #define SMC_CLKM_LEN		6
86 
87 struct aplsmc_sensor {
88 	const char	*key;
89 	const char	*key_type;
90 	enum sensor_type type;
91 	int		scale;
92 	const char	*desc;
93 	int		flags;
94 };
95 
96 /* SMC events */
97 #define SMC_EV_TYPE(d)		(((d) >> 48) & 0xffff)
98 #define SMC_EV_TYPE_BTN		0x7201
99 #define SMC_EV_TYPE_LID		0x7203
100 #define SMC_EV_SUBTYPE(d)	(((d) >> 40) & 0xff)
101 #define SMC_EV_DATA(d)		(((d) >> 32) & 0xff)
102 
103 /* Button events */
104 #define SMC_PWRBTN_OFF		0x00
105 #define SMC_PWRBTN_SHORT	0x01
106 #define SMC_PWRBTN_TOUCHID	0x06
107 #define SMC_PWRBTN_LONG		0xfe
108 
109 /* Lid events */
110 #define SMC_LID_OPEN		0x00
111 #define SMC_LID_CLOSE		0x01
112 
113 #define APLSMC_BE		(1 << 0)
114 #define APLSMC_HIDDEN		(1 << 1)
115 
116 #define APLSMC_MAX_SENSORS	19
117 
118 struct aplsmc_softc {
119 	struct device		sc_dev;
120 	bus_space_tag_t		sc_iot;
121 	bus_space_handle_t	sc_ioh;
122 	bus_space_handle_t	sc_sram_ioh;
123 
124 	void			*sc_ih;
125 
126 	struct rtkit_state	*sc_rs;
127 	uint8_t			sc_msgid;
128 	uint64_t		sc_data;
129 
130 	struct gpio_controller	sc_gc;
131 
132 	int			sc_rtc_node;
133 	struct todr_chip_handle sc_todr;
134 
135 	int			sc_reboot_node;
136 
137 	struct aplsmc_sensor	*sc_smcsensors[APLSMC_MAX_SENSORS];
138 	struct ksensor		sc_sensors[APLSMC_MAX_SENSORS];
139 	int			sc_nsensors;
140 	struct ksensordev	sc_sensordev;
141 	uint32_t		sc_suspend_pstr;
142 };
143 
144 #define CH0I_DISCHARGE		(1 << 0)
145 #define CH0C_INHIBIT		(1 << 0)
146 #define CHLS_FORCE_DISCHARGE	(1 << 8)
147 
148 struct aplsmc_softc *aplsmc_sc;
149 
150 #ifndef SMALL_KERNEL
151 
152 struct aplsmc_sensor aplsmc_sensors[] = {
153 	{ "ACDI", "ui16", SENSOR_INDICATOR, 1, "power supply" },
154 	{ "B0RM", "ui16", SENSOR_AMPHOUR, 1000, "remaining battery capacity",
155 	  APLSMC_BE },
156 	{ "B0FC", "ui16", SENSOR_AMPHOUR, 1000, "last full battery capacity" },
157 	{ "B0DC", "ui16", SENSOR_AMPHOUR, 1000, "battery design capacity" },
158 	{ "B0AV", "ui16", SENSOR_VOLTS_DC, 1000, "battery" },
159 	{ "B0CT", "ui16", SENSOR_INTEGER, 1, "battery discharge cycles" },
160 	{ "B0TF", "ui16", SENSOR_INTEGER, 1, "battery time-to-full",
161 	  APLSMC_HIDDEN },
162 	{ "B0TE", "ui16", SENSOR_INTEGER, 1, "battery time-to-empty",
163 	  APLSMC_HIDDEN },
164 	{ "F0Ac", "flt ", SENSOR_FANRPM, 1, "" },
165 	{ "ID0R", "flt ", SENSOR_AMPS, 1000000, "input" },
166 	{ "PDTR", "flt ", SENSOR_WATTS, 1000000, "input" },
167 	{ "PSTR", "flt ", SENSOR_WATTS, 1000000, "system" },
168 	{ "TB0T", "flt ", SENSOR_TEMP, 1000000, "battery" },
169 	{ "TCHP", "flt ", SENSOR_TEMP, 1000000, "charger" },
170 	{ "TW0P", "flt ", SENSOR_TEMP, 1000000, "wireless" },
171 	{ "Ts0P", "flt ", SENSOR_TEMP, 1000000, "palm rest" },
172 	{ "Ts1P", "flt ", SENSOR_TEMP, 1000000, "palm rest" },
173 	{ "VD0R", "flt ", SENSOR_VOLTS_DC, 1000000, "input" },
174 };
175 
176 #endif
177 
178 int	aplsmc_match(struct device *, void *, void *);
179 void	aplsmc_attach(struct device *, struct device *, void *);
180 int	aplsmc_activate(struct device *, int);
181 
182 const struct cfattach aplsmc_ca = {
183 	sizeof (struct aplsmc_softc), aplsmc_match, aplsmc_attach,
184 	NULL, aplsmc_activate
185 };
186 
187 struct cfdriver aplsmc_cd = {
188 	NULL, "aplsmc", DV_DULL
189 };
190 
191 void	aplsmc_callback(void *, uint64_t);
192 int	aplsmc_send_cmd(struct aplsmc_softc *, uint16_t, uint32_t, uint16_t);
193 int	aplsmc_wait_cmd(struct aplsmc_softc *sc);
194 int	aplsmc_read_key(struct aplsmc_softc *, uint32_t, void *, size_t);
195 int	aplsmc_write_key(struct aplsmc_softc *, uint32_t, void *, size_t);
196 int64_t aplsmc_convert_flt(uint32_t, int);
197 void	aplsmc_refresh_sensors(void *);
198 int	aplsmc_apminfo(struct apm_power_info *);
199 void	aplsmc_set_pin(void *, uint32_t *, int);
200 int	aplsmc_gettime(struct todr_chip_handle *, struct timeval *);
201 int	aplsmc_settime(struct todr_chip_handle *, struct timeval *);
202 void	aplsmc_reset(void);
203 void	aplsmc_powerdown(void);
204 void	aplsmc_reboot_attachhook(struct device *);
205 void	aplsmc_battery_init(struct aplsmc_softc *);
206 int	aplsmc_battery_setchargemode(int);
207 int	aplsmc_battery_setchargestart(int);
208 int	aplsmc_battery_chls_setchargestop(int);
209 int	aplsmc_battery_chwa_setchargestop(int);
210 
211 int
212 aplsmc_match(struct device *parent, void *match, void *aux)
213 {
214 	struct fdt_attach_args *faa = aux;
215 
216 	return OF_is_compatible(faa->fa_node, "apple,smc");
217 }
218 
219 void
220 aplsmc_attach(struct device *parent, struct device *self, void *aux)
221 {
222 	struct aplsmc_softc *sc = (struct aplsmc_softc *)self;
223 	struct fdt_attach_args *faa = aux;
224 	uint8_t data[SMC_CLKM_LEN];
225 	uint8_t ntap;
226 	int error, node;
227 #ifndef SMALL_KERNEL
228 	int i;
229 #endif
230 
231 	if (faa->fa_nreg < 1) {
232 		printf(": no registers\n");
233 		return;
234 	}
235 
236 	sc->sc_iot = faa->fa_iot;
237 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
238 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
239 		printf(": can't map registers\n");
240 		return;
241 	}
242 
243 	sc->sc_rs = rtkit_init(faa->fa_node, NULL, RK_WAKEUP, NULL);
244 	if (sc->sc_rs == NULL) {
245 		printf(": can't map mailbox channel\n");
246 		return;
247 	}
248 
249 	error = rtkit_boot(sc->sc_rs);
250 	if (error) {
251 		printf(": can't boot firmware\n");
252 		return;
253 	}
254 
255 	error = rtkit_start_endpoint(sc->sc_rs, SMC_EP, aplsmc_callback, sc);
256 	if (error) {
257 		printf(": can't start SMC endpoint\n");
258 		return;
259 	}
260 
261 	aplsmc_send_cmd(sc, SMC_GET_SRAM_ADDR, 0, 0);
262 	error = aplsmc_wait_cmd(sc);
263 	if (error) {
264 		printf(": can't get SRAM address\n");
265 		return;
266 	}
267 
268 	if (bus_space_map(sc->sc_iot, sc->sc_data,
269 	    SMC_SRAM_SIZE, 0, &sc->sc_sram_ioh)) {
270 		printf(": can't map SRAM\n");
271 		return;
272 	}
273 
274 	printf("\n");
275 
276 	aplsmc_sc = sc;
277 
278 	node = OF_getnodebyname(faa->fa_node, "gpio");
279 	if (node) {
280 		sc->sc_gc.gc_node = node;
281 		sc->sc_gc.gc_cookie = sc;
282 		sc->sc_gc.gc_set_pin = aplsmc_set_pin;
283 		gpio_controller_register(&sc->sc_gc);
284 	}
285 
286 	/*
287 	 * Only provide TODR implementation if the "CLKM" key is
288 	 * supported by the SMC firmware.
289 	 */
290 	error = aplsmc_read_key(sc, SMC_KEY("CLKM"), &data, SMC_CLKM_LEN);
291 	node = OF_getnodebyname(faa->fa_node, "rtc");
292 	if (node && error == 0) {
293 		sc->sc_rtc_node = node;
294 		sc->sc_todr.cookie = sc;
295 		sc->sc_todr.todr_gettime = aplsmc_gettime;
296 		sc->sc_todr.todr_settime = aplsmc_settime;
297 		sc->sc_todr.todr_quality = 1000;
298 		todr_attach(&sc->sc_todr);
299 	}
300 
301 	node = OF_getnodebyname(faa->fa_node, "reboot");
302 	if (node) {
303 		sc->sc_reboot_node = node;
304 		cpuresetfn = aplsmc_reset;
305 		powerdownfn = aplsmc_powerdown;
306 		config_mountroot(self, aplsmc_reboot_attachhook);
307 	}
308 
309 	/* Enable notifications. */
310 	ntap = 1;
311 	aplsmc_write_key(sc, SMC_KEY("NTAP"), &ntap, sizeof(ntap));
312 
313 #ifndef SMALL_KERNEL
314 
315 	for (i = 0; i < nitems(aplsmc_sensors); i++) {
316 		struct smc_key_info info;
317 
318 		aplsmc_send_cmd(sc, SMC_GET_KEY_INFO,
319 		    SMC_KEY(aplsmc_sensors[i].key), 0);
320 		error = aplsmc_wait_cmd(sc);
321 		if (error || SMC_ERROR(sc->sc_data) != SMC_OK)
322 			continue;
323 
324 		bus_space_read_region_1(sc->sc_iot, sc->sc_sram_ioh, 0,
325 		    (uint8_t *)&info, sizeof(info));
326 
327 		/* Skip if the key type doesn't match. */
328 		if (memcmp(aplsmc_sensors[i].key_type, info.type,
329 		    sizeof(info.type)) != 0)
330 			continue;
331 
332 		if (sc->sc_nsensors >= APLSMC_MAX_SENSORS) {
333 			printf("%s: maximum number of sensors exceeded\n",
334 			    sc->sc_dev.dv_xname);
335 			break;
336 		}
337 
338 		sc->sc_smcsensors[sc->sc_nsensors] = &aplsmc_sensors[i];
339 		strlcpy(sc->sc_sensors[sc->sc_nsensors].desc,
340 		    aplsmc_sensors[i].desc, sizeof(sc->sc_sensors[0].desc));
341 		sc->sc_sensors[sc->sc_nsensors].type = aplsmc_sensors[i].type;
342 		if (!(aplsmc_sensors[i].flags & APLSMC_HIDDEN)) {
343 			sensor_attach(&sc->sc_sensordev,
344 			    &sc->sc_sensors[sc->sc_nsensors]);
345 		}
346 		sc->sc_nsensors++;
347 	}
348 
349 	aplsmc_refresh_sensors(sc);
350 
351 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
352 	    sizeof(sc->sc_sensordev.xname));
353 	sensordev_install(&sc->sc_sensordev);
354 	sensor_task_register(sc, aplsmc_refresh_sensors, 5);
355 
356 #if NAPM > 0
357 	apm_setinfohook(aplsmc_apminfo);
358 #endif
359 
360 	aplsmc_battery_init(sc);
361 #endif
362 
363 #ifdef SUSPEND
364 	device_register_wakeup(&sc->sc_dev);
365 #endif
366 }
367 
368 int
369 aplsmc_activate(struct device *self, int act)
370 {
371 #ifdef SUSPEND
372 	struct aplsmc_softc *sc = (struct aplsmc_softc *)self;
373 	int64_t value;
374 
375 	switch (act) {
376 	case DVACT_WAKEUP:
377 		value = aplsmc_convert_flt(sc->sc_suspend_pstr, 100);
378 		printf("%s: system %lld.%02lld W\n", sc->sc_dev.dv_xname,
379 		    value / 100, value % 100);
380 	}
381 #endif
382 
383 	return 0;
384 }
385 
386 void
387 aplsmc_handle_notification(struct aplsmc_softc *sc, uint64_t data)
388 {
389 	extern int allowpowerdown;
390 #ifdef SUSPEND
391 	extern int cpu_suspended;
392 	uint32_t flt = 0;
393 
394 	if (cpu_suspended) {
395 		aplsmc_read_key(sc, 'PSTR', &flt, sizeof(flt));
396 		sc->sc_suspend_pstr = flt;
397 
398 		switch (SMC_EV_TYPE(data)) {
399 		case SMC_EV_TYPE_BTN:
400 			switch (SMC_EV_SUBTYPE(data)) {
401 			case SMC_PWRBTN_SHORT:
402 			case SMC_PWRBTN_TOUCHID:
403 				cpu_suspended = 0;
404 				break;
405 			}
406 			break;
407 		case SMC_EV_TYPE_LID:
408 			switch (SMC_EV_SUBTYPE(data)) {
409 			case SMC_LID_OPEN:
410 				cpu_suspended = 0;
411 				break;
412 			}
413 			break;
414 		}
415 
416 		return;
417 	}
418 #endif
419 
420 	switch (SMC_EV_TYPE(data)) {
421 	case SMC_EV_TYPE_BTN:
422 		switch (SMC_EV_SUBTYPE(data)) {
423 		case SMC_PWRBTN_SHORT:
424 		case SMC_PWRBTN_TOUCHID:
425 			if (SMC_EV_DATA(data) == 1) {
426 				if (allowpowerdown) {
427 					allowpowerdown = 0;
428 					prsignal(initprocess, SIGUSR2);
429 				}
430 			}
431 			break;
432 		case SMC_PWRBTN_LONG:
433 			break;
434 		case SMC_PWRBTN_OFF:
435 			/* XXX Do emergency shutdown? */
436 			break;
437 		default:
438 			printf("%s: SMV_EV_TYPE_BTN 0x%016llx\n",
439 			       sc->sc_dev.dv_xname, data);
440 			break;
441 		}
442 		break;
443 	case SMC_EV_TYPE_LID:
444 		switch (lid_action) {
445 		case 0:
446 			switch (SMC_EV_SUBTYPE(data)) {
447 			case SMC_LID_OPEN:
448 				if (simplefb_burn_hook)
449 					simplefb_burn_hook(1);
450 				break;
451 			case SMC_LID_CLOSE:
452 				if (simplefb_burn_hook)
453 					simplefb_burn_hook(0);
454 				break;
455 			default:
456 				printf("%s: SMV_EV_TYPE_LID 0x%016llx\n",
457 				       sc->sc_dev.dv_xname, data);
458 				break;
459 			}
460 		case 1:
461 #ifdef SUSPEND
462 			request_sleep(SLEEP_SUSPEND);
463 #endif
464 			break;
465 		case 2:
466 			/* XXX: hibernate */
467 			break;
468 		}
469 		break;
470 	default:
471 #ifdef APLSMC_DEBUG
472 		printf("%s: unhandled event 0x%016llx\n",
473 		    sc->sc_dev.dv_xname, data);
474 #endif
475 		break;
476 	}
477 }
478 
479 void
480 aplsmc_callback(void *arg, uint64_t data)
481 {
482 	struct aplsmc_softc *sc = arg;
483 
484 	if (SMC_CMD(data) == SMC_NOTIFICATION) {
485 		aplsmc_handle_notification(sc, data);
486 		return;
487 	}
488 
489 	sc->sc_data = data;
490 	wakeup(&sc->sc_data);
491 }
492 
493 int
494 aplsmc_send_cmd(struct aplsmc_softc *sc, uint16_t cmd, uint32_t key,
495     uint16_t len)
496 {
497 	uint64_t data;
498 
499 	data = cmd;
500 	data |= (uint64_t)len << 16;
501 	data |= (uint64_t)key << 32;
502 	data |= (sc->sc_msgid++ & 0xf) << 12;
503 
504 	return rtkit_send_endpoint(sc->sc_rs, SMC_EP, data);
505 }
506 
507 int
508 aplsmc_wait_cmd(struct aplsmc_softc *sc)
509 {
510 	if (cold) {
511 		int error, timo;
512 
513 		/* Poll for completion. */
514 		for (timo = 1000; timo > 0; timo--) {
515 			error = rtkit_poll(sc->sc_rs);
516 			if (error == 0)
517 				return 0;
518 			delay(10);
519 		}
520 
521 		return EWOULDBLOCK;
522 	}
523 
524 	/* Sleep until the callback wakes us up. */
525 	return tsleep_nsec(&sc->sc_data, PWAIT, "apsmc", 10000000);
526 }
527 
528 int
529 aplsmc_read_key(struct aplsmc_softc *sc, uint32_t key, void *data, size_t len)
530 {
531 	int error;
532 
533 	aplsmc_send_cmd(sc, SMC_READ_KEY, key, len);
534 	error = aplsmc_wait_cmd(sc);
535 	if (error)
536 		return error;
537 	switch (SMC_ERROR(sc->sc_data)) {
538 	case SMC_OK:
539 		break;
540 	case SMC_KEYNOTFOUND:
541 		return EINVAL;
542 		break;
543 	default:
544 		return EIO;
545 		break;
546 	}
547 
548 	len = min(len, (sc->sc_data >> 16) & 0xffff);
549 	if (len > sizeof(uint32_t)) {
550 		bus_space_read_region_1(sc->sc_iot, sc->sc_sram_ioh, 0,
551 		    data, len);
552 	} else {
553 		uint32_t tmp = (sc->sc_data >> 32);
554 		memcpy(data, &tmp, len);
555 	}
556 
557 	return 0;
558 }
559 
560 int
561 aplsmc_write_key(struct aplsmc_softc *sc, uint32_t key, void *data, size_t len)
562 {
563 	bus_space_write_region_1(sc->sc_iot, sc->sc_sram_ioh, 0, data, len);
564 	bus_space_barrier(sc->sc_iot, sc->sc_sram_ioh, 0, len,
565 	    BUS_SPACE_BARRIER_WRITE);
566 	aplsmc_send_cmd(sc, SMC_WRITE_KEY, key, len);
567 	return aplsmc_wait_cmd(sc);
568 }
569 
570 #ifndef SMALL_KERNEL
571 
572 int64_t
573 aplsmc_convert_flt(uint32_t flt, int scale)
574 {
575 	int64_t mant;
576 	int sign, exp;
577 
578 	/*
579 	 * Convert floating-point to integer, trying to keep as much
580 	 * resolution as possible given the scaling factor.
581 	 */
582 	sign = (flt >> 31) ? -1 : 1;
583 	exp = ((flt >> 23) & 0xff) - 127;
584 	mant = (flt & 0x7fffff) | 0x800000;
585 	mant *= scale;
586 	if (exp < 23)
587 		return sign * (mant >> (23 - exp));
588 	else
589 		return sign * (mant << (exp - 23));
590 }
591 
592 void
593 aplsmc_refresh_sensors(void *arg)
594 {
595 	extern int hw_power;
596 	struct aplsmc_softc *sc = arg;
597 	struct aplsmc_sensor *sensor;
598 	int64_t value;
599 	uint32_t key;
600 	int i, error;
601 
602 	for (i = 0; i < sc->sc_nsensors; i++) {
603 		sensor = sc->sc_smcsensors[i];
604 		key = SMC_KEY(sensor->key);
605 
606 		if (strcmp(sensor->key_type, "ui8 ") == 0) {
607 			uint8_t ui8;
608 
609 			error = aplsmc_read_key(sc, key, &ui8, sizeof(ui8));
610 			value = (int64_t)ui8 * sensor->scale;
611 		} else if (strcmp(sensor->key_type, "ui16") == 0) {
612 			uint16_t ui16;
613 
614 			error = aplsmc_read_key(sc, key, &ui16, sizeof(ui16));
615 			if (sensor->flags & APLSMC_BE)
616 				ui16 = betoh16(ui16);
617 			value = (int64_t)ui16 * sensor->scale;
618 		} else if (strcmp(sensor->key_type, "flt ") == 0) {
619 			uint32_t flt;
620 
621 			error = aplsmc_read_key(sc, key, &flt, sizeof(flt));
622 			if (sensor->flags & APLSMC_BE)
623 				flt = betoh32(flt);
624 			value = aplsmc_convert_flt(flt, sensor->scale);
625 		}
626 
627 		/* Apple reports temperatures in degC. */
628 		if (sensor->type == SENSOR_TEMP)
629 			value += 273150000;
630 
631 		if (error) {
632 			sc->sc_sensors[i].flags |= SENSOR_FUNKNOWN;
633 		} else {
634 			sc->sc_sensors[i].flags &= ~SENSOR_FUNKNOWN;
635 			sc->sc_sensors[i].value = value;
636 		}
637 
638 		if (strcmp(sensor->key, "ACDI") == 0)
639 			hw_power = (value > 0);
640 	}
641 }
642 
643 #if NAPM > 0
644 
645 int
646 aplsmc_apminfo(struct apm_power_info *info)
647 {
648 	struct aplsmc_sensor *sensor;
649 	struct ksensor *ksensor;
650 	struct aplsmc_softc *sc = aplsmc_sc;
651 	int remaining = -1, capacity = -1, i;
652 
653 	info->battery_state = APM_BATT_UNKNOWN;
654 	info->ac_state = APM_AC_UNKNOWN;
655 	info->battery_life = 0;
656 	info->minutes_left = -1;
657 
658 	for (i = 0; i < sc->sc_nsensors; i++) {
659 		sensor = sc->sc_smcsensors[i];
660 		ksensor = &sc->sc_sensors[i];
661 
662 		if (ksensor->flags & SENSOR_FUNKNOWN)
663 			continue;
664 
665 		if (strcmp(sensor->key, "ACDI") == 0) {
666 			info->ac_state = ksensor->value ?
667 				APM_AC_ON : APM_AC_OFF;
668 		} else if (strcmp(sensor->key, "B0RM") == 0)
669 			remaining = ksensor->value;
670 		else if (strcmp(sensor->key, "B0FC") == 0)
671 			capacity = ksensor->value;
672 		else if ((strcmp(sensor->key, "B0TE") == 0) &&
673 			 (ksensor->value != 0xffff))
674 			info->minutes_left = ksensor->value;
675 		else if ((strcmp(sensor->key, "B0TF") == 0) &&
676 			 (ksensor->value != 0xffff)) {
677 			info->battery_state = APM_BATT_CHARGING;
678 			info->minutes_left = ksensor->value;
679 		}
680 	}
681 
682 	/* calculate remaining battery if we have sane values */
683 	if (remaining > -1 && capacity > 0) {
684 		info->battery_life = ((remaining * 100) / capacity);
685 		if (info->battery_state != APM_BATT_CHARGING) {
686 			if (info->battery_life > 50)
687 				info->battery_state = APM_BATT_HIGH;
688 			else if (info->battery_life > 25)
689 				info->battery_state = APM_BATT_LOW;
690 			else
691 				info->battery_state = APM_BATT_CRITICAL;
692 		}
693 	}
694 
695 	return 0;
696 }
697 
698 #endif
699 #endif
700 
701 void
702 aplsmc_set_pin(void *cookie, uint32_t *cells, int val)
703 {
704 	struct aplsmc_softc *sc = cookie;
705 	static char *digits = "0123456789abcdef";
706 	uint32_t pin = cells[0];
707 	uint32_t flags = cells[1];
708 	uint32_t key = SMC_KEY("gP\0\0");
709 	uint32_t data;
710 
711 	KASSERT(pin < 256);
712 
713 	key |= (digits[(pin >> 0) & 0xf] << 0);
714 	key |= (digits[(pin >> 4) & 0xf] << 8);
715 
716 	if (flags & GPIO_ACTIVE_LOW)
717 		val = !val;
718 	data = SMC_GPIO_CMD_OUTPUT | !!val;
719 
720 	aplsmc_write_key(sc, key, &data, sizeof(data));
721 }
722 
723 int
724 aplsmc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
725 {
726 	struct aplsmc_softc *sc = handle->cookie;
727 	uint8_t data[8] = {};
728 	uint64_t offset, time;
729 	int error;
730 
731 	error = nvmem_read_cell(sc->sc_rtc_node, "rtc_offset", &data,
732 	    RTC_OFFSET_LEN);
733 	if (error)
734 		return error;
735 	offset = lemtoh64(data);
736 
737 	error = aplsmc_read_key(sc, SMC_KEY("CLKM"), &data, SMC_CLKM_LEN);
738 	if (error)
739 		return error;
740 	time = lemtoh64(data) + offset;
741 
742 	tv->tv_sec = (time >> 15);
743 	tv->tv_usec = (((time & 0x7fff) * 1000000) >> 15);
744 	return 0;
745 }
746 
747 int
748 aplsmc_settime(struct todr_chip_handle *handle, struct timeval *tv)
749 {
750 	struct aplsmc_softc *sc = handle->cookie;
751 	uint8_t data[8] = {};
752 	uint64_t offset, time;
753 	int error;
754 
755 	error = aplsmc_read_key(sc, SMC_KEY("CLKM"), &data, SMC_CLKM_LEN);
756 	if (error)
757 		return error;
758 
759 	time = ((uint64_t)tv->tv_sec << 15);
760 	time |= ((uint64_t)tv->tv_usec << 15) / 1000000;
761 	offset = time - lemtoh64(data);
762 
763 	htolem64(data, offset);
764 	return nvmem_write_cell(sc->sc_rtc_node, "rtc_offset", &data,
765 	    RTC_OFFSET_LEN);
766 }
767 
768 void
769 aplsmc_reboot_attachhook(struct device *self)
770 {
771 	struct aplsmc_softc *sc = (struct aplsmc_softc *)self;
772 	uint8_t count = 0;
773 
774 	/* Reset error counters. */
775 	nvmem_write_cell(sc->sc_reboot_node, "boot_error_count",
776 	    &count, sizeof(count));
777 	nvmem_write_cell(sc->sc_reboot_node, "panic_count",
778 	    &count, sizeof(count));
779 }
780 
781 void
782 aplsmc_reset(void)
783 {
784 	struct aplsmc_softc *sc = aplsmc_sc;
785 	uint32_t key = SMC_KEY("MBSE");
786 	uint32_t rest = SMC_KEY("rest");
787 	uint32_t phra = SMC_KEY("phra");
788 	uint8_t boot_stage = 0;
789 
790 	aplsmc_write_key(sc, key, &rest, sizeof(rest));
791 	nvmem_write_cell(sc->sc_reboot_node, "boot_stage",
792 	    &boot_stage, sizeof(boot_stage));
793 	aplsmc_write_key(sc, key, &phra, sizeof(phra));
794 }
795 
796 void
797 aplsmc_powerdown(void)
798 {
799 	struct aplsmc_softc *sc = aplsmc_sc;
800 	uint32_t key = SMC_KEY("MBSE");
801 	uint32_t offw = SMC_KEY("offw");
802 	uint32_t off1 = SMC_KEY("off1");
803 	uint8_t boot_stage = 0;
804 	uint8_t shutdown_flag = 1;
805 
806 	aplsmc_write_key(sc, key, &offw, sizeof(offw));
807 	nvmem_write_cell(sc->sc_reboot_node, "boot_stage",
808 	    &boot_stage, sizeof(boot_stage));
809 	nvmem_write_cell(sc->sc_reboot_node, "shutdown_flag",
810 	    &shutdown_flag, sizeof(shutdown_flag));
811 	aplsmc_write_key(sc, key, &off1, sizeof(off1));
812 }
813 
814 #ifndef SMALL_KERNEL
815 
816 void
817 aplsmc_battery_init(struct aplsmc_softc *sc)
818 {
819 	uint8_t ch0i, ch0c, chwa;
820 	int error, stop;
821 
822 	error = aplsmc_read_key(sc, SMC_KEY("CH0I"), &ch0i, sizeof(ch0i));
823 	if (error)
824 		return;
825 	error = aplsmc_read_key(sc, SMC_KEY("CH0C"), &ch0c, sizeof(ch0c));
826 	if (error)
827 		return;
828 
829 	if (ch0i & CH0I_DISCHARGE)
830 		hw_battery_chargemode = -1;
831 	else if (ch0c & CH0C_INHIBIT)
832 		hw_battery_chargemode = 0;
833 	else
834 		hw_battery_chargemode = 1;
835 
836 	hw_battery_setchargemode = aplsmc_battery_setchargemode;
837 
838 	/*
839 	 * The system firmware for macOS 15 (Sequoia) introduced a new
840 	 * CHLS key that allows setting the level at which to stop
841 	 * charging, and dropped support for the old CHWA key that
842 	 * only supports a fixed limit of 80%.  However, CHLS is
843 	 * broken in some beta versions.  Those versions still support
844 	 * CHWA so prefer that over CHLS.
845 	 */
846 	error = aplsmc_read_key(sc, SMC_KEY("CHWA"), &chwa, sizeof(chwa));
847 	if (error) {
848 		uint16_t chls;
849 
850 		error = aplsmc_read_key(sc, SMC_KEY("CHLS"),
851 		    &chls, sizeof(chls));
852 		if (error)
853 			return;
854 		stop = (chls & 0xff) ? (chls & 0xff) : 100;
855 		hw_battery_setchargestop = aplsmc_battery_chls_setchargestop;
856 	} else {
857 		stop = chwa ? 80 : 100;
858 		hw_battery_setchargestop = aplsmc_battery_chwa_setchargestop;
859 	}
860 
861 	hw_battery_setchargestart = aplsmc_battery_setchargestart;
862 
863 	hw_battery_chargestart = stop - 5;
864 	hw_battery_chargestop = stop;
865 }
866 
867 int
868 aplsmc_battery_setchargemode(int mode)
869 {
870 	struct aplsmc_softc *sc = aplsmc_sc;
871 	uint8_t val;
872 	int error;
873 
874 	switch (mode) {
875 	case -1:
876 		val = 0;
877 		error = aplsmc_write_key(sc, SMC_KEY("CH0C"),
878 		    &val, sizeof(val));
879 		if (error)
880 			return error;
881 		val = 1;
882 		error = aplsmc_write_key(sc, SMC_KEY("CH0I"),
883 		    &val, sizeof(val));
884 		if (error)
885 			return error;
886 		break;
887 	case 0:
888 		val = 0;
889 		error = aplsmc_write_key(sc, SMC_KEY("CH0I"),
890 		    &val, sizeof(val));
891 		if (error)
892 			return error;
893 		val = 1;
894 		error = aplsmc_write_key(sc, SMC_KEY("CH0C"),
895 		    &val, sizeof(val));
896 		if (error)
897 			return error;
898 		break;
899 	case 1:
900 		val = 0;
901 		error = aplsmc_write_key(sc, SMC_KEY("CH0I"),
902 		    &val, sizeof(val));
903 		if (error)
904 			return error;
905 		val = 0;
906 		error = aplsmc_write_key(sc, SMC_KEY("CH0C"),
907 		    &val, sizeof(val));
908 		if (error)
909 			return error;
910 		break;
911 	default:
912 		return EINVAL;
913 	}
914 
915 	hw_battery_chargemode = mode;
916 	return 0;
917 }
918 
919 int
920 aplsmc_battery_setchargestart(int start)
921 {
922 	return EOPNOTSUPP;
923 }
924 
925 int
926 aplsmc_battery_chls_setchargestop(int stop)
927 {
928 	struct aplsmc_softc *sc = aplsmc_sc;
929 	uint16_t chls;
930 	int error;
931 
932 	if (stop < 10)
933 		stop = 10;
934 
935 	/*
936 	 * Setting the CHLS_FORCE_DISCHARGE flags makes sure the
937 	 * battery is discharged until the configured charge level is
938 	 * reached when the limit is lowered.
939 	 */
940 	chls = (stop == 100 ? 0 : stop) | CHLS_FORCE_DISCHARGE;
941 	error = aplsmc_write_key(sc, SMC_KEY("CHLS"), &chls, sizeof(chls));
942 	if (error)
943 		return error;
944 
945 	hw_battery_chargestart = stop - 5;
946 	hw_battery_chargestop = stop;
947 
948 	return 0;
949 }
950 
951 int
952 aplsmc_battery_chwa_setchargestop(int stop)
953 {
954 	struct aplsmc_softc *sc = aplsmc_sc;
955 	uint8_t chwa;
956 	int error;
957 
958 	if (stop <= 80) {
959 		stop = 80;
960 		chwa = 1;
961 	} else {
962 		stop = 100;
963 		chwa = 0;
964 	}
965 
966 	error = aplsmc_write_key(sc, SMC_KEY("CHWA"), &chwa, sizeof(chwa));
967 	if (error)
968 		return error;
969 
970 	hw_battery_chargestart = stop - 5;
971 	hw_battery_chargestop = stop;
972 
973 	return 0;
974 }
975 
976 #endif
977