xref: /freebsd/sys/dev/asmc/asmc.c (revision 9336e0699bda8a301cd2bfa37106b6ec5e32012e)
1 /*-
2  * Copyright (c) 2007 Rui Paulo <rpaulo@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
18  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 /*
29  * Driver for Apple's System Management Console (SMC).
30  * SMC can be found on the MacBook, MacBook Pro and Mac Mini.
31  *
32  * Inspired by the Linux applesmc driver.
33  */
34 
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 #include <sys/param.h>
39 #include <sys/bus.h>
40 #include <sys/conf.h>
41 #include <sys/kernel.h>
42 #include <sys/lock.h>
43 #include <sys/malloc.h>
44 #include <sys/module.h>
45 #include <sys/mutex.h>
46 #include <sys/sysctl.h>
47 #include <sys/systm.h>
48 #include <sys/taskqueue.h>
49 #include <isa/isavar.h>
50 #include <machine/bus.h>
51 #include <sys/rman.h>
52 #include <machine/resource.h>
53 
54 #include <dev/asmc/asmcvar.h>
55 
56 /*
57  * Device interface.
58  */
59 static void 	asmc_identify(driver_t *driver, device_t parent);
60 static int 	asmc_probe(device_t dev);
61 static int 	asmc_attach(device_t dev);
62 static int 	asmc_detach(device_t dev);
63 
64 /*
65  * SMC functions.
66  */
67 static int 	asmc_init(device_t dev);
68 static int 	asmc_wait(device_t dev, uint8_t val);
69 static int 	asmc_key_write(device_t dev, const char *key, uint8_t *buf,
70     uint8_t len);
71 static int 	asmc_key_read(device_t dev, const char *key, uint8_t *buf,
72     uint8_t);
73 static int 	asmc_fan_count(device_t dev);
74 static int 	asmc_fan_getvalue(device_t dev, const char *key, int fan);
75 static int 	asmc_temp_getvalue(device_t dev, const char *key);
76 static int 	asmc_sms_read(device_t, const char *key, int16_t *val);
77 static void 	asmc_sms_calibrate(device_t dev);
78 static int 	asmc_sms_intrfast(void *arg);
79 #ifdef INTR_FILTER
80 static void 	asmc_sms_handler(void *arg);
81 #endif
82 static void 	asmc_sms_printintr(device_t dev, uint8_t);
83 static void 	asmc_sms_task(void *arg, int pending);
84 
85 /*
86  * Model functions.
87  */
88 static int 	asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS);
89 static int 	asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS);
90 static int 	asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS);
91 static int 	asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS);
92 static int 	asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS);
93 static int 	asmc_temp_sysctl(SYSCTL_HANDLER_ARGS);
94 static int 	asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS);
95 static int 	asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS);
96 static int 	asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS);
97 static int 	asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS);
98 static int 	asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS);
99 
100 struct asmc_model {
101 	const char 	 *smc_model;	/* smbios.system.product env var. */
102 	const char 	 *smc_desc;	/* driver description */
103 
104 	/* Helper functions */
105 	int (*smc_sms_x)(SYSCTL_HANDLER_ARGS);
106 	int (*smc_sms_y)(SYSCTL_HANDLER_ARGS);
107 	int (*smc_sms_z)(SYSCTL_HANDLER_ARGS);
108 	int (*smc_fan_speed)(SYSCTL_HANDLER_ARGS);
109 	int (*smc_fan_safespeed)(SYSCTL_HANDLER_ARGS);
110 	int (*smc_fan_minspeed)(SYSCTL_HANDLER_ARGS);
111 	int (*smc_fan_maxspeed)(SYSCTL_HANDLER_ARGS);
112 	int (*smc_fan_targetspeed)(SYSCTL_HANDLER_ARGS);
113 	int (*smc_light_left)(SYSCTL_HANDLER_ARGS);
114 	int (*smc_light_right)(SYSCTL_HANDLER_ARGS);
115 
116 	const char 	*smc_temps[8];
117 	const char 	*smc_tempnames[8];
118 	const char 	*smc_tempdescs[8];
119 };
120 
121 static struct asmc_model *asmc_match(device_t dev);
122 
123 #define ASMC_SMS_FUNCS	asmc_mb_sysctl_sms_x, asmc_mb_sysctl_sms_y, \
124 			asmc_mb_sysctl_sms_z
125 
126 #define ASMC_FAN_FUNCS	asmc_mb_sysctl_fanspeed, asmc_mb_sysctl_fansafespeed, \
127 			asmc_mb_sysctl_fanminspeed, \
128 			asmc_mb_sysctl_fanmaxspeed, \
129 			asmc_mb_sysctl_fantargetspeed
130 #define ASMC_LIGHT_FUNCS asmc_mbp_sysctl_light_left, \
131 			 asmc_mbp_sysctl_light_right
132 
133 struct asmc_model asmc_models[] = {
134 	{
135 	  "MacBook1,1", "Apple SMC MacBook Core Duo",
136 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL,
137 	  ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
138 	},
139 
140 	{
141 	  "MacBook2,1", "Apple SMC MacBook Core 2 Duo",
142 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL,
143 	  ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
144 	},
145 
146 	{
147 	  "MacBookPro1,1", "Apple SMC MacBook Pro Core Duo (15-inch)",
148 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
149 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
150 	},
151 
152 	{
153 	  "MacBookPro1,2", "Apple SMC MacBook Pro Core Duo (17-inch)",
154 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
155 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
156 	},
157 
158 	{
159 	  "MacBookPro2,1", "Apple SMC MacBook Pro Core 2 Duo (17-inch)",
160 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
161 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
162 	},
163 
164 	{
165 	  "MacBookPro2,2", "Apple SMC MacBook Pro Core 2 Duo (15-inch)",
166 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
167 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
168 	},
169 
170 	{
171 	  "MacBookPro3,1", "Apple SMC MacBook Pro Core 2 Duo (15-inch LED)",
172 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
173 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
174 	},
175 
176 	{
177 	  "MacBookPro3,2", "Apple SMC MacBook Pro Core 2 Duo (17-inch HD)",
178 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
179 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
180 	},
181 
182 	/* The Mac Mini has no SMS */
183 	{
184 	  "Macmini1,1", "Apple SMC Mac Mini",
185 	  NULL, NULL, NULL,
186 	  ASMC_FAN_FUNCS,
187 	  NULL, NULL,
188 	  ASMC_MM_TEMPS, ASMC_MM_TEMPNAMES, ASMC_MM_TEMPDESCS
189 	},
190 
191 	{ NULL, NULL }
192 };
193 
194 #undef ASMC_SMS_FUNCS
195 #undef ASMC_FAN_FUNCS
196 #undef ASMC_LIGHT_FUNCS
197 
198 /*
199  * Driver methods.
200  */
201 static device_method_t	asmc_methods[] = {
202 	DEVMETHOD(device_identify,	asmc_identify),
203 	DEVMETHOD(device_probe,		asmc_probe),
204 	DEVMETHOD(device_attach,	asmc_attach),
205 	DEVMETHOD(device_detach,	asmc_detach),
206 
207 	{ 0, 0 }
208 };
209 
210 static driver_t	asmc_driver = {
211 	"asmc",
212 	asmc_methods,
213 	sizeof(struct asmc_softc)
214 };
215 
216 static devclass_t asmc_devclass;
217 
218 DRIVER_MODULE(asmc, isa, asmc_driver, asmc_devclass, NULL, NULL);
219 
220 static void
221 asmc_identify(driver_t *driver, device_t parent)
222 {
223 	if (device_find_child(parent, "asmc", -1) == NULL &&
224 	    asmc_match(parent))
225 		BUS_ADD_CHILD(parent, 0, "asmc", -1);
226 }
227 
228 static struct asmc_model *
229 asmc_match(device_t dev)
230 {
231 	int i;
232 	char *model;
233 
234 	model = getenv("smbios.system.product");
235 	for (i = 0; asmc_models[i].smc_model; i++) {
236 		if (!strncmp(model, asmc_models[i].smc_model, strlen(model))) {
237 			freeenv(model);
238 			return (&asmc_models[i]);
239 		}
240 	}
241 	freeenv(model);
242 
243 	return (NULL);
244 }
245 
246 static int
247 asmc_probe(device_t dev)
248 {
249 	struct asmc_model *model;
250 
251 	if (resource_disabled("asmc", 0))
252 		return (ENXIO);
253 	model = asmc_match(dev);
254 	if (!model)
255 		return (ENXIO);
256 	if (isa_get_irq(dev) == -1)
257 		bus_set_resource(dev, SYS_RES_IRQ, 0, ASMC_IRQ, 1);
258 	device_set_desc(dev, model->smc_desc);
259 
260 	return (BUS_PROBE_DEFAULT);
261 }
262 
263 static int
264 asmc_attach(device_t dev)
265 {
266 	int i, j;
267 	int ret;
268 	char name[2];
269 	struct asmc_softc *sc = device_get_softc(dev);
270 	struct sysctl_ctx_list *sysctlctx;
271 	struct sysctl_oid *sysctlnode;
272 	struct asmc_model *model;
273 
274 	sysctlctx  = device_get_sysctl_ctx(dev);
275 	sysctlnode = device_get_sysctl_tree(dev);
276 
277 	model = asmc_match(dev);
278 
279 	mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN);
280 
281 	sc->sc_model = model;
282 	asmc_init(dev);
283 
284 	/*
285 	 * dev.asmc.n.fan.* tree.
286 	 */
287 	sc->sc_fan_tree[0] = SYSCTL_ADD_NODE(sysctlctx,
288 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "fan",
289 	    CTLFLAG_RD, 0, "Fan Root Tree");
290 
291 	for (i = 1; i <= sc->sc_nfan; i++) {
292 		j = i - 1;
293 		name[0] = '0' + j;
294 		name[1] = 0;
295 		sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx,
296 		    SYSCTL_CHILDREN(sc->sc_fan_tree[0]),
297 		    OID_AUTO, name, CTLFLAG_RD, 0,
298 		    "Fan Subtree");
299 
300 		SYSCTL_ADD_PROC(sysctlctx,
301 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
302 		    OID_AUTO, "speed", CTLTYPE_INT | CTLFLAG_RD,
303 		    dev, j, model->smc_fan_speed, "I",
304 		    "Fan speed in RPM");
305 
306 		SYSCTL_ADD_PROC(sysctlctx,
307 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
308 		    OID_AUTO, "safespeed",
309 		    CTLTYPE_INT | CTLFLAG_RD,
310 		    dev, j, model->smc_fan_safespeed, "I",
311 		    "Fan safe speed in RPM");
312 
313 		SYSCTL_ADD_PROC(sysctlctx,
314 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
315 		    OID_AUTO, "minspeed",
316 		    CTLTYPE_INT | CTLFLAG_RD,
317 		    dev, j, model->smc_fan_minspeed, "I",
318 		    "Fan minimum speed in RPM");
319 
320 		SYSCTL_ADD_PROC(sysctlctx,
321 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
322 		    OID_AUTO, "maxspeed",
323 		    CTLTYPE_INT | CTLFLAG_RD,
324 		    dev, j, model->smc_fan_maxspeed, "I",
325 		    "Fan maximum speed in RPM");
326 
327 		SYSCTL_ADD_PROC(sysctlctx,
328 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
329 		    OID_AUTO, "targetspeed",
330 		    CTLTYPE_INT | CTLFLAG_RD,
331 		    dev, j, model->smc_fan_targetspeed, "I",
332 		    "Fan target speed in RPM");
333 	}
334 
335 	/*
336 	 * dev.asmc.n.temp tree.
337 	 */
338 	sc->sc_temp_tree = SYSCTL_ADD_NODE(sysctlctx,
339 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp",
340 	    CTLFLAG_RD, 0, "Temperature sensors");
341 
342 	for (i = 0; model->smc_temps[i]; i++) {
343 		SYSCTL_ADD_PROC(sysctlctx,
344 		    SYSCTL_CHILDREN(sc->sc_temp_tree),
345 		    OID_AUTO, model->smc_tempnames[i],
346 		    CTLTYPE_INT | CTLFLAG_RD,
347 		    dev, i, asmc_temp_sysctl, "I",
348 		    model->smc_tempdescs[i]);
349 	}
350 
351 	if (model->smc_sms_x == NULL)
352 		goto nosms;
353 
354 	/*
355 	 * dev.asmc.n.sms tree.
356 	 */
357 	sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx,
358 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "sms",
359 	    CTLFLAG_RD, 0, "Sudden Motion Sensor");
360 
361 	SYSCTL_ADD_PROC(sysctlctx,
362 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
363 	    OID_AUTO, "x", CTLTYPE_INT | CTLFLAG_RD,
364 	    dev, 0, model->smc_sms_x, "I",
365 	    "Sudden Motion Sensor X value");
366 
367 	SYSCTL_ADD_PROC(sysctlctx,
368 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
369 	    OID_AUTO, "y", CTLTYPE_INT | CTLFLAG_RD,
370 	    dev, 0, model->smc_sms_y, "I",
371 	    "Sudden Motion Sensor Y value");
372 
373 	SYSCTL_ADD_PROC(sysctlctx,
374 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
375 	    OID_AUTO, "z", CTLTYPE_INT | CTLFLAG_RD,
376 	    dev, 0, model->smc_sms_z, "I",
377 	    "Sudden Motion Sensor Z value");
378 
379 	/*
380 	 * dev.asmc.n.light
381 	 */
382 	if (model->smc_light_left) {
383 		sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx,
384 		    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light",
385 		    CTLFLAG_RD, 0, "Keyboard backlight sensors");
386 
387 		SYSCTL_ADD_PROC(sysctlctx,
388 		    SYSCTL_CHILDREN(sc->sc_light_tree),
389 		    OID_AUTO, "left", CTLTYPE_INT | CTLFLAG_RW,
390 		    dev, 0, model->smc_light_left, "I",
391 		    "Keyboard backlight left sensor");
392 
393 		SYSCTL_ADD_PROC(sysctlctx,
394 		    SYSCTL_CHILDREN(sc->sc_light_tree),
395 		    OID_AUTO, "right", CTLTYPE_INT | CTLFLAG_RW,
396 		    dev, 0, model->smc_light_right, "I",
397 		    "Keyboard backlight right sensor");
398 	}
399 
400 	/*
401 	 * Need a taskqueue to send devctl_notify() events
402 	 * when the SMS interrupt us.
403 	 *
404 	 * PI_REALTIME is used due to the sensitivity of the
405 	 * interrupt. An interrupt from the SMS means that the
406 	 * disk heads should be turned off as quickly as possible.
407 	 *
408 	 * We only need to do this for the non INTR_FILTER case.
409 	 */
410 	sc->sc_sms_tq = NULL;
411 #ifndef INTR_FILTER
412 	TASK_INIT(&sc->sc_sms_task, 0, asmc_sms_task, sc);
413 	sc->sc_sms_tq = taskqueue_create_fast("asmc_taskq", M_WAITOK,
414 	    taskqueue_thread_enqueue, &sc->sc_sms_tq);
415 	taskqueue_start_threads(&sc->sc_sms_tq, 1, PI_REALTIME, "%s sms taskq",
416 	    device_get_nameunit(dev));
417 #endif
418 	/*
419 	 * Allocate an IRQ for the SMS.
420 	 */
421 	sc->sc_rid = 0;
422 	sc->sc_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_rid,
423 	    ASMC_IRQ, ASMC_IRQ, 1, RF_ACTIVE);
424 	if (sc->sc_res == NULL) {
425 		device_printf(dev, "unable to allocate IRQ resource\n");
426 		ret = ENXIO;
427 		goto err2;
428 	}
429 
430 	ret = bus_setup_intr(dev, sc->sc_res,
431 	          INTR_TYPE_MISC | INTR_MPSAFE,
432 #ifdef INTR_FILTER
433 	    asmc_sms_intrfast, asmc_sms_handler,
434 #else
435 	    asmc_sms_intrfast, NULL,
436 #endif
437 	    dev, &sc->sc_cookie);
438 
439 	if (ret) {
440 		device_printf(dev, "unable to setup SMS IRQ\n");
441 		goto err1;
442 	}
443 nosms:
444 	return (0);
445 err1:
446 	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid, sc->sc_res);
447 err2:
448 	mtx_destroy(&sc->sc_mtx);
449 	if (sc->sc_sms_tq)
450 		taskqueue_free(sc->sc_sms_tq);
451 
452 	return (ret);
453 }
454 
455 static int
456 asmc_detach(device_t dev)
457 {
458 	struct asmc_softc *sc = device_get_softc(dev);
459 
460 	if (sc->sc_sms_tq) {
461 		taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task);
462 		taskqueue_free(sc->sc_sms_tq);
463 	}
464 	if (sc->sc_cookie)
465 		bus_teardown_intr(dev, sc->sc_res, sc->sc_cookie);
466 	if (sc->sc_res)
467 		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid, sc->sc_res);
468 	mtx_destroy(&sc->sc_mtx);
469 
470 	return (0);
471 }
472 
473 static int
474 asmc_init(device_t dev)
475 {
476 	struct asmc_softc *sc = device_get_softc(dev);
477 	int i, error = 1;
478 	uint8_t buf[4];
479 
480 	if (sc->sc_model->smc_sms_x == NULL)
481 		goto nosms;
482 
483 	/*
484 	 * We are ready to recieve interrupts from the SMS.
485 	 */
486 	buf[0] = 0x01;
487 	asmc_key_write(dev, ASMC_KEY_INTOK, buf, 1);
488 	DELAY(50);
489 
490 	/*
491 	 * Initiate the polling intervals.
492 	 */
493 	buf[0] = 20; /* msecs */
494 	asmc_key_write(dev, ASMC_KEY_SMS_LOW_INT, buf, 1);
495 	DELAY(200);
496 
497 	buf[0] = 20; /* msecs */
498 	asmc_key_write(dev, ASMC_KEY_SMS_HIGH_INT, buf, 1);
499 	DELAY(200);
500 
501 	buf[0] = 0x00;
502 	buf[1] = 0x60;
503 	asmc_key_write(dev, ASMC_KEY_SMS_LOW, buf, 2);
504 	DELAY(200);
505 
506 	buf[0] = 0x01;
507 	buf[1] = 0xc0;
508 	asmc_key_write(dev, ASMC_KEY_SMS_HIGH, buf, 2);
509 	DELAY(200);
510 
511 	/*
512 	 * I'm not sure what this key does, but it seems to be
513 	 * required.
514 	 */
515 	buf[0] = 0x01;
516 	asmc_key_write(dev, ASMC_KEY_SMS_FLAG, buf, 1);
517 	DELAY(50);
518 
519 	/*
520 	 * Wait up to 5 seconds for SMS initialization.
521 	 */
522 	for (i = 0; i < 10000; i++) {
523 		if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 &&
524 		    (buf[0] != 0x00 || buf[1] != 0x00)) {
525 			error = 0;
526 			goto nosms;
527 		}
528 
529 		buf[0] = ASMC_SMS_INIT1;
530 		buf[1] = ASMC_SMS_INIT2;
531 		asmc_key_write(dev, ASMC_KEY_SMS, buf, 2);
532 		DELAY(50);
533 	}
534 
535 	asmc_sms_calibrate(dev);
536 nosms:
537 	sc->sc_nfan = asmc_fan_count(dev);
538 	if (sc->sc_nfan > ASMC_MAXFANS) {
539 		device_printf(dev, "more than %d fans were detected. Please "
540 		    "report this.\n", ASMC_MAXFANS);
541 		sc->sc_nfan = ASMC_MAXFANS;
542 	}
543 
544 	if (bootverbose) {
545 		/*
546 		 * XXX: The number of keys is a 32 bit buffer, but
547 		 * right now Apple only uses the last 8 bit.
548 		 */
549 		asmc_key_read(dev, ASMC_NKEYS, buf, 4);
550 		device_printf(dev, "number of keys: %d\n", buf[3]);
551 	}
552 
553 	return (error);
554 }
555 
556 /*
557  * We need to make sure that the SMC acks the byte sent.
558  * Just wait up to 100 ms.
559  */
560 static int
561 asmc_wait(device_t dev, uint8_t val)
562 {
563 	u_int i;
564 
565 	val = val & ASMC_STATUS_MASK;
566 
567 	for (i = 0; i < 1000; i++) {
568 		if ((inb(ASMC_CMDPORT) & ASMC_STATUS_MASK) == val)
569 			return (0);
570 		DELAY(10);
571 	}
572 
573 	device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, val,
574 		      inb(ASMC_CMDPORT));
575 
576 	return (1);
577 }
578 
579 static int
580 asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len)
581 {
582 	int i, error = 1;
583 	struct asmc_softc *sc = device_get_softc(dev);
584 
585 	mtx_lock_spin(&sc->sc_mtx);
586 
587 	outb(ASMC_CMDPORT, ASMC_CMDREAD);
588 	if (asmc_wait(dev, 0x0c))
589 		goto out;
590 
591 	for (i = 0; i < 4; i++) {
592 		outb(ASMC_DATAPORT, key[i]);
593 		if (asmc_wait(dev, 0x04))
594 			goto out;
595 	}
596 
597 	outb(ASMC_DATAPORT, len);
598 
599 	for (i = 0; i < len; i++) {
600 		if (asmc_wait(dev, 0x05))
601 			goto out;
602 		buf[i] = inb(ASMC_DATAPORT);
603 	}
604 
605 	error = 0;
606 out:
607 	mtx_unlock_spin(&sc->sc_mtx);
608 
609 	return (error);
610 }
611 
612 static int
613 asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len)
614 {
615 	int i, error = -1;
616 	struct asmc_softc *sc = device_get_softc(dev);
617 
618 	mtx_lock_spin(&sc->sc_mtx);
619 
620 	outb(ASMC_CMDPORT, ASMC_CMDWRITE);
621 	if (asmc_wait(dev, 0x0c))
622 		goto out;
623 
624 	for (i = 0; i < 4; i++) {
625 		outb(ASMC_DATAPORT, key[i]);
626 		if (asmc_wait(dev, 0x04))
627 			goto out;
628 	}
629 
630 	outb(ASMC_DATAPORT, len);
631 
632 	for (i = 0; i < len; i++) {
633 		if (asmc_wait(dev, 0x04))
634 			goto out;
635 		outb(ASMC_DATAPORT, buf[i]);
636 	}
637 
638 	error = 0;
639 out:
640 	mtx_unlock_spin(&sc->sc_mtx);
641 
642 	return (error);
643 
644 }
645 
646 /*
647  * Fan control functions.
648  */
649 static int
650 asmc_fan_count(device_t dev)
651 {
652 	uint8_t buf[1];
653 
654 	if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, 1) < 0)
655 		return (-1);
656 
657 	return (buf[0]);
658 }
659 
660 static int
661 asmc_fan_getvalue(device_t dev, const char *key, int fan)
662 {
663 	int speed;
664 	uint8_t buf[2];
665 	char fankey[5];
666 
667 	snprintf(fankey, sizeof(fankey), key, fan);
668 	if (asmc_key_read(dev, fankey, buf, 2) < 0)
669 		return (-1);
670 	speed = (buf[0] << 6) | (buf[1] >> 2);
671 
672 	return (speed);
673 }
674 
675 static int
676 asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS)
677 {
678 	device_t dev = (device_t) arg1;
679 	int fan = arg2;
680 	int error;
681 	int32_t v;
682 
683 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSPEED, fan);
684 	error = sysctl_handle_int(oidp, &v, 0, req);
685 
686 	return (error);
687 }
688 
689 static int
690 asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS)
691 {
692 	device_t dev = (device_t) arg1;
693 	int fan = arg2;
694 	int error;
695 	int32_t v;
696 
697 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSAFESPEED, fan);
698 	error = sysctl_handle_int(oidp, &v, 0, req);
699 
700 	return (error);
701 }
702 
703 
704 static int
705 asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS)
706 {
707 	device_t dev = (device_t) arg1;
708 	int fan = arg2;
709 	int error;
710 	int32_t v;
711 
712 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMINSPEED, fan);
713 	error = sysctl_handle_int(oidp, &v, 0, req);
714 
715 	return (error);
716 }
717 
718 static int
719 asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS)
720 {
721 	device_t dev = (device_t) arg1;
722 	int fan = arg2;
723 	int error;
724 	int32_t v;
725 
726 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMAXSPEED, fan);
727 	error = sysctl_handle_int(oidp, &v, 0, req);
728 
729 	return (error);
730 }
731 
732 static int
733 asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS)
734 {
735 	device_t dev = (device_t) arg1;
736 	int fan = arg2;
737 	int error;
738 	int32_t v;
739 
740 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANTARGETSPEED, fan);
741 	error = sysctl_handle_int(oidp, &v, 0, req);
742 
743 	return (error);
744 }
745 
746 /*
747  * Temperature functions.
748  */
749 static int
750 asmc_temp_getvalue(device_t dev, const char *key)
751 {
752 	uint8_t buf[2];
753 
754 	/*
755 	 * Check for invalid temperatures.
756 	 */
757 	if (asmc_key_read(dev, key, buf, 2) < 0)
758 		return (-1);
759 
760 	return (buf[0]);
761 }
762 
763 static int
764 asmc_temp_sysctl(SYSCTL_HANDLER_ARGS)
765 {
766 	device_t dev = (device_t) arg1;
767 	struct asmc_softc *sc = device_get_softc(dev);
768 	int error, val;
769 
770 	val = asmc_temp_getvalue(dev, sc->sc_model->smc_temps[arg2]);
771 	error = sysctl_handle_int(oidp, &val, 0, req);
772 
773 	return (error);
774 }
775 
776 /*
777  * Sudden Motion Sensor functions.
778  */
779 static int
780 asmc_sms_read(device_t dev, const char *key, int16_t *val)
781 {
782 	uint8_t buf[2];
783 	int error;
784 
785 	/* no need to do locking here as asmc_key_read() already does it */
786 	switch (key[3]) {
787 	case 'X':
788 	case 'Y':
789 	case 'Z':
790 		error =	asmc_key_read(dev, key, buf, 2);
791 		break;
792 	default:
793 		device_printf(dev, "%s called with invalid argument %s\n",
794 			      __func__, key);
795 		error = 1;
796 		goto out;
797 	}
798 	*val = ((int16_t)buf[0] << 8) | buf[1];
799 out:
800 	return (error);
801 }
802 
803 static void
804 asmc_sms_calibrate(device_t dev)
805 {
806 	struct asmc_softc *sc = device_get_softc(dev);
807 
808 	asmc_sms_read(dev, ASMC_KEY_SMS_X, &sc->sms_rest_x);
809 	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &sc->sms_rest_y);
810 	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &sc->sms_rest_z);
811 }
812 
813 static int
814 asmc_sms_intrfast(void *arg)
815 {
816 	uint8_t type;
817 	device_t dev = (device_t) arg;
818 	struct asmc_softc *sc = device_get_softc(dev);
819 
820 	mtx_lock_spin(&sc->sc_mtx);
821 	type = inb(ASMC_INTPORT);
822 	mtx_unlock_spin(&sc->sc_mtx);
823 
824 	sc->sc_sms_intrtype = type;
825 	asmc_sms_printintr(dev, type);
826 
827 #ifdef INTR_FILTER
828 	return (FILTER_SCHEDULE_THREAD | FILTER_HANDLED);
829 #else
830 	taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task);
831 #endif
832 	return (FILTER_HANDLED);
833 }
834 
835 #ifdef INTR_FILTER
836 static void
837 asmc_sms_handler(void *arg)
838 {
839 	struct asmc_softc *sc = device_get_softc(arg);
840 
841 	asmc_sms_task(sc, 0);
842 }
843 #endif
844 
845 
846 static void
847 asmc_sms_printintr(device_t dev, uint8_t type)
848 {
849 
850 	switch (type) {
851 	case ASMC_SMS_INTFF:
852 		device_printf(dev, "WARNING: possible free fall!\n");
853 		break;
854 	case ASMC_SMS_INTHA:
855 		device_printf(dev, "WARNING: high acceleration detected!\n");
856 		break;
857 	case ASMC_SMS_INTSH:
858 		device_printf(dev, "WARNING: possible shock!\n");
859 		break;
860 	default:
861 		device_printf(dev, "%s unknown interrupt\n", __func__);
862 	}
863 }
864 
865 static void
866 asmc_sms_task(void *arg, int pending)
867 {
868 	struct asmc_softc *sc = (struct asmc_softc *)arg;
869 	char notify[16];
870 	int type;
871 
872 	switch (sc->sc_sms_intrtype) {
873 	case ASMC_SMS_INTFF:
874 		type = 2;
875 		break;
876 	case ASMC_SMS_INTHA:
877 		type = 1;
878 		break;
879 	case ASMC_SMS_INTSH:
880 		type = 0;
881 		break;
882 	default:
883 		type = 255;
884 	}
885 
886 	snprintf(notify, sizeof(notify), " notify=0x%x", type);
887 	devctl_notify("ISA", "asmc", "SMS", notify);
888 }
889 
890 static int
891 asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS)
892 {
893 	device_t dev = (device_t) arg1;
894 	int error;
895 	int16_t val;
896 	int32_t v;
897 
898 	asmc_sms_read(dev, ASMC_KEY_SMS_X, &val);
899 	v = (int32_t) val;
900 	error = sysctl_handle_int(oidp, &v, 0, req);
901 
902 	return (error);
903 }
904 
905 static int
906 asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS)
907 {
908 	device_t dev = (device_t) arg1;
909 	int error;
910 	int16_t val;
911 	int32_t v;
912 
913 	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val);
914 	v = (int32_t) val;
915 	error = sysctl_handle_int(oidp, &v, 0, req);
916 
917 	return (error);
918 }
919 
920 static int
921 asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS)
922 {
923 	device_t dev = (device_t) arg1;
924 	int error;
925 	int16_t val;
926 	int32_t v;
927 
928 	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val);
929 	v = (int32_t) val;
930 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
931 
932 	return (error);
933 }
934 
935 static int
936 asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS)
937 {
938 	device_t dev = (device_t) arg1;
939 	uint8_t buf[6];
940 	int error;
941 	unsigned int level;
942 	int32_t v;
943 
944 	asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, 6);
945 	v = buf[2];
946 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
947 	if (error == 0 && req->newptr != NULL) {
948 		level = *(unsigned int *)req->newptr;
949 		if (level > 255)
950 			return (EINVAL);
951 		buf[0] = level;
952 		buf[1] = 0x00;
953 		asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2);
954 	}
955 
956 	return (error);
957 }
958 
959 static int
960 asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS)
961 {
962 	device_t dev = (device_t) arg1;
963 	uint8_t buf[6];
964 	int error;
965 	unsigned int level;
966 	int32_t v;
967 
968 	asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, 6);
969 	v = buf[2];
970 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
971 	if (error == 0 && req->newptr != NULL) {
972 		level = *(unsigned int *)req->newptr;
973 		if (level > 255)
974 			return (EINVAL);
975 		buf[0] = level;
976 		buf[1] = 0x00;
977 		asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2);
978 	}
979 
980 	return (error);
981 }
982