xref: /freebsd/sys/dev/asmc/asmc.c (revision 4c061448d16c5b6a5974c1c7c901d9fb9042965b)
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>
524470f0f3SRui Paulo #include <contrib/dev/acpica/acpi.h>
534470f0f3SRui Paulo #include <dev/acpica/acpivar.h>
5432a8088fSRui Paulo #include <dev/asmc/asmcvar.h>
5532a8088fSRui Paulo 
564c061448SRui Paulo #include "opt_intr_filter.h"
574c061448SRui Paulo 
5832a8088fSRui Paulo /*
5932a8088fSRui Paulo  * Device interface.
6032a8088fSRui Paulo  */
6132a8088fSRui Paulo static int 	asmc_probe(device_t dev);
6232a8088fSRui Paulo static int 	asmc_attach(device_t dev);
6332a8088fSRui Paulo static int 	asmc_detach(device_t dev);
6432a8088fSRui Paulo 
6532a8088fSRui Paulo /*
6632a8088fSRui Paulo  * SMC functions.
6732a8088fSRui Paulo  */
6832a8088fSRui Paulo static int 	asmc_init(device_t dev);
6932a8088fSRui Paulo static int 	asmc_wait(device_t dev, uint8_t val);
7032a8088fSRui Paulo static int 	asmc_key_write(device_t dev, const char *key, uint8_t *buf,
7132a8088fSRui Paulo     uint8_t len);
7232a8088fSRui Paulo static int 	asmc_key_read(device_t dev, const char *key, uint8_t *buf,
7332a8088fSRui Paulo     uint8_t);
7432a8088fSRui Paulo static int 	asmc_fan_count(device_t dev);
7532a8088fSRui Paulo static int 	asmc_fan_getvalue(device_t dev, const char *key, int fan);
7632a8088fSRui Paulo static int 	asmc_temp_getvalue(device_t dev, const char *key);
7732a8088fSRui Paulo static int 	asmc_sms_read(device_t, const char *key, int16_t *val);
7832a8088fSRui Paulo static void 	asmc_sms_calibrate(device_t dev);
7932a8088fSRui Paulo static int 	asmc_sms_intrfast(void *arg);
8032a8088fSRui Paulo #ifdef INTR_FILTER
8132a8088fSRui Paulo static void 	asmc_sms_handler(void *arg);
8232a8088fSRui Paulo #endif
8332a8088fSRui Paulo static void 	asmc_sms_printintr(device_t dev, uint8_t);
8432a8088fSRui Paulo static void 	asmc_sms_task(void *arg, int pending);
8532a8088fSRui Paulo 
8632a8088fSRui Paulo /*
8732a8088fSRui Paulo  * Model functions.
8832a8088fSRui Paulo  */
8932a8088fSRui Paulo static int 	asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS);
9032a8088fSRui Paulo static int 	asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS);
9132a8088fSRui Paulo static int 	asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS);
9232a8088fSRui Paulo static int 	asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS);
9332a8088fSRui Paulo static int 	asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS);
9432a8088fSRui Paulo static int 	asmc_temp_sysctl(SYSCTL_HANDLER_ARGS);
9532a8088fSRui Paulo static int 	asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS);
9632a8088fSRui Paulo static int 	asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS);
9732a8088fSRui Paulo static int 	asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS);
9832a8088fSRui Paulo static int 	asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS);
9932a8088fSRui Paulo static int 	asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS);
10032a8088fSRui Paulo 
10132a8088fSRui Paulo struct asmc_model {
10232a8088fSRui Paulo 	const char 	 *smc_model;	/* smbios.system.product env var. */
10332a8088fSRui Paulo 	const char 	 *smc_desc;	/* driver description */
10432a8088fSRui Paulo 
10532a8088fSRui Paulo 	/* Helper functions */
10632a8088fSRui Paulo 	int (*smc_sms_x)(SYSCTL_HANDLER_ARGS);
10732a8088fSRui Paulo 	int (*smc_sms_y)(SYSCTL_HANDLER_ARGS);
10832a8088fSRui Paulo 	int (*smc_sms_z)(SYSCTL_HANDLER_ARGS);
10932a8088fSRui Paulo 	int (*smc_fan_speed)(SYSCTL_HANDLER_ARGS);
11032a8088fSRui Paulo 	int (*smc_fan_safespeed)(SYSCTL_HANDLER_ARGS);
11132a8088fSRui Paulo 	int (*smc_fan_minspeed)(SYSCTL_HANDLER_ARGS);
11232a8088fSRui Paulo 	int (*smc_fan_maxspeed)(SYSCTL_HANDLER_ARGS);
11332a8088fSRui Paulo 	int (*smc_fan_targetspeed)(SYSCTL_HANDLER_ARGS);
11432a8088fSRui Paulo 	int (*smc_light_left)(SYSCTL_HANDLER_ARGS);
11532a8088fSRui Paulo 	int (*smc_light_right)(SYSCTL_HANDLER_ARGS);
11632a8088fSRui Paulo 
11732a8088fSRui Paulo 	const char 	*smc_temps[8];
11832a8088fSRui Paulo 	const char 	*smc_tempnames[8];
11932a8088fSRui Paulo 	const char 	*smc_tempdescs[8];
12032a8088fSRui Paulo };
12132a8088fSRui Paulo 
12232a8088fSRui Paulo static struct asmc_model *asmc_match(device_t dev);
12332a8088fSRui Paulo 
12432a8088fSRui Paulo #define ASMC_SMS_FUNCS	asmc_mb_sysctl_sms_x, asmc_mb_sysctl_sms_y, \
12532a8088fSRui Paulo 			asmc_mb_sysctl_sms_z
12632a8088fSRui Paulo 
12732a8088fSRui Paulo #define ASMC_FAN_FUNCS	asmc_mb_sysctl_fanspeed, asmc_mb_sysctl_fansafespeed, \
12832a8088fSRui Paulo 			asmc_mb_sysctl_fanminspeed, \
12932a8088fSRui Paulo 			asmc_mb_sysctl_fanmaxspeed, \
13032a8088fSRui Paulo 			asmc_mb_sysctl_fantargetspeed
13132a8088fSRui Paulo #define ASMC_LIGHT_FUNCS asmc_mbp_sysctl_light_left, \
13232a8088fSRui Paulo 			 asmc_mbp_sysctl_light_right
13332a8088fSRui Paulo 
13432a8088fSRui Paulo struct asmc_model asmc_models[] = {
13532a8088fSRui Paulo 	{
13632a8088fSRui Paulo 	  "MacBook1,1", "Apple SMC MacBook Core Duo",
13732a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL,
13832a8088fSRui Paulo 	  ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
13932a8088fSRui Paulo 	},
14032a8088fSRui Paulo 
14132a8088fSRui Paulo 	{
14232a8088fSRui Paulo 	  "MacBook2,1", "Apple SMC MacBook Core 2 Duo",
14332a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL,
14432a8088fSRui Paulo 	  ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
14532a8088fSRui Paulo 	},
14632a8088fSRui Paulo 
14732a8088fSRui Paulo 	{
14832a8088fSRui Paulo 	  "MacBookPro1,1", "Apple SMC MacBook Pro Core Duo (15-inch)",
14932a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
15032a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
15132a8088fSRui Paulo 	},
15232a8088fSRui Paulo 
15332a8088fSRui Paulo 	{
15432a8088fSRui Paulo 	  "MacBookPro1,2", "Apple SMC MacBook Pro Core Duo (17-inch)",
15532a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
15632a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
15732a8088fSRui Paulo 	},
15832a8088fSRui Paulo 
15932a8088fSRui Paulo 	{
16032a8088fSRui Paulo 	  "MacBookPro2,1", "Apple SMC MacBook Pro Core 2 Duo (17-inch)",
16132a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
16232a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
16332a8088fSRui Paulo 	},
16432a8088fSRui Paulo 
16532a8088fSRui Paulo 	{
16632a8088fSRui Paulo 	  "MacBookPro2,2", "Apple SMC MacBook Pro Core 2 Duo (15-inch)",
16732a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
16832a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
16932a8088fSRui Paulo 	},
17032a8088fSRui Paulo 
17132a8088fSRui Paulo 	{
17232a8088fSRui Paulo 	  "MacBookPro3,1", "Apple SMC MacBook Pro Core 2 Duo (15-inch LED)",
17332a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
17432a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
17532a8088fSRui Paulo 	},
17632a8088fSRui Paulo 
17732a8088fSRui Paulo 	{
17832a8088fSRui Paulo 	  "MacBookPro3,2", "Apple SMC MacBook Pro Core 2 Duo (17-inch HD)",
17932a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
18032a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
18132a8088fSRui Paulo 	},
18232a8088fSRui Paulo 
18332a8088fSRui Paulo 	/* The Mac Mini has no SMS */
18432a8088fSRui Paulo 	{
18532a8088fSRui Paulo 	  "Macmini1,1", "Apple SMC Mac Mini",
18632a8088fSRui Paulo 	  NULL, NULL, NULL,
18732a8088fSRui Paulo 	  ASMC_FAN_FUNCS,
1884b07bf84SRui Paulo 	  NULL, NULL,
18932a8088fSRui Paulo 	  ASMC_MM_TEMPS, ASMC_MM_TEMPNAMES, ASMC_MM_TEMPDESCS
19032a8088fSRui Paulo 	},
19132a8088fSRui Paulo 
19232a8088fSRui Paulo 	{ NULL, NULL }
19332a8088fSRui Paulo };
19432a8088fSRui Paulo 
19532a8088fSRui Paulo #undef ASMC_SMS_FUNCS
19632a8088fSRui Paulo #undef ASMC_FAN_FUNCS
19732a8088fSRui Paulo #undef ASMC_LIGHT_FUNCS
19832a8088fSRui Paulo 
19932a8088fSRui Paulo /*
20032a8088fSRui Paulo  * Driver methods.
20132a8088fSRui Paulo  */
20232a8088fSRui Paulo static device_method_t	asmc_methods[] = {
20332a8088fSRui Paulo 	DEVMETHOD(device_probe,		asmc_probe),
20432a8088fSRui Paulo 	DEVMETHOD(device_attach,	asmc_attach),
20532a8088fSRui Paulo 	DEVMETHOD(device_detach,	asmc_detach),
20632a8088fSRui Paulo 
20732a8088fSRui Paulo 	{ 0, 0 }
20832a8088fSRui Paulo };
20932a8088fSRui Paulo 
21032a8088fSRui Paulo static driver_t	asmc_driver = {
21132a8088fSRui Paulo 	"asmc",
21232a8088fSRui Paulo 	asmc_methods,
21332a8088fSRui Paulo 	sizeof(struct asmc_softc)
21432a8088fSRui Paulo };
21532a8088fSRui Paulo 
2164470f0f3SRui Paulo /*
2174470f0f3SRui Paulo  * Debugging
2184470f0f3SRui Paulo  */
2194470f0f3SRui Paulo #define	_COMPONENT	ACPI_OEM
2204470f0f3SRui Paulo ACPI_MODULE_NAME("ASMC")
2214470f0f3SRui Paulo #ifdef DEBUG
2224470f0f3SRui Paulo #define ASMC_DPRINTF(str)	device_printf(dev, str)
2234fb9bf66SRui Paulo #else
2244fb9bf66SRui Paulo #define ASMC_DPRINTF(str)
2254470f0f3SRui Paulo #endif
2264470f0f3SRui Paulo 
2274470f0f3SRui Paulo static char *asmc_ids[] = { "APP0001", NULL };
2284470f0f3SRui Paulo 
22932a8088fSRui Paulo static devclass_t asmc_devclass;
23032a8088fSRui Paulo 
2314470f0f3SRui Paulo DRIVER_MODULE(asmc, acpi, asmc_driver, asmc_devclass, NULL, NULL);
2324470f0f3SRui Paulo MODULE_DEPEND(asmc, acpi, 1, 1, 1);
23332a8088fSRui Paulo 
23432a8088fSRui Paulo static struct asmc_model *
23532a8088fSRui Paulo asmc_match(device_t dev)
23632a8088fSRui Paulo {
23732a8088fSRui Paulo 	int i;
23832a8088fSRui Paulo 	char *model;
23932a8088fSRui Paulo 
24032a8088fSRui Paulo 	model = getenv("smbios.system.product");
24132a8088fSRui Paulo 	for (i = 0; asmc_models[i].smc_model; i++) {
24232a8088fSRui Paulo 		if (!strncmp(model, asmc_models[i].smc_model, strlen(model))) {
24332a8088fSRui Paulo 			freeenv(model);
24432a8088fSRui Paulo 			return (&asmc_models[i]);
24532a8088fSRui Paulo 		}
24632a8088fSRui Paulo 	}
24732a8088fSRui Paulo 	freeenv(model);
24832a8088fSRui Paulo 
24932a8088fSRui Paulo 	return (NULL);
25032a8088fSRui Paulo }
25132a8088fSRui Paulo 
25232a8088fSRui Paulo static int
25332a8088fSRui Paulo asmc_probe(device_t dev)
25432a8088fSRui Paulo {
25532a8088fSRui Paulo 	struct asmc_model *model;
25632a8088fSRui Paulo 
2574470f0f3SRui Paulo 	if (acpi_disabled("asmc"))
25832a8088fSRui Paulo 		return (ENXIO);
2594470f0f3SRui Paulo 	if (ACPI_ID_PROBE(device_get_parent(dev), dev, asmc_ids) == NULL)
2604470f0f3SRui Paulo 		return (ENXIO);
2614470f0f3SRui Paulo 
26232a8088fSRui Paulo 	model = asmc_match(dev);
2634470f0f3SRui Paulo 	if (!model) {
2644470f0f3SRui Paulo 		device_printf(dev, "model not recognized\n");
26532a8088fSRui Paulo 		return (ENXIO);
2664470f0f3SRui Paulo 	}
26732a8088fSRui Paulo 	device_set_desc(dev, model->smc_desc);
26832a8088fSRui Paulo 
26932a8088fSRui Paulo 	return (BUS_PROBE_DEFAULT);
27032a8088fSRui Paulo }
27132a8088fSRui Paulo 
27232a8088fSRui Paulo static int
27332a8088fSRui Paulo asmc_attach(device_t dev)
27432a8088fSRui Paulo {
27532a8088fSRui Paulo 	int i, j;
27632a8088fSRui Paulo 	int ret;
27732a8088fSRui Paulo 	char name[2];
27832a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
27932a8088fSRui Paulo 	struct sysctl_ctx_list *sysctlctx;
28032a8088fSRui Paulo 	struct sysctl_oid *sysctlnode;
28132a8088fSRui Paulo 	struct asmc_model *model;
28232a8088fSRui Paulo 
2834470f0f3SRui Paulo 	sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
2844470f0f3SRui Paulo 	    &sc->sc_rid_port, RF_ACTIVE);
2854470f0f3SRui Paulo 	if (sc->sc_ioport == NULL) {
2864470f0f3SRui Paulo 		device_printf(dev, "unable to allocate IO port\n");
2874470f0f3SRui Paulo 		return (ENOMEM);
2884470f0f3SRui Paulo 	}
2894470f0f3SRui Paulo 
29032a8088fSRui Paulo 	sysctlctx  = device_get_sysctl_ctx(dev);
29132a8088fSRui Paulo 	sysctlnode = device_get_sysctl_tree(dev);
29232a8088fSRui Paulo 
29332a8088fSRui Paulo 	model = asmc_match(dev);
29432a8088fSRui Paulo 
29532a8088fSRui Paulo 	mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN);
29632a8088fSRui Paulo 
29732a8088fSRui Paulo 	sc->sc_model = model;
29832a8088fSRui Paulo 	asmc_init(dev);
29932a8088fSRui Paulo 
30032a8088fSRui Paulo 	/*
30132a8088fSRui Paulo 	 * dev.asmc.n.fan.* tree.
30232a8088fSRui Paulo 	 */
30332a8088fSRui Paulo 	sc->sc_fan_tree[0] = SYSCTL_ADD_NODE(sysctlctx,
30432a8088fSRui Paulo 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "fan",
30532a8088fSRui Paulo 	    CTLFLAG_RD, 0, "Fan Root Tree");
30632a8088fSRui Paulo 
30732a8088fSRui Paulo 	for (i = 1; i <= sc->sc_nfan; i++) {
30832a8088fSRui Paulo 		j = i - 1;
30932a8088fSRui Paulo 		name[0] = '0' + j;
31032a8088fSRui Paulo 		name[1] = 0;
31132a8088fSRui Paulo 		sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx,
31232a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[0]),
31332a8088fSRui Paulo 		    OID_AUTO, name, CTLFLAG_RD, 0,
31432a8088fSRui Paulo 		    "Fan Subtree");
31532a8088fSRui Paulo 
31632a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
31732a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
31832a8088fSRui Paulo 		    OID_AUTO, "speed", CTLTYPE_INT | CTLFLAG_RD,
31932a8088fSRui Paulo 		    dev, j, model->smc_fan_speed, "I",
32032a8088fSRui Paulo 		    "Fan speed in RPM");
32132a8088fSRui Paulo 
32232a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
32332a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
32432a8088fSRui Paulo 		    OID_AUTO, "safespeed",
32532a8088fSRui Paulo 		    CTLTYPE_INT | CTLFLAG_RD,
32632a8088fSRui Paulo 		    dev, j, model->smc_fan_safespeed, "I",
32732a8088fSRui Paulo 		    "Fan safe speed in RPM");
32832a8088fSRui Paulo 
32932a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
33032a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
33132a8088fSRui Paulo 		    OID_AUTO, "minspeed",
33232a8088fSRui Paulo 		    CTLTYPE_INT | CTLFLAG_RD,
33332a8088fSRui Paulo 		    dev, j, model->smc_fan_minspeed, "I",
33432a8088fSRui Paulo 		    "Fan minimum speed in RPM");
33532a8088fSRui Paulo 
33632a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
33732a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
33832a8088fSRui Paulo 		    OID_AUTO, "maxspeed",
33932a8088fSRui Paulo 		    CTLTYPE_INT | CTLFLAG_RD,
34032a8088fSRui Paulo 		    dev, j, model->smc_fan_maxspeed, "I",
34132a8088fSRui Paulo 		    "Fan maximum speed in RPM");
34232a8088fSRui Paulo 
34332a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
34432a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
34532a8088fSRui Paulo 		    OID_AUTO, "targetspeed",
34632a8088fSRui Paulo 		    CTLTYPE_INT | CTLFLAG_RD,
34732a8088fSRui Paulo 		    dev, j, model->smc_fan_targetspeed, "I",
34832a8088fSRui Paulo 		    "Fan target speed in RPM");
34932a8088fSRui Paulo 	}
35032a8088fSRui Paulo 
35132a8088fSRui Paulo 	/*
35232a8088fSRui Paulo 	 * dev.asmc.n.temp tree.
35332a8088fSRui Paulo 	 */
35432a8088fSRui Paulo 	sc->sc_temp_tree = SYSCTL_ADD_NODE(sysctlctx,
35532a8088fSRui Paulo 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp",
35632a8088fSRui Paulo 	    CTLFLAG_RD, 0, "Temperature sensors");
35732a8088fSRui Paulo 
35832a8088fSRui Paulo 	for (i = 0; model->smc_temps[i]; i++) {
35932a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
36032a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_temp_tree),
36132a8088fSRui Paulo 		    OID_AUTO, model->smc_tempnames[i],
36232a8088fSRui Paulo 		    CTLTYPE_INT | CTLFLAG_RD,
36332a8088fSRui Paulo 		    dev, i, asmc_temp_sysctl, "I",
36432a8088fSRui Paulo 		    model->smc_tempdescs[i]);
36532a8088fSRui Paulo 	}
36632a8088fSRui Paulo 
36732a8088fSRui Paulo 	if (model->smc_sms_x == NULL)
36832a8088fSRui Paulo 		goto nosms;
36932a8088fSRui Paulo 
37032a8088fSRui Paulo 	/*
37132a8088fSRui Paulo 	 * dev.asmc.n.sms tree.
37232a8088fSRui Paulo 	 */
37332a8088fSRui Paulo 	sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx,
37432a8088fSRui Paulo 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "sms",
37532a8088fSRui Paulo 	    CTLFLAG_RD, 0, "Sudden Motion Sensor");
37632a8088fSRui Paulo 
37732a8088fSRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
37832a8088fSRui Paulo 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
37932a8088fSRui Paulo 	    OID_AUTO, "x", CTLTYPE_INT | CTLFLAG_RD,
38032a8088fSRui Paulo 	    dev, 0, model->smc_sms_x, "I",
38132a8088fSRui Paulo 	    "Sudden Motion Sensor X value");
38232a8088fSRui Paulo 
38332a8088fSRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
38432a8088fSRui Paulo 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
38532a8088fSRui Paulo 	    OID_AUTO, "y", CTLTYPE_INT | CTLFLAG_RD,
38632a8088fSRui Paulo 	    dev, 0, model->smc_sms_y, "I",
38732a8088fSRui Paulo 	    "Sudden Motion Sensor Y value");
38832a8088fSRui Paulo 
38932a8088fSRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
39032a8088fSRui Paulo 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
39132a8088fSRui Paulo 	    OID_AUTO, "z", CTLTYPE_INT | CTLFLAG_RD,
39232a8088fSRui Paulo 	    dev, 0, model->smc_sms_z, "I",
39332a8088fSRui Paulo 	    "Sudden Motion Sensor Z value");
39432a8088fSRui Paulo 
39532a8088fSRui Paulo 	/*
39632a8088fSRui Paulo 	 * dev.asmc.n.light
39732a8088fSRui Paulo 	 */
39832a8088fSRui Paulo 	if (model->smc_light_left) {
39932a8088fSRui Paulo 		sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx,
40032a8088fSRui Paulo 		    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light",
40132a8088fSRui Paulo 		    CTLFLAG_RD, 0, "Keyboard backlight sensors");
40232a8088fSRui Paulo 
40332a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
40432a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_light_tree),
40532a8088fSRui Paulo 		    OID_AUTO, "left", CTLTYPE_INT | CTLFLAG_RW,
40632a8088fSRui Paulo 		    dev, 0, model->smc_light_left, "I",
40732a8088fSRui Paulo 		    "Keyboard backlight left sensor");
40832a8088fSRui Paulo 
40932a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
41032a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_light_tree),
41132a8088fSRui Paulo 		    OID_AUTO, "right", CTLTYPE_INT | CTLFLAG_RW,
41232a8088fSRui Paulo 		    dev, 0, model->smc_light_right, "I",
41332a8088fSRui Paulo 		    "Keyboard backlight right sensor");
41432a8088fSRui Paulo 	}
41532a8088fSRui Paulo 
41632a8088fSRui Paulo 	/*
41732a8088fSRui Paulo 	 * Need a taskqueue to send devctl_notify() events
41832a8088fSRui Paulo 	 * when the SMS interrupt us.
41932a8088fSRui Paulo 	 *
42032a8088fSRui Paulo 	 * PI_REALTIME is used due to the sensitivity of the
42132a8088fSRui Paulo 	 * interrupt. An interrupt from the SMS means that the
42232a8088fSRui Paulo 	 * disk heads should be turned off as quickly as possible.
42332a8088fSRui Paulo 	 *
42432a8088fSRui Paulo 	 * We only need to do this for the non INTR_FILTER case.
42532a8088fSRui Paulo 	 */
42632a8088fSRui Paulo 	sc->sc_sms_tq = NULL;
42732a8088fSRui Paulo #ifndef INTR_FILTER
42832a8088fSRui Paulo 	TASK_INIT(&sc->sc_sms_task, 0, asmc_sms_task, sc);
42932a8088fSRui Paulo 	sc->sc_sms_tq = taskqueue_create_fast("asmc_taskq", M_WAITOK,
43032a8088fSRui Paulo 	    taskqueue_thread_enqueue, &sc->sc_sms_tq);
43132a8088fSRui Paulo 	taskqueue_start_threads(&sc->sc_sms_tq, 1, PI_REALTIME, "%s sms taskq",
43232a8088fSRui Paulo 	    device_get_nameunit(dev));
43332a8088fSRui Paulo #endif
43432a8088fSRui Paulo 	/*
43532a8088fSRui Paulo 	 * Allocate an IRQ for the SMS.
43632a8088fSRui Paulo 	 */
4374470f0f3SRui Paulo 	sc->sc_rid_irq = 0;
4384470f0f3SRui Paulo 	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
4394470f0f3SRui Paulo 	    &sc->sc_rid_irq, RF_ACTIVE);
4404470f0f3SRui Paulo 	if (sc->sc_irq == NULL) {
44132a8088fSRui Paulo 		device_printf(dev, "unable to allocate IRQ resource\n");
44232a8088fSRui Paulo 		ret = ENXIO;
44332a8088fSRui Paulo 		goto err2;
44432a8088fSRui Paulo 	}
44532a8088fSRui Paulo 
4464470f0f3SRui Paulo 	ret = bus_setup_intr(dev, sc->sc_irq,
44732a8088fSRui Paulo 	          INTR_TYPE_MISC | INTR_MPSAFE,
44832a8088fSRui Paulo #ifdef INTR_FILTER
44932a8088fSRui Paulo 	    asmc_sms_intrfast, asmc_sms_handler,
45032a8088fSRui Paulo #else
45132a8088fSRui Paulo 	    asmc_sms_intrfast, NULL,
45232a8088fSRui Paulo #endif
45332a8088fSRui Paulo 	    dev, &sc->sc_cookie);
45432a8088fSRui Paulo 
45532a8088fSRui Paulo 	if (ret) {
45632a8088fSRui Paulo 		device_printf(dev, "unable to setup SMS IRQ\n");
45732a8088fSRui Paulo 		goto err1;
45832a8088fSRui Paulo 	}
45932a8088fSRui Paulo nosms:
46032a8088fSRui Paulo 	return (0);
46132a8088fSRui Paulo err1:
4624470f0f3SRui Paulo 	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, sc->sc_irq);
46332a8088fSRui Paulo err2:
4644470f0f3SRui Paulo 	bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
4654470f0f3SRui Paulo 	    sc->sc_ioport);
46632a8088fSRui Paulo 	mtx_destroy(&sc->sc_mtx);
46732a8088fSRui Paulo 	if (sc->sc_sms_tq)
46832a8088fSRui Paulo 		taskqueue_free(sc->sc_sms_tq);
46932a8088fSRui Paulo 
47032a8088fSRui Paulo 	return (ret);
47132a8088fSRui Paulo }
47232a8088fSRui Paulo 
47332a8088fSRui Paulo static int
47432a8088fSRui Paulo asmc_detach(device_t dev)
47532a8088fSRui Paulo {
47632a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
47732a8088fSRui Paulo 
47832a8088fSRui Paulo 	if (sc->sc_sms_tq) {
47932a8088fSRui Paulo 		taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task);
48032a8088fSRui Paulo 		taskqueue_free(sc->sc_sms_tq);
48132a8088fSRui Paulo 	}
48232a8088fSRui Paulo 	if (sc->sc_cookie)
4834470f0f3SRui Paulo 		bus_teardown_intr(dev, sc->sc_irq, sc->sc_cookie);
4844470f0f3SRui Paulo 	if (sc->sc_irq)
4854470f0f3SRui Paulo 		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq,
4864470f0f3SRui Paulo 		    sc->sc_irq);
4874470f0f3SRui Paulo 	if (sc->sc_ioport)
4884470f0f3SRui Paulo 		bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
4894470f0f3SRui Paulo 		    sc->sc_ioport);
49032a8088fSRui Paulo 	mtx_destroy(&sc->sc_mtx);
49132a8088fSRui Paulo 
49232a8088fSRui Paulo 	return (0);
49332a8088fSRui Paulo }
49432a8088fSRui Paulo 
49532a8088fSRui Paulo static int
49632a8088fSRui Paulo asmc_init(device_t dev)
49732a8088fSRui Paulo {
49832a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
49932a8088fSRui Paulo 	int i, error = 1;
50032a8088fSRui Paulo 	uint8_t buf[4];
50132a8088fSRui Paulo 
50232a8088fSRui Paulo 	if (sc->sc_model->smc_sms_x == NULL)
50332a8088fSRui Paulo 		goto nosms;
50432a8088fSRui Paulo 
50532a8088fSRui Paulo 	/*
50632a8088fSRui Paulo 	 * We are ready to recieve interrupts from the SMS.
50732a8088fSRui Paulo 	 */
50832a8088fSRui Paulo 	buf[0] = 0x01;
5094470f0f3SRui Paulo 	ASMC_DPRINTF(("intok key\n"));
51032a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_INTOK, buf, 1);
51132a8088fSRui Paulo 	DELAY(50);
51232a8088fSRui Paulo 
51332a8088fSRui Paulo 	/*
51432a8088fSRui Paulo 	 * Initiate the polling intervals.
51532a8088fSRui Paulo 	 */
51632a8088fSRui Paulo 	buf[0] = 20; /* msecs */
5174470f0f3SRui Paulo 	ASMC_DPRINTF(("low int key\n"));
51832a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_LOW_INT, buf, 1);
51932a8088fSRui Paulo 	DELAY(200);
52032a8088fSRui Paulo 
52132a8088fSRui Paulo 	buf[0] = 20; /* msecs */
5224470f0f3SRui Paulo 	ASMC_DPRINTF(("high int key\n"));
52332a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_HIGH_INT, buf, 1);
52432a8088fSRui Paulo 	DELAY(200);
52532a8088fSRui Paulo 
52632a8088fSRui Paulo 	buf[0] = 0x00;
52732a8088fSRui Paulo 	buf[1] = 0x60;
5284470f0f3SRui Paulo 	ASMC_DPRINTF(("sms low key\n"));
52932a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_LOW, buf, 2);
53032a8088fSRui Paulo 	DELAY(200);
53132a8088fSRui Paulo 
53232a8088fSRui Paulo 	buf[0] = 0x01;
53332a8088fSRui Paulo 	buf[1] = 0xc0;
5344470f0f3SRui Paulo 	ASMC_DPRINTF(("sms high key\n"));
53532a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_HIGH, buf, 2);
53632a8088fSRui Paulo 	DELAY(200);
53732a8088fSRui Paulo 
53832a8088fSRui Paulo 	/*
53932a8088fSRui Paulo 	 * I'm not sure what this key does, but it seems to be
54032a8088fSRui Paulo 	 * required.
54132a8088fSRui Paulo 	 */
54232a8088fSRui Paulo 	buf[0] = 0x01;
5434470f0f3SRui Paulo 	ASMC_DPRINTF(("sms flag key\n"));
54432a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_FLAG, buf, 1);
545b75dfbe8SRui Paulo 	DELAY(100);
54632a8088fSRui Paulo 
54732a8088fSRui Paulo 	/*
54832a8088fSRui Paulo 	 * Wait up to 5 seconds for SMS initialization.
54932a8088fSRui Paulo 	 */
55032a8088fSRui Paulo 	for (i = 0; i < 10000; i++) {
55132a8088fSRui Paulo 		if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 &&
55232a8088fSRui Paulo 		    (buf[0] != 0x00 || buf[1] != 0x00)) {
55332a8088fSRui Paulo 			error = 0;
5544fb9bf66SRui Paulo 			goto out;
55532a8088fSRui Paulo 		}
55632a8088fSRui Paulo 		buf[0] = ASMC_SMS_INIT1;
55732a8088fSRui Paulo 		buf[1] = ASMC_SMS_INIT2;
5584470f0f3SRui Paulo 		ASMC_DPRINTF(("sms key\n"));
55932a8088fSRui Paulo 		asmc_key_write(dev, ASMC_KEY_SMS, buf, 2);
56032a8088fSRui Paulo 		DELAY(50);
56132a8088fSRui Paulo 	}
5624fb9bf66SRui Paulo 	device_printf(dev, "WARNING: Sudden Motion Sensor not initialized!\n");
56332a8088fSRui Paulo 
5644fb9bf66SRui Paulo out:
56532a8088fSRui Paulo 	asmc_sms_calibrate(dev);
56632a8088fSRui Paulo nosms:
56732a8088fSRui Paulo 	sc->sc_nfan = asmc_fan_count(dev);
56832a8088fSRui Paulo 	if (sc->sc_nfan > ASMC_MAXFANS) {
56932a8088fSRui Paulo 		device_printf(dev, "more than %d fans were detected. Please "
57032a8088fSRui Paulo 		    "report this.\n", ASMC_MAXFANS);
57132a8088fSRui Paulo 		sc->sc_nfan = ASMC_MAXFANS;
57232a8088fSRui Paulo 	}
57332a8088fSRui Paulo 
57432a8088fSRui Paulo 	if (bootverbose) {
57532a8088fSRui Paulo 		/*
57632a8088fSRui Paulo 		 * XXX: The number of keys is a 32 bit buffer, but
57732a8088fSRui Paulo 		 * right now Apple only uses the last 8 bit.
57832a8088fSRui Paulo 		 */
57932a8088fSRui Paulo 		asmc_key_read(dev, ASMC_NKEYS, buf, 4);
58032a8088fSRui Paulo 		device_printf(dev, "number of keys: %d\n", buf[3]);
58132a8088fSRui Paulo 	}
58232a8088fSRui Paulo 
58332a8088fSRui Paulo 	return (error);
58432a8088fSRui Paulo }
58532a8088fSRui Paulo 
58632a8088fSRui Paulo /*
58732a8088fSRui Paulo  * We need to make sure that the SMC acks the byte sent.
58832a8088fSRui Paulo  * Just wait up to 100 ms.
58932a8088fSRui Paulo  */
59032a8088fSRui Paulo static int
59132a8088fSRui Paulo asmc_wait(device_t dev, uint8_t val)
59232a8088fSRui Paulo {
5934470f0f3SRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
59432a8088fSRui Paulo 	u_int i;
59532a8088fSRui Paulo 
59632a8088fSRui Paulo 	val = val & ASMC_STATUS_MASK;
59732a8088fSRui Paulo 
59832a8088fSRui Paulo 	for (i = 0; i < 1000; i++) {
5994470f0f3SRui Paulo 		if ((ASMC_CMDPORT_READ(sc) & ASMC_STATUS_MASK) == val)
60032a8088fSRui Paulo 			return (0);
60132a8088fSRui Paulo 		DELAY(10);
60232a8088fSRui Paulo 	}
60332a8088fSRui Paulo 
60432a8088fSRui Paulo 	device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, val,
6054470f0f3SRui Paulo 	    ASMC_CMDPORT_READ(sc));
60632a8088fSRui Paulo 
60732a8088fSRui Paulo 	return (1);
60832a8088fSRui Paulo }
60932a8088fSRui Paulo 
61032a8088fSRui Paulo static int
61132a8088fSRui Paulo asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len)
61232a8088fSRui Paulo {
61332a8088fSRui Paulo 	int i, error = 1;
61432a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
61532a8088fSRui Paulo 
61632a8088fSRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
61732a8088fSRui Paulo 
6184470f0f3SRui Paulo 	ASMC_CMDPORT_WRITE(sc, ASMC_CMDREAD);
61932a8088fSRui Paulo 	if (asmc_wait(dev, 0x0c))
62032a8088fSRui Paulo 		goto out;
62132a8088fSRui Paulo 
62232a8088fSRui Paulo 	for (i = 0; i < 4; i++) {
6234470f0f3SRui Paulo 		ASMC_DATAPORT_WRITE(sc, key[i]);
62432a8088fSRui Paulo 		if (asmc_wait(dev, 0x04))
62532a8088fSRui Paulo 			goto out;
62632a8088fSRui Paulo 	}
62732a8088fSRui Paulo 
6284470f0f3SRui Paulo 	ASMC_DATAPORT_WRITE(sc, len);
62932a8088fSRui Paulo 
63032a8088fSRui Paulo 	for (i = 0; i < len; i++) {
63132a8088fSRui Paulo 		if (asmc_wait(dev, 0x05))
63232a8088fSRui Paulo 			goto out;
6334470f0f3SRui Paulo 		buf[i] = ASMC_DATAPORT_READ(sc);
63432a8088fSRui Paulo 	}
63532a8088fSRui Paulo 
63632a8088fSRui Paulo 	error = 0;
63732a8088fSRui Paulo out:
63832a8088fSRui Paulo 	mtx_unlock_spin(&sc->sc_mtx);
63932a8088fSRui Paulo 
64032a8088fSRui Paulo 	return (error);
64132a8088fSRui Paulo }
64232a8088fSRui Paulo 
64332a8088fSRui Paulo static int
64432a8088fSRui Paulo asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len)
64532a8088fSRui Paulo {
64632a8088fSRui Paulo 	int i, error = -1;
64732a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
64832a8088fSRui Paulo 
64932a8088fSRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
65032a8088fSRui Paulo 
6514470f0f3SRui Paulo 	ASMC_DPRINTF(("cmd port: cmd write\n"));
6524470f0f3SRui Paulo 	ASMC_CMDPORT_WRITE(sc, ASMC_CMDWRITE);
65332a8088fSRui Paulo 	if (asmc_wait(dev, 0x0c))
65432a8088fSRui Paulo 		goto out;
65532a8088fSRui Paulo 
6564470f0f3SRui Paulo 	ASMC_DPRINTF(("data port: key\n"));
65732a8088fSRui Paulo 	for (i = 0; i < 4; i++) {
6584470f0f3SRui Paulo 		ASMC_DATAPORT_WRITE(sc, key[i]);
65932a8088fSRui Paulo 		if (asmc_wait(dev, 0x04))
66032a8088fSRui Paulo 			goto out;
66132a8088fSRui Paulo 	}
6624470f0f3SRui Paulo 	ASMC_DPRINTF(("data port: length\n"));
6634470f0f3SRui Paulo 	ASMC_DATAPORT_WRITE(sc, len);
66432a8088fSRui Paulo 
6654470f0f3SRui Paulo 	ASMC_DPRINTF(("data port: buffer\n"));
66632a8088fSRui Paulo 	for (i = 0; i < len; i++) {
66732a8088fSRui Paulo 		if (asmc_wait(dev, 0x04))
66832a8088fSRui Paulo 			goto out;
6694470f0f3SRui Paulo 		ASMC_DATAPORT_WRITE(sc, buf[i]);
67032a8088fSRui Paulo 	}
67132a8088fSRui Paulo 
67232a8088fSRui Paulo 	error = 0;
67332a8088fSRui Paulo out:
67432a8088fSRui Paulo 	mtx_unlock_spin(&sc->sc_mtx);
67532a8088fSRui Paulo 
67632a8088fSRui Paulo 	return (error);
67732a8088fSRui Paulo 
67832a8088fSRui Paulo }
67932a8088fSRui Paulo 
68032a8088fSRui Paulo /*
68132a8088fSRui Paulo  * Fan control functions.
68232a8088fSRui Paulo  */
68332a8088fSRui Paulo static int
68432a8088fSRui Paulo asmc_fan_count(device_t dev)
68532a8088fSRui Paulo {
68632a8088fSRui Paulo 	uint8_t buf[1];
68732a8088fSRui Paulo 
68832a8088fSRui Paulo 	if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, 1) < 0)
68932a8088fSRui Paulo 		return (-1);
69032a8088fSRui Paulo 
69132a8088fSRui Paulo 	return (buf[0]);
69232a8088fSRui Paulo }
69332a8088fSRui Paulo 
69432a8088fSRui Paulo static int
69532a8088fSRui Paulo asmc_fan_getvalue(device_t dev, const char *key, int fan)
69632a8088fSRui Paulo {
69732a8088fSRui Paulo 	int speed;
69832a8088fSRui Paulo 	uint8_t buf[2];
69932a8088fSRui Paulo 	char fankey[5];
70032a8088fSRui Paulo 
70132a8088fSRui Paulo 	snprintf(fankey, sizeof(fankey), key, fan);
70232a8088fSRui Paulo 	if (asmc_key_read(dev, fankey, buf, 2) < 0)
70332a8088fSRui Paulo 		return (-1);
70432a8088fSRui Paulo 	speed = (buf[0] << 6) | (buf[1] >> 2);
70532a8088fSRui Paulo 
70632a8088fSRui Paulo 	return (speed);
70732a8088fSRui Paulo }
70832a8088fSRui Paulo 
70932a8088fSRui Paulo static int
71032a8088fSRui Paulo asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS)
71132a8088fSRui Paulo {
71232a8088fSRui Paulo 	device_t dev = (device_t) arg1;
71332a8088fSRui Paulo 	int fan = arg2;
71432a8088fSRui Paulo 	int error;
71532a8088fSRui Paulo 	int32_t v;
71632a8088fSRui Paulo 
71732a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSPEED, fan);
71832a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
71932a8088fSRui Paulo 
72032a8088fSRui Paulo 	return (error);
72132a8088fSRui Paulo }
72232a8088fSRui Paulo 
72332a8088fSRui Paulo static int
72432a8088fSRui Paulo asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS)
72532a8088fSRui Paulo {
72632a8088fSRui Paulo 	device_t dev = (device_t) arg1;
72732a8088fSRui Paulo 	int fan = arg2;
72832a8088fSRui Paulo 	int error;
72932a8088fSRui Paulo 	int32_t v;
73032a8088fSRui Paulo 
73132a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSAFESPEED, fan);
73232a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
73332a8088fSRui Paulo 
73432a8088fSRui Paulo 	return (error);
73532a8088fSRui Paulo }
73632a8088fSRui Paulo 
73732a8088fSRui Paulo 
73832a8088fSRui Paulo static int
73932a8088fSRui Paulo asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS)
74032a8088fSRui Paulo {
74132a8088fSRui Paulo 	device_t dev = (device_t) arg1;
74232a8088fSRui Paulo 	int fan = arg2;
74332a8088fSRui Paulo 	int error;
74432a8088fSRui Paulo 	int32_t v;
74532a8088fSRui Paulo 
74632a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMINSPEED, fan);
74732a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
74832a8088fSRui Paulo 
74932a8088fSRui Paulo 	return (error);
75032a8088fSRui Paulo }
75132a8088fSRui Paulo 
75232a8088fSRui Paulo static int
75332a8088fSRui Paulo asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS)
75432a8088fSRui Paulo {
75532a8088fSRui Paulo 	device_t dev = (device_t) arg1;
75632a8088fSRui Paulo 	int fan = arg2;
75732a8088fSRui Paulo 	int error;
75832a8088fSRui Paulo 	int32_t v;
75932a8088fSRui Paulo 
76032a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMAXSPEED, fan);
76132a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
76232a8088fSRui Paulo 
76332a8088fSRui Paulo 	return (error);
76432a8088fSRui Paulo }
76532a8088fSRui Paulo 
76632a8088fSRui Paulo static int
76732a8088fSRui Paulo asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS)
76832a8088fSRui Paulo {
76932a8088fSRui Paulo 	device_t dev = (device_t) arg1;
77032a8088fSRui Paulo 	int fan = arg2;
77132a8088fSRui Paulo 	int error;
77232a8088fSRui Paulo 	int32_t v;
77332a8088fSRui Paulo 
77432a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANTARGETSPEED, fan);
77532a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
77632a8088fSRui Paulo 
77732a8088fSRui Paulo 	return (error);
77832a8088fSRui Paulo }
77932a8088fSRui Paulo 
78032a8088fSRui Paulo /*
78132a8088fSRui Paulo  * Temperature functions.
78232a8088fSRui Paulo  */
78332a8088fSRui Paulo static int
78432a8088fSRui Paulo asmc_temp_getvalue(device_t dev, const char *key)
78532a8088fSRui Paulo {
78632a8088fSRui Paulo 	uint8_t buf[2];
78732a8088fSRui Paulo 
78832a8088fSRui Paulo 	/*
78932a8088fSRui Paulo 	 * Check for invalid temperatures.
79032a8088fSRui Paulo 	 */
79132a8088fSRui Paulo 	if (asmc_key_read(dev, key, buf, 2) < 0)
79232a8088fSRui Paulo 		return (-1);
79332a8088fSRui Paulo 
79432a8088fSRui Paulo 	return (buf[0]);
79532a8088fSRui Paulo }
79632a8088fSRui Paulo 
79732a8088fSRui Paulo static int
79832a8088fSRui Paulo asmc_temp_sysctl(SYSCTL_HANDLER_ARGS)
79932a8088fSRui Paulo {
80032a8088fSRui Paulo 	device_t dev = (device_t) arg1;
80132a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
80232a8088fSRui Paulo 	int error, val;
80332a8088fSRui Paulo 
80432a8088fSRui Paulo 	val = asmc_temp_getvalue(dev, sc->sc_model->smc_temps[arg2]);
80532a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &val, 0, req);
80632a8088fSRui Paulo 
80732a8088fSRui Paulo 	return (error);
80832a8088fSRui Paulo }
80932a8088fSRui Paulo 
81032a8088fSRui Paulo /*
81132a8088fSRui Paulo  * Sudden Motion Sensor functions.
81232a8088fSRui Paulo  */
81332a8088fSRui Paulo static int
81432a8088fSRui Paulo asmc_sms_read(device_t dev, const char *key, int16_t *val)
81532a8088fSRui Paulo {
81632a8088fSRui Paulo 	uint8_t buf[2];
81732a8088fSRui Paulo 	int error;
81832a8088fSRui Paulo 
81932a8088fSRui Paulo 	/* no need to do locking here as asmc_key_read() already does it */
82032a8088fSRui Paulo 	switch (key[3]) {
82132a8088fSRui Paulo 	case 'X':
82232a8088fSRui Paulo 	case 'Y':
82332a8088fSRui Paulo 	case 'Z':
82432a8088fSRui Paulo 		error =	asmc_key_read(dev, key, buf, 2);
82532a8088fSRui Paulo 		break;
82632a8088fSRui Paulo 	default:
82732a8088fSRui Paulo 		device_printf(dev, "%s called with invalid argument %s\n",
82832a8088fSRui Paulo 			      __func__, key);
82932a8088fSRui Paulo 		error = 1;
83032a8088fSRui Paulo 		goto out;
83132a8088fSRui Paulo 	}
83232a8088fSRui Paulo 	*val = ((int16_t)buf[0] << 8) | buf[1];
83332a8088fSRui Paulo out:
83432a8088fSRui Paulo 	return (error);
83532a8088fSRui Paulo }
83632a8088fSRui Paulo 
83732a8088fSRui Paulo static void
83832a8088fSRui Paulo asmc_sms_calibrate(device_t dev)
83932a8088fSRui Paulo {
84032a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
84132a8088fSRui Paulo 
84232a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_X, &sc->sms_rest_x);
84332a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &sc->sms_rest_y);
84432a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &sc->sms_rest_z);
84532a8088fSRui Paulo }
84632a8088fSRui Paulo 
84732a8088fSRui Paulo static int
84832a8088fSRui Paulo asmc_sms_intrfast(void *arg)
84932a8088fSRui Paulo {
85032a8088fSRui Paulo 	uint8_t type;
85132a8088fSRui Paulo 	device_t dev = (device_t) arg;
85232a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
85332a8088fSRui Paulo 
85432a8088fSRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
8554470f0f3SRui Paulo 	type = ASMC_INTPORT_READ(sc);
85632a8088fSRui Paulo 	mtx_unlock_spin(&sc->sc_mtx);
85732a8088fSRui Paulo 
85832a8088fSRui Paulo 	sc->sc_sms_intrtype = type;
85932a8088fSRui Paulo 	asmc_sms_printintr(dev, type);
86032a8088fSRui Paulo 
86132a8088fSRui Paulo #ifdef INTR_FILTER
86232a8088fSRui Paulo 	return (FILTER_SCHEDULE_THREAD | FILTER_HANDLED);
86332a8088fSRui Paulo #else
86432a8088fSRui Paulo 	taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task);
86532a8088fSRui Paulo #endif
86632a8088fSRui Paulo 	return (FILTER_HANDLED);
86732a8088fSRui Paulo }
86832a8088fSRui Paulo 
86932a8088fSRui Paulo #ifdef INTR_FILTER
87032a8088fSRui Paulo static void
87132a8088fSRui Paulo asmc_sms_handler(void *arg)
87232a8088fSRui Paulo {
87332a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(arg);
87432a8088fSRui Paulo 
87532a8088fSRui Paulo 	asmc_sms_task(sc, 0);
87632a8088fSRui Paulo }
87732a8088fSRui Paulo #endif
87832a8088fSRui Paulo 
87932a8088fSRui Paulo 
88032a8088fSRui Paulo static void
88132a8088fSRui Paulo asmc_sms_printintr(device_t dev, uint8_t type)
88232a8088fSRui Paulo {
88332a8088fSRui Paulo 
88432a8088fSRui Paulo 	switch (type) {
88532a8088fSRui Paulo 	case ASMC_SMS_INTFF:
88632a8088fSRui Paulo 		device_printf(dev, "WARNING: possible free fall!\n");
88732a8088fSRui Paulo 		break;
88832a8088fSRui Paulo 	case ASMC_SMS_INTHA:
88932a8088fSRui Paulo 		device_printf(dev, "WARNING: high acceleration detected!\n");
89032a8088fSRui Paulo 		break;
89132a8088fSRui Paulo 	case ASMC_SMS_INTSH:
89232a8088fSRui Paulo 		device_printf(dev, "WARNING: possible shock!\n");
89332a8088fSRui Paulo 		break;
89432a8088fSRui Paulo 	default:
89532a8088fSRui Paulo 		device_printf(dev, "%s unknown interrupt\n", __func__);
89632a8088fSRui Paulo 	}
89732a8088fSRui Paulo }
89832a8088fSRui Paulo 
89932a8088fSRui Paulo static void
90032a8088fSRui Paulo asmc_sms_task(void *arg, int pending)
90132a8088fSRui Paulo {
90232a8088fSRui Paulo 	struct asmc_softc *sc = (struct asmc_softc *)arg;
90332a8088fSRui Paulo 	char notify[16];
90432a8088fSRui Paulo 	int type;
90532a8088fSRui Paulo 
90632a8088fSRui Paulo 	switch (sc->sc_sms_intrtype) {
90732a8088fSRui Paulo 	case ASMC_SMS_INTFF:
90832a8088fSRui Paulo 		type = 2;
90932a8088fSRui Paulo 		break;
91032a8088fSRui Paulo 	case ASMC_SMS_INTHA:
91132a8088fSRui Paulo 		type = 1;
91232a8088fSRui Paulo 		break;
91332a8088fSRui Paulo 	case ASMC_SMS_INTSH:
91432a8088fSRui Paulo 		type = 0;
91532a8088fSRui Paulo 		break;
91632a8088fSRui Paulo 	default:
91732a8088fSRui Paulo 		type = 255;
91832a8088fSRui Paulo 	}
91932a8088fSRui Paulo 
92032a8088fSRui Paulo 	snprintf(notify, sizeof(notify), " notify=0x%x", type);
9214470f0f3SRui Paulo 	devctl_notify("ACPI", "asmc", "SMS", notify);
92232a8088fSRui Paulo }
92332a8088fSRui Paulo 
92432a8088fSRui Paulo static int
92532a8088fSRui Paulo asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS)
92632a8088fSRui Paulo {
92732a8088fSRui Paulo 	device_t dev = (device_t) arg1;
92832a8088fSRui Paulo 	int error;
92932a8088fSRui Paulo 	int16_t val;
93032a8088fSRui Paulo 	int32_t v;
93132a8088fSRui Paulo 
93232a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_X, &val);
93332a8088fSRui Paulo 	v = (int32_t) val;
93432a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
93532a8088fSRui Paulo 
93632a8088fSRui Paulo 	return (error);
93732a8088fSRui Paulo }
93832a8088fSRui Paulo 
93932a8088fSRui Paulo static int
94032a8088fSRui Paulo asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS)
94132a8088fSRui Paulo {
94232a8088fSRui Paulo 	device_t dev = (device_t) arg1;
94332a8088fSRui Paulo 	int error;
94432a8088fSRui Paulo 	int16_t val;
94532a8088fSRui Paulo 	int32_t v;
94632a8088fSRui Paulo 
94732a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val);
94832a8088fSRui Paulo 	v = (int32_t) val;
94932a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
95032a8088fSRui Paulo 
95132a8088fSRui Paulo 	return (error);
95232a8088fSRui Paulo }
95332a8088fSRui Paulo 
95432a8088fSRui Paulo static int
95532a8088fSRui Paulo asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS)
95632a8088fSRui Paulo {
95732a8088fSRui Paulo 	device_t dev = (device_t) arg1;
95832a8088fSRui Paulo 	int error;
95932a8088fSRui Paulo 	int16_t val;
96032a8088fSRui Paulo 	int32_t v;
96132a8088fSRui Paulo 
96232a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val);
96332a8088fSRui Paulo 	v = (int32_t) val;
96432a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
96532a8088fSRui Paulo 
96632a8088fSRui Paulo 	return (error);
96732a8088fSRui Paulo }
96832a8088fSRui Paulo 
96932a8088fSRui Paulo static int
97032a8088fSRui Paulo asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS)
97132a8088fSRui Paulo {
97232a8088fSRui Paulo 	device_t dev = (device_t) arg1;
97332a8088fSRui Paulo 	uint8_t buf[6];
97432a8088fSRui Paulo 	int error;
97532a8088fSRui Paulo 	unsigned int level;
97632a8088fSRui Paulo 	int32_t v;
97732a8088fSRui Paulo 
97832a8088fSRui Paulo 	asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, 6);
97932a8088fSRui Paulo 	v = buf[2];
98032a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
98132a8088fSRui Paulo 	if (error == 0 && req->newptr != NULL) {
98232a8088fSRui Paulo 		level = *(unsigned int *)req->newptr;
98332a8088fSRui Paulo 		if (level > 255)
98432a8088fSRui Paulo 			return (EINVAL);
98532a8088fSRui Paulo 		buf[0] = level;
98632a8088fSRui Paulo 		buf[1] = 0x00;
98732a8088fSRui Paulo 		asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2);
98832a8088fSRui Paulo 	}
98932a8088fSRui Paulo 
99032a8088fSRui Paulo 	return (error);
99132a8088fSRui Paulo }
99232a8088fSRui Paulo 
99332a8088fSRui Paulo static int
99432a8088fSRui Paulo asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS)
99532a8088fSRui Paulo {
99632a8088fSRui Paulo 	device_t dev = (device_t) arg1;
99732a8088fSRui Paulo 	uint8_t buf[6];
99832a8088fSRui Paulo 	int error;
99932a8088fSRui Paulo 	unsigned int level;
100032a8088fSRui Paulo 	int32_t v;
100132a8088fSRui Paulo 
100232a8088fSRui Paulo 	asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, 6);
100332a8088fSRui Paulo 	v = buf[2];
100432a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
100532a8088fSRui Paulo 	if (error == 0 && req->newptr != NULL) {
100632a8088fSRui Paulo 		level = *(unsigned int *)req->newptr;
100732a8088fSRui Paulo 		if (level > 255)
100832a8088fSRui Paulo 			return (EINVAL);
100932a8088fSRui Paulo 		buf[0] = level;
101032a8088fSRui Paulo 		buf[1] = 0x00;
101132a8088fSRui Paulo 		asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2);
101232a8088fSRui Paulo 	}
101332a8088fSRui Paulo 
101432a8088fSRui Paulo 	return (error);
101532a8088fSRui Paulo }
1016