xref: /freebsd/sys/dev/asmc/asmc.c (revision d705a519525f2acae3c1efba11436ec6ee8aea0a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2007, 2008 Rui Paulo <rpaulo@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  *
28  */
29 
30 /*
31  * Driver for Apple's System Management Console (SMC).
32  * SMC can be found on the MacBook, MacBook Pro and Mac Mini.
33  *
34  * Inspired by the Linux applesmc driver.
35  */
36 
37 #include "opt_asmc.h"
38 
39 #include <sys/param.h>
40 #include <sys/bus.h>
41 #include <sys/conf.h>
42 #include <sys/endian.h>
43 #include <sys/kernel.h>
44 #include <sys/lock.h>
45 #include <sys/malloc.h>
46 #include <sys/module.h>
47 #include <sys/mutex.h>
48 #include <sys/sbuf.h>
49 #include <sys/sysctl.h>
50 #include <sys/systm.h>
51 #include <sys/taskqueue.h>
52 #include <sys/rman.h>
53 
54 #include <machine/resource.h>
55 #include <netinet/in.h>
56 
57 #include <contrib/dev/acpica/include/acpi.h>
58 
59 #include <dev/acpica/acpivar.h>
60 #include <dev/asmc/asmcvar.h>
61 
62 #include <dev/backlight/backlight.h>
63 #include "backlight_if.h"
64 
65 /*
66  * Device interface.
67  */
68 static int 	asmc_probe(device_t dev);
69 static int 	asmc_attach(device_t dev);
70 static int 	asmc_detach(device_t dev);
71 static int 	asmc_resume(device_t dev);
72 
73 /*
74  * Backlight interface.
75  */
76 static int	asmc_backlight_update_status(device_t dev,
77     struct backlight_props *props);
78 static int	asmc_backlight_get_status(device_t dev,
79     struct backlight_props *props);
80 static int	asmc_backlight_get_info(device_t dev, struct backlight_info *info);
81 
82 /*
83  * SMC functions.
84  */
85 static int 	asmc_init(device_t dev);
86 static int 	asmc_command(device_t dev, uint8_t command);
87 static int 	asmc_wait(device_t dev, uint8_t val);
88 static int 	asmc_wait_ack(device_t dev, uint8_t val, int amount);
89 static int 	asmc_key_write(device_t dev, const char *key, uint8_t *buf,
90     uint8_t len);
91 static int 	asmc_key_read(device_t dev, const char *key, uint8_t *buf,
92     uint8_t);
93 static int 	asmc_fan_count(device_t dev);
94 static int 	asmc_fan_getvalue(device_t dev, const char *key, int fan);
95 static int 	asmc_fan_setvalue(device_t dev, const char *key, int fan, int speed);
96 static int 	asmc_temp_getvalue(device_t dev, const char *key);
97 static int 	asmc_sms_read(device_t, const char *key, int16_t *val);
98 static void 	asmc_sms_calibrate(device_t dev);
99 static int 	asmc_sms_intrfast(void *arg);
100 static void 	asmc_sms_printintr(device_t dev, uint8_t);
101 static void 	asmc_sms_task(void *arg, int pending);
102 static void	asmc_sms_init(device_t dev);
103 static void	asmc_detect_capabilities(device_t dev);
104 #ifdef ASMC_DEBUG
105 void		asmc_dumpall(device_t);
106 static int	asmc_key_dump(device_t, int);
107 #endif
108 
109 /*
110  * Sysctl handlers.
111  */
112 static int 	asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS);
113 static int 	asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS);
114 static int 	asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS);
115 static int 	asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS);
116 static int 	asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS);
117 static int 	asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS);
118 static int 	asmc_mb_sysctl_fanmanual(SYSCTL_HANDLER_ARGS);
119 static int 	asmc_temp_sysctl(SYSCTL_HANDLER_ARGS);
120 static int 	asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS);
121 static int 	asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS);
122 static int 	asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS);
123 static int 	asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS);
124 static int 	asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS);
125 static int 	asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS);
126 static int 	asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS);
127 static int	asmc_aupo_sysctl(SYSCTL_HANDLER_ARGS);
128 
129 static int	asmc_key_getinfo(device_t, const char *, uint8_t *, char *);
130 
131 #ifdef ASMC_DEBUG
132 /* Raw key access */
133 static int	asmc_raw_key_sysctl(SYSCTL_HANDLER_ARGS);
134 static int	asmc_raw_value_sysctl(SYSCTL_HANDLER_ARGS);
135 static int	asmc_raw_len_sysctl(SYSCTL_HANDLER_ARGS);
136 static int	asmc_raw_type_sysctl(SYSCTL_HANDLER_ARGS);
137 #endif
138 
139 /* Voltage/Current/Power/Light sensor support */
140 static int	asmc_sensor_read(device_t, const char *, int *);
141 static int	asmc_sensor_sysctl(SYSCTL_HANDLER_ARGS);
142 static int	asmc_detect_sensors(device_t);
143 static int	asmc_key_dump_by_index(device_t, int, char *, char *, uint8_t *);
144 static int	asmc_key_search(device_t, const char *, unsigned int *);
145 static const char *asmc_temp_desc(const char *key);
146 
147 /*
148  * SMC temperature key descriptions.
149  * These are universal across all Intel Apple hardware.
150  */
151 static const struct {
152 	const char	*key;
153 	const char	*desc;
154 } asmc_temp_descs[] = {
155 	/* Ambient / airflow */
156 	{ "TA0P", "Ambient" },
157 	{ "TA0S", "PCIe Slot 1 Ambient" },
158 	{ "TA0p", "Ambient Air" },
159 	{ "TA1P", "Ambient 2" },
160 	{ "TA1S", "PCIe Slot 1 PCB" },
161 	{ "TA1p", "Ambient Air 2" },
162 	{ "TA2P", "Ambient 3" },
163 	{ "TA2S", "PCIe Slot 2 Ambient" },
164 	{ "TA3S", "PCIe Slot 2 PCB" },
165 	{ "TA0V", "Ambient" },
166 	{ "TALP", "Ambient Light Proximity" },
167 	{ "TaLC", "Airflow Left" },
168 	{ "TaRC", "Airflow Right" },
169 	{ "Ta0P", "Airflow Proximity" },
170 	/* Battery / enclosure */
171 	{ "TB0T", "Enclosure Bottom" },
172 	{ "TB1T", "Battery 1" },
173 	{ "TB2T", "Battery 2" },
174 	{ "TB3T", "Battery 3" },
175 	{ "TBXT", "Battery" },
176 	{ "Tb0P", "BLC Proximity" },
177 	/* CPU */
178 	{ "TC0C", "CPU Core 1" },
179 	{ "TC0D", "CPU Die" },
180 	{ "TC0E", "CPU 1" },
181 	{ "TC0F", "CPU 2" },
182 	{ "TC0G", "CPU Package GPU" },
183 	{ "TC0H", "CPU Heatsink" },
184 	{ "TC0h", "CPU Heatsink" },
185 	{ "TC0J", "CPU" },
186 	{ "TC0P", "CPU Proximity" },
187 	{ "TC0c", "CPU Core 1 PECI" },
188 	{ "TC0d", "CPU Die PECI" },
189 	{ "TC0p", "CPU Proximity" },
190 	{ "TC1C", "CPU Core 2" },
191 	{ "TC1c", "CPU Core 2 PECI" },
192 	{ "TC1P", "CPU Proximity 2" },
193 	{ "TC2C", "CPU Core 3" },
194 	{ "TC2P", "CPU Proximity 3" },
195 	{ "TC2c", "CPU Core 3 PECI" },
196 	{ "TC3C", "CPU Core 4" },
197 	{ "TC3P", "CPU Proximity 4" },
198 	{ "TC3c", "CPU Core 4 PECI" },
199 	{ "TC4C", "CPU Core 5" },
200 	{ "TC5C", "CPU Core 6" },
201 	{ "TC6C", "CPU Core 7" },
202 	{ "TC7C", "CPU Core 8" },
203 	{ "TC8C", "CPU Core 9" },
204 	{ "TCGC", "PECI GPU" },
205 	{ "TCGc", "PECI GPU" },
206 	{ "TCHP", "Charger Proximity" },
207 	{ "TCSA", "PECI SA" },
208 	{ "TCSC", "PECI SA" },
209 	{ "TCSc", "PECI SA" },
210 	{ "TCTD", "CPU DTS" },
211 	{ "TCXC", "PECI CPU" },
212 	{ "TCXc", "PECI CPU" },
213 	{ "TCPG", "CPU Package GPU" },
214 	{ "TCXR", "CPU PECI DTS" },
215 	/* CPU dual-socket (Mac Pro) */
216 	{ "TCAG", "CPU A Package" },
217 	{ "TCAH", "CPU A Heatsink" },
218 	{ "TCBG", "CPU B Package" },
219 	{ "TCBH", "CPU B Heatsink" },
220 	/* GPU */
221 	{ "TG0C", "GPU Core" },
222 	{ "TG0D", "GPU Diode" },
223 	{ "TG0H", "GPU Heatsink" },
224 	{ "TG0M", "GPU Memory" },
225 	{ "TG0P", "GPU Proximity" },
226 	{ "TG0T", "GPU Diode" },
227 	{ "TG0V", "GPU" },
228 	{ "TG0d", "GPU Die" },
229 	{ "TG0h", "GPU Heatsink" },
230 	{ "TG0p", "GPU Proximity" },
231 	{ "TGTV", "GPU" },
232 	{ "TG1D", "GPU 2 Diode" },
233 	{ "TG1H", "GPU 2 Heatsink" },
234 	{ "TG1P", "GPU 2 Proximity" },
235 	{ "TG1d", "GPU 2 Die" },
236 	{ "TGVP", "GPU Memory Proximity" },
237 	/* Storage */
238 	{ "TH0A", "SSD A" },
239 	{ "TH0B", "SSD B" },
240 	{ "TH0C", "SSD C" },
241 	{ "TH0F", "SSD" },
242 	{ "TH0O", "HDD" },
243 	{ "TH0P", "HDD Proximity" },
244 	{ "TH0R", "SSD" },
245 	{ "TH0V", "SSD" },
246 	{ "TH0a", "SSD A" },
247 	{ "TH0b", "SSD B" },
248 	{ "TH0c", "SSD C" },
249 	{ "TH1O", "HDD 2" },
250 	{ "TH1P", "HDD Bay 2" },
251 	{ "TH2P", "HDD Bay 3" },
252 	{ "TH3P", "HDD Bay 4" },
253 	{ "Th0H", "Heatpipe 1" },
254 	{ "Th0N", "SSD" },
255 	{ "Th1H", "Heatpipe 2" },
256 	{ "Th2H", "Heatpipe 3" },
257 	/* Thunderbolt */
258 	{ "THSP", "Thunderbolt Proximity" },
259 	{ "TI0P", "Thunderbolt 1" },
260 	{ "TI0p", "Thunderbolt 1" },
261 	{ "TI1P", "Thunderbolt 2" },
262 	{ "TI1p", "Thunderbolt 2" },
263 	{ "TTLD", "Thunderbolt Left" },
264 	{ "TTRD", "Thunderbolt Right" },
265 	{ "Te0T", "Thunderbolt Diode" },
266 	{ "Te0t", "Thunderbolt Diode" },
267 	/* LCD */
268 	{ "TL0P", "LCD Proximity" },
269 	{ "TL0V", "LCD" },
270 	{ "TL0p", "LCD Proximity" },
271 	{ "TL1P", "LCD Panel 1" },
272 	{ "TL1V", "LCD 1" },
273 	{ "TL1p", "LCD Panel 1" },
274 	{ "TL1v", "LCD 1" },
275 	{ "TL2V", "LCD 2" },
276 	{ "TLAV", "LCD" },
277 	{ "TLBV", "LCD" },
278 	{ "TLCV", "LCD" },
279 	/* Memory */
280 	{ "TM0P", "Memory Proximity" },
281 	{ "TM0S", "Memory Slot 1" },
282 	{ "TM0p", "Memory Proximity" },
283 	{ "TM1P", "Memory Riser A 2" },
284 	{ "TM1S", "Memory Slot 2" },
285 	{ "Tm0P", "Memory Proximity" },
286 	{ "Tm0p", "Memory Proximity" },
287 	{ "Tm1P", "Memory Proximity 2" },
288 	{ "TMBS", "Memory Bank" },
289 	{ "TMCD", "Memory DIMM" },
290 	/* Northbridge / MCH */
291 	{ "TN0C", "Northbridge Core" },
292 	{ "TN0D", "Northbridge Diode" },
293 	{ "TN0H", "MCH Heatsink" },
294 	{ "TN0P", "Northbridge Proximity" },
295 	{ "TN1D", "MCH Die 2" },
296 	{ "TN1P", "Northbridge Proximity 2" },
297 	/* PCH */
298 	{ "TP0P", "PCH Proximity" },
299 	{ "TP0p", "PCH Proximity" },
300 	{ "TPCD", "PCH Die" },
301 	{ "TPCd", "PCH Die" },
302 	/* Optical drive */
303 	{ "TO0P", "Optical Drive" },
304 	{ "TO0p", "Optical Drive" },
305 	/* Power supply */
306 	{ "Tp0C", "Power Supply" },
307 	{ "Tp0P", "Power Supply Proximity" },
308 	{ "Tp1C", "Power Supply 2" },
309 	{ "Tp1P", "Power Supply Component" },
310 	{ "Tp1p", "Power Supply Component" },
311 	{ "Tp2P", "Power Supply 2" },
312 	{ "Tp2h", "Power Supply 2" },
313 	{ "Tp2H", "Power Supply 2" },
314 	{ "Tp3P", "Power Supply 3 Inlet" },
315 	{ "Tp3h", "Power Supply 3" },
316 	{ "Tp3H", "Power Supply 3" },
317 	{ "Tp4P", "Power Supply 4" },
318 	{ "Tp5P", "Power Supply 5" },
319 	/* Palm rest / trackpad */
320 	{ "Ts0P", "Palm Rest" },
321 	{ "Ts0S", "Memory Proximity" },
322 	{ "Ts1P", "Palm Rest 2" },
323 	{ "Ts1S", "Palm Rest 2" },
324 	/* Wireless */
325 	{ "TW0P", "Wireless Proximity" },
326 	{ "TW0p", "Wireless Proximity" },
327 	{ "TBLR", "Bluetooth" },
328 	/* Camera */
329 	{ "TS2P", "Camera Proximity" },
330 	{ "TS2V", "Camera" },
331 	{ "TS2p", "Camera Proximity" },
332 	/* Expansion */
333 	{ "TS0C", "Expansion Slots" },
334 	{ "TS0P", "Expansion Proximity" },
335 	{ "TS0V", "Expansion" },
336 	{ "TS0p", "Expansion Proximity" },
337 	/* Air vent */
338 	{ "TV0P", "Air Vent" },
339 	/* VRM */
340 	{ "Tv0S", "VRM 1" },
341 	{ "Tv1S", "VRM 2" },
342 	/* Misc */
343 	{ "TTF0", "Fan" },
344 	{ "TMLB", "Logic Board" },
345 };
346 
347 static const char *
348 asmc_temp_desc(const char *key)
349 {
350 	unsigned int i;
351 
352 	for (i = 0; i < nitems(asmc_temp_descs); i++) {
353 		if (strcmp(asmc_temp_descs[i].key, key) == 0)
354 			return (asmc_temp_descs[i].desc);
355 	}
356 	return ("Temperature");
357 }
358 
359 /*
360  * Driver methods.
361  */
362 static device_method_t	asmc_methods[] = {
363 	DEVMETHOD(device_probe,		asmc_probe),
364 	DEVMETHOD(device_attach,	asmc_attach),
365 	DEVMETHOD(device_detach,	asmc_detach),
366 	DEVMETHOD(device_resume,	asmc_resume),
367 
368 	/* Backlight interface */
369 	DEVMETHOD(backlight_update_status, asmc_backlight_update_status),
370 	DEVMETHOD(backlight_get_status, asmc_backlight_get_status),
371 	DEVMETHOD(backlight_get_info, asmc_backlight_get_info),
372 
373 	DEVMETHOD_END
374 };
375 
376 static driver_t	asmc_driver = {
377 	"asmc",
378 	asmc_methods,
379 	sizeof(struct asmc_softc)
380 };
381 
382 /*
383  * Debugging
384  */
385 #define	_COMPONENT	ACPI_OEM
386 ACPI_MODULE_NAME("ASMC")
387 #ifdef ASMC_DEBUG
388 #define ASMC_DPRINTF(str, ...)	device_printf(dev, str, ##__VA_ARGS__)
389 #else
390 #define ASMC_DPRINTF(str, ...)
391 #endif
392 
393 /* NB: can't be const */
394 static char *asmc_ids[] = { "APP0001", NULL };
395 
396 static unsigned int light_control = 0;
397 
398 ACPI_PNP_INFO(asmc_ids);
399 DRIVER_MODULE(asmc, acpi, asmc_driver, NULL, NULL);
400 MODULE_DEPEND(asmc, acpi, 1, 1, 1);
401 MODULE_DEPEND(asmc, backlight, 1, 1, 1);
402 
403 static int
404 asmc_probe(device_t dev)
405 {
406 	char *product;
407 	int rv;
408 
409 	if (resource_disabled("asmc", 0))
410 		return (ENXIO);
411 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, asmc_ids, NULL);
412 	if (rv > 0)
413 		return (rv);
414 	product = kern_getenv("smbios.system.product");
415 	device_set_descf(dev, "Apple %s", product ? product : "SMC");
416 	freeenv(product);
417 	return (rv);
418 }
419 
420 static int
421 asmc_attach(device_t dev)
422 {
423 	int i, j;
424 	int ret;
425 	char name[2];
426 	struct asmc_softc *sc = device_get_softc(dev);
427 	struct sysctl_ctx_list *sysctlctx;
428 	struct sysctl_oid *sysctlnode;
429 
430 	sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
431 	    &sc->sc_rid_port, RF_ACTIVE);
432 	if (sc->sc_ioport == NULL) {
433 		device_printf(dev, "unable to allocate IO port\n");
434 		return (ENOMEM);
435 	}
436 
437 	sysctlctx = device_get_sysctl_ctx(dev);
438 	sysctlnode = device_get_sysctl_tree(dev);
439 
440 	mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN);
441 
442 	/* Read SMC revision, key count, fan count */
443 	ret = asmc_init(dev);
444 	if (ret != 0) {
445 		device_printf(dev, "SMC not responding\n");
446 		goto err;
447 	}
448 
449 	/* Probe SMC keys to detect capabilities */
450 	asmc_detect_capabilities(dev);
451 
452 	/* Auto-detect and register voltage/current/power/ambient/temp sensors */
453 	asmc_detect_sensors(dev);
454 
455 	/*
456 	 * dev.asmc.n.fan.* tree.
457 	 */
458 	sc->sc_fan_tree[0] = SYSCTL_ADD_NODE(sysctlctx,
459 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "fan",
460 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Fan Root Tree");
461 
462 	for (i = 1; i <= sc->sc_nfan; i++) {
463 		j = i - 1;
464 		name[0] = '0' + j;
465 		name[1] = 0;
466 		sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx,
467 		    SYSCTL_CHILDREN(sc->sc_fan_tree[0]), OID_AUTO, name,
468 		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Fan Subtree");
469 
470 		SYSCTL_ADD_PROC(sysctlctx,
471 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
472 		    OID_AUTO, "id",
473 		    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, j,
474 		    asmc_mb_sysctl_fanid, "I", "Fan ID");
475 
476 		SYSCTL_ADD_PROC(sysctlctx,
477 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
478 		    OID_AUTO, "speed",
479 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, j,
480 		    asmc_mb_sysctl_fanspeed, "I", "Fan speed in RPM");
481 
482 		if (sc->sc_has_safespeed) {
483 			SYSCTL_ADD_PROC(sysctlctx,
484 			    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
485 			    OID_AUTO, "safespeed",
486 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, j,
487 			    asmc_mb_sysctl_fansafespeed, "I",
488 			    "Fan safe speed in RPM");
489 		}
490 
491 		SYSCTL_ADD_PROC(sysctlctx,
492 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
493 		    OID_AUTO, "minspeed",
494 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, j,
495 		    asmc_mb_sysctl_fanminspeed, "I",
496 		    "Fan minimum speed in RPM");
497 
498 		SYSCTL_ADD_PROC(sysctlctx,
499 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
500 		    OID_AUTO, "maxspeed",
501 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, j,
502 		    asmc_mb_sysctl_fanmaxspeed, "I",
503 		    "Fan maximum speed in RPM");
504 
505 		SYSCTL_ADD_PROC(sysctlctx,
506 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
507 		    OID_AUTO, "targetspeed",
508 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, j,
509 		    asmc_mb_sysctl_fantargetspeed, "I",
510 		    "Fan target speed in RPM");
511 
512 		SYSCTL_ADD_PROC(sysctlctx,
513 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
514 		    OID_AUTO, "manual",
515 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, j,
516 		    asmc_mb_sysctl_fanmanual, "I",
517 		    "Fan manual mode (0=auto, 1=manual)");
518 	}
519 
520 	/*
521 	 * dev.asmc.n.temp tree.
522 	 */
523 	sc->sc_temp_tree = SYSCTL_ADD_NODE(sysctlctx,
524 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp",
525 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Temperature sensors");
526 
527 	for (i = 0; i < sc->sc_temp_count; i++) {
528 		SYSCTL_ADD_PROC(sysctlctx,
529 		    SYSCTL_CHILDREN(sc->sc_temp_tree),
530 		    OID_AUTO, sc->sc_temp_sensors[i],
531 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, i,
532 		    asmc_temp_sysctl, "I",
533 		    asmc_temp_desc(sc->sc_temp_sensors[i]));
534 	}
535 
536 	/*
537 	 * dev.asmc.n.light
538 	 */
539 	if (sc->sc_has_light) {
540 		sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx,
541 		    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light",
542 		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
543 		    "Keyboard backlight sensors");
544 
545 		SYSCTL_ADD_PROC(sysctlctx,
546 		    SYSCTL_CHILDREN(sc->sc_light_tree),
547 		    OID_AUTO, "left",
548 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
549 		    dev, 0,
550 		    sc->sc_light_len == ASMC_LIGHT_LONGLEN ?
551 		        asmc_mbp_sysctl_light_left_10byte :
552 		        asmc_mbp_sysctl_light_left,
553 		    "I", "Keyboard backlight left sensor");
554 
555 		if (sc->sc_light_len != ASMC_LIGHT_LONGLEN &&
556 		    asmc_key_getinfo(dev, ASMC_KEY_LIGHTRIGHT,
557 		    NULL, NULL) == 0) {
558 			SYSCTL_ADD_PROC(sysctlctx,
559 			    SYSCTL_CHILDREN(sc->sc_light_tree),
560 			    OID_AUTO, "right",
561 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
562 			    dev, 0,
563 			    asmc_mbp_sysctl_light_right, "I",
564 			    "Keyboard backlight right sensor");
565 		}
566 
567 		SYSCTL_ADD_PROC(sysctlctx,
568 		    SYSCTL_CHILDREN(sc->sc_light_tree),
569 		    OID_AUTO, "control",
570 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE,
571 		    dev, 0, asmc_mbp_sysctl_light_control, "I",
572 		    "Keyboard backlight brightness control");
573 
574 		sc->sc_kbd_bkl = backlight_register("asmc", dev);
575 		if (sc->sc_kbd_bkl == NULL) {
576 			device_printf(dev, "Can not register backlight\n");
577 			ret = ENXIO;
578 			goto err;
579 		}
580 	}
581 
582 #ifdef ASMC_DEBUG
583 	/*
584 	 * Raw SMC key access for debugging.
585 	 */
586 	sc->sc_raw_tree = SYSCTL_ADD_NODE(sysctlctx,
587 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
588 	    "raw", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Raw SMC key access");
589 
590 	SYSCTL_ADD_PROC(sysctlctx,
591 	    SYSCTL_CHILDREN(sc->sc_raw_tree),
592 	    OID_AUTO, "key",
593 	    CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
594 	    dev, 0, asmc_raw_key_sysctl, "A",
595 	    "SMC key name (4 chars)");
596 
597 	SYSCTL_ADD_PROC(sysctlctx,
598 	    SYSCTL_CHILDREN(sc->sc_raw_tree),
599 	    OID_AUTO, "value",
600 	    CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
601 	    dev, 0, asmc_raw_value_sysctl, "A",
602 	    "SMC key value (hex string)");
603 
604 	SYSCTL_ADD_PROC(sysctlctx,
605 	    SYSCTL_CHILDREN(sc->sc_raw_tree),
606 	    OID_AUTO, "len",
607 	    CTLTYPE_U8 | CTLFLAG_RD | CTLFLAG_MPSAFE,
608 	    dev, 0, asmc_raw_len_sysctl, "CU",
609 	    "SMC key value length");
610 
611 	SYSCTL_ADD_PROC(sysctlctx,
612 	    SYSCTL_CHILDREN(sc->sc_raw_tree),
613 	    OID_AUTO, "type",
614 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
615 	    dev, 0, asmc_raw_type_sysctl, "A",
616 	    "SMC key type (4 chars)");
617 #endif
618 
619 	if (!sc->sc_has_sms)
620 		goto nosms;
621 
622 	/*
623 	 * Initialize SMS hardware.
624 	 */
625 	asmc_sms_init(dev);
626 
627 	/*
628 	 * dev.asmc.n.sms tree.
629 	 */
630 	sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx,
631 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "sms",
632 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Sudden Motion Sensor");
633 
634 	SYSCTL_ADD_PROC(sysctlctx,
635 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
636 	    OID_AUTO, "x",
637 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
638 	    dev, 0, asmc_mb_sysctl_sms_x, "I",
639 	    "Sudden Motion Sensor X value");
640 
641 	SYSCTL_ADD_PROC(sysctlctx,
642 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
643 	    OID_AUTO, "y",
644 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
645 	    dev, 0, asmc_mb_sysctl_sms_y, "I",
646 	    "Sudden Motion Sensor Y value");
647 
648 	SYSCTL_ADD_PROC(sysctlctx,
649 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
650 	    OID_AUTO, "z",
651 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
652 	    dev, 0, asmc_mb_sysctl_sms_z, "I",
653 	    "Sudden Motion Sensor Z value");
654 
655 	/*
656 	 * Need a taskqueue to send devctl_notify() events
657 	 * when the SMS interrupt us.
658 	 *
659 	 * PI_REALTIME is used due to the sensitivity of the
660 	 * interrupt. An interrupt from the SMS means that the
661 	 * disk heads should be turned off as quickly as possible.
662 	 *
663 	 * We only need to do this for the non INTR_FILTER case.
664 	 */
665 	sc->sc_sms_tq = NULL;
666 	TASK_INIT(&sc->sc_sms_task, 0, asmc_sms_task, sc);
667 	sc->sc_sms_tq = taskqueue_create_fast("asmc_taskq", M_WAITOK,
668 	    taskqueue_thread_enqueue, &sc->sc_sms_tq);
669 	taskqueue_start_threads(&sc->sc_sms_tq, 1, PI_REALTIME, "%s sms taskq",
670 	    device_get_nameunit(dev));
671 	/*
672 	 * Allocate an IRQ for the SMS.
673 	 */
674 	sc->sc_rid_irq = 0;
675 	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_rid_irq,
676 	    RF_ACTIVE);
677 	if (sc->sc_irq == NULL) {
678 		device_printf(dev, "unable to allocate IRQ resource\n");
679 		ret = ENXIO;
680 		goto err;
681 	}
682 
683 	ret = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE,
684 	    asmc_sms_intrfast, NULL, dev, &sc->sc_cookie);
685 	if (ret) {
686 		device_printf(dev, "unable to setup SMS IRQ\n");
687 		goto err;
688 	}
689 
690 nosms:
691 	return (0);
692 
693 err:
694 	asmc_detach(dev);
695 
696 	return (ret);
697 }
698 
699 static int
700 asmc_detach(device_t dev)
701 {
702 	struct asmc_softc *sc = device_get_softc(dev);
703 
704 	if (sc->sc_kbd_bkl != NULL)
705 		backlight_destroy(sc->sc_kbd_bkl);
706 
707 	/* Free temperature sensor key arrays */
708 	for (int i = 0; i < sc->sc_temp_count; i++)
709 		free(sc->sc_temp_sensors[i], M_DEVBUF);
710 
711 	/* Free sensor key arrays */
712 	for (int i = 0; i < sc->sc_voltage_count; i++)
713 		free(sc->sc_voltage_sensors[i], M_DEVBUF);
714 	for (int i = 0; i < sc->sc_current_count; i++)
715 		free(sc->sc_current_sensors[i], M_DEVBUF);
716 	for (int i = 0; i < sc->sc_power_count; i++)
717 		free(sc->sc_power_sensors[i], M_DEVBUF);
718 	for (int i = 0; i < sc->sc_light_count; i++)
719 		free(sc->sc_light_sensors[i], M_DEVBUF);
720 
721 	if (sc->sc_sms_tq) {
722 		taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task);
723 		taskqueue_free(sc->sc_sms_tq);
724 		sc->sc_sms_tq = NULL;
725 	}
726 	if (sc->sc_cookie) {
727 		bus_teardown_intr(dev, sc->sc_irq, sc->sc_cookie);
728 		sc->sc_cookie = NULL;
729 	}
730 	if (sc->sc_irq) {
731 		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq,
732 		    sc->sc_irq);
733 		sc->sc_irq = NULL;
734 	}
735 	if (sc->sc_ioport) {
736 		bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
737 		    sc->sc_ioport);
738 		sc->sc_ioport = NULL;
739 	}
740 	if (mtx_initialized(&sc->sc_mtx)) {
741 		mtx_destroy(&sc->sc_mtx);
742 	}
743 
744 	return (0);
745 }
746 
747 static int
748 asmc_resume(device_t dev)
749 {
750 	uint8_t buf[2];
751 
752 	buf[0] = light_control;
753 	buf[1] = 0x00;
754 	asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof(buf));
755 
756 	return (0);
757 }
758 
759 #ifdef ASMC_DEBUG
760 void
761 asmc_dumpall(device_t dev)
762 {
763 	struct asmc_softc *sc = device_get_softc(dev);
764 	int i;
765 
766 	if (sc->sc_nkeys == 0) {
767 		device_printf(dev, "asmc_dumpall: key count not available\n");
768 		return;
769 	}
770 
771 	device_printf(dev, "asmc_dumpall: dumping %d keys\n", sc->sc_nkeys);
772 	for (i = 0; i < sc->sc_nkeys; i++)
773 		asmc_key_dump(dev, i);
774 }
775 #endif
776 
777 /*
778  * Initialize SMC: read revision, key count, fan count.
779  * SMS initialization is handled separately in asmc_sms_init().
780  */
781 static int
782 asmc_init(device_t dev)
783 {
784 	struct asmc_softc *sc = device_get_softc(dev);
785 	struct sysctl_ctx_list *sysctlctx;
786 	uint8_t buf[6];
787 	int error;
788 
789 	sysctlctx = device_get_sysctl_ctx(dev);
790 
791 	error = asmc_key_read(dev, ASMC_KEY_REV, buf, 6);
792 	if (error != 0)
793 		goto out;
794 	device_printf(dev, "SMC revision: %x.%x%x%x\n", buf[0], buf[1], buf[2],
795 	    ntohs(*(uint16_t *)buf + 4));
796 
797 	/* Auto power-on after AC power loss (AUPO). */
798 	if (asmc_key_read(dev, ASMC_KEY_AUPO, buf, 1) == 0) {
799 		SYSCTL_ADD_PROC(sysctlctx,
800 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
801 		    OID_AUTO, "auto_poweron",
802 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
803 		    dev, 0, asmc_aupo_sysctl, "I",
804 		    "Auto power-on after AC power loss (0=off, 1=on)");
805 	}
806 
807 	sc->sc_nfan = asmc_fan_count(dev);
808 	if (sc->sc_nfan > ASMC_MAXFANS) {
809 		device_printf(dev,
810 		    "more than %d fans were detected. Please report this.\n",
811 		    ASMC_MAXFANS);
812 		sc->sc_nfan = ASMC_MAXFANS;
813 	}
814 
815 	/*
816 	 * Read and cache the number of SMC keys (32 bit buffer)
817 	 */
818 	if (asmc_key_read(dev, ASMC_NKEYS, buf, 4) == 0) {
819 		sc->sc_nkeys = be32dec(buf);
820 		if (bootverbose)
821 			device_printf(dev, "number of keys: %d\n",
822 			    sc->sc_nkeys);
823 	} else {
824 		sc->sc_nkeys = 0;
825 	}
826 
827 out:
828 #ifdef ASMC_DEBUG
829 	asmc_dumpall(dev);
830 #endif
831 	return (error);
832 }
833 
834 /*
835  * Initialize the Sudden Motion Sensor hardware.
836  * Called from asmc_attach() after capabilities are detected.
837  */
838 static void
839 asmc_sms_init(device_t dev)
840 {
841 	struct asmc_softc *sc = device_get_softc(dev);
842 	uint8_t buf[2];
843 	int i;
844 
845 	/*
846 	 * We are ready to receive interrupts from the SMS.
847 	 */
848 	buf[0] = 0x01;
849 	ASMC_DPRINTF(("intok key\n"));
850 	asmc_key_write(dev, ASMC_KEY_INTOK, buf, 1);
851 	DELAY(50);
852 
853 	/*
854 	 * Initiate the polling intervals.
855 	 */
856 	buf[0] = 20; /* msecs */
857 	ASMC_DPRINTF(("low int key\n"));
858 	asmc_key_write(dev, ASMC_KEY_SMS_LOW_INT, buf, 1);
859 	DELAY(200);
860 
861 	buf[0] = 20; /* msecs */
862 	ASMC_DPRINTF(("high int key\n"));
863 	asmc_key_write(dev, ASMC_KEY_SMS_HIGH_INT, buf, 1);
864 	DELAY(200);
865 
866 	buf[0] = 0x00;
867 	buf[1] = 0x60;
868 	ASMC_DPRINTF(("sms low key\n"));
869 	asmc_key_write(dev, ASMC_KEY_SMS_LOW, buf, 2);
870 	DELAY(200);
871 
872 	buf[0] = 0x01;
873 	buf[1] = 0xc0;
874 	ASMC_DPRINTF(("sms high key\n"));
875 	asmc_key_write(dev, ASMC_KEY_SMS_HIGH, buf, 2);
876 	DELAY(200);
877 
878 	/*
879 	 * I'm not sure what this key does, but it seems to be
880 	 * required.
881 	 */
882 	buf[0] = 0x01;
883 	ASMC_DPRINTF(("sms flag key\n"));
884 	asmc_key_write(dev, ASMC_KEY_SMS_FLAG, buf, 1);
885 	DELAY(100);
886 
887 	sc->sc_sms_intr_works = 0;
888 
889 	/*
890 	 * Retry SMS initialization 1000 times
891 	 * (takes approx. 2 seconds in worst case)
892 	 */
893 	for (i = 0; i < 1000; i++) {
894 		if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 &&
895 		    (buf[0] == ASMC_SMS_INIT1 && buf[1] == ASMC_SMS_INIT2)) {
896 			sc->sc_sms_intr_works = 1;
897 			goto done;
898 		}
899 		buf[0] = ASMC_SMS_INIT1;
900 		buf[1] = ASMC_SMS_INIT2;
901 		ASMC_DPRINTF(("sms key\n"));
902 		asmc_key_write(dev, ASMC_KEY_SMS, buf, 2);
903 		DELAY(50);
904 	}
905 	device_printf(dev, "WARNING: Sudden Motion Sensor not initialized!\n");
906 
907 done:
908 	asmc_sms_calibrate(dev);
909 }
910 
911 /*
912  * Probe SMC keys to detect hardware capabilities.
913  */
914 static void
915 asmc_detect_capabilities(device_t dev)
916 {
917 	struct asmc_softc *sc = device_get_softc(dev);
918 	uint8_t len;
919 	char type[ASMC_TYPELEN + 1];
920 
921 	/* SMS: require all keys used by asmc_sms_init() */
922 	sc->sc_has_sms =
923 	    (asmc_key_getinfo(dev, ASMC_KEY_SMS,
924 	    &len, type) == 0 &&
925 	    asmc_key_getinfo(dev, ASMC_KEY_SMS_X,
926 	    &len, type) == 0 &&
927 	    asmc_key_getinfo(dev, ASMC_KEY_SMS_Y,
928 	    &len, type) == 0 &&
929 	    asmc_key_getinfo(dev, ASMC_KEY_SMS_Z,
930 	    &len, type) == 0 &&
931 	    asmc_key_getinfo(dev, ASMC_KEY_SMS_LOW,
932 	    &len, type) == 0 &&
933 	    asmc_key_getinfo(dev, ASMC_KEY_SMS_HIGH,
934 	    &len, type) == 0 &&
935 	    asmc_key_getinfo(dev, ASMC_KEY_SMS_LOW_INT,
936 	    &len, type) == 0 &&
937 	    asmc_key_getinfo(dev, ASMC_KEY_SMS_HIGH_INT,
938 	    &len, type) == 0 &&
939 	    asmc_key_getinfo(dev, ASMC_KEY_SMS_FLAG,
940 	    &len, type) == 0 &&
941 	    asmc_key_getinfo(dev, ASMC_KEY_INTOK,
942 	    &len, type) == 0);
943 
944 	/* Light sensor: require ALV0 (len 6 or 10) and LKSB */
945 	if (asmc_key_getinfo(dev, ASMC_KEY_LIGHTLEFT,
946 	    &len, type) == 0 &&
947 	    (len == ASMC_LIGHT_SHORTLEN || len == ASMC_LIGHT_LONGLEN) &&
948 	    asmc_key_getinfo(dev, ASMC_KEY_LIGHTVALUE,
949 	    NULL, NULL) == 0) {
950 		sc->sc_has_light = 1;
951 		sc->sc_light_len = len;
952 	} else {
953 		sc->sc_has_light = 0;
954 		sc->sc_light_len = 0;
955 	}
956 
957 	/* Fan safe speed */
958 	sc->sc_has_safespeed =
959 	    (asmc_key_getinfo(dev, ASMC_KEY_FANSAFESPEED0,
960 	    &len, type) == 0);
961 
962 	/* Ambient light interrupt source */
963 	sc->sc_has_alsl =
964 	    (asmc_key_getinfo(dev, ASMC_KEY_LIGHTSRC,
965 	    &len, type) == 0);
966 
967 	if (bootverbose)
968 		device_printf(dev,
969 		    "capabilities: sms=%d light=%d (len=%d) safespeed=%d alsl=%d\n",
970 		    sc->sc_has_sms, sc->sc_has_light, sc->sc_light_len,
971 		    sc->sc_has_safespeed, sc->sc_has_alsl);
972 }
973 
974 /*
975  * We need to make sure that the SMC acks the byte sent.
976  * Just wait up to (amount * 10)  ms.
977  */
978 static int
979 asmc_wait_ack(device_t dev, uint8_t val, int amount)
980 {
981 	struct asmc_softc *sc = device_get_softc(dev);
982 	u_int i;
983 
984 	val = val & ASMC_STATUS_MASK;
985 
986 	for (i = 0; i < amount; i++) {
987 		if ((ASMC_CMDPORT_READ(sc) & ASMC_STATUS_MASK) == val)
988 			return (0);
989 		DELAY(10);
990 	}
991 
992 	return (1);
993 }
994 
995 /*
996  * We need to make sure that the SMC acks the byte sent.
997  * Just wait up to 100 ms.
998  */
999 static int
1000 asmc_wait(device_t dev, uint8_t val)
1001 {
1002 #ifdef ASMC_DEBUG
1003 	struct asmc_softc *sc;
1004 #endif
1005 
1006 	if (asmc_wait_ack(dev, val, 1000) == 0)
1007 		return (0);
1008 
1009 #ifdef ASMC_DEBUG
1010 	sc = device_get_softc(dev);
1011 
1012 	device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__,
1013 	    val & ASMC_STATUS_MASK, ASMC_CMDPORT_READ(sc));
1014 #endif
1015 	return (1);
1016 }
1017 
1018 /*
1019  * Send the given command, retrying up to 10 times if
1020  * the acknowledgement fails.
1021  */
1022 static int
1023 asmc_command(device_t dev, uint8_t command)
1024 {
1025 	int i;
1026 	struct asmc_softc *sc = device_get_softc(dev);
1027 
1028 	for (i = 0; i < 10; i++) {
1029 		ASMC_CMDPORT_WRITE(sc, command);
1030 		if (asmc_wait_ack(dev, 0x0c, 100) == 0) {
1031 			return (0);
1032 		}
1033 	}
1034 
1035 #ifdef ASMC_DEBUG
1036 	device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, command,
1037 	    ASMC_CMDPORT_READ(sc));
1038 #endif
1039 	return (1);
1040 }
1041 
1042 static int
1043 asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len)
1044 {
1045 	int i, error = 1, try = 0;
1046 	struct asmc_softc *sc = device_get_softc(dev);
1047 
1048 	mtx_lock_spin(&sc->sc_mtx);
1049 
1050 begin:
1051 	if (asmc_command(dev, ASMC_CMDREAD))
1052 		goto out;
1053 
1054 	for (i = 0; i < 4; i++) {
1055 		ASMC_DATAPORT_WRITE(sc, key[i]);
1056 		if (asmc_wait(dev, 0x04))
1057 			goto out;
1058 	}
1059 
1060 	ASMC_DATAPORT_WRITE(sc, len);
1061 
1062 	for (i = 0; i < len; i++) {
1063 		if (asmc_wait(dev, 0x05))
1064 			goto out;
1065 		buf[i] = ASMC_DATAPORT_READ(sc);
1066 	}
1067 
1068 	error = 0;
1069 out:
1070 	if (error) {
1071 		if (++try < 10)
1072 			goto begin;
1073 		device_printf(dev, "%s for key %s failed %d times, giving up\n",
1074 		    __func__, key, try);
1075 	}
1076 
1077 	mtx_unlock_spin(&sc->sc_mtx);
1078 
1079 	return (error);
1080 }
1081 
1082 #ifdef ASMC_DEBUG
1083 static int
1084 asmc_key_dump(device_t dev, int number)
1085 {
1086 	struct asmc_softc *sc = device_get_softc(dev);
1087 	char key[ASMC_KEYLEN + 1] = { 0 };
1088 	char type[ASMC_KEYINFO_RESPLEN + 1] = { 0 };
1089 	uint8_t index[4];
1090 	uint8_t v[ASMC_MAXVAL];
1091 	uint8_t maxlen;
1092 	int i, error = 1, try = 0;
1093 
1094 	if (sc->sc_is_mmio) {
1095 		uint8_t len = 0;
1096 		char mmio_type[ASMC_TYPELEN + 1] = { 0 };
1097 		if (asmc_key_dump_by_index(dev, number, key, mmio_type, &len))
1098 			return (1);
1099 		memset(v, 0, sizeof(v));
1100 		len = MIN(len, sizeof(v));
1101 		asmc_key_read(dev, key, v, len);
1102 		struct sbuf sb;
1103 		char buf[128];
1104 		sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN);
1105 		sbuf_printf(&sb, "key %d: %s, type %s (len %d), data",
1106 		    number, key, mmio_type, len);
1107 		for (i = 0; i < len; i++)
1108 			sbuf_printf(&sb, " %02x", v[i]);
1109 		sbuf_finish(&sb);
1110 		device_printf(dev, "%s\n", sbuf_data(&sb));
1111 		sbuf_delete(&sb);
1112 		return (0);
1113 	}
1114 
1115 	mtx_lock_spin(&sc->sc_mtx);
1116 
1117 	index[0] = (number >> 24) & 0xff;
1118 	index[1] = (number >> 16) & 0xff;
1119 	index[2] = (number >> 8) & 0xff;
1120 	index[3] = number & 0xff;
1121 
1122 begin:
1123 	if (asmc_command(dev, ASMC_CMDGETBYINDEX))
1124 		goto out;
1125 
1126 	for (i = 0; i < ASMC_KEYLEN; i++) {
1127 		ASMC_DATAPORT_WRITE(sc, index[i]);
1128 		if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA))
1129 			goto out;
1130 	}
1131 
1132 	ASMC_DATAPORT_WRITE(sc, ASMC_KEYLEN);
1133 
1134 	for (i = 0; i < ASMC_KEYLEN; i++) {
1135 		if (asmc_wait(dev, ASMC_STATUS_DATA_READY))
1136 			goto out;
1137 		key[i] = ASMC_DATAPORT_READ(sc);
1138 	}
1139 
1140 	/* Get key info (length + type). */
1141 	if (asmc_command(dev, ASMC_CMDGETINFO))
1142 		goto out;
1143 
1144 	for (i = 0; i < ASMC_KEYLEN; i++) {
1145 		ASMC_DATAPORT_WRITE(sc, key[i]);
1146 		if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA))
1147 			goto out;
1148 	}
1149 
1150 	ASMC_DATAPORT_WRITE(sc, ASMC_KEYINFO_RESPLEN);
1151 
1152 	for (i = 0; i < ASMC_KEYINFO_RESPLEN; i++) {
1153 		if (asmc_wait(dev, ASMC_STATUS_DATA_READY))
1154 			goto out;
1155 		type[i] = ASMC_DATAPORT_READ(sc);
1156 	}
1157 
1158 	error = 0;
1159 out:
1160 	if (error) {
1161 		if (++try < ASMC_MAXRETRIES)
1162 			goto begin;
1163 		device_printf(dev,
1164 		    "%s for key %d failed %d times, giving up\n",
1165 		    __func__, number, try);
1166 	}
1167 	mtx_unlock_spin(&sc->sc_mtx);
1168 
1169 	if (error)
1170 		return (error);
1171 
1172 	maxlen = type[0];
1173 	type[0] = ' ';
1174 	type[5] = '\0';
1175 	maxlen = MIN(maxlen, sizeof(v));
1176 
1177 	memset(v, 0, sizeof(v));
1178 	error = asmc_key_read(dev, key, v, maxlen);
1179 	if (error)
1180 		return (error);
1181 
1182 	struct sbuf sb;
1183 	char buf[128];
1184 	sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN);
1185 	sbuf_printf(&sb, "key %d: %s, type%s (len %d), data",
1186 	    number, key, type, maxlen);
1187 	for (i = 0; i < maxlen; i++)
1188 		sbuf_printf(&sb, " %02x", v[i]);
1189 	sbuf_finish(&sb);
1190 	device_printf(dev, "%s\n", sbuf_data(&sb));
1191 	sbuf_delete(&sb);
1192 
1193 	return (0);
1194 }
1195 #endif /* ASMC_DEBUG */
1196 
1197 /*
1198  * Get key info (length and type) from SMC using command 0x13.
1199  * If len is non-NULL, stores the key's value length.
1200  * If type is non-NULL, stores the 4-char type string (must be at least 5 bytes).
1201  */
1202 static int
1203 asmc_key_getinfo(device_t dev, const char *key, uint8_t *len, char *type)
1204 {
1205 	struct asmc_softc *sc = device_get_softc(dev);
1206 	uint8_t info[ASMC_KEYINFO_RESPLEN];
1207 	int i, error = -1, try = 0;
1208 
1209 	mtx_lock_spin(&sc->sc_mtx);
1210 
1211 begin:
1212 	if (asmc_command(dev, ASMC_CMDGETINFO))
1213 		goto out;
1214 
1215 	for (i = 0; i < ASMC_KEYLEN; i++) {
1216 		ASMC_DATAPORT_WRITE(sc, key[i]);
1217 		if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA))
1218 			goto out;
1219 	}
1220 
1221 	ASMC_DATAPORT_WRITE(sc, ASMC_KEYINFO_RESPLEN);
1222 
1223 	for (i = 0; i < ASMC_KEYINFO_RESPLEN; i++) {
1224 		if (asmc_wait(dev, ASMC_STATUS_DATA_READY))
1225 			goto out;
1226 		info[i] = ASMC_DATAPORT_READ(sc);
1227 	}
1228 
1229 	error = 0;
1230 out:
1231 	if (error && ++try < ASMC_MAXRETRIES)
1232 		goto begin;
1233 	mtx_unlock_spin(&sc->sc_mtx);
1234 
1235 	if (error == 0) {
1236 		if (len != NULL)
1237 			*len = info[0];
1238 		if (type != NULL) {
1239 			for (i = 0; i < ASMC_TYPELEN; i++)
1240 				type[i] = info[i + 1];
1241 			type[ASMC_TYPELEN] = '\0';
1242 		}
1243 	}
1244 	return (error);
1245 }
1246 
1247 #ifdef ASMC_DEBUG
1248 /*
1249  * Raw SMC key access sysctls - enables reading/writing any SMC key by name
1250  * Usage:
1251  *   sysctl dev.asmc.0.raw.key=TC0P   # Set key, auto-detects length
1252  *   sysctl dev.asmc.0.raw.value      # Read current value (hex bytes)
1253  *   sysctl dev.asmc.0.raw.value=01   # Write new value
1254  */
1255 static int
1256 asmc_raw_key_sysctl(SYSCTL_HANDLER_ARGS)
1257 {
1258 	device_t dev = (device_t) arg1;
1259 	struct asmc_softc *sc = device_get_softc(dev);
1260 	char newkey[ASMC_KEYLEN + 1];
1261 	uint8_t keylen;
1262 	int error;
1263 
1264 	strlcpy(newkey, sc->sc_rawkey, sizeof(newkey));
1265 	error = sysctl_handle_string(oidp, newkey, sizeof(newkey), req);
1266 	if (error || req->newptr == NULL)
1267 		return (error);
1268 
1269 	if (strlen(newkey) != ASMC_KEYLEN)
1270 		return (EINVAL);
1271 
1272 	/* Get key info to auto-detect length and type */
1273 	if (asmc_key_getinfo(dev, newkey, &keylen, sc->sc_rawtype) != 0)
1274 		return (ENOENT);
1275 
1276 	if (keylen > ASMC_MAXVAL)
1277 		keylen = ASMC_MAXVAL;
1278 
1279 	strlcpy(sc->sc_rawkey, newkey, sizeof(sc->sc_rawkey));
1280 	sc->sc_rawlen = keylen;
1281 	memset(sc->sc_rawval, 0, sizeof(sc->sc_rawval));
1282 
1283 	/* Read the key value */
1284 	asmc_key_read(dev, sc->sc_rawkey, sc->sc_rawval, sc->sc_rawlen);
1285 
1286 	return (0);
1287 }
1288 
1289 static int
1290 asmc_raw_value_sysctl(SYSCTL_HANDLER_ARGS)
1291 {
1292 	device_t dev = (device_t) arg1;
1293 	struct asmc_softc *sc = device_get_softc(dev);
1294 	char hexbuf[ASMC_MAXVAL * 2 + 1];
1295 	int error, i;
1296 
1297 	/* Refresh from SMC if a key has been selected. */
1298 	if (sc->sc_rawkey[0] != '\0') {
1299 		asmc_key_read(dev, sc->sc_rawkey, sc->sc_rawval,
1300 		    sc->sc_rawlen > 0 ? sc->sc_rawlen : ASMC_MAXVAL);
1301 	}
1302 
1303 	/* Format as hex string */
1304 	for (i = 0; i < sc->sc_rawlen && i < ASMC_MAXVAL; i++)
1305 		snprintf(hexbuf + i * 2, 3, "%02x", sc->sc_rawval[i]);
1306 	hexbuf[i * 2] = '\0';
1307 
1308 	error = sysctl_handle_string(oidp, hexbuf, sizeof(hexbuf), req);
1309 	if (error || req->newptr == NULL)
1310 		return (error);
1311 
1312 	/* Reject writes until a key is selected via raw.key. */
1313 	if (sc->sc_rawkey[0] == '\0')
1314 		return (EINVAL);
1315 
1316 	memset(sc->sc_rawval, 0, sizeof(sc->sc_rawval));
1317 	for (i = 0; i < sc->sc_rawlen && hexbuf[i*2] && hexbuf[i*2+1]; i++) {
1318 		unsigned int val;
1319 		char tmp[3] = { hexbuf[i*2], hexbuf[i*2+1], 0 };
1320 		if (sscanf(tmp, "%02x", &val) == 1)
1321 			sc->sc_rawval[i] = (uint8_t)val;
1322 	}
1323 
1324 	if (asmc_key_write(dev, sc->sc_rawkey, sc->sc_rawval, sc->sc_rawlen) != 0)
1325 		return (EIO);
1326 
1327 	return (0);
1328 }
1329 
1330 static int
1331 asmc_raw_len_sysctl(SYSCTL_HANDLER_ARGS)
1332 {
1333 	device_t dev = (device_t) arg1;
1334 	struct asmc_softc *sc = device_get_softc(dev);
1335 
1336 	return (sysctl_handle_8(oidp, &sc->sc_rawlen, 0, req));
1337 }
1338 
1339 static int
1340 asmc_raw_type_sysctl(SYSCTL_HANDLER_ARGS)
1341 {
1342 	device_t dev = (device_t) arg1;
1343 	struct asmc_softc *sc = device_get_softc(dev);
1344 
1345 	return (sysctl_handle_string(oidp, sc->sc_rawtype,
1346 	    sizeof(sc->sc_rawtype), req));
1347 }
1348 #endif
1349 
1350 /*
1351  * Convert signed fixed-point SMC values to milli-units.
1352  * Format "spXY" means signed with X integer bits and Y fraction bits.
1353  */
1354 static int
1355 asmc_sp78_to_milli(const uint8_t *buf)
1356 {
1357 	int16_t val = (int16_t)be16dec(buf);
1358 
1359 	return ((int)val * 1000) / 256;
1360 }
1361 
1362 static int
1363 asmc_sp87_to_milli(const uint8_t *buf)
1364 {
1365 	int16_t val = (int16_t)be16dec(buf);
1366 
1367 	return ((int)val * 1000) / 128;
1368 }
1369 
1370 static int
1371 asmc_sp4b_to_milli(const uint8_t *buf)
1372 {
1373 	int16_t val = (int16_t)be16dec(buf);
1374 
1375 	return ((int)val * 1000) / 2048;
1376 }
1377 
1378 static int
1379 asmc_sp5a_to_milli(const uint8_t *buf)
1380 {
1381 	int16_t val = (int16_t)be16dec(buf);
1382 
1383 	return ((int)val * 1000) / 1024;
1384 }
1385 
1386 static int
1387 asmc_sp69_to_milli(const uint8_t *buf)
1388 {
1389 	int16_t val = (int16_t)be16dec(buf);
1390 
1391 	return ((int)val * 1000) / 512;
1392 }
1393 
1394 static int
1395 asmc_sp96_to_milli(const uint8_t *buf)
1396 {
1397 	int16_t val = (int16_t)be16dec(buf);
1398 
1399 	return ((int)val * 1000) / 64;
1400 }
1401 
1402 static int
1403 asmc_sp2d_to_milli(const uint8_t *buf)
1404 {
1405 	int16_t val = (int16_t)be16dec(buf);
1406 
1407 	return ((int)val * 1000) / 8192;
1408 }
1409 
1410 static bool
1411 asmc_sensor_type_supported(const char *type)
1412 {
1413 
1414 	return (strncmp(type, "sp78", 4) == 0 ||
1415 	    strncmp(type, "sp87", 4) == 0 ||
1416 	    strncmp(type, "sp4b", 4) == 0 ||
1417 	    strncmp(type, "sp5a", 4) == 0 ||
1418 	    strncmp(type, "sp69", 4) == 0 ||
1419 	    strncmp(type, "sp96", 4) == 0 ||
1420 	    strncmp(type, "sp2d", 4) == 0 ||
1421 	    strncmp(type, "ui16", 4) == 0);
1422 }
1423 
1424 /*
1425  * Generic sensor value reader with automatic type conversion.
1426  * Reads an SMC key, detects its type, and converts to millivalue.
1427  */
1428 static int
1429 asmc_sensor_read(device_t dev, const char *key, int *millivalue)
1430 {
1431 	uint8_t buf[2];
1432 	char type[ASMC_TYPELEN + 1];
1433 	uint8_t len;
1434 	int error;
1435 
1436 	error = asmc_key_getinfo(dev, key, &len, type);
1437 	if (error != 0)
1438 		return (error);
1439 
1440 	if (len != 2) {
1441 		if (bootverbose)
1442 			device_printf(dev,
1443 			    "%s: key %s unexpected length %d\n",
1444 			    __func__, key, len);
1445 		return (ENXIO);
1446 	}
1447 
1448 	error = asmc_key_read(dev, key, buf, sizeof(buf));
1449 	if (error != 0)
1450 		return (error);
1451 
1452 	if (strncmp(type, "sp78", 4) == 0) {
1453 		*millivalue = asmc_sp78_to_milli(buf);
1454 	} else if (strncmp(type, "sp87", 4) == 0) {
1455 		*millivalue = asmc_sp87_to_milli(buf);
1456 	} else if (strncmp(type, "sp4b", 4) == 0) {
1457 		*millivalue = asmc_sp4b_to_milli(buf);
1458 	} else if (strncmp(type, "sp5a", 4) == 0) {
1459 		*millivalue = asmc_sp5a_to_milli(buf);
1460 	} else if (strncmp(type, "sp69", 4) == 0) {
1461 		*millivalue = asmc_sp69_to_milli(buf);
1462 	} else if (strncmp(type, "sp96", 4) == 0) {
1463 		*millivalue = asmc_sp96_to_milli(buf);
1464 	} else if (strncmp(type, "sp2d", 4) == 0) {
1465 		*millivalue = asmc_sp2d_to_milli(buf);
1466 	} else if (strncmp(type, "ui16", 4) == 0) {
1467 		*millivalue = be16dec(buf);
1468 	} else {
1469 		if (bootverbose)
1470 			device_printf(dev,
1471 			    "%s: unknown type '%s' for key %s\n",
1472 			    __func__, type, key);
1473 		return (ENXIO);
1474 	}
1475 
1476 	return (0);
1477 }
1478 
1479 /*
1480  * Generic sensor sysctl handler for voltage/current/power/light sensors.
1481  * arg2 encodes: sensor_type (high byte) | sensor_index (low byte)
1482  * Sensor types: 'V'=voltage, 'I'=current, 'P'=power, 'L'=light
1483  */
1484 static int
1485 asmc_sensor_sysctl(SYSCTL_HANDLER_ARGS)
1486 {
1487 	device_t dev = (device_t) arg1;
1488 	struct asmc_softc *sc = device_get_softc(dev);
1489 	int error, val;
1490 	int sensor_type = (arg2 >> 8) & 0xFF;
1491 	int sensor_idx = arg2 & 0xFF;
1492 	const char *key = NULL;
1493 
1494 	/* Select sensor based on type and index */
1495 	switch (sensor_type) {
1496 	case 'V':  /* Voltage */
1497 		if (sensor_idx < sc->sc_voltage_count)
1498 			key = sc->sc_voltage_sensors[sensor_idx];
1499 		break;
1500 	case 'I':  /* Current */
1501 		if (sensor_idx < sc->sc_current_count)
1502 			key = sc->sc_current_sensors[sensor_idx];
1503 		break;
1504 	case 'P':  /* Power */
1505 		if (sensor_idx < sc->sc_power_count)
1506 			key = sc->sc_power_sensors[sensor_idx];
1507 		break;
1508 	case 'L':  /* Light */
1509 		if (sensor_idx < sc->sc_light_count)
1510 			key = sc->sc_light_sensors[sensor_idx];
1511 		break;
1512 	default:
1513 		return (EINVAL);
1514 	}
1515 
1516 	if (key == NULL)
1517 		return (ENOENT);
1518 
1519 	error = asmc_sensor_read(dev, key, &val);
1520 	if (error != 0)
1521 		return (error);
1522 
1523 	return (sysctl_handle_int(oidp, &val, 0, req));
1524 }
1525 
1526 /*
1527  * Scan a range of SMC key indices, adding matching sensors.
1528  * Only considers 2-byte keys with a supported type.
1529  */
1530 static void
1531 asmc_scan_sensor_range(device_t dev, unsigned int start,
1532     unsigned int end, char prefix, int *countp, char **sensors,
1533     int maxcount)
1534 {
1535 	char key[ASMC_KEYLEN + 1];
1536 	char type[ASMC_TYPELEN + 1];
1537 	uint8_t len;
1538 	unsigned int i;
1539 	char *sensor_key;
1540 
1541 	for (i = start; i < end; i++) {
1542 		if (asmc_key_dump_by_index(dev, i, key, type, &len))
1543 			continue;
1544 		if (key[0] != prefix || len != 2)
1545 			continue;
1546 		if (!asmc_sensor_type_supported(type))
1547 			continue;
1548 		if (*countp >= maxcount)
1549 			break;
1550 		sensor_key = malloc(ASMC_KEYLEN + 1,
1551 		    M_DEVBUF, M_WAITOK);
1552 		memcpy(sensor_key, key, ASMC_KEYLEN + 1);
1553 		sensors[(*countp)++] = sensor_key;
1554 	}
1555 }
1556 
1557 static int
1558 asmc_detect_sensors(device_t dev)
1559 {
1560 	struct asmc_softc *sc = device_get_softc(dev);
1561 	struct sysctl_ctx_list *sysctlctx;
1562 	struct sysctl_oid *tree_node;
1563 	char key[ASMC_KEYLEN + 1];
1564 	char type[ASMC_TYPELEN + 1];
1565 	uint8_t len;
1566 	unsigned int start, end, i;
1567 	int error;
1568 	char *sensor_key;
1569 
1570 	sc->sc_voltage_count = 0;
1571 	sc->sc_current_count = 0;
1572 	sc->sc_power_count = 0;
1573 	sc->sc_light_count = 0;
1574 	sc->sc_temp_count = 0;
1575 
1576 	if (sc->sc_nkeys == 0)
1577 		return (0);
1578 
1579 	/*
1580 	 * Temperature sensors: binary search for T..U range,
1581 	 * then filter by type sp78.
1582 	 */
1583 	error = asmc_key_search(dev, "T\0\0\0", &start);
1584 	if (error == 0)
1585 		error = asmc_key_search(dev, "U\0\0\0", &end);
1586 	if (error == 0) {
1587 		for (i = start; i < end; i++) {
1588 			if (asmc_key_dump_by_index(dev, i,
1589 			    key, type, &len))
1590 				continue;
1591 			if (len != 2 ||
1592 			    strncmp(type, "sp78", 4) != 0)
1593 				continue;
1594 			if (sc->sc_temp_count >= ASMC_TEMP_MAX)
1595 				break;
1596 			sensor_key = malloc(ASMC_KEYLEN + 1,
1597 			    M_DEVBUF, M_WAITOK);
1598 			memcpy(sensor_key, key, ASMC_KEYLEN + 1);
1599 			sc->sc_temp_sensors[sc->sc_temp_count++] =
1600 			    sensor_key;
1601 		}
1602 	}
1603 
1604 	/* Voltage sensors: V..W range */
1605 	error = asmc_key_search(dev, "V\0\0\0", &start);
1606 	if (error == 0)
1607 		error = asmc_key_search(dev, "W\0\0\0", &end);
1608 	if (error == 0)
1609 		asmc_scan_sensor_range(dev, start, end, 'V',
1610 		    &sc->sc_voltage_count, sc->sc_voltage_sensors,
1611 		    ASMC_MAX_SENSORS);
1612 
1613 	/* Current sensors: I..J range */
1614 	error = asmc_key_search(dev, "I\0\0\0", &start);
1615 	if (error == 0)
1616 		error = asmc_key_search(dev, "J\0\0\0", &end);
1617 	if (error == 0)
1618 		asmc_scan_sensor_range(dev, start, end, 'I',
1619 		    &sc->sc_current_count, sc->sc_current_sensors,
1620 		    ASMC_MAX_SENSORS);
1621 
1622 	/* Power sensors: P..Q range */
1623 	error = asmc_key_search(dev, "P\0\0\0", &start);
1624 	if (error == 0)
1625 		error = asmc_key_search(dev, "Q\0\0\0", &end);
1626 	if (error == 0)
1627 		asmc_scan_sensor_range(dev, start, end, 'P',
1628 		    &sc->sc_power_count, sc->sc_power_sensors,
1629 		    ASMC_MAX_SENSORS);
1630 
1631 	/* Ambient light sensors: AL* in A..B range */
1632 	error = asmc_key_search(dev, "A\0\0\0", &start);
1633 	if (error == 0)
1634 		error = asmc_key_search(dev, "B\0\0\0", &end);
1635 	if (error == 0) {
1636 		for (i = start; i < end; i++) {
1637 			if (asmc_key_dump_by_index(dev, i,
1638 			    key, type, &len))
1639 				continue;
1640 			if (key[0] != 'A' || key[1] != 'L' ||
1641 			    (key[2] != 'V' && key[2] != 'S') ||
1642 			    len != 2)
1643 				continue;
1644 			if (!asmc_sensor_type_supported(type))
1645 				continue;
1646 			if (sc->sc_light_count >= ASMC_MAX_SENSORS)
1647 				break;
1648 			sensor_key = malloc(ASMC_KEYLEN + 1,
1649 			    M_DEVBUF, M_WAITOK);
1650 			memcpy(sensor_key, key, ASMC_KEYLEN + 1);
1651 			sc->sc_light_sensors[sc->sc_light_count++] =
1652 			    sensor_key;
1653 		}
1654 	}
1655 
1656 	if (bootverbose)
1657 		device_printf(dev,
1658 		    "detected %d temp, %d voltage, %d current, "
1659 		    "%d power, %d light sensors\n",
1660 		    sc->sc_temp_count, sc->sc_voltage_count,
1661 		    sc->sc_current_count,
1662 		    sc->sc_power_count, sc->sc_light_count);
1663 
1664 	/* Register sysctls for detected sensors */
1665 	sysctlctx = device_get_sysctl_ctx(dev);
1666 
1667 	/* Voltage sensors */
1668 	if (sc->sc_voltage_count > 0) {
1669 		tree_node = SYSCTL_ADD_NODE(sysctlctx,
1670 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1671 		    "voltage", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Voltage sensors (millivolts)");
1672 
1673 		for (i = 0; i < sc->sc_voltage_count; i++) {
1674 			SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node),
1675 			    OID_AUTO, sc->sc_voltage_sensors[i],
1676 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
1677 			    dev, ('V' << 8) | i, asmc_sensor_sysctl, "I",
1678 			    "Voltage sensor (millivolts)");
1679 		}
1680 	}
1681 
1682 	/* Current sensors */
1683 	if (sc->sc_current_count > 0) {
1684 		tree_node = SYSCTL_ADD_NODE(sysctlctx,
1685 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1686 		    "current", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Current sensors (milliamps)");
1687 
1688 		for (i = 0; i < sc->sc_current_count; i++) {
1689 			SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node),
1690 			    OID_AUTO, sc->sc_current_sensors[i],
1691 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
1692 			    dev, ('I' << 8) | i, asmc_sensor_sysctl, "I",
1693 			    "Current sensor (milliamps)");
1694 		}
1695 	}
1696 
1697 	/* Power sensors */
1698 	if (sc->sc_power_count > 0) {
1699 		tree_node = SYSCTL_ADD_NODE(sysctlctx,
1700 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1701 		    "power", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Power sensors (milliwatts)");
1702 
1703 		for (i = 0; i < sc->sc_power_count; i++) {
1704 			SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node),
1705 			    OID_AUTO, sc->sc_power_sensors[i],
1706 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
1707 			    dev, ('P' << 8) | i, asmc_sensor_sysctl, "I",
1708 			    "Power sensor (milliwatts)");
1709 		}
1710 	}
1711 
1712 	/* Ambient light sensors */
1713 	if (sc->sc_light_count > 0) {
1714 		tree_node = SYSCTL_ADD_NODE(sysctlctx,
1715 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1716 		    "ambient", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Ambient light sensors");
1717 
1718 		for (i = 0; i < sc->sc_light_count; i++) {
1719 			SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node),
1720 			    OID_AUTO, sc->sc_light_sensors[i],
1721 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
1722 			    dev, ('L' << 8) | i, asmc_sensor_sysctl, "I",
1723 			    "Light sensor value");
1724 		}
1725 	}
1726 
1727 	return (0);
1728 }
1729 
1730 /*
1731  * Helper function to get key info by index (for sensor detection).
1732  */
1733 static int
1734 asmc_key_dump_by_index(device_t dev, int index, char *key_out,
1735     char *type_out, uint8_t *len_out)
1736 {
1737 	struct asmc_softc *sc = device_get_softc(dev);
1738 	uint8_t index_buf[ASMC_KEYLEN];
1739 	uint8_t key_buf[ASMC_KEYLEN];
1740 	uint8_t info_buf[ASMC_KEYINFO_RESPLEN];
1741 	int error = ENXIO, try = 0;
1742 	int i;
1743 
1744 	mtx_lock_spin(&sc->sc_mtx);
1745 
1746 	index_buf[0] = (index >> 24) & 0xff;
1747 	index_buf[1] = (index >> 16) & 0xff;
1748 	index_buf[2] = (index >> 8) & 0xff;
1749 	index_buf[3] = index & 0xff;
1750 
1751 begin:
1752 	if (asmc_command(dev, ASMC_CMDGETBYINDEX))
1753 		goto out;
1754 
1755 	for (i = 0; i < ASMC_KEYLEN; i++) {
1756 		ASMC_DATAPORT_WRITE(sc, index_buf[i]);
1757 		if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA))
1758 			goto out;
1759 	}
1760 
1761 	ASMC_DATAPORT_WRITE(sc, ASMC_KEYLEN);
1762 
1763 	for (i = 0; i < ASMC_KEYLEN; i++) {
1764 		if (asmc_wait(dev, ASMC_STATUS_DATA_READY))
1765 			goto out;
1766 		key_buf[i] = ASMC_DATAPORT_READ(sc);
1767 	}
1768 
1769 	if (asmc_command(dev, ASMC_CMDGETINFO))
1770 		goto out;
1771 
1772 	for (i = 0; i < ASMC_KEYLEN; i++) {
1773 		ASMC_DATAPORT_WRITE(sc, key_buf[i]);
1774 		if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA))
1775 			goto out;
1776 	}
1777 
1778 	ASMC_DATAPORT_WRITE(sc, ASMC_KEYINFO_RESPLEN);
1779 
1780 	for (i = 0; i < ASMC_KEYINFO_RESPLEN; i++) {
1781 		if (asmc_wait(dev, ASMC_STATUS_DATA_READY))
1782 			goto out;
1783 		info_buf[i] = ASMC_DATAPORT_READ(sc);
1784 	}
1785 
1786 	memcpy(key_out, key_buf, ASMC_KEYLEN);
1787 	key_out[ASMC_KEYLEN] = '\0';
1788 	*len_out = info_buf[0];
1789 	memcpy(type_out, &info_buf[1], ASMC_TYPELEN);
1790 	type_out[ASMC_TYPELEN] = '\0';
1791 	error = 0;
1792 
1793 out:
1794 	if (error) {
1795 		if (++try < ASMC_MAXRETRIES)
1796 			goto begin;
1797 	}
1798 
1799 	mtx_unlock_spin(&sc->sc_mtx);
1800 	return (error);
1801 }
1802 
1803 /*
1804  * Binary search for the first key index >= prefix.
1805  * SMC keys are sorted, so this finds the lower bound efficiently.
1806  */
1807 static int
1808 asmc_key_search(device_t dev, const char *prefix, unsigned int *idx)
1809 {
1810 	struct asmc_softc *sc = device_get_softc(dev);
1811 	unsigned int lo, hi, mid;
1812 	char key[ASMC_KEYLEN + 1];
1813 	char type[ASMC_TYPELEN + 1];
1814 	uint8_t len;
1815 	int error;
1816 
1817 	lo = 0;
1818 	hi = sc->sc_nkeys;
1819 	while (lo < hi) {
1820 		mid = lo + (hi - lo) / 2;
1821 		error = asmc_key_dump_by_index(dev, mid,
1822 		    key, type, &len);
1823 		if (error != 0)
1824 			return (error);
1825 		if (strncmp(key, prefix, ASMC_KEYLEN) < 0)
1826 			lo = mid + 1;
1827 		else
1828 			hi = mid;
1829 	}
1830 	*idx = lo;
1831 	return (0);
1832 }
1833 
1834 static int
1835 asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len)
1836 {
1837 	int i, error = -1, try = 0;
1838 	struct asmc_softc *sc = device_get_softc(dev);
1839 
1840 	mtx_lock_spin(&sc->sc_mtx);
1841 
1842 begin:
1843 	ASMC_DPRINTF(("cmd port: cmd write\n"));
1844 	if (asmc_command(dev, ASMC_CMDWRITE))
1845 		goto out;
1846 
1847 	ASMC_DPRINTF(("data port: key\n"));
1848 	for (i = 0; i < 4; i++) {
1849 		ASMC_DATAPORT_WRITE(sc, key[i]);
1850 		if (asmc_wait(dev, 0x04))
1851 			goto out;
1852 	}
1853 	ASMC_DPRINTF(("data port: length\n"));
1854 	ASMC_DATAPORT_WRITE(sc, len);
1855 
1856 	ASMC_DPRINTF(("data port: buffer\n"));
1857 	for (i = 0; i < len; i++) {
1858 		if (asmc_wait(dev, 0x04))
1859 			goto out;
1860 		ASMC_DATAPORT_WRITE(sc, buf[i]);
1861 	}
1862 
1863 	error = 0;
1864 out:
1865 	if (error) {
1866 		if (++try < 10)
1867 			goto begin;
1868 		device_printf(dev, "%s for key %s failed %d times, giving up\n",
1869 		    __func__, key, try);
1870 	}
1871 
1872 	mtx_unlock_spin(&sc->sc_mtx);
1873 
1874 	return (error);
1875 }
1876 
1877 /*
1878  * Fan control functions.
1879  */
1880 static int
1881 asmc_fan_count(device_t dev)
1882 {
1883 	uint8_t buf[1];
1884 
1885 	if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, sizeof(buf)) != 0)
1886 		return (-1);
1887 
1888 	return (buf[0]);
1889 }
1890 
1891 static int
1892 asmc_fan_getvalue(device_t dev, const char *key, int fan)
1893 {
1894 	int speed;
1895 	uint8_t buf[2];
1896 	char fankey[5];
1897 
1898 	snprintf(fankey, sizeof(fankey), key, fan);
1899 	if (asmc_key_read(dev, fankey, buf, sizeof(buf)) != 0)
1900 		return (-1);
1901 	speed = (buf[0] << 6) | (buf[1] >> 2);
1902 
1903 	return (speed);
1904 }
1905 
1906 static char *
1907 asmc_fan_getstring(device_t dev, const char *key, int fan, uint8_t *buf,
1908     uint8_t buflen)
1909 {
1910 	char fankey[5];
1911 	char *desc;
1912 
1913 	snprintf(fankey, sizeof(fankey), key, fan);
1914 	if (asmc_key_read(dev, fankey, buf, buflen) != 0)
1915 		return (NULL);
1916 	desc = buf + 4;
1917 
1918 	return (desc);
1919 }
1920 
1921 static int
1922 asmc_fan_setvalue(device_t dev, const char *key, int fan, int speed)
1923 {
1924 	uint8_t buf[2];
1925 	char fankey[5];
1926 
1927 	speed *= 4;
1928 
1929 	buf[0] = speed >> 8;
1930 	buf[1] = speed;
1931 
1932 	snprintf(fankey, sizeof(fankey), key, fan);
1933 	if (asmc_key_write(dev, fankey, buf, sizeof(buf)) < 0)
1934 		return (-1);
1935 
1936 	return (0);
1937 }
1938 
1939 static int
1940 asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS)
1941 {
1942 	device_t dev = (device_t)arg1;
1943 	int fan = arg2;
1944 	int error;
1945 	int32_t v;
1946 
1947 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSPEED, fan);
1948 	error = sysctl_handle_int(oidp, &v, 0, req);
1949 
1950 	return (error);
1951 }
1952 
1953 static int
1954 asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS)
1955 {
1956 	uint8_t buf[16];
1957 	device_t dev = (device_t)arg1;
1958 	int fan = arg2;
1959 	int error = true;
1960 	char *desc;
1961 
1962 	desc = asmc_fan_getstring(dev, ASMC_KEY_FANID, fan, buf, sizeof(buf));
1963 
1964 	if (desc != NULL)
1965 		error = sysctl_handle_string(oidp, desc, 0, req);
1966 
1967 	return (error);
1968 }
1969 
1970 static int
1971 asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS)
1972 {
1973 	device_t dev = (device_t)arg1;
1974 	int fan = arg2;
1975 	int error;
1976 	int32_t v;
1977 
1978 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSAFESPEED, fan);
1979 	error = sysctl_handle_int(oidp, &v, 0, req);
1980 
1981 	return (error);
1982 }
1983 
1984 static int
1985 asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS)
1986 {
1987 	device_t dev = (device_t)arg1;
1988 	int fan = arg2;
1989 	int error;
1990 	int32_t v;
1991 
1992 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMINSPEED, fan);
1993 	error = sysctl_handle_int(oidp, &v, 0, req);
1994 
1995 	if (error == 0 && req->newptr != NULL) {
1996 		unsigned int newspeed = v;
1997 		asmc_fan_setvalue(dev, ASMC_KEY_FANMINSPEED, fan, newspeed);
1998 	}
1999 
2000 	return (error);
2001 }
2002 
2003 static int
2004 asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS)
2005 {
2006 	device_t dev = (device_t)arg1;
2007 	int fan = arg2;
2008 	int error;
2009 	int32_t v;
2010 
2011 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMAXSPEED, fan);
2012 	error = sysctl_handle_int(oidp, &v, 0, req);
2013 
2014 	if (error == 0 && req->newptr != NULL) {
2015 		unsigned int newspeed = v;
2016 		asmc_fan_setvalue(dev, ASMC_KEY_FANMAXSPEED, fan, newspeed);
2017 	}
2018 
2019 	return (error);
2020 }
2021 
2022 static int
2023 asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS)
2024 {
2025 	device_t dev = (device_t)arg1;
2026 	int fan = arg2;
2027 	int error;
2028 	int32_t v;
2029 
2030 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANTARGETSPEED, fan);
2031 	error = sysctl_handle_int(oidp, &v, 0, req);
2032 
2033 	if (error == 0 && req->newptr != NULL) {
2034 		unsigned int newspeed = v;
2035 		asmc_fan_setvalue(dev, ASMC_KEY_FANTARGETSPEED, fan, newspeed);
2036 	}
2037 
2038 	return (error);
2039 }
2040 
2041 static int
2042 asmc_mb_sysctl_fanmanual(SYSCTL_HANDLER_ARGS)
2043 {
2044 	device_t dev = (device_t)arg1;
2045 	int fan = arg2;
2046 	int error;
2047 	int32_t v;
2048 	uint8_t buf[2];
2049 	uint16_t val;
2050 
2051 	/* Read current FS! bitmask (asmc_key_read locks internally) */
2052 	error = asmc_key_read(dev, ASMC_KEY_FANMANUAL, buf, sizeof(buf));
2053 	if (error != 0)
2054 		return (error);
2055 
2056 	/* Extract manual bit for this fan (big-endian) */
2057 	val = (buf[0] << 8) | buf[1];
2058 	v = (val >> fan) & 0x01;
2059 
2060 	/* Let sysctl handle the value */
2061 	error = sysctl_handle_int(oidp, &v, 0, req);
2062 
2063 	if (error == 0 && req->newptr != NULL) {
2064 		/* Validate input (0 = auto, 1 = manual) */
2065 		if (v != 0 && v != 1)
2066 			return (EINVAL);
2067 		/* Read-modify-write of FS! bitmask */
2068 		error = asmc_key_read(dev, ASMC_KEY_FANMANUAL, buf,
2069 		    sizeof(buf));
2070 		if (error == 0) {
2071 			val = (buf[0] << 8) | buf[1];
2072 
2073 			/* Modify single bit */
2074 			if (v)
2075 				val |= (1 << fan);   /* Set to manual */
2076 			else
2077 				val &= ~(1 << fan);  /* Set to auto */
2078 
2079 			/* Write back */
2080 			buf[0] = val >> 8;
2081 			buf[1] = val & 0xff;
2082 			error = asmc_key_write(dev, ASMC_KEY_FANMANUAL, buf,
2083 			    sizeof(buf));
2084 		}
2085 	}
2086 
2087 	return (error);
2088 }
2089 
2090 /*
2091  * Temperature functions.
2092  */
2093 static int
2094 asmc_temp_getvalue(device_t dev, const char *key)
2095 {
2096 	uint8_t buf[2];
2097 
2098 	/*
2099 	 * Check for invalid temperatures.
2100 	 */
2101 	if (asmc_key_read(dev, key, buf, sizeof(buf)) != 0)
2102 		return (-1);
2103 
2104 	return (buf[0]);
2105 }
2106 
2107 static int
2108 asmc_temp_sysctl(SYSCTL_HANDLER_ARGS)
2109 {
2110 	device_t dev = (device_t)arg1;
2111 	struct asmc_softc *sc = device_get_softc(dev);
2112 	int error, val;
2113 
2114 	if (arg2 < 0 || arg2 >= sc->sc_temp_count)
2115 		return (EINVAL);
2116 
2117 	val = asmc_temp_getvalue(dev, sc->sc_temp_sensors[arg2]);
2118 	error = sysctl_handle_int(oidp, &val, 0, req);
2119 
2120 	return (error);
2121 }
2122 
2123 /*
2124  * Sudden Motion Sensor functions.
2125  */
2126 static int
2127 asmc_sms_read(device_t dev, const char *key, int16_t *val)
2128 {
2129 	uint8_t buf[2];
2130 	int error;
2131 
2132 	/* no need to do locking here as asmc_key_read() already does it */
2133 	switch (key[3]) {
2134 	case 'X':
2135 	case 'Y':
2136 	case 'Z':
2137 		error = asmc_key_read(dev, key, buf, sizeof(buf));
2138 		break;
2139 	default:
2140 		device_printf(dev, "%s called with invalid argument %s\n",
2141 		    __func__, key);
2142 		error = EINVAL;
2143 		goto out;
2144 	}
2145 	*val = ((int16_t)buf[0] << 8) | buf[1];
2146 out:
2147 	return (error);
2148 }
2149 
2150 static void
2151 asmc_sms_calibrate(device_t dev)
2152 {
2153 	struct asmc_softc *sc = device_get_softc(dev);
2154 
2155 	asmc_sms_read(dev, ASMC_KEY_SMS_X, &sc->sms_rest_x);
2156 	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &sc->sms_rest_y);
2157 	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &sc->sms_rest_z);
2158 }
2159 
2160 static int
2161 asmc_sms_intrfast(void *arg)
2162 {
2163 	uint8_t type;
2164 	device_t dev = (device_t)arg;
2165 	struct asmc_softc *sc = device_get_softc(dev);
2166 	if (!sc->sc_sms_intr_works)
2167 		return (FILTER_HANDLED);
2168 
2169 	mtx_lock_spin(&sc->sc_mtx);
2170 	type = ASMC_INTPORT_READ(sc);
2171 	mtx_unlock_spin(&sc->sc_mtx);
2172 
2173 	sc->sc_sms_intrtype = type;
2174 	asmc_sms_printintr(dev, type);
2175 
2176 	/* Don't queue SMS task for ambient light interrupts */
2177 	if (type == ASMC_ALSL_INT2A && sc->sc_has_alsl)
2178 		return (FILTER_HANDLED);
2179 
2180 	taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task);
2181 	return (FILTER_HANDLED);
2182 }
2183 
2184 static void
2185 asmc_sms_printintr(device_t dev, uint8_t type)
2186 {
2187 	struct asmc_softc *sc = device_get_softc(dev);
2188 
2189 	switch (type) {
2190 	case ASMC_SMS_INTFF:
2191 		device_printf(dev, "WARNING: possible free fall!\n");
2192 		break;
2193 	case ASMC_SMS_INTHA:
2194 		device_printf(dev, "WARNING: high acceleration detected!\n");
2195 		break;
2196 	case ASMC_SMS_INTSH:
2197 		device_printf(dev, "WARNING: possible shock!\n");
2198 		break;
2199 	case ASMC_ALSL_INT2A:
2200 		/*
2201 		 * This suppresses console and log messages for the ambient
2202 		 * light sensor interrupt on models that have ALSL.
2203 		 */
2204 		if (sc->sc_has_alsl)
2205 			break;
2206 		/* FALLTHROUGH */
2207 	default:
2208 		device_printf(dev, "unknown interrupt: 0x%x\n", type);
2209 	}
2210 }
2211 
2212 static void
2213 asmc_sms_task(void *arg, int pending)
2214 {
2215 	struct asmc_softc *sc = (struct asmc_softc *)arg;
2216 	char notify[16];
2217 	int type;
2218 
2219 	switch (sc->sc_sms_intrtype) {
2220 	case ASMC_SMS_INTFF:
2221 		type = 2;
2222 		break;
2223 	case ASMC_SMS_INTHA:
2224 		type = 1;
2225 		break;
2226 	case ASMC_SMS_INTSH:
2227 		type = 0;
2228 		break;
2229 	default:
2230 		type = 255;
2231 	}
2232 
2233 	snprintf(notify, sizeof(notify), " notify=0x%x", type);
2234 	devctl_notify("ACPI", "asmc", "SMS", notify);
2235 }
2236 
2237 static int
2238 asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS)
2239 {
2240 	device_t dev = (device_t)arg1;
2241 	int error;
2242 	int16_t val;
2243 	int32_t v;
2244 
2245 	asmc_sms_read(dev, ASMC_KEY_SMS_X, &val);
2246 	v = (int32_t)val;
2247 	error = sysctl_handle_int(oidp, &v, 0, req);
2248 
2249 	return (error);
2250 }
2251 
2252 static int
2253 asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS)
2254 {
2255 	device_t dev = (device_t)arg1;
2256 	int error;
2257 	int16_t val;
2258 	int32_t v;
2259 
2260 	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val);
2261 	v = (int32_t)val;
2262 	error = sysctl_handle_int(oidp, &v, 0, req);
2263 
2264 	return (error);
2265 }
2266 
2267 static int
2268 asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS)
2269 {
2270 	device_t dev = (device_t)arg1;
2271 	int error;
2272 	int16_t val;
2273 	int32_t v;
2274 
2275 	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val);
2276 	v = (int32_t)val;
2277 	error = sysctl_handle_int(oidp, &v, 0, req);
2278 
2279 	return (error);
2280 }
2281 
2282 static int
2283 asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS)
2284 {
2285 	device_t dev = (device_t)arg1;
2286 	uint8_t buf[6];
2287 	int error;
2288 	int32_t v;
2289 
2290 	asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, sizeof(buf));
2291 	v = buf[2];
2292 	error = sysctl_handle_int(oidp, &v, 0, req);
2293 
2294 	return (error);
2295 }
2296 
2297 static int
2298 asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS)
2299 {
2300 	device_t dev = (device_t)arg1;
2301 	uint8_t buf[6];
2302 	int error;
2303 	int32_t v;
2304 
2305 	asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, sizeof(buf));
2306 	v = buf[2];
2307 	error = sysctl_handle_int(oidp, &v, 0, req);
2308 
2309 	return (error);
2310 }
2311 
2312 static int
2313 asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS)
2314 {
2315 	device_t dev = (device_t)arg1;
2316 	struct asmc_softc *sc = device_get_softc(dev);
2317 	uint8_t buf[2];
2318 	int error;
2319 	int v;
2320 
2321 	v = light_control;
2322 	error = sysctl_handle_int(oidp, &v, 0, req);
2323 
2324 	if (error == 0 && req->newptr != NULL) {
2325 		if (v < 0 || v > 255)
2326 			return (EINVAL);
2327 		light_control = v;
2328 		sc->sc_kbd_bkl_level = v * 100 / 255;
2329 		buf[0] = light_control;
2330 		buf[1] = 0x00;
2331 		asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof(buf));
2332 	}
2333 	return (error);
2334 }
2335 
2336 static int
2337 asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS)
2338 {
2339 	device_t dev = (device_t)arg1;
2340 	uint8_t buf[10];
2341 	int error;
2342 	uint32_t v;
2343 
2344 	asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, sizeof(buf));
2345 
2346 	/*
2347 	 * This seems to be a 32 bit big endian value from buf[6] -> buf[9].
2348 	 *
2349 	 * Extract it out manually here, then shift/clamp it.
2350 	 */
2351 	v = be32dec(&buf[6]);
2352 
2353 	/*
2354 	 * Shift out, clamp at 255; that way it looks like the
2355 	 * earlier SMC firmware version responses.
2356 	 */
2357 	v = v >> 8;
2358 	if (v > 255)
2359 		v = 255;
2360 
2361 	error = sysctl_handle_int(oidp, &v, 0, req);
2362 
2363 	return (error);
2364 }
2365 
2366 /*
2367  * Auto power-on after AC power loss (AUPO key).
2368  * When non-zero the machine boots automatically when AC is restored
2369  * after an unclean power loss.  Useful for always-on servers / home labs.
2370  */
2371 static int
2372 asmc_aupo_sysctl(SYSCTL_HANDLER_ARGS)
2373 {
2374 	device_t dev = (device_t)arg1;
2375 	uint8_t aupo;
2376 	int val, error;
2377 
2378 	if (asmc_key_read(dev, ASMC_KEY_AUPO, &aupo, 1) != 0)
2379 		return (EIO);
2380 
2381 	val = (aupo != 0) ? 1 : 0;
2382 	error = sysctl_handle_int(oidp, &val, 0, req);
2383 	if (error != 0 || req->newptr == NULL)
2384 		return (error);
2385 
2386 	aupo = (val != 0) ? 1 : 0;
2387 	if (asmc_key_write(dev, ASMC_KEY_AUPO, &aupo, 1) != 0)
2388 		return (EIO);
2389 
2390 	return (0);
2391 }
2392 
2393 static int
2394 asmc_backlight_update_status(device_t dev, struct backlight_props *props)
2395 {
2396 	struct asmc_softc *sc = device_get_softc(dev);
2397 	uint8_t buf[2];
2398 
2399 	sc->sc_kbd_bkl_level = props->brightness;
2400 	light_control = props->brightness * 255 / 100;
2401 	buf[0] = light_control;
2402 	buf[1] = 0x00;
2403 	asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof(buf));
2404 
2405 	return (0);
2406 }
2407 
2408 static int
2409 asmc_backlight_get_status(device_t dev, struct backlight_props *props)
2410 {
2411 	struct asmc_softc *sc = device_get_softc(dev);
2412 
2413 	props->brightness = sc->sc_kbd_bkl_level;
2414 	props->nlevels = 0;
2415 
2416 	return (0);
2417 }
2418 
2419 static int
2420 asmc_backlight_get_info(device_t dev, struct backlight_info *info)
2421 {
2422 	info->type = BACKLIGHT_TYPE_KEYBOARD;
2423 	strlcpy(info->name, "Apple MacBook Keyboard", BACKLIGHTMAXNAMELENGTH);
2424 
2425 	return (0);
2426 }
2427