xref: /freebsd/sys/dev/asmc/asmc.c (revision 1269f4d431b01f8c035eefc393835b58d0b4dc17)
132a8088fSRui Paulo /*-
24470f0f3SRui Paulo  * Copyright (c) 2007, 2008 Rui Paulo <rpaulo@FreeBSD.org>
332a8088fSRui Paulo  * All rights reserved.
432a8088fSRui Paulo  *
532a8088fSRui Paulo  * Redistribution and use in source and binary forms, with or without
632a8088fSRui Paulo  * modification, are permitted provided that the following conditions
732a8088fSRui Paulo  * are met:
832a8088fSRui Paulo  * 1. Redistributions of source code must retain the above copyright
932a8088fSRui Paulo  *    notice, this list of conditions and the following disclaimer.
1032a8088fSRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
1132a8088fSRui Paulo  *    notice, this list of conditions and the following disclaimer in the
1232a8088fSRui Paulo  *    documentation and/or other materials provided with the distribution.
1332a8088fSRui Paulo  *
1432a8088fSRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1532a8088fSRui Paulo  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1632a8088fSRui Paulo  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1732a8088fSRui Paulo  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
1832a8088fSRui Paulo  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1932a8088fSRui Paulo  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2032a8088fSRui Paulo  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2132a8088fSRui Paulo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2232a8088fSRui Paulo  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2332a8088fSRui Paulo  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2432a8088fSRui Paulo  * POSSIBILITY OF SUCH DAMAGE.
2532a8088fSRui Paulo  *
2632a8088fSRui Paulo  */
2732a8088fSRui Paulo 
2832a8088fSRui Paulo /*
2932a8088fSRui Paulo  * Driver for Apple's System Management Console (SMC).
3032a8088fSRui Paulo  * SMC can be found on the MacBook, MacBook Pro and Mac Mini.
3132a8088fSRui Paulo  *
3232a8088fSRui Paulo  * Inspired by the Linux applesmc driver.
3332a8088fSRui Paulo  */
3432a8088fSRui Paulo 
3532a8088fSRui Paulo #include <sys/cdefs.h>
3632a8088fSRui Paulo __FBSDID("$FreeBSD$");
3732a8088fSRui Paulo 
3832a8088fSRui Paulo #include <sys/param.h>
3932a8088fSRui Paulo #include <sys/bus.h>
4032a8088fSRui Paulo #include <sys/conf.h>
4132a8088fSRui Paulo #include <sys/kernel.h>
4232a8088fSRui Paulo #include <sys/lock.h>
4332a8088fSRui Paulo #include <sys/malloc.h>
4432a8088fSRui Paulo #include <sys/module.h>
4532a8088fSRui Paulo #include <sys/mutex.h>
4632a8088fSRui Paulo #include <sys/sysctl.h>
4732a8088fSRui Paulo #include <sys/systm.h>
4832a8088fSRui Paulo #include <sys/taskqueue.h>
4932a8088fSRui Paulo #include <sys/rman.h>
504c061448SRui Paulo 
5132a8088fSRui Paulo #include <machine/resource.h>
52129d3046SJung-uk Kim 
53129d3046SJung-uk Kim #include <contrib/dev/acpica/include/acpi.h>
54129d3046SJung-uk Kim 
554470f0f3SRui Paulo #include <dev/acpica/acpivar.h>
5632a8088fSRui Paulo #include <dev/asmc/asmcvar.h>
5732a8088fSRui Paulo 
584c061448SRui Paulo #include "opt_intr_filter.h"
594c061448SRui Paulo 
6032a8088fSRui Paulo /*
6132a8088fSRui Paulo  * Device interface.
6232a8088fSRui Paulo  */
6332a8088fSRui Paulo static int 	asmc_probe(device_t dev);
6432a8088fSRui Paulo static int 	asmc_attach(device_t dev);
6532a8088fSRui Paulo static int 	asmc_detach(device_t dev);
6632a8088fSRui Paulo 
6732a8088fSRui Paulo /*
6832a8088fSRui Paulo  * SMC functions.
6932a8088fSRui Paulo  */
7032a8088fSRui Paulo static int 	asmc_init(device_t dev);
71be80e49aSRui Paulo static int 	asmc_command(device_t dev, uint8_t command);
7232a8088fSRui Paulo static int 	asmc_wait(device_t dev, uint8_t val);
73be80e49aSRui Paulo static int 	asmc_wait_ack(device_t dev, uint8_t val, int amount);
7432a8088fSRui Paulo static int 	asmc_key_write(device_t dev, const char *key, uint8_t *buf,
7532a8088fSRui Paulo     uint8_t len);
7632a8088fSRui Paulo static int 	asmc_key_read(device_t dev, const char *key, uint8_t *buf,
7732a8088fSRui Paulo     uint8_t);
7832a8088fSRui Paulo static int 	asmc_fan_count(device_t dev);
7932a8088fSRui Paulo static int 	asmc_fan_getvalue(device_t dev, const char *key, int fan);
8032a8088fSRui Paulo static int 	asmc_temp_getvalue(device_t dev, const char *key);
8132a8088fSRui Paulo static int 	asmc_sms_read(device_t, const char *key, int16_t *val);
8232a8088fSRui Paulo static void 	asmc_sms_calibrate(device_t dev);
8332a8088fSRui Paulo static int 	asmc_sms_intrfast(void *arg);
8432a8088fSRui Paulo #ifdef INTR_FILTER
8532a8088fSRui Paulo static void 	asmc_sms_handler(void *arg);
8632a8088fSRui Paulo #endif
8732a8088fSRui Paulo static void 	asmc_sms_printintr(device_t dev, uint8_t);
8832a8088fSRui Paulo static void 	asmc_sms_task(void *arg, int pending);
891269f4d4SRui Paulo #ifdef DEBUG
901269f4d4SRui Paulo void		asmc_dumpall(device_t);
911269f4d4SRui Paulo static int	asmc_key_dump(device_t, int);
921269f4d4SRui Paulo #endif
9332a8088fSRui Paulo 
9432a8088fSRui Paulo /*
9532a8088fSRui Paulo  * Model functions.
9632a8088fSRui Paulo  */
9732a8088fSRui Paulo static int 	asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS);
9832a8088fSRui Paulo static int 	asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS);
9932a8088fSRui Paulo static int 	asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS);
10032a8088fSRui Paulo static int 	asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS);
10132a8088fSRui Paulo static int 	asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS);
10232a8088fSRui Paulo static int 	asmc_temp_sysctl(SYSCTL_HANDLER_ARGS);
10332a8088fSRui Paulo static int 	asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS);
10432a8088fSRui Paulo static int 	asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS);
10532a8088fSRui Paulo static int 	asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS);
10632a8088fSRui Paulo static int 	asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS);
10732a8088fSRui Paulo static int 	asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS);
108be80e49aSRui Paulo static int 	asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS);
10932a8088fSRui Paulo 
11032a8088fSRui Paulo struct asmc_model {
11132a8088fSRui Paulo 	const char 	 *smc_model;	/* smbios.system.product env var. */
11232a8088fSRui Paulo 	const char 	 *smc_desc;	/* driver description */
11332a8088fSRui Paulo 
11432a8088fSRui Paulo 	/* Helper functions */
11532a8088fSRui Paulo 	int (*smc_sms_x)(SYSCTL_HANDLER_ARGS);
11632a8088fSRui Paulo 	int (*smc_sms_y)(SYSCTL_HANDLER_ARGS);
11732a8088fSRui Paulo 	int (*smc_sms_z)(SYSCTL_HANDLER_ARGS);
11832a8088fSRui Paulo 	int (*smc_fan_speed)(SYSCTL_HANDLER_ARGS);
11932a8088fSRui Paulo 	int (*smc_fan_safespeed)(SYSCTL_HANDLER_ARGS);
12032a8088fSRui Paulo 	int (*smc_fan_minspeed)(SYSCTL_HANDLER_ARGS);
12132a8088fSRui Paulo 	int (*smc_fan_maxspeed)(SYSCTL_HANDLER_ARGS);
12232a8088fSRui Paulo 	int (*smc_fan_targetspeed)(SYSCTL_HANDLER_ARGS);
12332a8088fSRui Paulo 	int (*smc_light_left)(SYSCTL_HANDLER_ARGS);
12432a8088fSRui Paulo 	int (*smc_light_right)(SYSCTL_HANDLER_ARGS);
125be80e49aSRui Paulo 	int (*smc_light_control)(SYSCTL_HANDLER_ARGS);
12632a8088fSRui Paulo 
127d8246db0SRui Paulo 	const char 	*smc_temps[ASMC_TEMP_MAX];
128d8246db0SRui Paulo 	const char 	*smc_tempnames[ASMC_TEMP_MAX];
129d8246db0SRui Paulo 	const char 	*smc_tempdescs[ASMC_TEMP_MAX];
13032a8088fSRui Paulo };
13132a8088fSRui Paulo 
13232a8088fSRui Paulo static struct asmc_model *asmc_match(device_t dev);
13332a8088fSRui Paulo 
13432a8088fSRui Paulo #define ASMC_SMS_FUNCS	asmc_mb_sysctl_sms_x, asmc_mb_sysctl_sms_y, \
13532a8088fSRui Paulo 			asmc_mb_sysctl_sms_z
13632a8088fSRui Paulo 
13732a8088fSRui Paulo #define ASMC_FAN_FUNCS	asmc_mb_sysctl_fanspeed, asmc_mb_sysctl_fansafespeed, \
13832a8088fSRui Paulo 			asmc_mb_sysctl_fanminspeed, \
13932a8088fSRui Paulo 			asmc_mb_sysctl_fanmaxspeed, \
14032a8088fSRui Paulo 			asmc_mb_sysctl_fantargetspeed
14132a8088fSRui Paulo #define ASMC_LIGHT_FUNCS asmc_mbp_sysctl_light_left, \
142be80e49aSRui Paulo 			 asmc_mbp_sysctl_light_right, \
143be80e49aSRui Paulo 			 asmc_mbp_sysctl_light_control
14432a8088fSRui Paulo 
14532a8088fSRui Paulo struct asmc_model asmc_models[] = {
14632a8088fSRui Paulo 	{
14732a8088fSRui Paulo 	  "MacBook1,1", "Apple SMC MacBook Core Duo",
148be80e49aSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
14932a8088fSRui Paulo 	  ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
15032a8088fSRui Paulo 	},
15132a8088fSRui Paulo 
15232a8088fSRui Paulo 	{
15332a8088fSRui Paulo 	  "MacBook2,1", "Apple SMC MacBook Core 2 Duo",
154be80e49aSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
15532a8088fSRui Paulo 	  ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
15632a8088fSRui Paulo 	},
15732a8088fSRui Paulo 
15832a8088fSRui Paulo 	{
15932a8088fSRui Paulo 	  "MacBookPro1,1", "Apple SMC MacBook Pro Core Duo (15-inch)",
16032a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
16132a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
16232a8088fSRui Paulo 	},
16332a8088fSRui Paulo 
16432a8088fSRui Paulo 	{
16532a8088fSRui Paulo 	  "MacBookPro1,2", "Apple SMC MacBook Pro Core Duo (17-inch)",
16632a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
16732a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
16832a8088fSRui Paulo 	},
16932a8088fSRui Paulo 
17032a8088fSRui Paulo 	{
17132a8088fSRui Paulo 	  "MacBookPro2,1", "Apple SMC MacBook Pro Core 2 Duo (17-inch)",
17232a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
17332a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
17432a8088fSRui Paulo 	},
17532a8088fSRui Paulo 
17632a8088fSRui Paulo 	{
17732a8088fSRui Paulo 	  "MacBookPro2,2", "Apple SMC MacBook Pro Core 2 Duo (15-inch)",
17832a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
17932a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
18032a8088fSRui Paulo 	},
18132a8088fSRui Paulo 
18232a8088fSRui Paulo 	{
18332a8088fSRui Paulo 	  "MacBookPro3,1", "Apple SMC MacBook Pro Core 2 Duo (15-inch LED)",
18432a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
18532a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
18632a8088fSRui Paulo 	},
18732a8088fSRui Paulo 
18832a8088fSRui Paulo 	{
18932a8088fSRui Paulo 	  "MacBookPro3,2", "Apple SMC MacBook Pro Core 2 Duo (17-inch HD)",
19032a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
19132a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
19232a8088fSRui Paulo 	},
19332a8088fSRui Paulo 
194be80e49aSRui Paulo 	{
195be80e49aSRui Paulo 	  "MacBookPro4,1", "Apple SMC MacBook Pro Core 2 Duo (Penryn)",
196be80e49aSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
197be80e49aSRui Paulo 	  ASMC_MBP4_TEMPS, ASMC_MBP4_TEMPNAMES, ASMC_MBP4_TEMPDESCS
198be80e49aSRui Paulo 	},
199be80e49aSRui Paulo 
20032a8088fSRui Paulo 	/* The Mac Mini has no SMS */
20132a8088fSRui Paulo 	{
20232a8088fSRui Paulo 	  "Macmini1,1", "Apple SMC Mac Mini",
20332a8088fSRui Paulo 	  NULL, NULL, NULL,
20432a8088fSRui Paulo 	  ASMC_FAN_FUNCS,
205be80e49aSRui Paulo 	  NULL, NULL, NULL,
20632a8088fSRui Paulo 	  ASMC_MM_TEMPS, ASMC_MM_TEMPNAMES, ASMC_MM_TEMPDESCS
20732a8088fSRui Paulo 	},
20832a8088fSRui Paulo 
209d8246db0SRui Paulo 	/* Idem for the MacPro */
210d8246db0SRui Paulo 	{
211d8246db0SRui Paulo 	  "MacPro2", "Apple SMC Mac Pro (8-core)",
212d8246db0SRui Paulo 	  NULL, NULL, NULL,
213d8246db0SRui Paulo 	  ASMC_FAN_FUNCS,
214be80e49aSRui Paulo 	  NULL, NULL, NULL,
215d8246db0SRui Paulo 	  ASMC_MP_TEMPS, ASMC_MP_TEMPNAMES, ASMC_MP_TEMPDESCS
216d8246db0SRui Paulo 	},
217941f9f10SRui Paulo 
218941f9f10SRui Paulo 	{
219941f9f10SRui Paulo 	  "MacBookAir1,1", "Apple SMC MacBook Air",
220be80e49aSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
221941f9f10SRui Paulo 	  ASMC_MBA_TEMPS, ASMC_MBA_TEMPNAMES, ASMC_MBA_TEMPDESCS
222941f9f10SRui Paulo 	},
223941f9f10SRui Paulo 
224d8246db0SRui Paulo 
22532a8088fSRui Paulo 	{ NULL, NULL }
22632a8088fSRui Paulo };
22732a8088fSRui Paulo 
22832a8088fSRui Paulo #undef ASMC_SMS_FUNCS
22932a8088fSRui Paulo #undef ASMC_FAN_FUNCS
23032a8088fSRui Paulo #undef ASMC_LIGHT_FUNCS
23132a8088fSRui Paulo 
23232a8088fSRui Paulo /*
23332a8088fSRui Paulo  * Driver methods.
23432a8088fSRui Paulo  */
23532a8088fSRui Paulo static device_method_t	asmc_methods[] = {
23632a8088fSRui Paulo 	DEVMETHOD(device_probe,		asmc_probe),
23732a8088fSRui Paulo 	DEVMETHOD(device_attach,	asmc_attach),
23832a8088fSRui Paulo 	DEVMETHOD(device_detach,	asmc_detach),
23932a8088fSRui Paulo 
24032a8088fSRui Paulo 	{ 0, 0 }
24132a8088fSRui Paulo };
24232a8088fSRui Paulo 
24332a8088fSRui Paulo static driver_t	asmc_driver = {
24432a8088fSRui Paulo 	"asmc",
24532a8088fSRui Paulo 	asmc_methods,
24632a8088fSRui Paulo 	sizeof(struct asmc_softc)
24732a8088fSRui Paulo };
24832a8088fSRui Paulo 
2494470f0f3SRui Paulo /*
2504470f0f3SRui Paulo  * Debugging
2514470f0f3SRui Paulo  */
2524470f0f3SRui Paulo #define	_COMPONENT	ACPI_OEM
2534470f0f3SRui Paulo ACPI_MODULE_NAME("ASMC")
2544470f0f3SRui Paulo #ifdef DEBUG
2554470f0f3SRui Paulo #define ASMC_DPRINTF(str)	device_printf(dev, str)
2564fb9bf66SRui Paulo #else
2574fb9bf66SRui Paulo #define ASMC_DPRINTF(str)
2584470f0f3SRui Paulo #endif
2594470f0f3SRui Paulo 
260be80e49aSRui Paulo /* NB: can't be const */
2614470f0f3SRui Paulo static char *asmc_ids[] = { "APP0001", NULL };
2624470f0f3SRui Paulo 
26332a8088fSRui Paulo static devclass_t asmc_devclass;
26432a8088fSRui Paulo 
2654470f0f3SRui Paulo DRIVER_MODULE(asmc, acpi, asmc_driver, asmc_devclass, NULL, NULL);
2664470f0f3SRui Paulo MODULE_DEPEND(asmc, acpi, 1, 1, 1);
26732a8088fSRui Paulo 
26832a8088fSRui Paulo static struct asmc_model *
26932a8088fSRui Paulo asmc_match(device_t dev)
27032a8088fSRui Paulo {
27132a8088fSRui Paulo 	int i;
27232a8088fSRui Paulo 	char *model;
27332a8088fSRui Paulo 
27432a8088fSRui Paulo 	model = getenv("smbios.system.product");
27547105877SRui Paulo 	if (model == NULL)
27647105877SRui Paulo 		return (NULL);
27747105877SRui Paulo 
27832a8088fSRui Paulo 	for (i = 0; asmc_models[i].smc_model; i++) {
27932a8088fSRui Paulo 		if (!strncmp(model, asmc_models[i].smc_model, strlen(model))) {
28032a8088fSRui Paulo 			freeenv(model);
28132a8088fSRui Paulo 			return (&asmc_models[i]);
28232a8088fSRui Paulo 		}
28332a8088fSRui Paulo 	}
28432a8088fSRui Paulo 	freeenv(model);
28532a8088fSRui Paulo 
28632a8088fSRui Paulo 	return (NULL);
28732a8088fSRui Paulo }
28832a8088fSRui Paulo 
28932a8088fSRui Paulo static int
29032a8088fSRui Paulo asmc_probe(device_t dev)
29132a8088fSRui Paulo {
29232a8088fSRui Paulo 	struct asmc_model *model;
29332a8088fSRui Paulo 
2949cb3ef6eSRui Paulo 	if (resource_disabled("asmc", 0))
29532a8088fSRui Paulo 		return (ENXIO);
2964470f0f3SRui Paulo 	if (ACPI_ID_PROBE(device_get_parent(dev), dev, asmc_ids) == NULL)
2974470f0f3SRui Paulo 		return (ENXIO);
2984470f0f3SRui Paulo 
29932a8088fSRui Paulo 	model = asmc_match(dev);
3004470f0f3SRui Paulo 	if (!model) {
3014470f0f3SRui Paulo 		device_printf(dev, "model not recognized\n");
30232a8088fSRui Paulo 		return (ENXIO);
3034470f0f3SRui Paulo 	}
30432a8088fSRui Paulo 	device_set_desc(dev, model->smc_desc);
30532a8088fSRui Paulo 
30632a8088fSRui Paulo 	return (BUS_PROBE_DEFAULT);
30732a8088fSRui Paulo }
30832a8088fSRui Paulo 
30932a8088fSRui Paulo static int
31032a8088fSRui Paulo asmc_attach(device_t dev)
31132a8088fSRui Paulo {
31232a8088fSRui Paulo 	int i, j;
31332a8088fSRui Paulo 	int ret;
31432a8088fSRui Paulo 	char name[2];
31532a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
31632a8088fSRui Paulo 	struct sysctl_ctx_list *sysctlctx;
31732a8088fSRui Paulo 	struct sysctl_oid *sysctlnode;
31832a8088fSRui Paulo 	struct asmc_model *model;
31932a8088fSRui Paulo 
3204470f0f3SRui Paulo 	sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
3214470f0f3SRui Paulo 	    &sc->sc_rid_port, RF_ACTIVE);
3224470f0f3SRui Paulo 	if (sc->sc_ioport == NULL) {
3234470f0f3SRui Paulo 		device_printf(dev, "unable to allocate IO port\n");
3244470f0f3SRui Paulo 		return (ENOMEM);
3254470f0f3SRui Paulo 	}
3264470f0f3SRui Paulo 
32732a8088fSRui Paulo 	sysctlctx  = device_get_sysctl_ctx(dev);
32832a8088fSRui Paulo 	sysctlnode = device_get_sysctl_tree(dev);
32932a8088fSRui Paulo 
33032a8088fSRui Paulo 	model = asmc_match(dev);
33132a8088fSRui Paulo 
33232a8088fSRui Paulo 	mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN);
33332a8088fSRui Paulo 
33432a8088fSRui Paulo 	sc->sc_model = model;
33532a8088fSRui Paulo 	asmc_init(dev);
33632a8088fSRui Paulo 
33732a8088fSRui Paulo 	/*
33832a8088fSRui Paulo 	 * dev.asmc.n.fan.* tree.
33932a8088fSRui Paulo 	 */
34032a8088fSRui Paulo 	sc->sc_fan_tree[0] = SYSCTL_ADD_NODE(sysctlctx,
34132a8088fSRui Paulo 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "fan",
34232a8088fSRui Paulo 	    CTLFLAG_RD, 0, "Fan Root Tree");
34332a8088fSRui Paulo 
34432a8088fSRui Paulo 	for (i = 1; i <= sc->sc_nfan; i++) {
34532a8088fSRui Paulo 		j = i - 1;
34632a8088fSRui Paulo 		name[0] = '0' + j;
34732a8088fSRui Paulo 		name[1] = 0;
34832a8088fSRui Paulo 		sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx,
34932a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[0]),
35032a8088fSRui Paulo 		    OID_AUTO, name, CTLFLAG_RD, 0,
35132a8088fSRui Paulo 		    "Fan Subtree");
35232a8088fSRui Paulo 
35332a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
35432a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
35532a8088fSRui Paulo 		    OID_AUTO, "speed", CTLTYPE_INT | CTLFLAG_RD,
35632a8088fSRui Paulo 		    dev, j, model->smc_fan_speed, "I",
35732a8088fSRui Paulo 		    "Fan speed in RPM");
35832a8088fSRui Paulo 
35932a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
36032a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
36132a8088fSRui Paulo 		    OID_AUTO, "safespeed",
36232a8088fSRui Paulo 		    CTLTYPE_INT | CTLFLAG_RD,
36332a8088fSRui Paulo 		    dev, j, model->smc_fan_safespeed, "I",
36432a8088fSRui Paulo 		    "Fan safe speed in RPM");
36532a8088fSRui Paulo 
36632a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
36732a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
36832a8088fSRui Paulo 		    OID_AUTO, "minspeed",
36932a8088fSRui Paulo 		    CTLTYPE_INT | CTLFLAG_RD,
37032a8088fSRui Paulo 		    dev, j, model->smc_fan_minspeed, "I",
37132a8088fSRui Paulo 		    "Fan minimum speed in RPM");
37232a8088fSRui Paulo 
37332a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
37432a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
37532a8088fSRui Paulo 		    OID_AUTO, "maxspeed",
37632a8088fSRui Paulo 		    CTLTYPE_INT | CTLFLAG_RD,
37732a8088fSRui Paulo 		    dev, j, model->smc_fan_maxspeed, "I",
37832a8088fSRui Paulo 		    "Fan maximum speed in RPM");
37932a8088fSRui Paulo 
38032a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
38132a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
38232a8088fSRui Paulo 		    OID_AUTO, "targetspeed",
38332a8088fSRui Paulo 		    CTLTYPE_INT | CTLFLAG_RD,
38432a8088fSRui Paulo 		    dev, j, model->smc_fan_targetspeed, "I",
38532a8088fSRui Paulo 		    "Fan target speed in RPM");
38632a8088fSRui Paulo 	}
38732a8088fSRui Paulo 
38832a8088fSRui Paulo 	/*
38932a8088fSRui Paulo 	 * dev.asmc.n.temp tree.
39032a8088fSRui Paulo 	 */
39132a8088fSRui Paulo 	sc->sc_temp_tree = SYSCTL_ADD_NODE(sysctlctx,
39232a8088fSRui Paulo 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp",
39332a8088fSRui Paulo 	    CTLFLAG_RD, 0, "Temperature sensors");
39432a8088fSRui Paulo 
39532a8088fSRui Paulo 	for (i = 0; model->smc_temps[i]; i++) {
39632a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
39732a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_temp_tree),
39832a8088fSRui Paulo 		    OID_AUTO, model->smc_tempnames[i],
39932a8088fSRui Paulo 		    CTLTYPE_INT | CTLFLAG_RD,
40032a8088fSRui Paulo 		    dev, i, asmc_temp_sysctl, "I",
40132a8088fSRui Paulo 		    model->smc_tempdescs[i]);
40232a8088fSRui Paulo 	}
40332a8088fSRui Paulo 
404be80e49aSRui Paulo 	/*
405be80e49aSRui Paulo 	 * dev.asmc.n.light
406be80e49aSRui Paulo 	 */
407be80e49aSRui Paulo 	if (model->smc_light_left) {
408be80e49aSRui Paulo 		sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx,
409be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light",
410be80e49aSRui Paulo 		    CTLFLAG_RD, 0, "Keyboard backlight sensors");
411be80e49aSRui Paulo 
412be80e49aSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
413be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_light_tree),
414be80e49aSRui Paulo 		    OID_AUTO, "left", CTLTYPE_INT | CTLFLAG_RD,
415be80e49aSRui Paulo 		    dev, 0, model->smc_light_left, "I",
416be80e49aSRui Paulo 		    "Keyboard backlight left sensor");
417be80e49aSRui Paulo 
418be80e49aSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
419be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_light_tree),
420be80e49aSRui Paulo 		    OID_AUTO, "right", CTLTYPE_INT | CTLFLAG_RD,
421be80e49aSRui Paulo 		    dev, 0, model->smc_light_right, "I",
422be80e49aSRui Paulo 		    "Keyboard backlight right sensor");
423be80e49aSRui Paulo 
424be80e49aSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
425be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_light_tree),
4263471c35dSRui Paulo 		    OID_AUTO, "control",
4273471c35dSRui Paulo 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
428be80e49aSRui Paulo 		    dev, 0, model->smc_light_control, "I",
429be80e49aSRui Paulo 		    "Keyboard backlight brightness control");
430be80e49aSRui Paulo 	}
431be80e49aSRui Paulo 
43232a8088fSRui Paulo 	if (model->smc_sms_x == NULL)
43332a8088fSRui Paulo 		goto nosms;
43432a8088fSRui Paulo 
43532a8088fSRui Paulo 	/*
43632a8088fSRui Paulo 	 * dev.asmc.n.sms tree.
43732a8088fSRui Paulo 	 */
43832a8088fSRui Paulo 	sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx,
43932a8088fSRui Paulo 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "sms",
44032a8088fSRui Paulo 	    CTLFLAG_RD, 0, "Sudden Motion Sensor");
44132a8088fSRui Paulo 
44232a8088fSRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
44332a8088fSRui Paulo 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
44432a8088fSRui Paulo 	    OID_AUTO, "x", CTLTYPE_INT | CTLFLAG_RD,
44532a8088fSRui Paulo 	    dev, 0, model->smc_sms_x, "I",
44632a8088fSRui Paulo 	    "Sudden Motion Sensor X value");
44732a8088fSRui Paulo 
44832a8088fSRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
44932a8088fSRui Paulo 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
45032a8088fSRui Paulo 	    OID_AUTO, "y", CTLTYPE_INT | CTLFLAG_RD,
45132a8088fSRui Paulo 	    dev, 0, model->smc_sms_y, "I",
45232a8088fSRui Paulo 	    "Sudden Motion Sensor Y value");
45332a8088fSRui Paulo 
45432a8088fSRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
45532a8088fSRui Paulo 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
45632a8088fSRui Paulo 	    OID_AUTO, "z", CTLTYPE_INT | CTLFLAG_RD,
45732a8088fSRui Paulo 	    dev, 0, model->smc_sms_z, "I",
45832a8088fSRui Paulo 	    "Sudden Motion Sensor Z value");
45932a8088fSRui Paulo 
46032a8088fSRui Paulo 	/*
46132a8088fSRui Paulo 	 * Need a taskqueue to send devctl_notify() events
46232a8088fSRui Paulo 	 * when the SMS interrupt us.
46332a8088fSRui Paulo 	 *
46432a8088fSRui Paulo 	 * PI_REALTIME is used due to the sensitivity of the
46532a8088fSRui Paulo 	 * interrupt. An interrupt from the SMS means that the
46632a8088fSRui Paulo 	 * disk heads should be turned off as quickly as possible.
46732a8088fSRui Paulo 	 *
46832a8088fSRui Paulo 	 * We only need to do this for the non INTR_FILTER case.
46932a8088fSRui Paulo 	 */
47032a8088fSRui Paulo 	sc->sc_sms_tq = NULL;
47132a8088fSRui Paulo #ifndef INTR_FILTER
47232a8088fSRui Paulo 	TASK_INIT(&sc->sc_sms_task, 0, asmc_sms_task, sc);
47332a8088fSRui Paulo 	sc->sc_sms_tq = taskqueue_create_fast("asmc_taskq", M_WAITOK,
47432a8088fSRui Paulo 	    taskqueue_thread_enqueue, &sc->sc_sms_tq);
47532a8088fSRui Paulo 	taskqueue_start_threads(&sc->sc_sms_tq, 1, PI_REALTIME, "%s sms taskq",
47632a8088fSRui Paulo 	    device_get_nameunit(dev));
47732a8088fSRui Paulo #endif
47832a8088fSRui Paulo 	/*
47932a8088fSRui Paulo 	 * Allocate an IRQ for the SMS.
48032a8088fSRui Paulo 	 */
4814470f0f3SRui Paulo 	sc->sc_rid_irq = 0;
4824470f0f3SRui Paulo 	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
4834470f0f3SRui Paulo 	    &sc->sc_rid_irq, RF_ACTIVE);
4844470f0f3SRui Paulo 	if (sc->sc_irq == NULL) {
48532a8088fSRui Paulo 		device_printf(dev, "unable to allocate IRQ resource\n");
48632a8088fSRui Paulo 		ret = ENXIO;
48732a8088fSRui Paulo 		goto err2;
48832a8088fSRui Paulo 	}
48932a8088fSRui Paulo 
4904470f0f3SRui Paulo 	ret = bus_setup_intr(dev, sc->sc_irq,
49132a8088fSRui Paulo 	          INTR_TYPE_MISC | INTR_MPSAFE,
49232a8088fSRui Paulo #ifdef INTR_FILTER
49332a8088fSRui Paulo 	    asmc_sms_intrfast, asmc_sms_handler,
49432a8088fSRui Paulo #else
49532a8088fSRui Paulo 	    asmc_sms_intrfast, NULL,
49632a8088fSRui Paulo #endif
49732a8088fSRui Paulo 	    dev, &sc->sc_cookie);
49832a8088fSRui Paulo 
49932a8088fSRui Paulo 	if (ret) {
50032a8088fSRui Paulo 		device_printf(dev, "unable to setup SMS IRQ\n");
50132a8088fSRui Paulo 		goto err1;
50232a8088fSRui Paulo 	}
50332a8088fSRui Paulo nosms:
50432a8088fSRui Paulo 	return (0);
50532a8088fSRui Paulo err1:
5064470f0f3SRui Paulo 	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, sc->sc_irq);
50732a8088fSRui Paulo err2:
5084470f0f3SRui Paulo 	bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
5094470f0f3SRui Paulo 	    sc->sc_ioport);
51032a8088fSRui Paulo 	mtx_destroy(&sc->sc_mtx);
51132a8088fSRui Paulo 	if (sc->sc_sms_tq)
51232a8088fSRui Paulo 		taskqueue_free(sc->sc_sms_tq);
51332a8088fSRui Paulo 
51432a8088fSRui Paulo 	return (ret);
51532a8088fSRui Paulo }
51632a8088fSRui Paulo 
51732a8088fSRui Paulo static int
51832a8088fSRui Paulo asmc_detach(device_t dev)
51932a8088fSRui Paulo {
52032a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
52132a8088fSRui Paulo 
52232a8088fSRui Paulo 	if (sc->sc_sms_tq) {
52332a8088fSRui Paulo 		taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task);
52432a8088fSRui Paulo 		taskqueue_free(sc->sc_sms_tq);
52532a8088fSRui Paulo 	}
52632a8088fSRui Paulo 	if (sc->sc_cookie)
5274470f0f3SRui Paulo 		bus_teardown_intr(dev, sc->sc_irq, sc->sc_cookie);
5284470f0f3SRui Paulo 	if (sc->sc_irq)
5294470f0f3SRui Paulo 		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq,
5304470f0f3SRui Paulo 		    sc->sc_irq);
5314470f0f3SRui Paulo 	if (sc->sc_ioport)
5324470f0f3SRui Paulo 		bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
5334470f0f3SRui Paulo 		    sc->sc_ioport);
53432a8088fSRui Paulo 	mtx_destroy(&sc->sc_mtx);
53532a8088fSRui Paulo 
53632a8088fSRui Paulo 	return (0);
53732a8088fSRui Paulo }
53832a8088fSRui Paulo 
5391269f4d4SRui Paulo #ifdef DEBUG
5401269f4d4SRui Paulo void asmc_dumpall(device_t dev)
5411269f4d4SRui Paulo {
5421269f4d4SRui Paulo 	int i;
5431269f4d4SRui Paulo 
5441269f4d4SRui Paulo 	/* XXX magic number */
5451269f4d4SRui Paulo 	for (i=0; i < 0x100; i++)
5461269f4d4SRui Paulo 		asmc_key_dump(dev, i);
5471269f4d4SRui Paulo }
5481269f4d4SRui Paulo #endif
5491269f4d4SRui Paulo 
55032a8088fSRui Paulo static int
55132a8088fSRui Paulo asmc_init(device_t dev)
55232a8088fSRui Paulo {
55332a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
55432a8088fSRui Paulo 	int i, error = 1;
55532a8088fSRui Paulo 	uint8_t buf[4];
55632a8088fSRui Paulo 
55732a8088fSRui Paulo 	if (sc->sc_model->smc_sms_x == NULL)
55832a8088fSRui Paulo 		goto nosms;
55932a8088fSRui Paulo 
56032a8088fSRui Paulo 	/*
56132a8088fSRui Paulo 	 * We are ready to recieve interrupts from the SMS.
56232a8088fSRui Paulo 	 */
56332a8088fSRui Paulo 	buf[0] = 0x01;
5644470f0f3SRui Paulo 	ASMC_DPRINTF(("intok key\n"));
56532a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_INTOK, buf, 1);
56632a8088fSRui Paulo 	DELAY(50);
56732a8088fSRui Paulo 
56832a8088fSRui Paulo 	/*
56932a8088fSRui Paulo 	 * Initiate the polling intervals.
57032a8088fSRui Paulo 	 */
57132a8088fSRui Paulo 	buf[0] = 20; /* msecs */
5724470f0f3SRui Paulo 	ASMC_DPRINTF(("low int key\n"));
57332a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_LOW_INT, buf, 1);
57432a8088fSRui Paulo 	DELAY(200);
57532a8088fSRui Paulo 
57632a8088fSRui Paulo 	buf[0] = 20; /* msecs */
5774470f0f3SRui Paulo 	ASMC_DPRINTF(("high int key\n"));
57832a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_HIGH_INT, buf, 1);
57932a8088fSRui Paulo 	DELAY(200);
58032a8088fSRui Paulo 
58132a8088fSRui Paulo 	buf[0] = 0x00;
58232a8088fSRui Paulo 	buf[1] = 0x60;
5834470f0f3SRui Paulo 	ASMC_DPRINTF(("sms low key\n"));
58432a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_LOW, buf, 2);
58532a8088fSRui Paulo 	DELAY(200);
58632a8088fSRui Paulo 
58732a8088fSRui Paulo 	buf[0] = 0x01;
58832a8088fSRui Paulo 	buf[1] = 0xc0;
5894470f0f3SRui Paulo 	ASMC_DPRINTF(("sms high key\n"));
59032a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_HIGH, buf, 2);
59132a8088fSRui Paulo 	DELAY(200);
59232a8088fSRui Paulo 
59332a8088fSRui Paulo 	/*
59432a8088fSRui Paulo 	 * I'm not sure what this key does, but it seems to be
59532a8088fSRui Paulo 	 * required.
59632a8088fSRui Paulo 	 */
59732a8088fSRui Paulo 	buf[0] = 0x01;
5984470f0f3SRui Paulo 	ASMC_DPRINTF(("sms flag key\n"));
59932a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_FLAG, buf, 1);
600b75dfbe8SRui Paulo 	DELAY(100);
60132a8088fSRui Paulo 
6021269f4d4SRui Paulo 	sc->sc_sms_intr_works = 0;
6031269f4d4SRui Paulo 
60432a8088fSRui Paulo 	/*
6051269f4d4SRui Paulo 	 * Retry SMS initialization 1000 times
6061269f4d4SRui Paulo 	 * (takes approx. 2 seconds in worst case)
60732a8088fSRui Paulo 	 */
6081269f4d4SRui Paulo 	for (i = 0; i < 1000; i++) {
60932a8088fSRui Paulo 		if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 &&
6101269f4d4SRui Paulo 		    (buf[0] == ASMC_SMS_INIT1 && buf[1] == ASMC_SMS_INIT2)) {
61132a8088fSRui Paulo 			error = 0;
6121269f4d4SRui Paulo 			sc->sc_sms_intr_works = 1;
6134fb9bf66SRui Paulo 			goto out;
61432a8088fSRui Paulo 		}
61532a8088fSRui Paulo 		buf[0] = ASMC_SMS_INIT1;
61632a8088fSRui Paulo 		buf[1] = ASMC_SMS_INIT2;
6174470f0f3SRui Paulo 		ASMC_DPRINTF(("sms key\n"));
61832a8088fSRui Paulo 		asmc_key_write(dev, ASMC_KEY_SMS, buf, 2);
61932a8088fSRui Paulo 		DELAY(50);
62032a8088fSRui Paulo 	}
6214fb9bf66SRui Paulo 	device_printf(dev, "WARNING: Sudden Motion Sensor not initialized!\n");
62232a8088fSRui Paulo 
6234fb9bf66SRui Paulo out:
62432a8088fSRui Paulo 	asmc_sms_calibrate(dev);
62532a8088fSRui Paulo nosms:
62632a8088fSRui Paulo 	sc->sc_nfan = asmc_fan_count(dev);
62732a8088fSRui Paulo 	if (sc->sc_nfan > ASMC_MAXFANS) {
62832a8088fSRui Paulo 		device_printf(dev, "more than %d fans were detected. Please "
62932a8088fSRui Paulo 		    "report this.\n", ASMC_MAXFANS);
63032a8088fSRui Paulo 		sc->sc_nfan = ASMC_MAXFANS;
63132a8088fSRui Paulo 	}
63232a8088fSRui Paulo 
63332a8088fSRui Paulo 	if (bootverbose) {
63432a8088fSRui Paulo 		/*
63532a8088fSRui Paulo 		 * XXX: The number of keys is a 32 bit buffer, but
63632a8088fSRui Paulo 		 * right now Apple only uses the last 8 bit.
63732a8088fSRui Paulo 		 */
63832a8088fSRui Paulo 		asmc_key_read(dev, ASMC_NKEYS, buf, 4);
63932a8088fSRui Paulo 		device_printf(dev, "number of keys: %d\n", buf[3]);
64032a8088fSRui Paulo 	}
64132a8088fSRui Paulo 
6421269f4d4SRui Paulo #ifdef DEBUG
6431269f4d4SRui Paulo 	asmc_dumpall(dev);
6441269f4d4SRui Paulo #endif
6451269f4d4SRui Paulo 
64632a8088fSRui Paulo 	return (error);
64732a8088fSRui Paulo }
64832a8088fSRui Paulo 
64932a8088fSRui Paulo /*
65032a8088fSRui Paulo  * We need to make sure that the SMC acks the byte sent.
651be80e49aSRui Paulo  * Just wait up to (amount * 10)  ms.
65232a8088fSRui Paulo  */
65332a8088fSRui Paulo static int
654be80e49aSRui Paulo asmc_wait_ack(device_t dev, uint8_t val, int amount)
65532a8088fSRui Paulo {
6564470f0f3SRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
65732a8088fSRui Paulo 	u_int i;
65832a8088fSRui Paulo 
65932a8088fSRui Paulo 	val = val & ASMC_STATUS_MASK;
66032a8088fSRui Paulo 
661be80e49aSRui Paulo 	for (i = 0; i < amount; i++) {
6624470f0f3SRui Paulo 		if ((ASMC_CMDPORT_READ(sc) & ASMC_STATUS_MASK) == val)
66332a8088fSRui Paulo 			return (0);
66432a8088fSRui Paulo 		DELAY(10);
66532a8088fSRui Paulo 	}
66632a8088fSRui Paulo 
667be80e49aSRui Paulo 	return (1);
668be80e49aSRui Paulo }
669be80e49aSRui Paulo 
670be80e49aSRui Paulo /*
671be80e49aSRui Paulo  * We need to make sure that the SMC acks the byte sent.
672be80e49aSRui Paulo  * Just wait up to 100 ms.
673be80e49aSRui Paulo  */
674be80e49aSRui Paulo static int
675be80e49aSRui Paulo asmc_wait(device_t dev, uint8_t val)
676be80e49aSRui Paulo {
677be80e49aSRui Paulo 	struct asmc_softc *sc;
678be80e49aSRui Paulo 
679be80e49aSRui Paulo 	if (asmc_wait_ack(dev, val, 1000) == 0)
680be80e49aSRui Paulo 		return (0);
681be80e49aSRui Paulo 
682be80e49aSRui Paulo 	sc = device_get_softc(dev);
683be80e49aSRui Paulo 	val = val & ASMC_STATUS_MASK;
684be80e49aSRui Paulo 
685be80e49aSRui Paulo #ifdef DEBUG
68632a8088fSRui Paulo 	device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, val,
6874470f0f3SRui Paulo 	    ASMC_CMDPORT_READ(sc));
688be80e49aSRui Paulo #endif
689be80e49aSRui Paulo 	return (1);
690be80e49aSRui Paulo }
69132a8088fSRui Paulo 
692be80e49aSRui Paulo /*
693be80e49aSRui Paulo  * Send the given command, retrying up to 10 times if
694be80e49aSRui Paulo  * the acknowledgement fails.
695be80e49aSRui Paulo  */
696be80e49aSRui Paulo static int
697be80e49aSRui Paulo asmc_command(device_t dev, uint8_t command) {
698be80e49aSRui Paulo 
699be80e49aSRui Paulo 	int i;
700be80e49aSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
701be80e49aSRui Paulo 
702be80e49aSRui Paulo 	for (i=0; i < 10; i++) {
703be80e49aSRui Paulo 		ASMC_CMDPORT_WRITE(sc, command);
704be80e49aSRui Paulo 		if (asmc_wait_ack(dev, 0x0c, 100) == 0) {
705be80e49aSRui Paulo 			return (0);
706be80e49aSRui Paulo 		}
707be80e49aSRui Paulo 	}
708be80e49aSRui Paulo 
709be80e49aSRui Paulo #ifdef DEBUG
710be80e49aSRui Paulo 	device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, command,
711be80e49aSRui Paulo 	    ASMC_CMDPORT_READ(sc));
712be80e49aSRui Paulo #endif
71332a8088fSRui Paulo 	return (1);
71432a8088fSRui Paulo }
71532a8088fSRui Paulo 
71632a8088fSRui Paulo static int
71732a8088fSRui Paulo asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len)
71832a8088fSRui Paulo {
719be80e49aSRui Paulo 	int i, error = 1, try = 0;
72032a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
72132a8088fSRui Paulo 
72232a8088fSRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
72332a8088fSRui Paulo 
724be80e49aSRui Paulo begin:
725be80e49aSRui Paulo 	if (asmc_command(dev, ASMC_CMDREAD))
72632a8088fSRui Paulo 		goto out;
72732a8088fSRui Paulo 
72832a8088fSRui Paulo 	for (i = 0; i < 4; i++) {
7294470f0f3SRui Paulo 		ASMC_DATAPORT_WRITE(sc, key[i]);
73032a8088fSRui Paulo 		if (asmc_wait(dev, 0x04))
73132a8088fSRui Paulo 			goto out;
73232a8088fSRui Paulo 	}
73332a8088fSRui Paulo 
7344470f0f3SRui Paulo 	ASMC_DATAPORT_WRITE(sc, len);
73532a8088fSRui Paulo 
73632a8088fSRui Paulo 	for (i = 0; i < len; i++) {
73732a8088fSRui Paulo 		if (asmc_wait(dev, 0x05))
73832a8088fSRui Paulo 			goto out;
7394470f0f3SRui Paulo 		buf[i] = ASMC_DATAPORT_READ(sc);
74032a8088fSRui Paulo 	}
74132a8088fSRui Paulo 
74232a8088fSRui Paulo 	error = 0;
74332a8088fSRui Paulo out:
744be80e49aSRui Paulo 	if (error) {
745be80e49aSRui Paulo 		if (++try < 10) goto begin;
746be80e49aSRui Paulo 		device_printf(dev,"%s for key %s failed %d times, giving up\n",
747be80e49aSRui Paulo 			__func__, key, try);
748be80e49aSRui Paulo 	}
749be80e49aSRui Paulo 
75032a8088fSRui Paulo 	mtx_unlock_spin(&sc->sc_mtx);
75132a8088fSRui Paulo 
75232a8088fSRui Paulo 	return (error);
75332a8088fSRui Paulo }
75432a8088fSRui Paulo 
7551269f4d4SRui Paulo #ifdef DEBUG
7561269f4d4SRui Paulo static int
7571269f4d4SRui Paulo asmc_key_dump(device_t dev, int number)
7581269f4d4SRui Paulo {
7591269f4d4SRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
7601269f4d4SRui Paulo 	char key[5] = { 0 };
7611269f4d4SRui Paulo 	char type[7] = { 0 };
7621269f4d4SRui Paulo 	uint8_t index[4];
7631269f4d4SRui Paulo 	uint8_t v[32];
7641269f4d4SRui Paulo 	uint8_t maxlen;
7651269f4d4SRui Paulo 	int i, error = 1, try = 0;
7661269f4d4SRui Paulo 
7671269f4d4SRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
7681269f4d4SRui Paulo 
7691269f4d4SRui Paulo 	index[0] = (number >> 24) & 0xff;
7701269f4d4SRui Paulo 	index[1] = (number >> 16) & 0xff;
7711269f4d4SRui Paulo 	index[2] = (number >> 8) & 0xff;
7721269f4d4SRui Paulo 	index[3] = (number) & 0xff;
7731269f4d4SRui Paulo 
7741269f4d4SRui Paulo begin:
7751269f4d4SRui Paulo 	if (asmc_command(dev, 0x12))
7761269f4d4SRui Paulo 		goto out;
7771269f4d4SRui Paulo 
7781269f4d4SRui Paulo 	for (i = 0; i < 4; i++) {
7791269f4d4SRui Paulo 		ASMC_DATAPORT_WRITE(sc, index[i]);
7801269f4d4SRui Paulo 		if (asmc_wait(dev, 0x04))
7811269f4d4SRui Paulo 			goto out;
7821269f4d4SRui Paulo 	}
7831269f4d4SRui Paulo 
7841269f4d4SRui Paulo 	ASMC_DATAPORT_WRITE(sc, 4);
7851269f4d4SRui Paulo 
7861269f4d4SRui Paulo 	for (i = 0; i < 4; i++) {
7871269f4d4SRui Paulo 		if (asmc_wait(dev, 0x05))
7881269f4d4SRui Paulo 			goto out;
7891269f4d4SRui Paulo 		key[i] = ASMC_DATAPORT_READ(sc);
7901269f4d4SRui Paulo 	}
7911269f4d4SRui Paulo 
7921269f4d4SRui Paulo 	/* get type */
7931269f4d4SRui Paulo 	if (asmc_command(dev, 0x13))
7941269f4d4SRui Paulo 		goto out;
7951269f4d4SRui Paulo 
7961269f4d4SRui Paulo 	for (i = 0; i < 4; i++) {
7971269f4d4SRui Paulo 		ASMC_DATAPORT_WRITE(sc, key[i]);
7981269f4d4SRui Paulo 		if (asmc_wait(dev, 0x04))
7991269f4d4SRui Paulo 			goto out;
8001269f4d4SRui Paulo 	}
8011269f4d4SRui Paulo 
8021269f4d4SRui Paulo 	ASMC_DATAPORT_WRITE(sc, 6);
8031269f4d4SRui Paulo 
8041269f4d4SRui Paulo 	for (i = 0; i < 6; i++) {
8051269f4d4SRui Paulo 		if (asmc_wait(dev, 0x05))
8061269f4d4SRui Paulo 			goto out;
8071269f4d4SRui Paulo 		type[i] = ASMC_DATAPORT_READ(sc);
8081269f4d4SRui Paulo 	}
8091269f4d4SRui Paulo 
8101269f4d4SRui Paulo 	error = 0;
8111269f4d4SRui Paulo out:
8121269f4d4SRui Paulo 	if (error) {
8131269f4d4SRui Paulo 		if (++try < 10) goto begin;
8141269f4d4SRui Paulo 		device_printf(dev,"%s for key %s failed %d times, giving up\n",
8151269f4d4SRui Paulo 			__func__, key, try);
8161269f4d4SRui Paulo 		mtx_unlock_spin(&sc->sc_mtx);
8171269f4d4SRui Paulo 	}
8181269f4d4SRui Paulo 	else {
8191269f4d4SRui Paulo 		char buf[1024];
8201269f4d4SRui Paulo 		char buf2[8];
8211269f4d4SRui Paulo 		mtx_unlock_spin(&sc->sc_mtx);
8221269f4d4SRui Paulo 		maxlen = type[0];
8231269f4d4SRui Paulo 		type[0] = ' ';
8241269f4d4SRui Paulo 		type[5] = 0;
8251269f4d4SRui Paulo 		if (maxlen > sizeof(v)) {
8261269f4d4SRui Paulo 			device_printf(dev, "WARNING: cropping maxlen "
8271269f4d4SRui Paulo 			    "from %d to %lud\n", maxlen, sizeof(v));
8281269f4d4SRui Paulo 			maxlen = sizeof(v);
8291269f4d4SRui Paulo 		}
8301269f4d4SRui Paulo 		for (i = 0; i < sizeof(v); i++) {
8311269f4d4SRui Paulo 			v[i] = 0;
8321269f4d4SRui Paulo 		}
8331269f4d4SRui Paulo 		asmc_key_read(dev, key, v, maxlen);
8341269f4d4SRui Paulo 		snprintf(buf, sizeof(buf), "key %d is: %s, type %s "
8351269f4d4SRui Paulo 		    "(len %d), data", number, key, type, maxlen);
8361269f4d4SRui Paulo 		for (i = 0; i < maxlen; i++) {
8371269f4d4SRui Paulo 			snprintf(buf2, sizeof(buf), " %02x", v[i]);
8381269f4d4SRui Paulo 			strlcat(buf, buf2, sizeof(buf));
8391269f4d4SRui Paulo 		}
8401269f4d4SRui Paulo 		strlcat(buf, " \n", sizeof(buf));
8411269f4d4SRui Paulo 		device_printf(dev, buf);
8421269f4d4SRui Paulo 	}
8431269f4d4SRui Paulo 
8441269f4d4SRui Paulo 	return (error);
8451269f4d4SRui Paulo }
8461269f4d4SRui Paulo #endif
8471269f4d4SRui Paulo 
84832a8088fSRui Paulo static int
84932a8088fSRui Paulo asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len)
85032a8088fSRui Paulo {
851be80e49aSRui Paulo 	int i, error = -1, try = 0;
85232a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
85332a8088fSRui Paulo 
85432a8088fSRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
85532a8088fSRui Paulo 
856be80e49aSRui Paulo begin:
8574470f0f3SRui Paulo 	ASMC_DPRINTF(("cmd port: cmd write\n"));
858be80e49aSRui Paulo 	if (asmc_command(dev, ASMC_CMDWRITE))
85932a8088fSRui Paulo 		goto out;
86032a8088fSRui Paulo 
8614470f0f3SRui Paulo 	ASMC_DPRINTF(("data port: key\n"));
86232a8088fSRui Paulo 	for (i = 0; i < 4; i++) {
8634470f0f3SRui Paulo 		ASMC_DATAPORT_WRITE(sc, key[i]);
86432a8088fSRui Paulo 		if (asmc_wait(dev, 0x04))
86532a8088fSRui Paulo 			goto out;
86632a8088fSRui Paulo 	}
8674470f0f3SRui Paulo 	ASMC_DPRINTF(("data port: length\n"));
8684470f0f3SRui Paulo 	ASMC_DATAPORT_WRITE(sc, len);
86932a8088fSRui Paulo 
8704470f0f3SRui Paulo 	ASMC_DPRINTF(("data port: buffer\n"));
87132a8088fSRui Paulo 	for (i = 0; i < len; i++) {
87232a8088fSRui Paulo 		if (asmc_wait(dev, 0x04))
87332a8088fSRui Paulo 			goto out;
8744470f0f3SRui Paulo 		ASMC_DATAPORT_WRITE(sc, buf[i]);
87532a8088fSRui Paulo 	}
87632a8088fSRui Paulo 
87732a8088fSRui Paulo 	error = 0;
87832a8088fSRui Paulo out:
879be80e49aSRui Paulo 	if (error) {
880be80e49aSRui Paulo 		if (++try < 10) goto begin;
881be80e49aSRui Paulo 		device_printf(dev,"%s for key %s failed %d times, giving up\n",
882be80e49aSRui Paulo 			__func__, key, try);
883be80e49aSRui Paulo 	}
884be80e49aSRui Paulo 
88532a8088fSRui Paulo 	mtx_unlock_spin(&sc->sc_mtx);
88632a8088fSRui Paulo 
88732a8088fSRui Paulo 	return (error);
88832a8088fSRui Paulo 
88932a8088fSRui Paulo }
89032a8088fSRui Paulo 
89132a8088fSRui Paulo /*
89232a8088fSRui Paulo  * Fan control functions.
89332a8088fSRui Paulo  */
89432a8088fSRui Paulo static int
89532a8088fSRui Paulo asmc_fan_count(device_t dev)
89632a8088fSRui Paulo {
89732a8088fSRui Paulo 	uint8_t buf[1];
89832a8088fSRui Paulo 
89932a8088fSRui Paulo 	if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, 1) < 0)
90032a8088fSRui Paulo 		return (-1);
90132a8088fSRui Paulo 
90232a8088fSRui Paulo 	return (buf[0]);
90332a8088fSRui Paulo }
90432a8088fSRui Paulo 
90532a8088fSRui Paulo static int
90632a8088fSRui Paulo asmc_fan_getvalue(device_t dev, const char *key, int fan)
90732a8088fSRui Paulo {
90832a8088fSRui Paulo 	int speed;
90932a8088fSRui Paulo 	uint8_t buf[2];
91032a8088fSRui Paulo 	char fankey[5];
91132a8088fSRui Paulo 
91232a8088fSRui Paulo 	snprintf(fankey, sizeof(fankey), key, fan);
91332a8088fSRui Paulo 	if (asmc_key_read(dev, fankey, buf, 2) < 0)
91432a8088fSRui Paulo 		return (-1);
91532a8088fSRui Paulo 	speed = (buf[0] << 6) | (buf[1] >> 2);
91632a8088fSRui Paulo 
91732a8088fSRui Paulo 	return (speed);
91832a8088fSRui Paulo }
91932a8088fSRui Paulo 
92032a8088fSRui Paulo static int
92132a8088fSRui Paulo asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS)
92232a8088fSRui Paulo {
92332a8088fSRui Paulo 	device_t dev = (device_t) arg1;
92432a8088fSRui Paulo 	int fan = arg2;
92532a8088fSRui Paulo 	int error;
92632a8088fSRui Paulo 	int32_t v;
92732a8088fSRui Paulo 
92832a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSPEED, fan);
92932a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
93032a8088fSRui Paulo 
93132a8088fSRui Paulo 	return (error);
93232a8088fSRui Paulo }
93332a8088fSRui Paulo 
93432a8088fSRui Paulo static int
93532a8088fSRui Paulo asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS)
93632a8088fSRui Paulo {
93732a8088fSRui Paulo 	device_t dev = (device_t) arg1;
93832a8088fSRui Paulo 	int fan = arg2;
93932a8088fSRui Paulo 	int error;
94032a8088fSRui Paulo 	int32_t v;
94132a8088fSRui Paulo 
94232a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSAFESPEED, fan);
94332a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
94432a8088fSRui Paulo 
94532a8088fSRui Paulo 	return (error);
94632a8088fSRui Paulo }
94732a8088fSRui Paulo 
94832a8088fSRui Paulo 
94932a8088fSRui Paulo static int
95032a8088fSRui Paulo asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS)
95132a8088fSRui Paulo {
95232a8088fSRui Paulo 	device_t dev = (device_t) arg1;
95332a8088fSRui Paulo 	int fan = arg2;
95432a8088fSRui Paulo 	int error;
95532a8088fSRui Paulo 	int32_t v;
95632a8088fSRui Paulo 
95732a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMINSPEED, fan);
95832a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
95932a8088fSRui Paulo 
96032a8088fSRui Paulo 	return (error);
96132a8088fSRui Paulo }
96232a8088fSRui Paulo 
96332a8088fSRui Paulo static int
96432a8088fSRui Paulo asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS)
96532a8088fSRui Paulo {
96632a8088fSRui Paulo 	device_t dev = (device_t) arg1;
96732a8088fSRui Paulo 	int fan = arg2;
96832a8088fSRui Paulo 	int error;
96932a8088fSRui Paulo 	int32_t v;
97032a8088fSRui Paulo 
97132a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMAXSPEED, fan);
97232a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
97332a8088fSRui Paulo 
97432a8088fSRui Paulo 	return (error);
97532a8088fSRui Paulo }
97632a8088fSRui Paulo 
97732a8088fSRui Paulo static int
97832a8088fSRui Paulo asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS)
97932a8088fSRui Paulo {
98032a8088fSRui Paulo 	device_t dev = (device_t) arg1;
98132a8088fSRui Paulo 	int fan = arg2;
98232a8088fSRui Paulo 	int error;
98332a8088fSRui Paulo 	int32_t v;
98432a8088fSRui Paulo 
98532a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANTARGETSPEED, fan);
98632a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
98732a8088fSRui Paulo 
98832a8088fSRui Paulo 	return (error);
98932a8088fSRui Paulo }
99032a8088fSRui Paulo 
99132a8088fSRui Paulo /*
99232a8088fSRui Paulo  * Temperature functions.
99332a8088fSRui Paulo  */
99432a8088fSRui Paulo static int
99532a8088fSRui Paulo asmc_temp_getvalue(device_t dev, const char *key)
99632a8088fSRui Paulo {
99732a8088fSRui Paulo 	uint8_t buf[2];
99832a8088fSRui Paulo 
99932a8088fSRui Paulo 	/*
100032a8088fSRui Paulo 	 * Check for invalid temperatures.
100132a8088fSRui Paulo 	 */
100232a8088fSRui Paulo 	if (asmc_key_read(dev, key, buf, 2) < 0)
100332a8088fSRui Paulo 		return (-1);
100432a8088fSRui Paulo 
100532a8088fSRui Paulo 	return (buf[0]);
100632a8088fSRui Paulo }
100732a8088fSRui Paulo 
100832a8088fSRui Paulo static int
100932a8088fSRui Paulo asmc_temp_sysctl(SYSCTL_HANDLER_ARGS)
101032a8088fSRui Paulo {
101132a8088fSRui Paulo 	device_t dev = (device_t) arg1;
101232a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
101332a8088fSRui Paulo 	int error, val;
101432a8088fSRui Paulo 
101532a8088fSRui Paulo 	val = asmc_temp_getvalue(dev, sc->sc_model->smc_temps[arg2]);
101632a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &val, 0, req);
101732a8088fSRui Paulo 
101832a8088fSRui Paulo 	return (error);
101932a8088fSRui Paulo }
102032a8088fSRui Paulo 
102132a8088fSRui Paulo /*
102232a8088fSRui Paulo  * Sudden Motion Sensor functions.
102332a8088fSRui Paulo  */
102432a8088fSRui Paulo static int
102532a8088fSRui Paulo asmc_sms_read(device_t dev, const char *key, int16_t *val)
102632a8088fSRui Paulo {
102732a8088fSRui Paulo 	uint8_t buf[2];
102832a8088fSRui Paulo 	int error;
102932a8088fSRui Paulo 
103032a8088fSRui Paulo 	/* no need to do locking here as asmc_key_read() already does it */
103132a8088fSRui Paulo 	switch (key[3]) {
103232a8088fSRui Paulo 	case 'X':
103332a8088fSRui Paulo 	case 'Y':
103432a8088fSRui Paulo 	case 'Z':
103532a8088fSRui Paulo 		error =	asmc_key_read(dev, key, buf, 2);
103632a8088fSRui Paulo 		break;
103732a8088fSRui Paulo 	default:
103832a8088fSRui Paulo 		device_printf(dev, "%s called with invalid argument %s\n",
103932a8088fSRui Paulo 			      __func__, key);
104032a8088fSRui Paulo 		error = 1;
104132a8088fSRui Paulo 		goto out;
104232a8088fSRui Paulo 	}
104332a8088fSRui Paulo 	*val = ((int16_t)buf[0] << 8) | buf[1];
104432a8088fSRui Paulo out:
104532a8088fSRui Paulo 	return (error);
104632a8088fSRui Paulo }
104732a8088fSRui Paulo 
104832a8088fSRui Paulo static void
104932a8088fSRui Paulo asmc_sms_calibrate(device_t dev)
105032a8088fSRui Paulo {
105132a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
105232a8088fSRui Paulo 
105332a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_X, &sc->sms_rest_x);
105432a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &sc->sms_rest_y);
105532a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &sc->sms_rest_z);
105632a8088fSRui Paulo }
105732a8088fSRui Paulo 
105832a8088fSRui Paulo static int
105932a8088fSRui Paulo asmc_sms_intrfast(void *arg)
106032a8088fSRui Paulo {
106132a8088fSRui Paulo 	uint8_t type;
106232a8088fSRui Paulo 	device_t dev = (device_t) arg;
106332a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
10641269f4d4SRui Paulo 	if (!sc->sc_sms_intr_works)
10651269f4d4SRui Paulo 		return (FILTER_HANDLED);
106632a8088fSRui Paulo 
106732a8088fSRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
10684470f0f3SRui Paulo 	type = ASMC_INTPORT_READ(sc);
106932a8088fSRui Paulo 	mtx_unlock_spin(&sc->sc_mtx);
107032a8088fSRui Paulo 
107132a8088fSRui Paulo 	sc->sc_sms_intrtype = type;
107232a8088fSRui Paulo 	asmc_sms_printintr(dev, type);
107332a8088fSRui Paulo 
107432a8088fSRui Paulo #ifdef INTR_FILTER
107532a8088fSRui Paulo 	return (FILTER_SCHEDULE_THREAD | FILTER_HANDLED);
107632a8088fSRui Paulo #else
107732a8088fSRui Paulo 	taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task);
107832a8088fSRui Paulo #endif
107932a8088fSRui Paulo 	return (FILTER_HANDLED);
108032a8088fSRui Paulo }
108132a8088fSRui Paulo 
108232a8088fSRui Paulo #ifdef INTR_FILTER
108332a8088fSRui Paulo static void
108432a8088fSRui Paulo asmc_sms_handler(void *arg)
108532a8088fSRui Paulo {
108632a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(arg);
108732a8088fSRui Paulo 
108832a8088fSRui Paulo 	asmc_sms_task(sc, 0);
108932a8088fSRui Paulo }
109032a8088fSRui Paulo #endif
109132a8088fSRui Paulo 
109232a8088fSRui Paulo 
109332a8088fSRui Paulo static void
109432a8088fSRui Paulo asmc_sms_printintr(device_t dev, uint8_t type)
109532a8088fSRui Paulo {
109632a8088fSRui Paulo 
109732a8088fSRui Paulo 	switch (type) {
109832a8088fSRui Paulo 	case ASMC_SMS_INTFF:
109932a8088fSRui Paulo 		device_printf(dev, "WARNING: possible free fall!\n");
110032a8088fSRui Paulo 		break;
110132a8088fSRui Paulo 	case ASMC_SMS_INTHA:
110232a8088fSRui Paulo 		device_printf(dev, "WARNING: high acceleration detected!\n");
110332a8088fSRui Paulo 		break;
110432a8088fSRui Paulo 	case ASMC_SMS_INTSH:
110532a8088fSRui Paulo 		device_printf(dev, "WARNING: possible shock!\n");
110632a8088fSRui Paulo 		break;
110732a8088fSRui Paulo 	default:
110832a8088fSRui Paulo 		device_printf(dev, "%s unknown interrupt\n", __func__);
110932a8088fSRui Paulo 	}
111032a8088fSRui Paulo }
111132a8088fSRui Paulo 
111232a8088fSRui Paulo static void
111332a8088fSRui Paulo asmc_sms_task(void *arg, int pending)
111432a8088fSRui Paulo {
111532a8088fSRui Paulo 	struct asmc_softc *sc = (struct asmc_softc *)arg;
111632a8088fSRui Paulo 	char notify[16];
111732a8088fSRui Paulo 	int type;
111832a8088fSRui Paulo 
111932a8088fSRui Paulo 	switch (sc->sc_sms_intrtype) {
112032a8088fSRui Paulo 	case ASMC_SMS_INTFF:
112132a8088fSRui Paulo 		type = 2;
112232a8088fSRui Paulo 		break;
112332a8088fSRui Paulo 	case ASMC_SMS_INTHA:
112432a8088fSRui Paulo 		type = 1;
112532a8088fSRui Paulo 		break;
112632a8088fSRui Paulo 	case ASMC_SMS_INTSH:
112732a8088fSRui Paulo 		type = 0;
112832a8088fSRui Paulo 		break;
112932a8088fSRui Paulo 	default:
113032a8088fSRui Paulo 		type = 255;
113132a8088fSRui Paulo 	}
113232a8088fSRui Paulo 
113332a8088fSRui Paulo 	snprintf(notify, sizeof(notify), " notify=0x%x", type);
11344470f0f3SRui Paulo 	devctl_notify("ACPI", "asmc", "SMS", notify);
113532a8088fSRui Paulo }
113632a8088fSRui Paulo 
113732a8088fSRui Paulo static int
113832a8088fSRui Paulo asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS)
113932a8088fSRui Paulo {
114032a8088fSRui Paulo 	device_t dev = (device_t) arg1;
114132a8088fSRui Paulo 	int error;
114232a8088fSRui Paulo 	int16_t val;
114332a8088fSRui Paulo 	int32_t v;
114432a8088fSRui Paulo 
114532a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_X, &val);
114632a8088fSRui Paulo 	v = (int32_t) val;
114732a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
114832a8088fSRui Paulo 
114932a8088fSRui Paulo 	return (error);
115032a8088fSRui Paulo }
115132a8088fSRui Paulo 
115232a8088fSRui Paulo static int
115332a8088fSRui Paulo asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS)
115432a8088fSRui Paulo {
115532a8088fSRui Paulo 	device_t dev = (device_t) arg1;
115632a8088fSRui Paulo 	int error;
115732a8088fSRui Paulo 	int16_t val;
115832a8088fSRui Paulo 	int32_t v;
115932a8088fSRui Paulo 
116032a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val);
116132a8088fSRui Paulo 	v = (int32_t) val;
116232a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
116332a8088fSRui Paulo 
116432a8088fSRui Paulo 	return (error);
116532a8088fSRui Paulo }
116632a8088fSRui Paulo 
116732a8088fSRui Paulo static int
116832a8088fSRui Paulo asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS)
116932a8088fSRui Paulo {
117032a8088fSRui Paulo 	device_t dev = (device_t) arg1;
117132a8088fSRui Paulo 	int error;
117232a8088fSRui Paulo 	int16_t val;
117332a8088fSRui Paulo 	int32_t v;
117432a8088fSRui Paulo 
117532a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val);
117632a8088fSRui Paulo 	v = (int32_t) val;
117732a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
117832a8088fSRui Paulo 
117932a8088fSRui Paulo 	return (error);
118032a8088fSRui Paulo }
118132a8088fSRui Paulo 
118232a8088fSRui Paulo static int
118332a8088fSRui Paulo asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS)
118432a8088fSRui Paulo {
118532a8088fSRui Paulo 	device_t dev = (device_t) arg1;
118632a8088fSRui Paulo 	uint8_t buf[6];
118732a8088fSRui Paulo 	int error;
118832a8088fSRui Paulo 	int32_t v;
118932a8088fSRui Paulo 
119032a8088fSRui Paulo 	asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, 6);
119132a8088fSRui Paulo 	v = buf[2];
119232a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
119332a8088fSRui Paulo 
119432a8088fSRui Paulo 	return (error);
119532a8088fSRui Paulo }
119632a8088fSRui Paulo 
119732a8088fSRui Paulo static int
119832a8088fSRui Paulo asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS)
119932a8088fSRui Paulo {
120032a8088fSRui Paulo 	device_t dev = (device_t) arg1;
120132a8088fSRui Paulo 	uint8_t buf[6];
120232a8088fSRui Paulo 	int error;
120332a8088fSRui Paulo 	int32_t v;
120432a8088fSRui Paulo 
120532a8088fSRui Paulo 	asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, 6);
120632a8088fSRui Paulo 	v = buf[2];
120732a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
1208be80e49aSRui Paulo 
1209be80e49aSRui Paulo 	return (error);
1210be80e49aSRui Paulo }
1211be80e49aSRui Paulo 
1212be80e49aSRui Paulo static int
1213be80e49aSRui Paulo asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS)
1214be80e49aSRui Paulo {
1215be80e49aSRui Paulo 	device_t dev = (device_t) arg1;
1216be80e49aSRui Paulo 	uint8_t buf[2];
1217be80e49aSRui Paulo 	int error;
1218be80e49aSRui Paulo 	unsigned int level;
1219be80e49aSRui Paulo 	static int32_t v;
1220be80e49aSRui Paulo 
1221be80e49aSRui Paulo 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
122232a8088fSRui Paulo 	if (error == 0 && req->newptr != NULL) {
122332a8088fSRui Paulo 		level = *(unsigned int *)req->newptr;
122432a8088fSRui Paulo 		if (level > 255)
122532a8088fSRui Paulo 			return (EINVAL);
1226be80e49aSRui Paulo 		v = level;
122732a8088fSRui Paulo 		buf[0] = level;
122832a8088fSRui Paulo 		buf[1] = 0x00;
122932a8088fSRui Paulo 		asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2);
123032a8088fSRui Paulo 	}
123132a8088fSRui Paulo 
123232a8088fSRui Paulo 	return (error);
123332a8088fSRui Paulo }
1234