asmc.c revision 177979
1173426Srpaulo/*-
2177972Srpaulo * Copyright (c) 2007, 2008 Rui Paulo <rpaulo@FreeBSD.org>
3173426Srpaulo * All rights reserved.
4173426Srpaulo *
5173426Srpaulo * Redistribution and use in source and binary forms, with or without
6173426Srpaulo * modification, are permitted provided that the following conditions
7173426Srpaulo * are met:
8173426Srpaulo * 1. Redistributions of source code must retain the above copyright
9173426Srpaulo *    notice, this list of conditions and the following disclaimer.
10173426Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
11173426Srpaulo *    notice, this list of conditions and the following disclaimer in the
12173426Srpaulo *    documentation and/or other materials provided with the distribution.
13173426Srpaulo *
14173426Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15173426Srpaulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16173426Srpaulo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17173426Srpaulo * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
18173426Srpaulo * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19173426Srpaulo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20173426Srpaulo * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21173426Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22173426Srpaulo * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23173426Srpaulo * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24173426Srpaulo * POSSIBILITY OF SUCH DAMAGE.
25173426Srpaulo *
26173426Srpaulo */
27173426Srpaulo
28173426Srpaulo/*
29173426Srpaulo * Driver for Apple's System Management Console (SMC).
30173426Srpaulo * SMC can be found on the MacBook, MacBook Pro and Mac Mini.
31173426Srpaulo *
32173426Srpaulo * Inspired by the Linux applesmc driver.
33173426Srpaulo */
34173426Srpaulo
35173426Srpaulo#include <sys/cdefs.h>
36173426Srpaulo__FBSDID("$FreeBSD: head/sys/dev/asmc/asmc.c 177979 2008-04-07 12:58:43Z rpaulo $");
37173426Srpaulo
38177941Sjhb#include "opt_intr_filter.h"
39177941Sjhb
40173426Srpaulo#include <sys/param.h>
41173426Srpaulo#include <sys/bus.h>
42173426Srpaulo#include <sys/conf.h>
43173426Srpaulo#include <sys/kernel.h>
44173426Srpaulo#include <sys/lock.h>
45173426Srpaulo#include <sys/malloc.h>
46173426Srpaulo#include <sys/module.h>
47173426Srpaulo#include <sys/mutex.h>
48173426Srpaulo#include <sys/sysctl.h>
49173426Srpaulo#include <sys/systm.h>
50173426Srpaulo#include <sys/taskqueue.h>
51173426Srpaulo#include <sys/rman.h>
52173426Srpaulo#include <machine/resource.h>
53177972Srpaulo#include <contrib/dev/acpica/acpi.h>
54177972Srpaulo#include <dev/acpica/acpivar.h>
55173426Srpaulo#include <dev/asmc/asmcvar.h>
56173426Srpaulo
57173426Srpaulo/*
58173426Srpaulo * Device interface.
59173426Srpaulo */
60173426Srpaulostatic int 	asmc_probe(device_t dev);
61173426Srpaulostatic int 	asmc_attach(device_t dev);
62173426Srpaulostatic int 	asmc_detach(device_t dev);
63173426Srpaulo
64173426Srpaulo/*
65173426Srpaulo * SMC functions.
66173426Srpaulo */
67173426Srpaulostatic int 	asmc_init(device_t dev);
68173426Srpaulostatic int 	asmc_wait(device_t dev, uint8_t val);
69173426Srpaulostatic int 	asmc_key_write(device_t dev, const char *key, uint8_t *buf,
70173426Srpaulo    uint8_t len);
71173426Srpaulostatic int 	asmc_key_read(device_t dev, const char *key, uint8_t *buf,
72173426Srpaulo    uint8_t);
73173426Srpaulostatic int 	asmc_fan_count(device_t dev);
74173426Srpaulostatic int 	asmc_fan_getvalue(device_t dev, const char *key, int fan);
75173426Srpaulostatic int 	asmc_temp_getvalue(device_t dev, const char *key);
76173426Srpaulostatic int 	asmc_sms_read(device_t, const char *key, int16_t *val);
77173426Srpaulostatic void 	asmc_sms_calibrate(device_t dev);
78173426Srpaulostatic int 	asmc_sms_intrfast(void *arg);
79173426Srpaulo#ifdef INTR_FILTER
80173426Srpaulostatic void 	asmc_sms_handler(void *arg);
81173426Srpaulo#endif
82173426Srpaulostatic void 	asmc_sms_printintr(device_t dev, uint8_t);
83173426Srpaulostatic void 	asmc_sms_task(void *arg, int pending);
84173426Srpaulo
85173426Srpaulo/*
86173426Srpaulo * Model functions.
87173426Srpaulo */
88173426Srpaulostatic int 	asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS);
89173426Srpaulostatic int 	asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS);
90173426Srpaulostatic int 	asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS);
91173426Srpaulostatic int 	asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS);
92173426Srpaulostatic int 	asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS);
93173426Srpaulostatic int 	asmc_temp_sysctl(SYSCTL_HANDLER_ARGS);
94173426Srpaulostatic int 	asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS);
95173426Srpaulostatic int 	asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS);
96173426Srpaulostatic int 	asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS);
97173426Srpaulostatic int 	asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS);
98173426Srpaulostatic int 	asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS);
99173426Srpaulo
100173426Srpaulostruct asmc_model {
101173426Srpaulo	const char 	 *smc_model;	/* smbios.system.product env var. */
102173426Srpaulo	const char 	 *smc_desc;	/* driver description */
103173426Srpaulo
104173426Srpaulo	/* Helper functions */
105173426Srpaulo	int (*smc_sms_x)(SYSCTL_HANDLER_ARGS);
106173426Srpaulo	int (*smc_sms_y)(SYSCTL_HANDLER_ARGS);
107173426Srpaulo	int (*smc_sms_z)(SYSCTL_HANDLER_ARGS);
108173426Srpaulo	int (*smc_fan_speed)(SYSCTL_HANDLER_ARGS);
109173426Srpaulo	int (*smc_fan_safespeed)(SYSCTL_HANDLER_ARGS);
110173426Srpaulo	int (*smc_fan_minspeed)(SYSCTL_HANDLER_ARGS);
111173426Srpaulo	int (*smc_fan_maxspeed)(SYSCTL_HANDLER_ARGS);
112173426Srpaulo	int (*smc_fan_targetspeed)(SYSCTL_HANDLER_ARGS);
113173426Srpaulo	int (*smc_light_left)(SYSCTL_HANDLER_ARGS);
114173426Srpaulo	int (*smc_light_right)(SYSCTL_HANDLER_ARGS);
115173426Srpaulo
116173426Srpaulo	const char 	*smc_temps[8];
117173426Srpaulo	const char 	*smc_tempnames[8];
118173426Srpaulo	const char 	*smc_tempdescs[8];
119173426Srpaulo};
120173426Srpaulo
121173426Srpaulostatic struct asmc_model *asmc_match(device_t dev);
122173426Srpaulo
123173426Srpaulo#define ASMC_SMS_FUNCS	asmc_mb_sysctl_sms_x, asmc_mb_sysctl_sms_y, \
124173426Srpaulo			asmc_mb_sysctl_sms_z
125173426Srpaulo
126173426Srpaulo#define ASMC_FAN_FUNCS	asmc_mb_sysctl_fanspeed, asmc_mb_sysctl_fansafespeed, \
127173426Srpaulo			asmc_mb_sysctl_fanminspeed, \
128173426Srpaulo			asmc_mb_sysctl_fanmaxspeed, \
129173426Srpaulo			asmc_mb_sysctl_fantargetspeed
130173426Srpaulo#define ASMC_LIGHT_FUNCS asmc_mbp_sysctl_light_left, \
131173426Srpaulo			 asmc_mbp_sysctl_light_right
132173426Srpaulo
133173426Srpaulostruct asmc_model asmc_models[] = {
134173426Srpaulo	{
135173426Srpaulo	  "MacBook1,1", "Apple SMC MacBook Core Duo",
136173426Srpaulo	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL,
137173426Srpaulo	  ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
138173426Srpaulo	},
139173426Srpaulo
140173426Srpaulo	{
141173426Srpaulo	  "MacBook2,1", "Apple SMC MacBook Core 2 Duo",
142173426Srpaulo	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL,
143173426Srpaulo	  ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
144173426Srpaulo	},
145173426Srpaulo
146173426Srpaulo	{
147173426Srpaulo	  "MacBookPro1,1", "Apple SMC MacBook Pro Core Duo (15-inch)",
148173426Srpaulo	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
149173426Srpaulo	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
150173426Srpaulo	},
151173426Srpaulo
152173426Srpaulo	{
153173426Srpaulo	  "MacBookPro1,2", "Apple SMC MacBook Pro Core Duo (17-inch)",
154173426Srpaulo	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
155173426Srpaulo	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
156173426Srpaulo	},
157173426Srpaulo
158173426Srpaulo	{
159173426Srpaulo	  "MacBookPro2,1", "Apple SMC MacBook Pro Core 2 Duo (17-inch)",
160173426Srpaulo	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
161173426Srpaulo	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
162173426Srpaulo	},
163173426Srpaulo
164173426Srpaulo	{
165173426Srpaulo	  "MacBookPro2,2", "Apple SMC MacBook Pro Core 2 Duo (15-inch)",
166173426Srpaulo	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
167173426Srpaulo	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
168173426Srpaulo	},
169173426Srpaulo
170173426Srpaulo	{
171173426Srpaulo	  "MacBookPro3,1", "Apple SMC MacBook Pro Core 2 Duo (15-inch LED)",
172173426Srpaulo	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
173173426Srpaulo	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
174173426Srpaulo	},
175173426Srpaulo
176173426Srpaulo	{
177173426Srpaulo	  "MacBookPro3,2", "Apple SMC MacBook Pro Core 2 Duo (17-inch HD)",
178173426Srpaulo	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
179173426Srpaulo	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
180173426Srpaulo	},
181173426Srpaulo
182173426Srpaulo	/* The Mac Mini has no SMS */
183173426Srpaulo	{
184173426Srpaulo	  "Macmini1,1", "Apple SMC Mac Mini",
185173426Srpaulo	  NULL, NULL, NULL,
186173851Srpaulo	  ASMC_FAN_FUNCS,
187173426Srpaulo	  NULL, NULL,
188173426Srpaulo	  ASMC_MM_TEMPS, ASMC_MM_TEMPNAMES, ASMC_MM_TEMPDESCS
189173426Srpaulo	},
190173426Srpaulo
191173426Srpaulo	{ NULL, NULL }
192173426Srpaulo};
193173426Srpaulo
194173426Srpaulo#undef ASMC_SMS_FUNCS
195173426Srpaulo#undef ASMC_FAN_FUNCS
196173426Srpaulo#undef ASMC_LIGHT_FUNCS
197173426Srpaulo
198173426Srpaulo/*
199173426Srpaulo * Driver methods.
200173426Srpaulo */
201173426Srpaulostatic device_method_t	asmc_methods[] = {
202173426Srpaulo	DEVMETHOD(device_probe,		asmc_probe),
203173426Srpaulo	DEVMETHOD(device_attach,	asmc_attach),
204173426Srpaulo	DEVMETHOD(device_detach,	asmc_detach),
205173426Srpaulo
206173426Srpaulo	{ 0, 0 }
207173426Srpaulo};
208173426Srpaulo
209173426Srpaulostatic driver_t	asmc_driver = {
210173426Srpaulo	"asmc",
211173426Srpaulo	asmc_methods,
212173426Srpaulo	sizeof(struct asmc_softc)
213173426Srpaulo};
214173426Srpaulo
215177972Srpaulo/*
216177972Srpaulo * Debugging
217177972Srpaulo */
218177972Srpaulo#define	_COMPONENT	ACPI_OEM
219177972SrpauloACPI_MODULE_NAME("ASMC")
220177972Srpaulo#ifdef DEBUG
221177972Srpaulo#define ASMC_DPRINTF(str)	device_printf(dev, str)
222177977Srpaulo#else
223177977Srpaulo#define ASMC_DPRINTF(str)
224177972Srpaulo#endif
225177972Srpaulo
226177972Srpaulostatic char *asmc_ids[] = { "APP0001", NULL };
227177972Srpaulo
228173426Srpaulostatic devclass_t asmc_devclass;
229173426Srpaulo
230177972SrpauloDRIVER_MODULE(asmc, acpi, asmc_driver, asmc_devclass, NULL, NULL);
231177972SrpauloMODULE_DEPEND(asmc, acpi, 1, 1, 1);
232173426Srpaulo
233173426Srpaulostatic struct asmc_model *
234173426Srpauloasmc_match(device_t dev)
235173426Srpaulo{
236173426Srpaulo	int i;
237173426Srpaulo	char *model;
238173426Srpaulo
239173426Srpaulo	model = getenv("smbios.system.product");
240173426Srpaulo	for (i = 0; asmc_models[i].smc_model; i++) {
241173426Srpaulo		if (!strncmp(model, asmc_models[i].smc_model, strlen(model))) {
242173426Srpaulo			freeenv(model);
243173426Srpaulo			return (&asmc_models[i]);
244173426Srpaulo		}
245173426Srpaulo	}
246173426Srpaulo	freeenv(model);
247173426Srpaulo
248173426Srpaulo	return (NULL);
249173426Srpaulo}
250173426Srpaulo
251173426Srpaulostatic int
252173426Srpauloasmc_probe(device_t dev)
253173426Srpaulo{
254173426Srpaulo	struct asmc_model *model;
255173426Srpaulo
256177972Srpaulo	if (acpi_disabled("asmc"))
257173426Srpaulo		return (ENXIO);
258177972Srpaulo	if (ACPI_ID_PROBE(device_get_parent(dev), dev, asmc_ids) == NULL)
259177972Srpaulo		return (ENXIO);
260177972Srpaulo
261173426Srpaulo	model = asmc_match(dev);
262177972Srpaulo	if (!model) {
263177972Srpaulo		device_printf(dev, "model not recognized\n");
264173426Srpaulo		return (ENXIO);
265177972Srpaulo	}
266173426Srpaulo	device_set_desc(dev, model->smc_desc);
267173426Srpaulo
268173426Srpaulo	return (BUS_PROBE_DEFAULT);
269173426Srpaulo}
270173426Srpaulo
271173426Srpaulostatic int
272173426Srpauloasmc_attach(device_t dev)
273173426Srpaulo{
274173426Srpaulo	int i, j;
275173426Srpaulo	int ret;
276173426Srpaulo	char name[2];
277173426Srpaulo	struct asmc_softc *sc = device_get_softc(dev);
278173426Srpaulo	struct sysctl_ctx_list *sysctlctx;
279173426Srpaulo	struct sysctl_oid *sysctlnode;
280173426Srpaulo	struct asmc_model *model;
281173426Srpaulo
282177972Srpaulo	sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
283177972Srpaulo	    &sc->sc_rid_port, RF_ACTIVE);
284177972Srpaulo	if (sc->sc_ioport == NULL) {
285177972Srpaulo		device_printf(dev, "unable to allocate IO port\n");
286177972Srpaulo		return (ENOMEM);
287177972Srpaulo	}
288177972Srpaulo
289173426Srpaulo	sysctlctx  = device_get_sysctl_ctx(dev);
290173426Srpaulo	sysctlnode = device_get_sysctl_tree(dev);
291173426Srpaulo
292173426Srpaulo	model = asmc_match(dev);
293173426Srpaulo
294173426Srpaulo	mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN);
295173426Srpaulo
296173426Srpaulo	sc->sc_model = model;
297173426Srpaulo	asmc_init(dev);
298173426Srpaulo
299173426Srpaulo	/*
300173426Srpaulo	 * dev.asmc.n.fan.* tree.
301173426Srpaulo	 */
302173426Srpaulo	sc->sc_fan_tree[0] = SYSCTL_ADD_NODE(sysctlctx,
303173426Srpaulo	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "fan",
304173426Srpaulo	    CTLFLAG_RD, 0, "Fan Root Tree");
305173426Srpaulo
306173426Srpaulo	for (i = 1; i <= sc->sc_nfan; i++) {
307173426Srpaulo		j = i - 1;
308173426Srpaulo		name[0] = '0' + j;
309173426Srpaulo		name[1] = 0;
310173426Srpaulo		sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx,
311173426Srpaulo		    SYSCTL_CHILDREN(sc->sc_fan_tree[0]),
312173426Srpaulo		    OID_AUTO, name, CTLFLAG_RD, 0,
313173426Srpaulo		    "Fan Subtree");
314173426Srpaulo
315173426Srpaulo		SYSCTL_ADD_PROC(sysctlctx,
316173426Srpaulo		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
317173426Srpaulo		    OID_AUTO, "speed", CTLTYPE_INT | CTLFLAG_RD,
318173426Srpaulo		    dev, j, model->smc_fan_speed, "I",
319173426Srpaulo		    "Fan speed in RPM");
320173426Srpaulo
321173426Srpaulo		SYSCTL_ADD_PROC(sysctlctx,
322173426Srpaulo		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
323173426Srpaulo		    OID_AUTO, "safespeed",
324173426Srpaulo		    CTLTYPE_INT | CTLFLAG_RD,
325173426Srpaulo		    dev, j, model->smc_fan_safespeed, "I",
326173426Srpaulo		    "Fan safe speed in RPM");
327173426Srpaulo
328173426Srpaulo		SYSCTL_ADD_PROC(sysctlctx,
329173426Srpaulo		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
330173426Srpaulo		    OID_AUTO, "minspeed",
331173426Srpaulo		    CTLTYPE_INT | CTLFLAG_RD,
332173426Srpaulo		    dev, j, model->smc_fan_minspeed, "I",
333173426Srpaulo		    "Fan minimum speed in RPM");
334173426Srpaulo
335173426Srpaulo		SYSCTL_ADD_PROC(sysctlctx,
336173426Srpaulo		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
337173426Srpaulo		    OID_AUTO, "maxspeed",
338173426Srpaulo		    CTLTYPE_INT | CTLFLAG_RD,
339173426Srpaulo		    dev, j, model->smc_fan_maxspeed, "I",
340173426Srpaulo		    "Fan maximum speed in RPM");
341173426Srpaulo
342173426Srpaulo		SYSCTL_ADD_PROC(sysctlctx,
343173426Srpaulo		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
344173426Srpaulo		    OID_AUTO, "targetspeed",
345173426Srpaulo		    CTLTYPE_INT | CTLFLAG_RD,
346173426Srpaulo		    dev, j, model->smc_fan_targetspeed, "I",
347173426Srpaulo		    "Fan target speed in RPM");
348173426Srpaulo	}
349173426Srpaulo
350173426Srpaulo	/*
351173426Srpaulo	 * dev.asmc.n.temp tree.
352173426Srpaulo	 */
353173426Srpaulo	sc->sc_temp_tree = SYSCTL_ADD_NODE(sysctlctx,
354173426Srpaulo	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp",
355173426Srpaulo	    CTLFLAG_RD, 0, "Temperature sensors");
356173426Srpaulo
357173426Srpaulo	for (i = 0; model->smc_temps[i]; i++) {
358173426Srpaulo		SYSCTL_ADD_PROC(sysctlctx,
359173426Srpaulo		    SYSCTL_CHILDREN(sc->sc_temp_tree),
360173426Srpaulo		    OID_AUTO, model->smc_tempnames[i],
361173426Srpaulo		    CTLTYPE_INT | CTLFLAG_RD,
362173426Srpaulo		    dev, i, asmc_temp_sysctl, "I",
363173426Srpaulo		    model->smc_tempdescs[i]);
364173426Srpaulo	}
365173426Srpaulo
366173426Srpaulo	if (model->smc_sms_x == NULL)
367173426Srpaulo		goto nosms;
368173426Srpaulo
369173426Srpaulo	/*
370173426Srpaulo	 * dev.asmc.n.sms tree.
371173426Srpaulo	 */
372173426Srpaulo	sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx,
373173426Srpaulo	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "sms",
374173426Srpaulo	    CTLFLAG_RD, 0, "Sudden Motion Sensor");
375173426Srpaulo
376173426Srpaulo	SYSCTL_ADD_PROC(sysctlctx,
377173426Srpaulo	    SYSCTL_CHILDREN(sc->sc_sms_tree),
378173426Srpaulo	    OID_AUTO, "x", CTLTYPE_INT | CTLFLAG_RD,
379173426Srpaulo	    dev, 0, model->smc_sms_x, "I",
380173426Srpaulo	    "Sudden Motion Sensor X value");
381173426Srpaulo
382173426Srpaulo	SYSCTL_ADD_PROC(sysctlctx,
383173426Srpaulo	    SYSCTL_CHILDREN(sc->sc_sms_tree),
384173426Srpaulo	    OID_AUTO, "y", CTLTYPE_INT | CTLFLAG_RD,
385173426Srpaulo	    dev, 0, model->smc_sms_y, "I",
386173426Srpaulo	    "Sudden Motion Sensor Y value");
387173426Srpaulo
388173426Srpaulo	SYSCTL_ADD_PROC(sysctlctx,
389173426Srpaulo	    SYSCTL_CHILDREN(sc->sc_sms_tree),
390173426Srpaulo	    OID_AUTO, "z", CTLTYPE_INT | CTLFLAG_RD,
391173426Srpaulo	    dev, 0, model->smc_sms_z, "I",
392173426Srpaulo	    "Sudden Motion Sensor Z value");
393173426Srpaulo
394173426Srpaulo	/*
395173426Srpaulo	 * dev.asmc.n.light
396173426Srpaulo	 */
397173426Srpaulo	if (model->smc_light_left) {
398173426Srpaulo		sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx,
399173426Srpaulo		    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light",
400173426Srpaulo		    CTLFLAG_RD, 0, "Keyboard backlight sensors");
401173426Srpaulo
402173426Srpaulo		SYSCTL_ADD_PROC(sysctlctx,
403173426Srpaulo		    SYSCTL_CHILDREN(sc->sc_light_tree),
404173426Srpaulo		    OID_AUTO, "left", CTLTYPE_INT | CTLFLAG_RW,
405173426Srpaulo		    dev, 0, model->smc_light_left, "I",
406173426Srpaulo		    "Keyboard backlight left sensor");
407173426Srpaulo
408173426Srpaulo		SYSCTL_ADD_PROC(sysctlctx,
409173426Srpaulo		    SYSCTL_CHILDREN(sc->sc_light_tree),
410173426Srpaulo		    OID_AUTO, "right", CTLTYPE_INT | CTLFLAG_RW,
411173426Srpaulo		    dev, 0, model->smc_light_right, "I",
412173426Srpaulo		    "Keyboard backlight right sensor");
413173426Srpaulo	}
414173426Srpaulo
415173426Srpaulo	/*
416173426Srpaulo	 * Need a taskqueue to send devctl_notify() events
417173426Srpaulo	 * when the SMS interrupt us.
418173426Srpaulo	 *
419173426Srpaulo	 * PI_REALTIME is used due to the sensitivity of the
420173426Srpaulo	 * interrupt. An interrupt from the SMS means that the
421173426Srpaulo	 * disk heads should be turned off as quickly as possible.
422173426Srpaulo	 *
423173426Srpaulo	 * We only need to do this for the non INTR_FILTER case.
424173426Srpaulo	 */
425173426Srpaulo	sc->sc_sms_tq = NULL;
426173426Srpaulo#ifndef INTR_FILTER
427173426Srpaulo	TASK_INIT(&sc->sc_sms_task, 0, asmc_sms_task, sc);
428173426Srpaulo	sc->sc_sms_tq = taskqueue_create_fast("asmc_taskq", M_WAITOK,
429173426Srpaulo	    taskqueue_thread_enqueue, &sc->sc_sms_tq);
430173426Srpaulo	taskqueue_start_threads(&sc->sc_sms_tq, 1, PI_REALTIME, "%s sms taskq",
431173426Srpaulo	    device_get_nameunit(dev));
432173426Srpaulo#endif
433173426Srpaulo	/*
434173426Srpaulo	 * Allocate an IRQ for the SMS.
435173426Srpaulo	 */
436177972Srpaulo	sc->sc_rid_irq = 0;
437177972Srpaulo	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
438177972Srpaulo	    &sc->sc_rid_irq, RF_ACTIVE);
439177972Srpaulo	if (sc->sc_irq == NULL) {
440173426Srpaulo		device_printf(dev, "unable to allocate IRQ resource\n");
441173426Srpaulo		ret = ENXIO;
442173426Srpaulo		goto err2;
443173426Srpaulo	}
444173426Srpaulo
445177972Srpaulo	ret = bus_setup_intr(dev, sc->sc_irq,
446173426Srpaulo	          INTR_TYPE_MISC | INTR_MPSAFE,
447173426Srpaulo#ifdef INTR_FILTER
448173426Srpaulo	    asmc_sms_intrfast, asmc_sms_handler,
449173426Srpaulo#else
450173426Srpaulo	    asmc_sms_intrfast, NULL,
451173426Srpaulo#endif
452173426Srpaulo	    dev, &sc->sc_cookie);
453173426Srpaulo
454173426Srpaulo	if (ret) {
455173426Srpaulo		device_printf(dev, "unable to setup SMS IRQ\n");
456173426Srpaulo		goto err1;
457173426Srpaulo	}
458173426Srpaulonosms:
459173426Srpaulo	return (0);
460173426Srpauloerr1:
461177972Srpaulo	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, sc->sc_irq);
462173426Srpauloerr2:
463177972Srpaulo	bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
464177972Srpaulo	    sc->sc_ioport);
465173426Srpaulo	mtx_destroy(&sc->sc_mtx);
466173426Srpaulo	if (sc->sc_sms_tq)
467173426Srpaulo		taskqueue_free(sc->sc_sms_tq);
468173426Srpaulo
469173426Srpaulo	return (ret);
470173426Srpaulo}
471173426Srpaulo
472173426Srpaulostatic int
473173426Srpauloasmc_detach(device_t dev)
474173426Srpaulo{
475173426Srpaulo	struct asmc_softc *sc = device_get_softc(dev);
476173426Srpaulo
477173426Srpaulo	if (sc->sc_sms_tq) {
478173426Srpaulo		taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task);
479173426Srpaulo		taskqueue_free(sc->sc_sms_tq);
480173426Srpaulo	}
481173426Srpaulo	if (sc->sc_cookie)
482177972Srpaulo		bus_teardown_intr(dev, sc->sc_irq, sc->sc_cookie);
483177972Srpaulo	if (sc->sc_irq)
484177972Srpaulo		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq,
485177972Srpaulo		    sc->sc_irq);
486177972Srpaulo	if (sc->sc_ioport)
487177972Srpaulo		bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
488177972Srpaulo		    sc->sc_ioport);
489173426Srpaulo	mtx_destroy(&sc->sc_mtx);
490173426Srpaulo
491173426Srpaulo	return (0);
492173426Srpaulo}
493173426Srpaulo
494173426Srpaulostatic int
495173426Srpauloasmc_init(device_t dev)
496173426Srpaulo{
497173426Srpaulo	struct asmc_softc *sc = device_get_softc(dev);
498173426Srpaulo	int i, error = 1;
499173426Srpaulo	uint8_t buf[4];
500173426Srpaulo
501173426Srpaulo	if (sc->sc_model->smc_sms_x == NULL)
502173426Srpaulo		goto nosms;
503173426Srpaulo
504173426Srpaulo	/*
505173426Srpaulo	 * We are ready to recieve interrupts from the SMS.
506173426Srpaulo	 */
507173426Srpaulo	buf[0] = 0x01;
508177972Srpaulo	ASMC_DPRINTF(("intok key\n"));
509173426Srpaulo	asmc_key_write(dev, ASMC_KEY_INTOK, buf, 1);
510173426Srpaulo	DELAY(50);
511173426Srpaulo
512173426Srpaulo	/*
513173426Srpaulo	 * Initiate the polling intervals.
514173426Srpaulo	 */
515173426Srpaulo	buf[0] = 20; /* msecs */
516177972Srpaulo	ASMC_DPRINTF(("low int key\n"));
517173426Srpaulo	asmc_key_write(dev, ASMC_KEY_SMS_LOW_INT, buf, 1);
518173426Srpaulo	DELAY(200);
519173426Srpaulo
520173426Srpaulo	buf[0] = 20; /* msecs */
521177972Srpaulo	ASMC_DPRINTF(("high int key\n"));
522173426Srpaulo	asmc_key_write(dev, ASMC_KEY_SMS_HIGH_INT, buf, 1);
523173426Srpaulo	DELAY(200);
524173426Srpaulo
525173426Srpaulo	buf[0] = 0x00;
526173426Srpaulo	buf[1] = 0x60;
527177972Srpaulo	ASMC_DPRINTF(("sms low key\n"));
528173426Srpaulo	asmc_key_write(dev, ASMC_KEY_SMS_LOW, buf, 2);
529173426Srpaulo	DELAY(200);
530173426Srpaulo
531173426Srpaulo	buf[0] = 0x01;
532173426Srpaulo	buf[1] = 0xc0;
533177972Srpaulo	ASMC_DPRINTF(("sms high key\n"));
534173426Srpaulo	asmc_key_write(dev, ASMC_KEY_SMS_HIGH, buf, 2);
535173426Srpaulo	DELAY(200);
536173426Srpaulo
537173426Srpaulo	/*
538173426Srpaulo	 * I'm not sure what this key does, but it seems to be
539173426Srpaulo	 * required.
540173426Srpaulo	 */
541173426Srpaulo	buf[0] = 0x01;
542177972Srpaulo	ASMC_DPRINTF(("sms flag key\n"));
543173426Srpaulo	asmc_key_write(dev, ASMC_KEY_SMS_FLAG, buf, 1);
544177979Srpaulo	DELAY(100);
545173426Srpaulo
546173426Srpaulo	/*
547173426Srpaulo	 * Wait up to 5 seconds for SMS initialization.
548173426Srpaulo	 */
549173426Srpaulo	for (i = 0; i < 10000; i++) {
550173426Srpaulo		if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 &&
551173426Srpaulo		    (buf[0] != 0x00 || buf[1] != 0x00)) {
552173426Srpaulo			error = 0;
553177977Srpaulo			goto out;
554173426Srpaulo		}
555173426Srpaulo		buf[0] = ASMC_SMS_INIT1;
556173426Srpaulo		buf[1] = ASMC_SMS_INIT2;
557177972Srpaulo		ASMC_DPRINTF(("sms key\n"));
558173426Srpaulo		asmc_key_write(dev, ASMC_KEY_SMS, buf, 2);
559173426Srpaulo		DELAY(50);
560173426Srpaulo	}
561177977Srpaulo	device_printf(dev, "WARNING: Sudden Motion Sensor not initialized!\n");
562173426Srpaulo
563177977Srpauloout:
564173426Srpaulo	asmc_sms_calibrate(dev);
565173426Srpaulonosms:
566173426Srpaulo	sc->sc_nfan = asmc_fan_count(dev);
567173426Srpaulo	if (sc->sc_nfan > ASMC_MAXFANS) {
568173426Srpaulo		device_printf(dev, "more than %d fans were detected. Please "
569173426Srpaulo		    "report this.\n", ASMC_MAXFANS);
570173426Srpaulo		sc->sc_nfan = ASMC_MAXFANS;
571173426Srpaulo	}
572173426Srpaulo
573173426Srpaulo	if (bootverbose) {
574173426Srpaulo		/*
575173426Srpaulo		 * XXX: The number of keys is a 32 bit buffer, but
576173426Srpaulo		 * right now Apple only uses the last 8 bit.
577173426Srpaulo		 */
578173426Srpaulo		asmc_key_read(dev, ASMC_NKEYS, buf, 4);
579173426Srpaulo		device_printf(dev, "number of keys: %d\n", buf[3]);
580173426Srpaulo	}
581173426Srpaulo
582173426Srpaulo	return (error);
583173426Srpaulo}
584173426Srpaulo
585173426Srpaulo/*
586173426Srpaulo * We need to make sure that the SMC acks the byte sent.
587173426Srpaulo * Just wait up to 100 ms.
588173426Srpaulo */
589173426Srpaulostatic int
590173426Srpauloasmc_wait(device_t dev, uint8_t val)
591173426Srpaulo{
592177972Srpaulo	struct asmc_softc *sc = device_get_softc(dev);
593173426Srpaulo	u_int i;
594173426Srpaulo
595173426Srpaulo	val = val & ASMC_STATUS_MASK;
596173426Srpaulo
597173426Srpaulo	for (i = 0; i < 1000; i++) {
598177972Srpaulo		if ((ASMC_CMDPORT_READ(sc) & ASMC_STATUS_MASK) == val)
599173426Srpaulo			return (0);
600173426Srpaulo		DELAY(10);
601173426Srpaulo	}
602173426Srpaulo
603173426Srpaulo	device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, val,
604177972Srpaulo	    ASMC_CMDPORT_READ(sc));
605173426Srpaulo
606173426Srpaulo	return (1);
607173426Srpaulo}
608173426Srpaulo
609173426Srpaulostatic int
610173426Srpauloasmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len)
611173426Srpaulo{
612173426Srpaulo	int i, error = 1;
613173426Srpaulo	struct asmc_softc *sc = device_get_softc(dev);
614173426Srpaulo
615173426Srpaulo	mtx_lock_spin(&sc->sc_mtx);
616173426Srpaulo
617177972Srpaulo	ASMC_CMDPORT_WRITE(sc, ASMC_CMDREAD);
618173426Srpaulo	if (asmc_wait(dev, 0x0c))
619173426Srpaulo		goto out;
620173426Srpaulo
621173426Srpaulo	for (i = 0; i < 4; i++) {
622177972Srpaulo		ASMC_DATAPORT_WRITE(sc, key[i]);
623173426Srpaulo		if (asmc_wait(dev, 0x04))
624173426Srpaulo			goto out;
625173426Srpaulo	}
626173426Srpaulo
627177972Srpaulo	ASMC_DATAPORT_WRITE(sc, len);
628173426Srpaulo
629173426Srpaulo	for (i = 0; i < len; i++) {
630173426Srpaulo		if (asmc_wait(dev, 0x05))
631173426Srpaulo			goto out;
632177972Srpaulo		buf[i] = ASMC_DATAPORT_READ(sc);
633173426Srpaulo	}
634173426Srpaulo
635173426Srpaulo	error = 0;
636173426Srpauloout:
637173426Srpaulo	mtx_unlock_spin(&sc->sc_mtx);
638173426Srpaulo
639173426Srpaulo	return (error);
640173426Srpaulo}
641173426Srpaulo
642173426Srpaulostatic int
643173426Srpauloasmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len)
644173426Srpaulo{
645173426Srpaulo	int i, error = -1;
646173426Srpaulo	struct asmc_softc *sc = device_get_softc(dev);
647173426Srpaulo
648173426Srpaulo	mtx_lock_spin(&sc->sc_mtx);
649173426Srpaulo
650177972Srpaulo	ASMC_DPRINTF(("cmd port: cmd write\n"));
651177972Srpaulo	ASMC_CMDPORT_WRITE(sc, ASMC_CMDWRITE);
652173426Srpaulo	if (asmc_wait(dev, 0x0c))
653173426Srpaulo		goto out;
654173426Srpaulo
655177972Srpaulo	ASMC_DPRINTF(("data port: key\n"));
656173426Srpaulo	for (i = 0; i < 4; i++) {
657177972Srpaulo		ASMC_DATAPORT_WRITE(sc, key[i]);
658173426Srpaulo		if (asmc_wait(dev, 0x04))
659173426Srpaulo			goto out;
660173426Srpaulo	}
661177972Srpaulo	ASMC_DPRINTF(("data port: length\n"));
662177972Srpaulo	ASMC_DATAPORT_WRITE(sc, len);
663173426Srpaulo
664177972Srpaulo	ASMC_DPRINTF(("data port: buffer\n"));
665173426Srpaulo	for (i = 0; i < len; i++) {
666173426Srpaulo		if (asmc_wait(dev, 0x04))
667173426Srpaulo			goto out;
668177972Srpaulo		ASMC_DATAPORT_WRITE(sc, buf[i]);
669173426Srpaulo	}
670173426Srpaulo
671173426Srpaulo	error = 0;
672173426Srpauloout:
673173426Srpaulo	mtx_unlock_spin(&sc->sc_mtx);
674173426Srpaulo
675173426Srpaulo	return (error);
676173426Srpaulo
677173426Srpaulo}
678173426Srpaulo
679173426Srpaulo/*
680173426Srpaulo * Fan control functions.
681173426Srpaulo */
682173426Srpaulostatic int
683173426Srpauloasmc_fan_count(device_t dev)
684173426Srpaulo{
685173426Srpaulo	uint8_t buf[1];
686173426Srpaulo
687173426Srpaulo	if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, 1) < 0)
688173426Srpaulo		return (-1);
689173426Srpaulo
690173426Srpaulo	return (buf[0]);
691173426Srpaulo}
692173426Srpaulo
693173426Srpaulostatic int
694173426Srpauloasmc_fan_getvalue(device_t dev, const char *key, int fan)
695173426Srpaulo{
696173426Srpaulo	int speed;
697173426Srpaulo	uint8_t buf[2];
698173426Srpaulo	char fankey[5];
699173426Srpaulo
700173426Srpaulo	snprintf(fankey, sizeof(fankey), key, fan);
701173426Srpaulo	if (asmc_key_read(dev, fankey, buf, 2) < 0)
702173426Srpaulo		return (-1);
703173426Srpaulo	speed = (buf[0] << 6) | (buf[1] >> 2);
704173426Srpaulo
705173426Srpaulo	return (speed);
706173426Srpaulo}
707173426Srpaulo
708173426Srpaulostatic int
709173426Srpauloasmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS)
710173426Srpaulo{
711173426Srpaulo	device_t dev = (device_t) arg1;
712173426Srpaulo	int fan = arg2;
713173426Srpaulo	int error;
714173426Srpaulo	int32_t v;
715173426Srpaulo
716173426Srpaulo	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSPEED, fan);
717173426Srpaulo	error = sysctl_handle_int(oidp, &v, 0, req);
718173426Srpaulo
719173426Srpaulo	return (error);
720173426Srpaulo}
721173426Srpaulo
722173426Srpaulostatic int
723173426Srpauloasmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS)
724173426Srpaulo{
725173426Srpaulo	device_t dev = (device_t) arg1;
726173426Srpaulo	int fan = arg2;
727173426Srpaulo	int error;
728173426Srpaulo	int32_t v;
729173426Srpaulo
730173426Srpaulo	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSAFESPEED, fan);
731173426Srpaulo	error = sysctl_handle_int(oidp, &v, 0, req);
732173426Srpaulo
733173426Srpaulo	return (error);
734173426Srpaulo}
735173426Srpaulo
736173426Srpaulo
737173426Srpaulostatic int
738173426Srpauloasmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS)
739173426Srpaulo{
740173426Srpaulo	device_t dev = (device_t) arg1;
741173426Srpaulo	int fan = arg2;
742173426Srpaulo	int error;
743173426Srpaulo	int32_t v;
744173426Srpaulo
745173426Srpaulo	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMINSPEED, fan);
746173426Srpaulo	error = sysctl_handle_int(oidp, &v, 0, req);
747173426Srpaulo
748173426Srpaulo	return (error);
749173426Srpaulo}
750173426Srpaulo
751173426Srpaulostatic int
752173426Srpauloasmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS)
753173426Srpaulo{
754173426Srpaulo	device_t dev = (device_t) arg1;
755173426Srpaulo	int fan = arg2;
756173426Srpaulo	int error;
757173426Srpaulo	int32_t v;
758173426Srpaulo
759173426Srpaulo	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMAXSPEED, fan);
760173426Srpaulo	error = sysctl_handle_int(oidp, &v, 0, req);
761173426Srpaulo
762173426Srpaulo	return (error);
763173426Srpaulo}
764173426Srpaulo
765173426Srpaulostatic int
766173426Srpauloasmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS)
767173426Srpaulo{
768173426Srpaulo	device_t dev = (device_t) arg1;
769173426Srpaulo	int fan = arg2;
770173426Srpaulo	int error;
771173426Srpaulo	int32_t v;
772173426Srpaulo
773173426Srpaulo	v = asmc_fan_getvalue(dev, ASMC_KEY_FANTARGETSPEED, fan);
774173426Srpaulo	error = sysctl_handle_int(oidp, &v, 0, req);
775173426Srpaulo
776173426Srpaulo	return (error);
777173426Srpaulo}
778173426Srpaulo
779173426Srpaulo/*
780173426Srpaulo * Temperature functions.
781173426Srpaulo */
782173426Srpaulostatic int
783173426Srpauloasmc_temp_getvalue(device_t dev, const char *key)
784173426Srpaulo{
785173426Srpaulo	uint8_t buf[2];
786173426Srpaulo
787173426Srpaulo	/*
788173426Srpaulo	 * Check for invalid temperatures.
789173426Srpaulo	 */
790173426Srpaulo	if (asmc_key_read(dev, key, buf, 2) < 0)
791173426Srpaulo		return (-1);
792173426Srpaulo
793173426Srpaulo	return (buf[0]);
794173426Srpaulo}
795173426Srpaulo
796173426Srpaulostatic int
797173426Srpauloasmc_temp_sysctl(SYSCTL_HANDLER_ARGS)
798173426Srpaulo{
799173426Srpaulo	device_t dev = (device_t) arg1;
800173426Srpaulo	struct asmc_softc *sc = device_get_softc(dev);
801173426Srpaulo	int error, val;
802173426Srpaulo
803173426Srpaulo	val = asmc_temp_getvalue(dev, sc->sc_model->smc_temps[arg2]);
804173426Srpaulo	error = sysctl_handle_int(oidp, &val, 0, req);
805173426Srpaulo
806173426Srpaulo	return (error);
807173426Srpaulo}
808173426Srpaulo
809173426Srpaulo/*
810173426Srpaulo * Sudden Motion Sensor functions.
811173426Srpaulo */
812173426Srpaulostatic int
813173426Srpauloasmc_sms_read(device_t dev, const char *key, int16_t *val)
814173426Srpaulo{
815173426Srpaulo	uint8_t buf[2];
816173426Srpaulo	int error;
817173426Srpaulo
818173426Srpaulo	/* no need to do locking here as asmc_key_read() already does it */
819173426Srpaulo	switch (key[3]) {
820173426Srpaulo	case 'X':
821173426Srpaulo	case 'Y':
822173426Srpaulo	case 'Z':
823173426Srpaulo		error =	asmc_key_read(dev, key, buf, 2);
824173426Srpaulo		break;
825173426Srpaulo	default:
826173426Srpaulo		device_printf(dev, "%s called with invalid argument %s\n",
827173426Srpaulo			      __func__, key);
828173426Srpaulo		error = 1;
829173426Srpaulo		goto out;
830173426Srpaulo	}
831173426Srpaulo	*val = ((int16_t)buf[0] << 8) | buf[1];
832173426Srpauloout:
833173426Srpaulo	return (error);
834173426Srpaulo}
835173426Srpaulo
836173426Srpaulostatic void
837173426Srpauloasmc_sms_calibrate(device_t dev)
838173426Srpaulo{
839173426Srpaulo	struct asmc_softc *sc = device_get_softc(dev);
840173426Srpaulo
841173426Srpaulo	asmc_sms_read(dev, ASMC_KEY_SMS_X, &sc->sms_rest_x);
842173426Srpaulo	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &sc->sms_rest_y);
843173426Srpaulo	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &sc->sms_rest_z);
844173426Srpaulo}
845173426Srpaulo
846173426Srpaulostatic int
847173426Srpauloasmc_sms_intrfast(void *arg)
848173426Srpaulo{
849173426Srpaulo	uint8_t type;
850173426Srpaulo	device_t dev = (device_t) arg;
851173426Srpaulo	struct asmc_softc *sc = device_get_softc(dev);
852173426Srpaulo
853173426Srpaulo	mtx_lock_spin(&sc->sc_mtx);
854177972Srpaulo	type = ASMC_INTPORT_READ(sc);
855173426Srpaulo	mtx_unlock_spin(&sc->sc_mtx);
856173426Srpaulo
857173426Srpaulo	sc->sc_sms_intrtype = type;
858173426Srpaulo	asmc_sms_printintr(dev, type);
859173426Srpaulo
860173426Srpaulo#ifdef INTR_FILTER
861173426Srpaulo	return (FILTER_SCHEDULE_THREAD | FILTER_HANDLED);
862173426Srpaulo#else
863173426Srpaulo	taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task);
864173426Srpaulo#endif
865173426Srpaulo	return (FILTER_HANDLED);
866173426Srpaulo}
867173426Srpaulo
868173426Srpaulo#ifdef INTR_FILTER
869173426Srpaulostatic void
870173426Srpauloasmc_sms_handler(void *arg)
871173426Srpaulo{
872173426Srpaulo	struct asmc_softc *sc = device_get_softc(arg);
873173426Srpaulo
874173426Srpaulo	asmc_sms_task(sc, 0);
875173426Srpaulo}
876173426Srpaulo#endif
877173426Srpaulo
878173426Srpaulo
879173426Srpaulostatic void
880173426Srpauloasmc_sms_printintr(device_t dev, uint8_t type)
881173426Srpaulo{
882173426Srpaulo
883173426Srpaulo	switch (type) {
884173426Srpaulo	case ASMC_SMS_INTFF:
885173426Srpaulo		device_printf(dev, "WARNING: possible free fall!\n");
886173426Srpaulo		break;
887173426Srpaulo	case ASMC_SMS_INTHA:
888173426Srpaulo		device_printf(dev, "WARNING: high acceleration detected!\n");
889173426Srpaulo		break;
890173426Srpaulo	case ASMC_SMS_INTSH:
891173426Srpaulo		device_printf(dev, "WARNING: possible shock!\n");
892173426Srpaulo		break;
893173426Srpaulo	default:
894173426Srpaulo		device_printf(dev, "%s unknown interrupt\n", __func__);
895173426Srpaulo	}
896173426Srpaulo}
897173426Srpaulo
898173426Srpaulostatic void
899173426Srpauloasmc_sms_task(void *arg, int pending)
900173426Srpaulo{
901173426Srpaulo	struct asmc_softc *sc = (struct asmc_softc *)arg;
902173426Srpaulo	char notify[16];
903173426Srpaulo	int type;
904173426Srpaulo
905173426Srpaulo	switch (sc->sc_sms_intrtype) {
906173426Srpaulo	case ASMC_SMS_INTFF:
907173426Srpaulo		type = 2;
908173426Srpaulo		break;
909173426Srpaulo	case ASMC_SMS_INTHA:
910173426Srpaulo		type = 1;
911173426Srpaulo		break;
912173426Srpaulo	case ASMC_SMS_INTSH:
913173426Srpaulo		type = 0;
914173426Srpaulo		break;
915173426Srpaulo	default:
916173426Srpaulo		type = 255;
917173426Srpaulo	}
918173426Srpaulo
919173426Srpaulo	snprintf(notify, sizeof(notify), " notify=0x%x", type);
920177972Srpaulo	devctl_notify("ACPI", "asmc", "SMS", notify);
921173426Srpaulo}
922173426Srpaulo
923173426Srpaulostatic int
924173426Srpauloasmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS)
925173426Srpaulo{
926173426Srpaulo	device_t dev = (device_t) arg1;
927173426Srpaulo	int error;
928173426Srpaulo	int16_t val;
929173426Srpaulo	int32_t v;
930173426Srpaulo
931173426Srpaulo	asmc_sms_read(dev, ASMC_KEY_SMS_X, &val);
932173426Srpaulo	v = (int32_t) val;
933173426Srpaulo	error = sysctl_handle_int(oidp, &v, 0, req);
934173426Srpaulo
935173426Srpaulo	return (error);
936173426Srpaulo}
937173426Srpaulo
938173426Srpaulostatic int
939173426Srpauloasmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS)
940173426Srpaulo{
941173426Srpaulo	device_t dev = (device_t) arg1;
942173426Srpaulo	int error;
943173426Srpaulo	int16_t val;
944173426Srpaulo	int32_t v;
945173426Srpaulo
946173426Srpaulo	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val);
947173426Srpaulo	v = (int32_t) val;
948173426Srpaulo	error = sysctl_handle_int(oidp, &v, 0, req);
949173426Srpaulo
950173426Srpaulo	return (error);
951173426Srpaulo}
952173426Srpaulo
953173426Srpaulostatic int
954173426Srpauloasmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS)
955173426Srpaulo{
956173426Srpaulo	device_t dev = (device_t) arg1;
957173426Srpaulo	int error;
958173426Srpaulo	int16_t val;
959173426Srpaulo	int32_t v;
960173426Srpaulo
961173426Srpaulo	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val);
962173426Srpaulo	v = (int32_t) val;
963173426Srpaulo	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
964173426Srpaulo
965173426Srpaulo	return (error);
966173426Srpaulo}
967173426Srpaulo
968173426Srpaulostatic int
969173426Srpauloasmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS)
970173426Srpaulo{
971173426Srpaulo	device_t dev = (device_t) arg1;
972173426Srpaulo	uint8_t buf[6];
973173426Srpaulo	int error;
974173426Srpaulo	unsigned int level;
975173426Srpaulo	int32_t v;
976173426Srpaulo
977173426Srpaulo	asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, 6);
978173426Srpaulo	v = buf[2];
979173426Srpaulo	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
980173426Srpaulo	if (error == 0 && req->newptr != NULL) {
981173426Srpaulo		level = *(unsigned int *)req->newptr;
982173426Srpaulo		if (level > 255)
983173426Srpaulo			return (EINVAL);
984173426Srpaulo		buf[0] = level;
985173426Srpaulo		buf[1] = 0x00;
986173426Srpaulo		asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2);
987173426Srpaulo	}
988173426Srpaulo
989173426Srpaulo	return (error);
990173426Srpaulo}
991173426Srpaulo
992173426Srpaulostatic int
993173426Srpauloasmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS)
994173426Srpaulo{
995173426Srpaulo	device_t dev = (device_t) arg1;
996173426Srpaulo	uint8_t buf[6];
997173426Srpaulo	int error;
998173426Srpaulo	unsigned int level;
999173426Srpaulo	int32_t v;
1000173426Srpaulo
1001173426Srpaulo	asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, 6);
1002173426Srpaulo	v = buf[2];
1003173426Srpaulo	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
1004173426Srpaulo	if (error == 0 && req->newptr != NULL) {
1005173426Srpaulo		level = *(unsigned int *)req->newptr;
1006173426Srpaulo		if (level > 255)
1007173426Srpaulo			return (EINVAL);
1008173426Srpaulo		buf[0] = level;
1009173426Srpaulo		buf[1] = 0x00;
1010173426Srpaulo		asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2);
1011173426Srpaulo	}
1012173426Srpaulo
1013173426Srpaulo	return (error);
1014173426Srpaulo}
1015