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