asmc.c revision 193530
138465Smsmith/*-
238465Smsmith * Copyright (c) 2007, 2008 Rui Paulo <rpaulo@FreeBSD.org>
338465Smsmith * All rights reserved.
438465Smsmith *
538465Smsmith * Redistribution and use in source and binary forms, with or without
638465Smsmith * modification, are permitted provided that the following conditions
738465Smsmith * are met:
838465Smsmith * 1. Redistributions of source code must retain the above copyright
938465Smsmith *    notice, this list of conditions and the following disclaimer.
1038465Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1138465Smsmith *    notice, this list of conditions and the following disclaimer in the
1238465Smsmith *    documentation and/or other materials provided with the distribution.
1338465Smsmith *
1438465Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1538465Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1638465Smsmith * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1738465Smsmith * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
1838465Smsmith * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1938465Smsmith * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2038465Smsmith * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138465Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2238465Smsmith * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2338465Smsmith * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2438465Smsmith * POSSIBILITY OF SUCH DAMAGE.
2538465Smsmith *
2638465Smsmith */
27119482Sobrien
28119482Sobrien/*
29119482Sobrien * Driver for Apple's System Management Console (SMC).
3038465Smsmith * SMC can be found on the MacBook, MacBook Pro and Mac Mini.
3138465Smsmith *
3238465Smsmith * Inspired by the Linux applesmc driver.
3338465Smsmith */
3438465Smsmith
3538465Smsmith#include <sys/cdefs.h>
36235158Savg__FBSDID("$FreeBSD: head/sys/dev/asmc/asmc.c 193530 2009-06-05 18:44:36Z jkim $");
3738465Smsmith
3839664Smsmith#include <sys/param.h>
39240637Savg#include <sys/bus.h>
40181436Sjhb#include <sys/conf.h>
4139664Smsmith#include <sys/kernel.h>
4238465Smsmith#include <sys/lock.h>
4338465Smsmith#include <sys/malloc.h>
44235154Savg#include <sys/module.h>
4538465Smsmith#include <sys/mutex.h>
46281138Srpaulo#include <sys/sysctl.h>
4739441Smsmith#include <sys/systm.h>
4838465Smsmith#include <sys/taskqueue.h>
49235329Savg#include <sys/rman.h>
50235329Savg
51235329Savg#include <machine/resource.h>
52235329Savg
53235154Savg#include <contrib/dev/acpica/include/acpi.h>
54235154Savg
55235154Savg#include <dev/acpica/acpivar.h>
56235154Savg#include <dev/asmc/asmcvar.h>
5756693Sjhb
5839664Smsmith#include "opt_intr_filter.h"
59235154Savg
6039664Smsmith/*
6139944Smsmith * Device interface.
6239944Smsmith */
6339944Smsmithstatic int 	asmc_probe(device_t dev);
6439664Smsmithstatic int 	asmc_attach(device_t dev);
6538465Smsmithstatic int 	asmc_detach(device_t dev);
6638465Smsmith
6739897Smsmith/*
6840555Smsmith * SMC functions.
6940555Smsmith */
7064187Sjhbstatic int 	asmc_init(device_t dev);
71296963Sallanjudestatic int 	asmc_wait(device_t dev, uint8_t val);
72296963Sallanjudestatic int 	asmc_key_write(device_t dev, const char *key, uint8_t *buf,
73296963Sallanjude    uint8_t len);
74235329Savgstatic int 	asmc_key_read(device_t dev, const char *key, uint8_t *buf,
75296963Sallanjude    uint8_t);
76235329Savgstatic int 	asmc_fan_count(device_t dev);
77235329Savgstatic int 	asmc_fan_getvalue(device_t dev, const char *key, int fan);
7839897Smsmithstatic int 	asmc_temp_getvalue(device_t dev, const char *key);
7938465Smsmithstatic int 	asmc_sms_read(device_t, const char *key, int16_t *val);
8038465Smsmithstatic void 	asmc_sms_calibrate(device_t dev);
8138465Smsmithstatic int 	asmc_sms_intrfast(void *arg);
8238465Smsmith#ifdef INTR_FILTER
8338465Smsmithstatic void 	asmc_sms_handler(void *arg);
8438465Smsmith#endif
85153536Ssobomaxstatic void 	asmc_sms_printintr(device_t dev, uint8_t);
86153536Ssobomaxstatic void 	asmc_sms_task(void *arg, int pending);
87153536Ssobomax
8864187Sjhb/*
8938465Smsmith * Model functions.
9038465Smsmith */
9138465Smsmithstatic int 	asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS);
9239178Smsmithstatic int 	asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS);
9339664Smsmithstatic int 	asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS);
9439664Smsmithstatic int 	asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS);
9539944Smsmithstatic int 	asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS);
9639944Smsmithstatic int 	asmc_temp_sysctl(SYSCTL_HANDLER_ARGS);
9758713Sjhbstatic int 	asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS);
9839664Smsmithstatic int 	asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS);
99181436Sjhbstatic int 	asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS);
100181436Sjhbstatic int 	asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS);
101181436Sjhbstatic int 	asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS);
102181436Sjhb
10338465Smsmithstruct asmc_model {
10439441Smsmith	const char 	 *smc_model;	/* smbios.system.product env var. */
10538465Smsmith	const char 	 *smc_desc;	/* driver description */
10655211Smsmith
10759087Sps	/* Helper functions */
108200216Sjhb	int (*smc_sms_x)(SYSCTL_HANDLER_ARGS);
109200216Sjhb	int (*smc_sms_y)(SYSCTL_HANDLER_ARGS);
110200219Sjhb	int (*smc_sms_z)(SYSCTL_HANDLER_ARGS);
111200219Sjhb	int (*smc_fan_speed)(SYSCTL_HANDLER_ARGS);
112200219Sjhb	int (*smc_fan_safespeed)(SYSCTL_HANDLER_ARGS);
113200219Sjhb	int (*smc_fan_minspeed)(SYSCTL_HANDLER_ARGS);
114200219Sjhb	int (*smc_fan_maxspeed)(SYSCTL_HANDLER_ARGS);
115200219Sjhb	int (*smc_fan_targetspeed)(SYSCTL_HANDLER_ARGS);
116153536Ssobomax	int (*smc_light_left)(SYSCTL_HANDLER_ARGS);
117200219Sjhb	int (*smc_light_right)(SYSCTL_HANDLER_ARGS);
118200219Sjhb
119200219Sjhb	const char 	*smc_temps[ASMC_TEMP_MAX];
120200219Sjhb	const char 	*smc_tempnames[ASMC_TEMP_MAX];
121153536Ssobomax	const char 	*smc_tempdescs[ASMC_TEMP_MAX];
12258713Sjhb};
123281138Srpaulo
12438465Smsmithstatic struct asmc_model *asmc_match(device_t dev);
12538465Smsmith
12638465Smsmith#define ASMC_SMS_FUNCS	asmc_mb_sysctl_sms_x, asmc_mb_sysctl_sms_y, \
12738465Smsmith			asmc_mb_sysctl_sms_z
12838465Smsmith
12939664Smsmith#define ASMC_FAN_FUNCS	asmc_mb_sysctl_fanspeed, asmc_mb_sysctl_fansafespeed, \
13038465Smsmith			asmc_mb_sysctl_fanminspeed, \
131150470Sru			asmc_mb_sysctl_fanmaxspeed, \
132146698Sjhb			asmc_mb_sysctl_fantargetspeed
133146698Sjhb#define ASMC_LIGHT_FUNCS asmc_mbp_sysctl_light_left, \
134146698Sjhb			 asmc_mbp_sysctl_light_right
135146698Sjhb
136146698Sjhbstruct asmc_model asmc_models[] = {
137146698Sjhb	{
13839725Speter	  "MacBook1,1", "Apple SMC MacBook Core Duo",
139146698Sjhb	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL,
14066133Sarchie	  ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
14138465Smsmith	},
14238465Smsmith
14338465Smsmith	{
144298230Sallanjude	  "MacBook2,1", "Apple SMC MacBook Core 2 Duo",
14540834Smsmith	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL,
146298230Sallanjude	  ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
14740834Smsmith	},
14840834Smsmith
14986094Sjhb	{
15058713Sjhb	  "MacBookPro1,1", "Apple SMC MacBook Pro Core Duo (15-inch)",
151126958Sbde	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
15286094Sjhb	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
15386094Sjhb	},
15486094Sjhb
15586094Sjhb	{
15686094Sjhb	  "MacBookPro1,2", "Apple SMC MacBook Pro Core Duo (17-inch)",
15786094Sjhb	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
15886094Sjhb	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
15986094Sjhb	},
16058713Sjhb
16158713Sjhb	{
162185029Spjd	  "MacBookPro2,1", "Apple SMC MacBook Pro Core 2 Duo (17-inch)",
163185029Spjd	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
164185029Spjd	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
165185029Spjd	},
166185029Spjd
167185029Spjd	{
168185029Spjd	  "MacBookPro2,2", "Apple SMC MacBook Pro Core 2 Duo (15-inch)",
169235329Savg	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
170235329Savg	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
171185029Spjd	},
172296963Sallanjude
173296963Sallanjude	{
174296963Sallanjude	  "MacBookPro3,1", "Apple SMC MacBook Pro Core 2 Duo (15-inch LED)",
175296963Sallanjude	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
176296963Sallanjude	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
177296963Sallanjude	},
178296963Sallanjude
179296963Sallanjude	{
180296963Sallanjude	  "MacBookPro3,2", "Apple SMC MacBook Pro Core 2 Duo (17-inch HD)",
181296963Sallanjude	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
182296963Sallanjude	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
183296963Sallanjude	},
184296963Sallanjude
185296963Sallanjude	/* The Mac Mini has no SMS */
186296963Sallanjude	{
187296963Sallanjude	  "Macmini1,1", "Apple SMC Mac Mini",
188296963Sallanjude	  NULL, NULL, NULL,
189296963Sallanjude	  ASMC_FAN_FUNCS,
190296963Sallanjude	  NULL, NULL,
191296963Sallanjude	  ASMC_MM_TEMPS, ASMC_MM_TEMPNAMES, ASMC_MM_TEMPDESCS
192296963Sallanjude	},
193296963Sallanjude
194296963Sallanjude	/* Idem for the MacPro */
195296963Sallanjude	{
196296963Sallanjude	  "MacPro2", "Apple SMC Mac Pro (8-core)",
19758713Sjhb	  NULL, NULL, NULL,
19838465Smsmith	  ASMC_FAN_FUNCS,
19938465Smsmith	  NULL, NULL,
20038465Smsmith	  ASMC_MP_TEMPS, ASMC_MP_TEMPNAMES, ASMC_MP_TEMPDESCS
20138465Smsmith	},
20238465Smsmith
20355211Smsmith	{
204104315Siwasaki	  "MacBookAir1,1", "Apple SMC MacBook Air",
205104315Siwasaki	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL,
206104315Siwasaki	  ASMC_MBA_TEMPS, ASMC_MBA_TEMPNAMES, ASMC_MBA_TEMPDESCS
207104315Siwasaki	},
20838465Smsmith
20982531Smsmith
21082531Smsmith	{ NULL, NULL }
21182531Smsmith};
212148006Sjkim
213281138Srpaulo#undef ASMC_SMS_FUNCS
214148006Sjkim#undef ASMC_FAN_FUNCS
215271406Simp#undef ASMC_LIGHT_FUNCS
216271406Simp
217271406Simp/*
21838465Smsmith * Driver methods.
21955211Smsmith */
22038465Smsmithstatic device_method_t	asmc_methods[] = {
22139450Smsmith	DEVMETHOD(device_probe,		asmc_probe),
22239897Smsmith	DEVMETHOD(device_attach,	asmc_attach),
22339897Smsmith	DEVMETHOD(device_detach,	asmc_detach),
22438465Smsmith
225114379Speter	{ 0, 0 }
226114379Speter};
227269153Smarcel
22864187Sjhbstatic driver_t	asmc_driver = {
22964187Sjhb	"asmc",
23064187Sjhb	asmc_methods,
23138465Smsmith	sizeof(struct asmc_softc)
23238465Smsmith};
23339897Smsmith
23439897Smsmith/*
23539897Smsmith * Debugging
23639897Smsmith */
23739897Smsmith#define	_COMPONENT	ACPI_OEM
23839897SmsmithACPI_MODULE_NAME("ASMC")
23939897Smsmith#ifdef DEBUG
24039897Smsmith#define ASMC_DPRINTF(str)	device_printf(dev, str)
24139897Smsmith#else
242235329Savg#define ASMC_DPRINTF(str)
243235329Savg#endif
244241293Savg
245235329Savgstatic char *asmc_ids[] = { "APP0001", NULL };
246235329Savg
24739897Smsmithstatic devclass_t asmc_devclass;
24858713Sjhb
24964187SjhbDRIVER_MODULE(asmc, acpi, asmc_driver, asmc_devclass, NULL, NULL);
25039897SmsmithMODULE_DEPEND(asmc, acpi, 1, 1, 1);
25158713Sjhb
252126958Sbdestatic struct asmc_model *
25358713Sjhbasmc_match(device_t dev)
25486094Sjhb{
25586094Sjhb	int i;
256163897Smarcel	char *model;
25758713Sjhb
25858713Sjhb	model = getenv("smbios.system.product");
25964187Sjhb	if (model == NULL)
260163897Smarcel		return (NULL);
26158713Sjhb
26258713Sjhb	for (i = 0; asmc_models[i].smc_model; i++) {
26364187Sjhb		if (!strncmp(model, asmc_models[i].smc_model, strlen(model))) {
26464187Sjhb			freeenv(model);
26558713Sjhb			return (&asmc_models[i]);
26658713Sjhb		}
267235329Savg	}
268235329Savg	freeenv(model);
269235329Savg
270235329Savg	return (NULL);
271235329Savg}
272235329Savg
273235329Savgstatic int
274241293Savgasmc_probe(device_t dev)
275241293Savg{
276235329Savg	struct asmc_model *model;
277235329Savg
278235329Savg	if (resource_disabled("asmc", 0))
279241293Savg		return (ENXIO);
280241293Savg	if (ACPI_ID_PROBE(device_get_parent(dev), dev, asmc_ids) == NULL)
281241293Savg		return (ENXIO);
282241293Savg
283241293Savg	model = asmc_match(dev);
284241293Savg	if (!model) {
285235329Savg		device_printf(dev, "model not recognized\n");
286235329Savg		return (ENXIO);
287235329Savg	}
288235329Savg	device_set_desc(dev, model->smc_desc);
289235329Savg
290235329Savg	return (BUS_PROBE_DEFAULT);
291235329Savg}
29256693Sjhb
29339944Smsmithstatic int
29464187Sjhbasmc_attach(device_t dev)
29564187Sjhb{
29639944Smsmith	int i, j;
29739944Smsmith	int ret;
298172921Sjhb	char name[2];
29964187Sjhb	struct asmc_softc *sc = device_get_softc(dev);
30039944Smsmith	struct sysctl_ctx_list *sysctlctx;
30139897Smsmith	struct sysctl_oid *sysctlnode;
30239944Smsmith	struct asmc_model *model;
30339944Smsmith
304160964Syar	sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
30539944Smsmith	    &sc->sc_rid_port, RF_ACTIVE);
30639944Smsmith	if (sc->sc_ioport == NULL) {
30739944Smsmith		device_printf(dev, "unable to allocate IO port\n");
30839944Smsmith		return (ENOMEM);
30939944Smsmith	}
31039944Smsmith
31186094Sjhb	sysctlctx  = device_get_sysctl_ctx(dev);
312235329Savg	sysctlnode = device_get_sysctl_tree(dev);
31358713Sjhb
31458713Sjhb	model = asmc_match(dev);
31558713Sjhb
31658713Sjhb	mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN);
31786094Sjhb
318163897Smarcel	sc->sc_model = model;
31939944Smsmith	asmc_init(dev);
32039944Smsmith
321163897Smarcel	/*
32239897Smsmith	 * dev.asmc.n.fan.* tree.
323235329Savg	 */
324293612Sallanjude	sc->sc_fan_tree[0] = SYSCTL_ADD_NODE(sysctlctx,
325293612Sallanjude	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "fan",
326293612Sallanjude	    CTLFLAG_RD, 0, "Fan Root Tree");
327293612Sallanjude
328293612Sallanjude	for (i = 1; i <= sc->sc_nfan; i++) {
32964187Sjhb		j = i - 1;
33064187Sjhb		name[0] = '0' + j;
33164187Sjhb		name[1] = 0;
33264187Sjhb		sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx,
33339897Smsmith		    SYSCTL_CHILDREN(sc->sc_fan_tree[0]),
33439897Smsmith		    OID_AUTO, name, CTLFLAG_RD, 0,
33538465Smsmith		    "Fan Subtree");
33638465Smsmith
33738465Smsmith		SYSCTL_ADD_PROC(sysctlctx,
33838465Smsmith		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
33938465Smsmith		    OID_AUTO, "speed", CTLTYPE_INT | CTLFLAG_RD,
34059408Sps		    dev, j, model->smc_fan_speed, "I",
34138465Smsmith		    "Fan speed in RPM");
34259408Sps
34359408Sps		SYSCTL_ADD_PROC(sysctlctx,
34459408Sps		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
34559408Sps		    OID_AUTO, "safespeed",
34638465Smsmith		    CTLTYPE_INT | CTLFLAG_RD,
34738465Smsmith		    dev, j, model->smc_fan_safespeed, "I",
34839441Smsmith		    "Fan safe speed in RPM");
34938465Smsmith
35038465Smsmith		SYSCTL_ADD_PROC(sysctlctx,
35139450Smsmith		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
35239441Smsmith		    OID_AUTO, "minspeed",
35339441Smsmith		    CTLTYPE_INT | CTLFLAG_RD,
35439441Smsmith		    dev, j, model->smc_fan_minspeed, "I",
35539441Smsmith		    "Fan minimum speed in RPM");
35639441Smsmith
35739441Smsmith		SYSCTL_ADD_PROC(sysctlctx,
35838465Smsmith		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
35938465Smsmith		    OID_AUTO, "maxspeed",
36038465Smsmith		    CTLTYPE_INT | CTLFLAG_RD,
36138465Smsmith		    dev, j, model->smc_fan_maxspeed, "I",
36238465Smsmith		    "Fan maximum speed in RPM");
36339664Smsmith
364153536Ssobomax		SYSCTL_ADD_PROC(sysctlctx,
365153536Ssobomax		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
36638465Smsmith		    OID_AUTO, "targetspeed",
36738465Smsmith		    CTLTYPE_INT | CTLFLAG_RD,
36840555Smsmith		    dev, j, model->smc_fan_targetspeed, "I",
369241284Savg		    "Fan target speed in RPM");
370241284Savg	}
371241284Savg
372241284Savg	/*
373241284Savg	 * dev.asmc.n.temp tree.
374241284Savg	 */
375241284Savg	sc->sc_temp_tree = SYSCTL_ADD_NODE(sysctlctx,
376241284Savg	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp",
377241284Savg	    CTLFLAG_RD, 0, "Temperature sensors");
378241284Savg
379241284Savg	for (i = 0; model->smc_temps[i]; i++) {
380241284Savg		SYSCTL_ADD_PROC(sysctlctx,
381241284Savg		    SYSCTL_CHILDREN(sc->sc_temp_tree),
382241284Savg		    OID_AUTO, model->smc_tempnames[i],
383241284Savg		    CTLTYPE_INT | CTLFLAG_RD,
384241284Savg		    dev, i, asmc_temp_sysctl, "I",
385241284Savg		    model->smc_tempdescs[i]);
386241284Savg	}
387241284Savg
388293001Sallanjude	if (model->smc_sms_x == NULL)
389241284Savg		goto nosms;
390241284Savg
391293001Sallanjude	/*
392293001Sallanjude	 * dev.asmc.n.sms tree.
393293001Sallanjude	 */
394293001Sallanjude	sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx,
395293001Sallanjude	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "sms",
396293001Sallanjude	    CTLFLAG_RD, 0, "Sudden Motion Sensor");
397293001Sallanjude
398293001Sallanjude	SYSCTL_ADD_PROC(sysctlctx,
399293454Sallanjude	    SYSCTL_CHILDREN(sc->sc_sms_tree),
400293001Sallanjude	    OID_AUTO, "x", CTLTYPE_INT | CTLFLAG_RD,
401293001Sallanjude	    dev, 0, model->smc_sms_x, "I",
402293001Sallanjude	    "Sudden Motion Sensor X value");
403293001Sallanjude
404293001Sallanjude	SYSCTL_ADD_PROC(sysctlctx,
405293001Sallanjude	    SYSCTL_CHILDREN(sc->sc_sms_tree),
406293001Sallanjude	    OID_AUTO, "y", CTLTYPE_INT | CTLFLAG_RD,
407293001Sallanjude	    dev, 0, model->smc_sms_y, "I",
408293001Sallanjude	    "Sudden Motion Sensor Y value");
409293454Sallanjude
410293454Sallanjude	SYSCTL_ADD_PROC(sysctlctx,
411293454Sallanjude	    SYSCTL_CHILDREN(sc->sc_sms_tree),
412293454Sallanjude	    OID_AUTO, "z", CTLTYPE_INT | CTLFLAG_RD,
413293454Sallanjude	    dev, 0, model->smc_sms_z, "I",
414293001Sallanjude	    "Sudden Motion Sensor Z value");
415293001Sallanjude
416293001Sallanjude	/*
417293001Sallanjude	 * dev.asmc.n.light
418293001Sallanjude	 */
419293001Sallanjude	if (model->smc_light_left) {
420293001Sallanjude		sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx,
421293001Sallanjude		    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light",
422293001Sallanjude		    CTLFLAG_RD, 0, "Keyboard backlight sensors");
423293001Sallanjude
424241284Savg		SYSCTL_ADD_PROC(sysctlctx,
425241284Savg		    SYSCTL_CHILDREN(sc->sc_light_tree),
426240637Savg		    OID_AUTO, "left", CTLTYPE_INT | CTLFLAG_RW,
427240341Savg		    dev, 0, model->smc_light_left, "I",
42840555Smsmith		    "Keyboard backlight left sensor");
42940555Smsmith
430240341Savg		SYSCTL_ADD_PROC(sysctlctx,
431240637Savg		    SYSCTL_CHILDREN(sc->sc_light_tree),
43240555Smsmith		    OID_AUTO, "right", CTLTYPE_INT | CTLFLAG_RW,
43340555Smsmith		    dev, 0, model->smc_light_right, "I",
43440555Smsmith		    "Keyboard backlight right sensor");
43540555Smsmith	}
43640555Smsmith
437240341Savg	/*
438240637Savg	 * Need a taskqueue to send devctl_notify() events
43940555Smsmith	 * when the SMS interrupt us.
44040555Smsmith	 *
441235329Savg	 * PI_REALTIME is used due to the sensitivity of the
442235329Savg	 * interrupt. An interrupt from the SMS means that the
443235329Savg	 * disk heads should be turned off as quickly as possible.
444235329Savg	 *
445235329Savg	 * We only need to do this for the non INTR_FILTER case.
446239068Sae	 */
447235329Savg	sc->sc_sms_tq = NULL;
448235329Savg#ifndef INTR_FILTER
449235329Savg	TASK_INIT(&sc->sc_sms_task, 0, asmc_sms_task, sc);
450239068Sae	sc->sc_sms_tq = taskqueue_create_fast("asmc_taskq", M_WAITOK,
451235329Savg	    taskqueue_thread_enqueue, &sc->sc_sms_tq);
452235329Savg	taskqueue_start_threads(&sc->sc_sms_tq, 1, PI_REALTIME, "%s sms taskq",
453239068Sae	    device_get_nameunit(dev));
454239068Sae#endif
455235329Savg	/*
456239068Sae	 * Allocate an IRQ for the SMS.
457235329Savg	 */
458235329Savg	sc->sc_rid_irq = 0;
459235329Savg	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
460	    &sc->sc_rid_irq, RF_ACTIVE);
461	if (sc->sc_irq == NULL) {
462		device_printf(dev, "unable to allocate IRQ resource\n");
463		ret = ENXIO;
464		goto err2;
465	}
466
467	ret = bus_setup_intr(dev, sc->sc_irq,
468	          INTR_TYPE_MISC | INTR_MPSAFE,
469#ifdef INTR_FILTER
470	    asmc_sms_intrfast, asmc_sms_handler,
471#else
472	    asmc_sms_intrfast, NULL,
473#endif
474	    dev, &sc->sc_cookie);
475
476	if (ret) {
477		device_printf(dev, "unable to setup SMS IRQ\n");
478		goto err1;
479	}
480nosms:
481	return (0);
482err1:
483	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, sc->sc_irq);
484err2:
485	bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
486	    sc->sc_ioport);
487	mtx_destroy(&sc->sc_mtx);
488	if (sc->sc_sms_tq)
489		taskqueue_free(sc->sc_sms_tq);
490
491	return (ret);
492}
493
494static int
495asmc_detach(device_t dev)
496{
497	struct asmc_softc *sc = device_get_softc(dev);
498
499	if (sc->sc_sms_tq) {
500		taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task);
501		taskqueue_free(sc->sc_sms_tq);
502	}
503	if (sc->sc_cookie)
504		bus_teardown_intr(dev, sc->sc_irq, sc->sc_cookie);
505	if (sc->sc_irq)
506		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq,
507		    sc->sc_irq);
508	if (sc->sc_ioport)
509		bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
510		    sc->sc_ioport);
511	mtx_destroy(&sc->sc_mtx);
512
513	return (0);
514}
515
516static int
517asmc_init(device_t dev)
518{
519	struct asmc_softc *sc = device_get_softc(dev);
520	int i, error = 1;
521	uint8_t buf[4];
522
523	if (sc->sc_model->smc_sms_x == NULL)
524		goto nosms;
525
526	/*
527	 * We are ready to recieve interrupts from the SMS.
528	 */
529	buf[0] = 0x01;
530	ASMC_DPRINTF(("intok key\n"));
531	asmc_key_write(dev, ASMC_KEY_INTOK, buf, 1);
532	DELAY(50);
533
534	/*
535	 * Initiate the polling intervals.
536	 */
537	buf[0] = 20; /* msecs */
538	ASMC_DPRINTF(("low int key\n"));
539	asmc_key_write(dev, ASMC_KEY_SMS_LOW_INT, buf, 1);
540	DELAY(200);
541
542	buf[0] = 20; /* msecs */
543	ASMC_DPRINTF(("high int key\n"));
544	asmc_key_write(dev, ASMC_KEY_SMS_HIGH_INT, buf, 1);
545	DELAY(200);
546
547	buf[0] = 0x00;
548	buf[1] = 0x60;
549	ASMC_DPRINTF(("sms low key\n"));
550	asmc_key_write(dev, ASMC_KEY_SMS_LOW, buf, 2);
551	DELAY(200);
552
553	buf[0] = 0x01;
554	buf[1] = 0xc0;
555	ASMC_DPRINTF(("sms high key\n"));
556	asmc_key_write(dev, ASMC_KEY_SMS_HIGH, buf, 2);
557	DELAY(200);
558
559	/*
560	 * I'm not sure what this key does, but it seems to be
561	 * required.
562	 */
563	buf[0] = 0x01;
564	ASMC_DPRINTF(("sms flag key\n"));
565	asmc_key_write(dev, ASMC_KEY_SMS_FLAG, buf, 1);
566	DELAY(100);
567
568	/*
569	 * Wait up to 5 seconds for SMS initialization.
570	 */
571	for (i = 0; i < 10000; i++) {
572		if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 &&
573		    (buf[0] != 0x00 || buf[1] != 0x00)) {
574			error = 0;
575			goto out;
576		}
577		buf[0] = ASMC_SMS_INIT1;
578		buf[1] = ASMC_SMS_INIT2;
579		ASMC_DPRINTF(("sms key\n"));
580		asmc_key_write(dev, ASMC_KEY_SMS, buf, 2);
581		DELAY(50);
582	}
583	device_printf(dev, "WARNING: Sudden Motion Sensor not initialized!\n");
584
585out:
586	asmc_sms_calibrate(dev);
587nosms:
588	sc->sc_nfan = asmc_fan_count(dev);
589	if (sc->sc_nfan > ASMC_MAXFANS) {
590		device_printf(dev, "more than %d fans were detected. Please "
591		    "report this.\n", ASMC_MAXFANS);
592		sc->sc_nfan = ASMC_MAXFANS;
593	}
594
595	if (bootverbose) {
596		/*
597		 * XXX: The number of keys is a 32 bit buffer, but
598		 * right now Apple only uses the last 8 bit.
599		 */
600		asmc_key_read(dev, ASMC_NKEYS, buf, 4);
601		device_printf(dev, "number of keys: %d\n", buf[3]);
602	}
603
604	return (error);
605}
606
607/*
608 * We need to make sure that the SMC acks the byte sent.
609 * Just wait up to 100 ms.
610 */
611static int
612asmc_wait(device_t dev, uint8_t val)
613{
614	struct asmc_softc *sc = device_get_softc(dev);
615	u_int i;
616
617	val = val & ASMC_STATUS_MASK;
618
619	for (i = 0; i < 1000; i++) {
620		if ((ASMC_CMDPORT_READ(sc) & ASMC_STATUS_MASK) == val)
621			return (0);
622		DELAY(10);
623	}
624
625	device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, val,
626	    ASMC_CMDPORT_READ(sc));
627
628	return (1);
629}
630
631static int
632asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len)
633{
634	int i, error = 1;
635	struct asmc_softc *sc = device_get_softc(dev);
636
637	mtx_lock_spin(&sc->sc_mtx);
638
639	ASMC_CMDPORT_WRITE(sc, ASMC_CMDREAD);
640	if (asmc_wait(dev, 0x0c))
641		goto out;
642
643	for (i = 0; i < 4; i++) {
644		ASMC_DATAPORT_WRITE(sc, key[i]);
645		if (asmc_wait(dev, 0x04))
646			goto out;
647	}
648
649	ASMC_DATAPORT_WRITE(sc, len);
650
651	for (i = 0; i < len; i++) {
652		if (asmc_wait(dev, 0x05))
653			goto out;
654		buf[i] = ASMC_DATAPORT_READ(sc);
655	}
656
657	error = 0;
658out:
659	mtx_unlock_spin(&sc->sc_mtx);
660
661	return (error);
662}
663
664static int
665asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len)
666{
667	int i, error = -1;
668	struct asmc_softc *sc = device_get_softc(dev);
669
670	mtx_lock_spin(&sc->sc_mtx);
671
672	ASMC_DPRINTF(("cmd port: cmd write\n"));
673	ASMC_CMDPORT_WRITE(sc, ASMC_CMDWRITE);
674	if (asmc_wait(dev, 0x0c))
675		goto out;
676
677	ASMC_DPRINTF(("data port: key\n"));
678	for (i = 0; i < 4; i++) {
679		ASMC_DATAPORT_WRITE(sc, key[i]);
680		if (asmc_wait(dev, 0x04))
681			goto out;
682	}
683	ASMC_DPRINTF(("data port: length\n"));
684	ASMC_DATAPORT_WRITE(sc, len);
685
686	ASMC_DPRINTF(("data port: buffer\n"));
687	for (i = 0; i < len; i++) {
688		if (asmc_wait(dev, 0x04))
689			goto out;
690		ASMC_DATAPORT_WRITE(sc, buf[i]);
691	}
692
693	error = 0;
694out:
695	mtx_unlock_spin(&sc->sc_mtx);
696
697	return (error);
698
699}
700
701/*
702 * Fan control functions.
703 */
704static int
705asmc_fan_count(device_t dev)
706{
707	uint8_t buf[1];
708
709	if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, 1) < 0)
710		return (-1);
711
712	return (buf[0]);
713}
714
715static int
716asmc_fan_getvalue(device_t dev, const char *key, int fan)
717{
718	int speed;
719	uint8_t buf[2];
720	char fankey[5];
721
722	snprintf(fankey, sizeof(fankey), key, fan);
723	if (asmc_key_read(dev, fankey, buf, 2) < 0)
724		return (-1);
725	speed = (buf[0] << 6) | (buf[1] >> 2);
726
727	return (speed);
728}
729
730static int
731asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS)
732{
733	device_t dev = (device_t) arg1;
734	int fan = arg2;
735	int error;
736	int32_t v;
737
738	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSPEED, fan);
739	error = sysctl_handle_int(oidp, &v, 0, req);
740
741	return (error);
742}
743
744static int
745asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS)
746{
747	device_t dev = (device_t) arg1;
748	int fan = arg2;
749	int error;
750	int32_t v;
751
752	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSAFESPEED, fan);
753	error = sysctl_handle_int(oidp, &v, 0, req);
754
755	return (error);
756}
757
758
759static int
760asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS)
761{
762	device_t dev = (device_t) arg1;
763	int fan = arg2;
764	int error;
765	int32_t v;
766
767	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMINSPEED, fan);
768	error = sysctl_handle_int(oidp, &v, 0, req);
769
770	return (error);
771}
772
773static int
774asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS)
775{
776	device_t dev = (device_t) arg1;
777	int fan = arg2;
778	int error;
779	int32_t v;
780
781	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMAXSPEED, fan);
782	error = sysctl_handle_int(oidp, &v, 0, req);
783
784	return (error);
785}
786
787static int
788asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS)
789{
790	device_t dev = (device_t) arg1;
791	int fan = arg2;
792	int error;
793	int32_t v;
794
795	v = asmc_fan_getvalue(dev, ASMC_KEY_FANTARGETSPEED, fan);
796	error = sysctl_handle_int(oidp, &v, 0, req);
797
798	return (error);
799}
800
801/*
802 * Temperature functions.
803 */
804static int
805asmc_temp_getvalue(device_t dev, const char *key)
806{
807	uint8_t buf[2];
808
809	/*
810	 * Check for invalid temperatures.
811	 */
812	if (asmc_key_read(dev, key, buf, 2) < 0)
813		return (-1);
814
815	return (buf[0]);
816}
817
818static int
819asmc_temp_sysctl(SYSCTL_HANDLER_ARGS)
820{
821	device_t dev = (device_t) arg1;
822	struct asmc_softc *sc = device_get_softc(dev);
823	int error, val;
824
825	val = asmc_temp_getvalue(dev, sc->sc_model->smc_temps[arg2]);
826	error = sysctl_handle_int(oidp, &val, 0, req);
827
828	return (error);
829}
830
831/*
832 * Sudden Motion Sensor functions.
833 */
834static int
835asmc_sms_read(device_t dev, const char *key, int16_t *val)
836{
837	uint8_t buf[2];
838	int error;
839
840	/* no need to do locking here as asmc_key_read() already does it */
841	switch (key[3]) {
842	case 'X':
843	case 'Y':
844	case 'Z':
845		error =	asmc_key_read(dev, key, buf, 2);
846		break;
847	default:
848		device_printf(dev, "%s called with invalid argument %s\n",
849			      __func__, key);
850		error = 1;
851		goto out;
852	}
853	*val = ((int16_t)buf[0] << 8) | buf[1];
854out:
855	return (error);
856}
857
858static void
859asmc_sms_calibrate(device_t dev)
860{
861	struct asmc_softc *sc = device_get_softc(dev);
862
863	asmc_sms_read(dev, ASMC_KEY_SMS_X, &sc->sms_rest_x);
864	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &sc->sms_rest_y);
865	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &sc->sms_rest_z);
866}
867
868static int
869asmc_sms_intrfast(void *arg)
870{
871	uint8_t type;
872	device_t dev = (device_t) arg;
873	struct asmc_softc *sc = device_get_softc(dev);
874
875	mtx_lock_spin(&sc->sc_mtx);
876	type = ASMC_INTPORT_READ(sc);
877	mtx_unlock_spin(&sc->sc_mtx);
878
879	sc->sc_sms_intrtype = type;
880	asmc_sms_printintr(dev, type);
881
882#ifdef INTR_FILTER
883	return (FILTER_SCHEDULE_THREAD | FILTER_HANDLED);
884#else
885	taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task);
886#endif
887	return (FILTER_HANDLED);
888}
889
890#ifdef INTR_FILTER
891static void
892asmc_sms_handler(void *arg)
893{
894	struct asmc_softc *sc = device_get_softc(arg);
895
896	asmc_sms_task(sc, 0);
897}
898#endif
899
900
901static void
902asmc_sms_printintr(device_t dev, uint8_t type)
903{
904
905	switch (type) {
906	case ASMC_SMS_INTFF:
907		device_printf(dev, "WARNING: possible free fall!\n");
908		break;
909	case ASMC_SMS_INTHA:
910		device_printf(dev, "WARNING: high acceleration detected!\n");
911		break;
912	case ASMC_SMS_INTSH:
913		device_printf(dev, "WARNING: possible shock!\n");
914		break;
915	default:
916		device_printf(dev, "%s unknown interrupt\n", __func__);
917	}
918}
919
920static void
921asmc_sms_task(void *arg, int pending)
922{
923	struct asmc_softc *sc = (struct asmc_softc *)arg;
924	char notify[16];
925	int type;
926
927	switch (sc->sc_sms_intrtype) {
928	case ASMC_SMS_INTFF:
929		type = 2;
930		break;
931	case ASMC_SMS_INTHA:
932		type = 1;
933		break;
934	case ASMC_SMS_INTSH:
935		type = 0;
936		break;
937	default:
938		type = 255;
939	}
940
941	snprintf(notify, sizeof(notify), " notify=0x%x", type);
942	devctl_notify("ACPI", "asmc", "SMS", notify);
943}
944
945static int
946asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS)
947{
948	device_t dev = (device_t) arg1;
949	int error;
950	int16_t val;
951	int32_t v;
952
953	asmc_sms_read(dev, ASMC_KEY_SMS_X, &val);
954	v = (int32_t) val;
955	error = sysctl_handle_int(oidp, &v, 0, req);
956
957	return (error);
958}
959
960static int
961asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS)
962{
963	device_t dev = (device_t) arg1;
964	int error;
965	int16_t val;
966	int32_t v;
967
968	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val);
969	v = (int32_t) val;
970	error = sysctl_handle_int(oidp, &v, 0, req);
971
972	return (error);
973}
974
975static int
976asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS)
977{
978	device_t dev = (device_t) arg1;
979	int error;
980	int16_t val;
981	int32_t v;
982
983	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val);
984	v = (int32_t) val;
985	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
986
987	return (error);
988}
989
990static int
991asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS)
992{
993	device_t dev = (device_t) arg1;
994	uint8_t buf[6];
995	int error;
996	unsigned int level;
997	int32_t v;
998
999	asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, 6);
1000	v = buf[2];
1001	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
1002	if (error == 0 && req->newptr != NULL) {
1003		level = *(unsigned int *)req->newptr;
1004		if (level > 255)
1005			return (EINVAL);
1006		buf[0] = level;
1007		buf[1] = 0x00;
1008		asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2);
1009	}
1010
1011	return (error);
1012}
1013
1014static int
1015asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS)
1016{
1017	device_t dev = (device_t) arg1;
1018	uint8_t buf[6];
1019	int error;
1020	unsigned int level;
1021	int32_t v;
1022
1023	asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, 6);
1024	v = buf[2];
1025	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
1026	if (error == 0 && req->newptr != NULL) {
1027		level = *(unsigned int *)req->newptr;
1028		if (level > 255)
1029			return (EINVAL);
1030		buf[0] = level;
1031		buf[1] = 0x00;
1032		asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2);
1033	}
1034
1035	return (error);
1036}
1037