xref: /freebsd/sys/dev/asmc/asmc.c (revision 7d5fef1837ef4741244e6009dfe42835deab369b)
132a8088fSRui Paulo /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
44470f0f3SRui Paulo  * Copyright (c) 2007, 2008 Rui Paulo <rpaulo@FreeBSD.org>
532a8088fSRui Paulo  * All rights reserved.
632a8088fSRui Paulo  *
732a8088fSRui Paulo  * Redistribution and use in source and binary forms, with or without
832a8088fSRui Paulo  * modification, are permitted provided that the following conditions
932a8088fSRui Paulo  * are met:
1032a8088fSRui Paulo  * 1. Redistributions of source code must retain the above copyright
1132a8088fSRui Paulo  *    notice, this list of conditions and the following disclaimer.
1232a8088fSRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
1332a8088fSRui Paulo  *    notice, this list of conditions and the following disclaimer in the
1432a8088fSRui Paulo  *    documentation and/or other materials provided with the distribution.
1532a8088fSRui Paulo  *
1632a8088fSRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1732a8088fSRui Paulo  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1832a8088fSRui Paulo  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1932a8088fSRui Paulo  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2032a8088fSRui Paulo  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2132a8088fSRui Paulo  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2232a8088fSRui Paulo  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2332a8088fSRui Paulo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2432a8088fSRui Paulo  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2532a8088fSRui Paulo  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2632a8088fSRui Paulo  * POSSIBILITY OF SUCH DAMAGE.
2732a8088fSRui Paulo  *
2832a8088fSRui Paulo  */
2932a8088fSRui Paulo 
3032a8088fSRui Paulo /*
3132a8088fSRui Paulo  * Driver for Apple's System Management Console (SMC).
3232a8088fSRui Paulo  * SMC can be found on the MacBook, MacBook Pro and Mac Mini.
3332a8088fSRui Paulo  *
3432a8088fSRui Paulo  * Inspired by the Linux applesmc driver.
3532a8088fSRui Paulo  */
3632a8088fSRui Paulo 
3732a8088fSRui Paulo #include <sys/cdefs.h>
3832a8088fSRui Paulo __FBSDID("$FreeBSD$");
3932a8088fSRui Paulo 
4032a8088fSRui Paulo #include <sys/param.h>
4132a8088fSRui Paulo #include <sys/bus.h>
4232a8088fSRui Paulo #include <sys/conf.h>
4332a8088fSRui Paulo #include <sys/kernel.h>
4432a8088fSRui Paulo #include <sys/lock.h>
4532a8088fSRui Paulo #include <sys/malloc.h>
4632a8088fSRui Paulo #include <sys/module.h>
4732a8088fSRui Paulo #include <sys/mutex.h>
4832a8088fSRui Paulo #include <sys/sysctl.h>
4932a8088fSRui Paulo #include <sys/systm.h>
5032a8088fSRui Paulo #include <sys/taskqueue.h>
5132a8088fSRui Paulo #include <sys/rman.h>
524c061448SRui Paulo 
5332a8088fSRui Paulo #include <machine/resource.h>
54129d3046SJung-uk Kim 
55129d3046SJung-uk Kim #include <contrib/dev/acpica/include/acpi.h>
56129d3046SJung-uk Kim 
574470f0f3SRui Paulo #include <dev/acpica/acpivar.h>
5832a8088fSRui Paulo #include <dev/asmc/asmcvar.h>
5932a8088fSRui 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);
66108e3076SAdrian Chadd static int 	asmc_resume(device_t dev);
6732a8088fSRui Paulo 
6832a8088fSRui Paulo /*
6932a8088fSRui Paulo  * SMC functions.
7032a8088fSRui Paulo  */
7132a8088fSRui Paulo static int 	asmc_init(device_t dev);
72be80e49aSRui Paulo static int 	asmc_command(device_t dev, uint8_t command);
7332a8088fSRui Paulo static int 	asmc_wait(device_t dev, uint8_t val);
74be80e49aSRui Paulo static int 	asmc_wait_ack(device_t dev, uint8_t val, int amount);
7532a8088fSRui Paulo static int 	asmc_key_write(device_t dev, const char *key, uint8_t *buf,
7632a8088fSRui Paulo     uint8_t len);
7732a8088fSRui Paulo static int 	asmc_key_read(device_t dev, const char *key, uint8_t *buf,
7832a8088fSRui Paulo     uint8_t);
7932a8088fSRui Paulo static int 	asmc_fan_count(device_t dev);
8032a8088fSRui Paulo static int 	asmc_fan_getvalue(device_t dev, const char *key, int fan);
81447666f0SRui Paulo static int 	asmc_fan_setvalue(device_t dev, const char *key, int fan, int speed);
8232a8088fSRui Paulo static int 	asmc_temp_getvalue(device_t dev, const char *key);
8332a8088fSRui Paulo static int 	asmc_sms_read(device_t, const char *key, int16_t *val);
8432a8088fSRui Paulo static void 	asmc_sms_calibrate(device_t dev);
8532a8088fSRui Paulo static int 	asmc_sms_intrfast(void *arg);
8632a8088fSRui Paulo static void 	asmc_sms_printintr(device_t dev, uint8_t);
8732a8088fSRui Paulo static void 	asmc_sms_task(void *arg, int pending);
881269f4d4SRui Paulo #ifdef DEBUG
891269f4d4SRui Paulo void		asmc_dumpall(device_t);
901269f4d4SRui Paulo static int	asmc_key_dump(device_t, int);
911269f4d4SRui Paulo #endif
9232a8088fSRui Paulo 
9332a8088fSRui Paulo /*
9432a8088fSRui Paulo  * Model functions.
9532a8088fSRui Paulo  */
96447666f0SRui Paulo static int 	asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS);
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);
118447666f0SRui Paulo 	int (*smc_fan_id)(SYSCTL_HANDLER_ARGS);
11932a8088fSRui Paulo 	int (*smc_fan_speed)(SYSCTL_HANDLER_ARGS);
12032a8088fSRui Paulo 	int (*smc_fan_safespeed)(SYSCTL_HANDLER_ARGS);
12132a8088fSRui Paulo 	int (*smc_fan_minspeed)(SYSCTL_HANDLER_ARGS);
12232a8088fSRui Paulo 	int (*smc_fan_maxspeed)(SYSCTL_HANDLER_ARGS);
12332a8088fSRui Paulo 	int (*smc_fan_targetspeed)(SYSCTL_HANDLER_ARGS);
12432a8088fSRui Paulo 	int (*smc_light_left)(SYSCTL_HANDLER_ARGS);
12532a8088fSRui Paulo 	int (*smc_light_right)(SYSCTL_HANDLER_ARGS);
126be80e49aSRui Paulo 	int (*smc_light_control)(SYSCTL_HANDLER_ARGS);
12732a8088fSRui Paulo 
128d8246db0SRui Paulo 	const char 	*smc_temps[ASMC_TEMP_MAX];
129d8246db0SRui Paulo 	const char 	*smc_tempnames[ASMC_TEMP_MAX];
130d8246db0SRui Paulo 	const char 	*smc_tempdescs[ASMC_TEMP_MAX];
13132a8088fSRui Paulo };
13232a8088fSRui Paulo 
13332a8088fSRui Paulo static struct asmc_model *asmc_match(device_t dev);
13432a8088fSRui Paulo 
13532a8088fSRui Paulo #define ASMC_SMS_FUNCS	asmc_mb_sysctl_sms_x, asmc_mb_sysctl_sms_y, \
13632a8088fSRui Paulo 			asmc_mb_sysctl_sms_z
13732a8088fSRui Paulo 
138108e3076SAdrian Chadd #define ASMC_SMS_FUNCS_DISABLED NULL,NULL,NULL
139108e3076SAdrian Chadd 
140447666f0SRui Paulo #define ASMC_FAN_FUNCS	asmc_mb_sysctl_fanid, asmc_mb_sysctl_fanspeed, asmc_mb_sysctl_fansafespeed, \
14132a8088fSRui Paulo 			asmc_mb_sysctl_fanminspeed, \
14232a8088fSRui Paulo 			asmc_mb_sysctl_fanmaxspeed, \
14332a8088fSRui Paulo 			asmc_mb_sysctl_fantargetspeed
144108e3076SAdrian Chadd 
145108e3076SAdrian Chadd #define ASMC_FAN_FUNCS2	asmc_mb_sysctl_fanid, asmc_mb_sysctl_fanspeed, NULL, \
146108e3076SAdrian Chadd 			asmc_mb_sysctl_fanminspeed, \
147108e3076SAdrian Chadd 			asmc_mb_sysctl_fanmaxspeed, \
148108e3076SAdrian Chadd 			asmc_mb_sysctl_fantargetspeed
149108e3076SAdrian Chadd 
15032a8088fSRui Paulo #define ASMC_LIGHT_FUNCS asmc_mbp_sysctl_light_left, \
151be80e49aSRui Paulo 			 asmc_mbp_sysctl_light_right, \
152be80e49aSRui Paulo 			 asmc_mbp_sysctl_light_control
15332a8088fSRui Paulo 
1549f0bfc51SDavid Bright #define ASMC_LIGHT_FUNCS_DISABLED NULL, NULL, NULL
1559f0bfc51SDavid Bright 
15632a8088fSRui Paulo struct asmc_model asmc_models[] = {
15732a8088fSRui Paulo 	{
15832a8088fSRui Paulo 	  "MacBook1,1", "Apple SMC MacBook Core Duo",
159be80e49aSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
16032a8088fSRui Paulo 	  ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
16132a8088fSRui Paulo 	},
16232a8088fSRui Paulo 
16332a8088fSRui Paulo 	{
16432a8088fSRui Paulo 	  "MacBook2,1", "Apple SMC MacBook Core 2 Duo",
165be80e49aSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
16632a8088fSRui Paulo 	  ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
16732a8088fSRui Paulo 	},
16832a8088fSRui Paulo 
16932a8088fSRui Paulo 	{
170108e3076SAdrian Chadd 	  "MacBook3,1", "Apple SMC MacBook Core 2 Duo",
171108e3076SAdrian Chadd 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
172108e3076SAdrian Chadd 	  ASMC_MB31_TEMPS, ASMC_MB31_TEMPNAMES, ASMC_MB31_TEMPDESCS
173108e3076SAdrian Chadd 	},
174108e3076SAdrian Chadd 
175108e3076SAdrian Chadd 	{
176e4a14ce7SMark Johnston 	  "MacBook7,1", "Apple SMC MacBook Core 2 Duo (mid 2010)",
177e4a14ce7SMark Johnston 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS2, ASMC_LIGHT_FUNCS_DISABLED,
178e4a14ce7SMark Johnston 	  ASMC_MB71_TEMPS, ASMC_MB71_TEMPNAMES, ASMC_MB71_TEMPDESCS
179e4a14ce7SMark Johnston 	},
180e4a14ce7SMark Johnston 
181e4a14ce7SMark Johnston 	{
18232a8088fSRui Paulo 	  "MacBookPro1,1", "Apple SMC MacBook Pro Core Duo (15-inch)",
18332a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
18432a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
18532a8088fSRui Paulo 	},
18632a8088fSRui Paulo 
18732a8088fSRui Paulo 	{
18832a8088fSRui Paulo 	  "MacBookPro1,2", "Apple SMC MacBook Pro Core Duo (17-inch)",
18932a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
19032a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
19132a8088fSRui Paulo 	},
19232a8088fSRui Paulo 
19332a8088fSRui Paulo 	{
19432a8088fSRui Paulo 	  "MacBookPro2,1", "Apple SMC MacBook Pro Core 2 Duo (17-inch)",
19532a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
19632a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
19732a8088fSRui Paulo 	},
19832a8088fSRui Paulo 
19932a8088fSRui Paulo 	{
20032a8088fSRui Paulo 	  "MacBookPro2,2", "Apple SMC MacBook Pro Core 2 Duo (15-inch)",
20132a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
20232a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
20332a8088fSRui Paulo 	},
20432a8088fSRui Paulo 
20532a8088fSRui Paulo 	{
20632a8088fSRui Paulo 	  "MacBookPro3,1", "Apple SMC MacBook Pro Core 2 Duo (15-inch LED)",
20732a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
20832a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
20932a8088fSRui Paulo 	},
21032a8088fSRui Paulo 
21132a8088fSRui Paulo 	{
21232a8088fSRui Paulo 	  "MacBookPro3,2", "Apple SMC MacBook Pro Core 2 Duo (17-inch HD)",
21332a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
21432a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
21532a8088fSRui Paulo 	},
21632a8088fSRui Paulo 
217be80e49aSRui Paulo 	{
218be80e49aSRui Paulo 	  "MacBookPro4,1", "Apple SMC MacBook Pro Core 2 Duo (Penryn)",
219be80e49aSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
220be80e49aSRui Paulo 	  ASMC_MBP4_TEMPS, ASMC_MBP4_TEMPNAMES, ASMC_MBP4_TEMPDESCS
221be80e49aSRui Paulo 	},
222be80e49aSRui Paulo 
223447666f0SRui Paulo 	{
22431ae3b07SAdrian Chadd 	  "MacBookPro5,1", "Apple SMC MacBook Pro Core 2 Duo (2008/2009)",
22531ae3b07SAdrian Chadd 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
22631ae3b07SAdrian Chadd 	  ASMC_MBP5_TEMPS, ASMC_MBP5_TEMPNAMES, ASMC_MBP5_TEMPDESCS
22731ae3b07SAdrian Chadd 	},
22831ae3b07SAdrian Chadd 
22931ae3b07SAdrian Chadd 	{
23009ff71d3SDavid Bright 	  "MacBookPro8,1", "Apple SMC MacBook Pro (early 2011, 13-inch)",
23109ff71d3SDavid Bright 	  ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS2, ASMC_LIGHT_FUNCS,
23209ff71d3SDavid Bright 	  ASMC_MBP81_TEMPS, ASMC_MBP81_TEMPNAMES, ASMC_MBP81_TEMPDESCS
23309ff71d3SDavid Bright 	},
23409ff71d3SDavid Bright 
23509ff71d3SDavid Bright 	{
236447666f0SRui Paulo 	  "MacBookPro8,2", "Apple SMC MacBook Pro (early 2011)",
237447666f0SRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
23809ff71d3SDavid Bright 	  ASMC_MBP82_TEMPS, ASMC_MBP82_TEMPNAMES, ASMC_MBP82_TEMPDESCS
239447666f0SRui Paulo 	},
240447666f0SRui Paulo 
241447666f0SRui Paulo 	{
24298ae4866SDavid Bright 	 "MacBookPro9,2", "Apple SMC MacBook Pro (mid 2012)",
24398ae4866SDavid Bright 	  ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
24498ae4866SDavid Bright 	  ASMC_MBP9_TEMPS, ASMC_MBP9_TEMPNAMES, ASMC_MBP9_TEMPDESCS
24598ae4866SDavid Bright 	},
24698ae4866SDavid Bright 
24798ae4866SDavid Bright 	{
248109e2d29SAdrian Chadd 	  "MacBookPro11,2", "Apple SMC MacBook Pro Retina Core i7 (2013/2014)",
249109e2d29SAdrian Chadd 	  ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS2, ASMC_LIGHT_FUNCS,
250109e2d29SAdrian Chadd 	  ASMC_MBP112_TEMPS, ASMC_MBP112_TEMPNAMES, ASMC_MBP112_TEMPDESCS
251109e2d29SAdrian Chadd 	},
252109e2d29SAdrian Chadd 
253109e2d29SAdrian Chadd 	{
254447666f0SRui Paulo 	  "MacBookPro11,3", "Apple SMC MacBook Pro Retina Core i7 (2013/2014)",
255447666f0SRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
256109e2d29SAdrian Chadd 	  ASMC_MBP113_TEMPS, ASMC_MBP113_TEMPNAMES, ASMC_MBP113_TEMPDESCS
257447666f0SRui Paulo 	},
258447666f0SRui Paulo 
25932a8088fSRui Paulo 	/* The Mac Mini has no SMS */
26032a8088fSRui Paulo 	{
26132a8088fSRui Paulo 	  "Macmini1,1", "Apple SMC Mac Mini",
26232a8088fSRui Paulo 	  NULL, NULL, NULL,
26332a8088fSRui Paulo 	  ASMC_FAN_FUNCS,
264be80e49aSRui Paulo 	  NULL, NULL, NULL,
26532a8088fSRui Paulo 	  ASMC_MM_TEMPS, ASMC_MM_TEMPNAMES, ASMC_MM_TEMPDESCS
26632a8088fSRui Paulo 	},
26732a8088fSRui Paulo 
2689e33968bSDavid Bright         /* The Mac Mini 2,1 has no SMS */
2699e33968bSDavid Bright         {
2709e33968bSDavid Bright           "Macmini2,1", "Apple SMC Mac Mini 2,1",
2719e33968bSDavid Bright           ASMC_SMS_FUNCS_DISABLED,
2729e33968bSDavid Bright           ASMC_FAN_FUNCS,
2739e33968bSDavid Bright           ASMC_LIGHT_FUNCS_DISABLED,
2749e33968bSDavid Bright           ASMC_MM21_TEMPS, ASMC_MM21_TEMPNAMES, ASMC_MM21_TEMPDESCS
2759e33968bSDavid Bright         },
2769e33968bSDavid Bright 
277764442e0SGavin Atkinson 	/* The Mac Mini 3,1 has no SMS */
278764442e0SGavin Atkinson 	{
279764442e0SGavin Atkinson 	  "Macmini3,1", "Apple SMC Mac Mini 3,1",
280764442e0SGavin Atkinson 	  NULL, NULL, NULL,
281764442e0SGavin Atkinson 	  ASMC_FAN_FUNCS,
282764442e0SGavin Atkinson 	  NULL, NULL, NULL,
283764442e0SGavin Atkinson 	  ASMC_MM31_TEMPS, ASMC_MM31_TEMPNAMES, ASMC_MM31_TEMPDESCS
284764442e0SGavin Atkinson 	},
285764442e0SGavin Atkinson 
2869f0bfc51SDavid Bright 	/* The Mac Mini 4,1 (Mid-2010) has no SMS */
2879f0bfc51SDavid Bright 	{
2889f0bfc51SDavid Bright 	  "Macmini4,1", "Apple SMC Mac mini 4,1 (Mid-2010)",
2899f0bfc51SDavid Bright 	  ASMC_SMS_FUNCS_DISABLED,
2909f0bfc51SDavid Bright 	  ASMC_FAN_FUNCS,
2919f0bfc51SDavid Bright 	  ASMC_LIGHT_FUNCS_DISABLED,
2929f0bfc51SDavid Bright 	  ASMC_MM41_TEMPS, ASMC_MM41_TEMPNAMES, ASMC_MM41_TEMPDESCS
2939f0bfc51SDavid Bright 	},
2949f0bfc51SDavid Bright 
29571b475e7SDavid Bright 	/* The Mac Mini 5,2 has no SMS */
29671b475e7SDavid Bright 	{
29771b475e7SDavid Bright 	  "Macmini5,2", "Apple SMC Mac Mini 5,2",
29871b475e7SDavid Bright 	  NULL, NULL, NULL,
29971b475e7SDavid Bright 	  ASMC_FAN_FUNCS2,
30071b475e7SDavid Bright 	  NULL, NULL, NULL,
30171b475e7SDavid Bright 	  ASMC_MM52_TEMPS, ASMC_MM52_TEMPNAMES, ASMC_MM52_TEMPDESCS
30271b475e7SDavid Bright 	},
30371b475e7SDavid Bright 
30439a8ee13SDavid Bright 	/* Idem for the Mac Pro "Quad Core" (original) */
30539a8ee13SDavid Bright 	{
30639a8ee13SDavid Bright 	  "MacPro1,1", "Apple SMC Mac Pro (Quad Core)",
30739a8ee13SDavid Bright 	  NULL, NULL, NULL,
30839a8ee13SDavid Bright 	  ASMC_FAN_FUNCS,
30939a8ee13SDavid Bright 	  NULL, NULL, NULL,
31039a8ee13SDavid Bright 	  ASMC_MP1_TEMPS, ASMC_MP1_TEMPNAMES, ASMC_MP1_TEMPDESCS
31139a8ee13SDavid Bright 	},
31239a8ee13SDavid Bright 
31339a8ee13SDavid Bright 	/* Idem for the Mac Pro (8-core) */
314d8246db0SRui Paulo 	{
315d8246db0SRui Paulo 	  "MacPro2", "Apple SMC Mac Pro (8-core)",
316d8246db0SRui Paulo 	  NULL, NULL, NULL,
317d8246db0SRui Paulo 	  ASMC_FAN_FUNCS,
318be80e49aSRui Paulo 	  NULL, NULL, NULL,
31939a8ee13SDavid Bright 	  ASMC_MP2_TEMPS, ASMC_MP2_TEMPNAMES, ASMC_MP2_TEMPDESCS
320d8246db0SRui Paulo 	},
321941f9f10SRui Paulo 
322447666f0SRui Paulo 	/* Idem for the MacPro  2010*/
323447666f0SRui Paulo 	{
324447666f0SRui Paulo 	  "MacPro5,1", "Apple SMC MacPro (2010)",
325447666f0SRui Paulo 	  NULL, NULL, NULL,
326447666f0SRui Paulo 	  ASMC_FAN_FUNCS,
327447666f0SRui Paulo 	  NULL, NULL, NULL,
328447666f0SRui Paulo 	  ASMC_MP5_TEMPS, ASMC_MP5_TEMPNAMES, ASMC_MP5_TEMPDESCS
329447666f0SRui Paulo 	},
330447666f0SRui Paulo 
331*7d5fef18SAdam S 	/* Idem for the MacPro 2013 (cylinder) */
332*7d5fef18SAdam S 	{
333*7d5fef18SAdam S 	  "MacPro6,1", "Apple SMC MacPro (2013)",
334*7d5fef18SAdam S 	  ASMC_SMS_FUNCS_DISABLED,
335*7d5fef18SAdam S 	  ASMC_FAN_FUNCS,
336*7d5fef18SAdam S 	  ASMC_LIGHT_FUNCS_DISABLED,
337*7d5fef18SAdam S 	  ASMC_MP6_TEMPS, ASMC_MP6_TEMPNAMES, ASMC_MP6_TEMPDESCS
338*7d5fef18SAdam S 	},
339*7d5fef18SAdam S 
340941f9f10SRui Paulo 	{
341941f9f10SRui Paulo 	  "MacBookAir1,1", "Apple SMC MacBook Air",
342be80e49aSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
343941f9f10SRui Paulo 	  ASMC_MBA_TEMPS, ASMC_MBA_TEMPNAMES, ASMC_MBA_TEMPDESCS
344941f9f10SRui Paulo 	},
345941f9f10SRui Paulo 
346447666f0SRui Paulo 	{
347447666f0SRui Paulo 	  "MacBookAir3,1", "Apple SMC MacBook Air Core 2 Duo (Late 2010)",
348447666f0SRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
349447666f0SRui Paulo 	  ASMC_MBA3_TEMPS, ASMC_MBA3_TEMPNAMES, ASMC_MBA3_TEMPDESCS
350447666f0SRui Paulo 	},
351447666f0SRui Paulo 
352108e3076SAdrian Chadd 	{
353308340ccSMark Johnston 	  "MacBookAir4,1", "Apple SMC Macbook Air 11-inch (Mid 2011)",
354308340ccSMark Johnston 	  ASMC_SMS_FUNCS_DISABLED,
355308340ccSMark Johnston 	  ASMC_FAN_FUNCS2,
356308340ccSMark Johnston 	  ASMC_LIGHT_FUNCS,
357308340ccSMark Johnston 	  ASMC_MBA4_TEMPS, ASMC_MBA4_TEMPNAMES, ASMC_MBA4_TEMPDESCS
358308340ccSMark Johnston 	},
359308340ccSMark Johnston 
360308340ccSMark Johnston 	{
361308340ccSMark Johnston 	  "MacBookAir4,2", "Apple SMC Macbook Air 13-inch (Mid 2011)",
362308340ccSMark Johnston 	  ASMC_SMS_FUNCS_DISABLED,
363308340ccSMark Johnston 	  ASMC_FAN_FUNCS2,
364308340ccSMark Johnston 	  ASMC_LIGHT_FUNCS,
365308340ccSMark Johnston 	  ASMC_MBA4_TEMPS, ASMC_MBA4_TEMPNAMES, ASMC_MBA4_TEMPDESCS
366308340ccSMark Johnston 	},
367308340ccSMark Johnston 
368308340ccSMark Johnston 	{
369108e3076SAdrian Chadd 	  "MacBookAir5,1", "Apple SMC MacBook Air 11-inch (Mid 2012)",
370108e3076SAdrian Chadd 	  ASMC_SMS_FUNCS_DISABLED,
371108e3076SAdrian Chadd 	  ASMC_FAN_FUNCS2,
372108e3076SAdrian Chadd 	  ASMC_LIGHT_FUNCS,
373108e3076SAdrian Chadd 	  ASMC_MBA5_TEMPS, ASMC_MBA5_TEMPNAMES, ASMC_MBA5_TEMPDESCS
374108e3076SAdrian Chadd 	},
375108e3076SAdrian Chadd 
376108e3076SAdrian Chadd 	{
377108e3076SAdrian Chadd 	  "MacBookAir5,2", "Apple SMC MacBook Air 13-inch (Mid 2012)",
378108e3076SAdrian Chadd 	  ASMC_SMS_FUNCS_DISABLED,
379108e3076SAdrian Chadd 	  ASMC_FAN_FUNCS2,
380108e3076SAdrian Chadd 	  ASMC_LIGHT_FUNCS,
381108e3076SAdrian Chadd 	  ASMC_MBA5_TEMPS, ASMC_MBA5_TEMPNAMES, ASMC_MBA5_TEMPDESCS
382108e3076SAdrian Chadd 	},
383108e3076SAdrian Chadd 
384081954d3SDavid Bright 	{
385081954d3SDavid Bright 	  "MacBookAir7,1", "Apple SMC MacBook Air 11-inch (Early 2015)",
386081954d3SDavid Bright 	  ASMC_SMS_FUNCS_DISABLED,
387081954d3SDavid Bright 	  ASMC_FAN_FUNCS2,
388081954d3SDavid Bright 	  ASMC_LIGHT_FUNCS,
389081954d3SDavid Bright 	  ASMC_MBA7_TEMPS, ASMC_MBA7_TEMPNAMES, ASMC_MBA7_TEMPDESCS
390081954d3SDavid Bright 	},
391081954d3SDavid Bright 
392081954d3SDavid Bright 	{
393081954d3SDavid Bright 	  "MacBookAir7,2", "Apple SMC MacBook Air 13-inch (Early 2015)",
394081954d3SDavid Bright 	  ASMC_SMS_FUNCS_DISABLED,
395081954d3SDavid Bright 	  ASMC_FAN_FUNCS2,
396081954d3SDavid Bright 	  ASMC_LIGHT_FUNCS,
397081954d3SDavid Bright 	  ASMC_MBA7_TEMPS, ASMC_MBA7_TEMPNAMES, ASMC_MBA7_TEMPDESCS
398081954d3SDavid Bright 	},
39932a8088fSRui Paulo 	{ NULL, NULL }
40032a8088fSRui Paulo };
40132a8088fSRui Paulo 
40232a8088fSRui Paulo #undef ASMC_SMS_FUNCS
403108e3076SAdrian Chadd #undef ASMC_SMS_FUNCS_DISABLED
40432a8088fSRui Paulo #undef ASMC_FAN_FUNCS
405108e3076SAdrian Chadd #undef ASMC_FAN_FUNCS2
40632a8088fSRui Paulo #undef ASMC_LIGHT_FUNCS
40732a8088fSRui Paulo 
40832a8088fSRui Paulo /*
40932a8088fSRui Paulo  * Driver methods.
41032a8088fSRui Paulo  */
41132a8088fSRui Paulo static device_method_t	asmc_methods[] = {
41232a8088fSRui Paulo 	DEVMETHOD(device_probe,		asmc_probe),
41332a8088fSRui Paulo 	DEVMETHOD(device_attach,	asmc_attach),
41432a8088fSRui Paulo 	DEVMETHOD(device_detach,	asmc_detach),
415108e3076SAdrian Chadd 	DEVMETHOD(device_resume,	asmc_resume),
41632a8088fSRui Paulo 	{ 0, 0 }
41732a8088fSRui Paulo };
41832a8088fSRui Paulo 
41932a8088fSRui Paulo static driver_t	asmc_driver = {
42032a8088fSRui Paulo 	"asmc",
42132a8088fSRui Paulo 	asmc_methods,
42232a8088fSRui Paulo 	sizeof(struct asmc_softc)
42332a8088fSRui Paulo };
42432a8088fSRui Paulo 
4254470f0f3SRui Paulo /*
4264470f0f3SRui Paulo  * Debugging
4274470f0f3SRui Paulo  */
4284470f0f3SRui Paulo #define	_COMPONENT	ACPI_OEM
4294470f0f3SRui Paulo ACPI_MODULE_NAME("ASMC")
4304470f0f3SRui Paulo #ifdef DEBUG
4314470f0f3SRui Paulo #define ASMC_DPRINTF(str)	device_printf(dev, str)
4324fb9bf66SRui Paulo #else
4334fb9bf66SRui Paulo #define ASMC_DPRINTF(str)
4344470f0f3SRui Paulo #endif
4354470f0f3SRui Paulo 
436be80e49aSRui Paulo /* NB: can't be const */
4374470f0f3SRui Paulo static char *asmc_ids[] = { "APP0001", NULL };
4384470f0f3SRui Paulo 
43932a8088fSRui Paulo static devclass_t asmc_devclass;
44032a8088fSRui Paulo 
441108e3076SAdrian Chadd static unsigned int light_control = 0;
442108e3076SAdrian Chadd 
4434470f0f3SRui Paulo DRIVER_MODULE(asmc, acpi, asmc_driver, asmc_devclass, NULL, NULL);
4444470f0f3SRui Paulo MODULE_DEPEND(asmc, acpi, 1, 1, 1);
44532a8088fSRui Paulo 
44632a8088fSRui Paulo static struct asmc_model *
44732a8088fSRui Paulo asmc_match(device_t dev)
44832a8088fSRui Paulo {
44932a8088fSRui Paulo 	int i;
45032a8088fSRui Paulo 	char *model;
45132a8088fSRui Paulo 
4522be111bfSDavide Italiano 	model = kern_getenv("smbios.system.product");
45347105877SRui Paulo 	if (model == NULL)
45447105877SRui Paulo 		return (NULL);
45547105877SRui Paulo 
45632a8088fSRui Paulo 	for (i = 0; asmc_models[i].smc_model; i++) {
45732a8088fSRui Paulo 		if (!strncmp(model, asmc_models[i].smc_model, strlen(model))) {
45832a8088fSRui Paulo 			freeenv(model);
45932a8088fSRui Paulo 			return (&asmc_models[i]);
46032a8088fSRui Paulo 		}
46132a8088fSRui Paulo 	}
46232a8088fSRui Paulo 	freeenv(model);
46332a8088fSRui Paulo 
46432a8088fSRui Paulo 	return (NULL);
46532a8088fSRui Paulo }
46632a8088fSRui Paulo 
46732a8088fSRui Paulo static int
46832a8088fSRui Paulo asmc_probe(device_t dev)
46932a8088fSRui Paulo {
47032a8088fSRui Paulo 	struct asmc_model *model;
4715efca36fSTakanori Watanabe 	int rv;
47232a8088fSRui Paulo 
473a8de37b0SEitan Adler 	if (resource_disabled("asmc", 0))
474a8de37b0SEitan Adler 		return (ENXIO);
4755efca36fSTakanori Watanabe 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, asmc_ids, NULL);
4765efca36fSTakanori Watanabe 	if (rv > 0)
4775efca36fSTakanori Watanabe 		return (rv);
4784470f0f3SRui Paulo 
47932a8088fSRui Paulo 	model = asmc_match(dev);
4804470f0f3SRui Paulo 	if (!model) {
4814470f0f3SRui Paulo 		device_printf(dev, "model not recognized\n");
48232a8088fSRui Paulo 		return (ENXIO);
4834470f0f3SRui Paulo 	}
48432a8088fSRui Paulo 	device_set_desc(dev, model->smc_desc);
48532a8088fSRui Paulo 
4865efca36fSTakanori Watanabe 	return (rv);
48732a8088fSRui Paulo }
48832a8088fSRui Paulo 
48932a8088fSRui Paulo static int
49032a8088fSRui Paulo asmc_attach(device_t dev)
49132a8088fSRui Paulo {
49232a8088fSRui Paulo 	int i, j;
49332a8088fSRui Paulo 	int ret;
49432a8088fSRui Paulo 	char name[2];
49532a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
49632a8088fSRui Paulo 	struct sysctl_ctx_list *sysctlctx;
49732a8088fSRui Paulo 	struct sysctl_oid *sysctlnode;
49832a8088fSRui Paulo 	struct asmc_model *model;
49932a8088fSRui Paulo 
5004470f0f3SRui Paulo 	sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
5014470f0f3SRui Paulo 	    &sc->sc_rid_port, RF_ACTIVE);
5024470f0f3SRui Paulo 	if (sc->sc_ioport == NULL) {
5034470f0f3SRui Paulo 		device_printf(dev, "unable to allocate IO port\n");
5044470f0f3SRui Paulo 		return (ENOMEM);
5054470f0f3SRui Paulo 	}
5064470f0f3SRui Paulo 
50732a8088fSRui Paulo 	sysctlctx  = device_get_sysctl_ctx(dev);
50832a8088fSRui Paulo 	sysctlnode = device_get_sysctl_tree(dev);
50932a8088fSRui Paulo 
51032a8088fSRui Paulo 	model = asmc_match(dev);
51132a8088fSRui Paulo 
51232a8088fSRui Paulo 	mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN);
51332a8088fSRui Paulo 
51432a8088fSRui Paulo 	sc->sc_model = model;
51532a8088fSRui Paulo 	asmc_init(dev);
51632a8088fSRui Paulo 
51732a8088fSRui Paulo 	/*
51832a8088fSRui Paulo 	 * dev.asmc.n.fan.* tree.
51932a8088fSRui Paulo 	 */
52032a8088fSRui Paulo 	sc->sc_fan_tree[0] = SYSCTL_ADD_NODE(sysctlctx,
52132a8088fSRui Paulo 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "fan",
5227029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Fan Root Tree");
52332a8088fSRui Paulo 
52432a8088fSRui Paulo 	for (i = 1; i <= sc->sc_nfan; i++) {
52532a8088fSRui Paulo 		j = i - 1;
52632a8088fSRui Paulo 		name[0] = '0' + j;
52732a8088fSRui Paulo 		name[1] = 0;
52832a8088fSRui Paulo 		sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx,
52932a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[0]),
5307029da5cSPawel Biernacki 		    OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
53132a8088fSRui Paulo 		    "Fan Subtree");
53232a8088fSRui Paulo 
53332a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
53432a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
5357029da5cSPawel Biernacki 		    OID_AUTO, "id",
5367029da5cSPawel Biernacki 		    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
537447666f0SRui Paulo 		    dev, j, model->smc_fan_id, "I",
538447666f0SRui Paulo 		    "Fan ID");
539447666f0SRui Paulo 
540447666f0SRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
541447666f0SRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
5427029da5cSPawel Biernacki 		    OID_AUTO, "speed",
5437029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
54432a8088fSRui Paulo 		    dev, j, model->smc_fan_speed, "I",
54532a8088fSRui Paulo 		    "Fan speed in RPM");
54632a8088fSRui Paulo 
54732a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
54832a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
54932a8088fSRui Paulo 		    OID_AUTO, "safespeed",
5507029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
55132a8088fSRui Paulo 		    dev, j, model->smc_fan_safespeed, "I",
55232a8088fSRui Paulo 		    "Fan safe speed in RPM");
55332a8088fSRui Paulo 
55432a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
55532a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
55632a8088fSRui Paulo 		    OID_AUTO, "minspeed",
5577029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
55832a8088fSRui Paulo 		    dev, j, model->smc_fan_minspeed, "I",
55932a8088fSRui Paulo 		    "Fan minimum speed in RPM");
56032a8088fSRui Paulo 
56132a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
56232a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
56332a8088fSRui Paulo 		    OID_AUTO, "maxspeed",
5647029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
56532a8088fSRui Paulo 		    dev, j, model->smc_fan_maxspeed, "I",
56632a8088fSRui Paulo 		    "Fan maximum speed in RPM");
56732a8088fSRui Paulo 
56832a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
56932a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
57032a8088fSRui Paulo 		    OID_AUTO, "targetspeed",
5717029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
57232a8088fSRui Paulo 		    dev, j, model->smc_fan_targetspeed, "I",
57332a8088fSRui Paulo 		    "Fan target speed in RPM");
57432a8088fSRui Paulo 	}
57532a8088fSRui Paulo 
57632a8088fSRui Paulo 	/*
57732a8088fSRui Paulo 	 * dev.asmc.n.temp tree.
57832a8088fSRui Paulo 	 */
57932a8088fSRui Paulo 	sc->sc_temp_tree = SYSCTL_ADD_NODE(sysctlctx,
58032a8088fSRui Paulo 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp",
5817029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Temperature sensors");
58232a8088fSRui Paulo 
58332a8088fSRui Paulo 	for (i = 0; model->smc_temps[i]; i++) {
58432a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
58532a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_temp_tree),
58632a8088fSRui Paulo 		    OID_AUTO, model->smc_tempnames[i],
5877029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
58832a8088fSRui Paulo 		    dev, i, asmc_temp_sysctl, "I",
58932a8088fSRui Paulo 		    model->smc_tempdescs[i]);
59032a8088fSRui Paulo 	}
59132a8088fSRui Paulo 
592be80e49aSRui Paulo 	/*
593be80e49aSRui Paulo 	 * dev.asmc.n.light
594be80e49aSRui Paulo 	 */
595be80e49aSRui Paulo 	if (model->smc_light_left) {
596be80e49aSRui Paulo 		sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx,
597be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light",
5987029da5cSPawel Biernacki 		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
5997029da5cSPawel Biernacki 		    "Keyboard backlight sensors");
600be80e49aSRui Paulo 
601be80e49aSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
602be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_light_tree),
6037029da5cSPawel Biernacki 		    OID_AUTO, "left",
6047029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
605be80e49aSRui Paulo 		    dev, 0, model->smc_light_left, "I",
606be80e49aSRui Paulo 		    "Keyboard backlight left sensor");
607be80e49aSRui Paulo 
608be80e49aSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
609be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_light_tree),
6107029da5cSPawel Biernacki 		    OID_AUTO, "right",
6117029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
612be80e49aSRui Paulo 		    dev, 0, model->smc_light_right, "I",
613be80e49aSRui Paulo 		    "Keyboard backlight right sensor");
614be80e49aSRui Paulo 
615be80e49aSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
616be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_light_tree),
6173471c35dSRui Paulo 		    OID_AUTO, "control",
6187029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY |
6197029da5cSPawel Biernacki 		    CTLFLAG_NEEDGIANT, dev, 0,
6207029da5cSPawel Biernacki 		    model->smc_light_control, "I",
621be80e49aSRui Paulo 		    "Keyboard backlight brightness control");
622be80e49aSRui Paulo 	}
623be80e49aSRui Paulo 
62432a8088fSRui Paulo 	if (model->smc_sms_x == NULL)
62532a8088fSRui Paulo 		goto nosms;
62632a8088fSRui Paulo 
62732a8088fSRui Paulo 	/*
62832a8088fSRui Paulo 	 * dev.asmc.n.sms tree.
62932a8088fSRui Paulo 	 */
63032a8088fSRui Paulo 	sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx,
63132a8088fSRui Paulo 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "sms",
6327029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Sudden Motion Sensor");
63332a8088fSRui Paulo 
63432a8088fSRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
63532a8088fSRui Paulo 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
6367029da5cSPawel Biernacki 	    OID_AUTO, "x",
6377029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
63832a8088fSRui Paulo 	    dev, 0, model->smc_sms_x, "I",
63932a8088fSRui Paulo 	    "Sudden Motion Sensor X value");
64032a8088fSRui Paulo 
64132a8088fSRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
64232a8088fSRui Paulo 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
6437029da5cSPawel Biernacki 	    OID_AUTO, "y",
6447029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
64532a8088fSRui Paulo 	    dev, 0, model->smc_sms_y, "I",
64632a8088fSRui Paulo 	    "Sudden Motion Sensor Y value");
64732a8088fSRui Paulo 
64832a8088fSRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
64932a8088fSRui Paulo 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
6507029da5cSPawel Biernacki 	    OID_AUTO, "z",
6517029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
65232a8088fSRui Paulo 	    dev, 0, model->smc_sms_z, "I",
65332a8088fSRui Paulo 	    "Sudden Motion Sensor Z value");
65432a8088fSRui Paulo 
65532a8088fSRui Paulo 	/*
65632a8088fSRui Paulo 	 * Need a taskqueue to send devctl_notify() events
65732a8088fSRui Paulo 	 * when the SMS interrupt us.
65832a8088fSRui Paulo 	 *
65932a8088fSRui Paulo 	 * PI_REALTIME is used due to the sensitivity of the
66032a8088fSRui Paulo 	 * interrupt. An interrupt from the SMS means that the
66132a8088fSRui Paulo 	 * disk heads should be turned off as quickly as possible.
66232a8088fSRui Paulo 	 *
66332a8088fSRui Paulo 	 * We only need to do this for the non INTR_FILTER case.
66432a8088fSRui Paulo 	 */
66532a8088fSRui Paulo 	sc->sc_sms_tq = NULL;
66632a8088fSRui Paulo 	TASK_INIT(&sc->sc_sms_task, 0, asmc_sms_task, sc);
66732a8088fSRui Paulo 	sc->sc_sms_tq = taskqueue_create_fast("asmc_taskq", M_WAITOK,
66832a8088fSRui Paulo 	    taskqueue_thread_enqueue, &sc->sc_sms_tq);
66932a8088fSRui Paulo 	taskqueue_start_threads(&sc->sc_sms_tq, 1, PI_REALTIME, "%s sms taskq",
67032a8088fSRui Paulo 	    device_get_nameunit(dev));
67132a8088fSRui Paulo 	/*
67232a8088fSRui Paulo 	 * Allocate an IRQ for the SMS.
67332a8088fSRui Paulo 	 */
6744470f0f3SRui Paulo 	sc->sc_rid_irq = 0;
6754470f0f3SRui Paulo 	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
6764470f0f3SRui Paulo 	    &sc->sc_rid_irq, RF_ACTIVE);
6774470f0f3SRui Paulo 	if (sc->sc_irq == NULL) {
67832a8088fSRui Paulo 		device_printf(dev, "unable to allocate IRQ resource\n");
67932a8088fSRui Paulo 		ret = ENXIO;
68032a8088fSRui Paulo 		goto err2;
68132a8088fSRui Paulo 	}
68232a8088fSRui Paulo 
6834470f0f3SRui Paulo 	ret = bus_setup_intr(dev, sc->sc_irq,
68432a8088fSRui Paulo 	          INTR_TYPE_MISC | INTR_MPSAFE,
68532a8088fSRui Paulo 	    asmc_sms_intrfast, NULL,
68632a8088fSRui Paulo 	    dev, &sc->sc_cookie);
68732a8088fSRui Paulo 
68832a8088fSRui Paulo 	if (ret) {
68932a8088fSRui Paulo 		device_printf(dev, "unable to setup SMS IRQ\n");
69032a8088fSRui Paulo 		goto err1;
69132a8088fSRui Paulo 	}
69232a8088fSRui Paulo nosms:
69332a8088fSRui Paulo 	return (0);
69432a8088fSRui Paulo err1:
6954470f0f3SRui Paulo 	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, sc->sc_irq);
69632a8088fSRui Paulo err2:
6974470f0f3SRui Paulo 	bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
6984470f0f3SRui Paulo 	    sc->sc_ioport);
69932a8088fSRui Paulo 	mtx_destroy(&sc->sc_mtx);
70032a8088fSRui Paulo 	if (sc->sc_sms_tq)
70132a8088fSRui Paulo 		taskqueue_free(sc->sc_sms_tq);
70232a8088fSRui Paulo 
70332a8088fSRui Paulo 	return (ret);
70432a8088fSRui Paulo }
70532a8088fSRui Paulo 
70632a8088fSRui Paulo static int
70732a8088fSRui Paulo asmc_detach(device_t dev)
70832a8088fSRui Paulo {
70932a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
71032a8088fSRui Paulo 
71132a8088fSRui Paulo 	if (sc->sc_sms_tq) {
71232a8088fSRui Paulo 		taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task);
71332a8088fSRui Paulo 		taskqueue_free(sc->sc_sms_tq);
71432a8088fSRui Paulo 	}
71532a8088fSRui Paulo 	if (sc->sc_cookie)
7164470f0f3SRui Paulo 		bus_teardown_intr(dev, sc->sc_irq, sc->sc_cookie);
7174470f0f3SRui Paulo 	if (sc->sc_irq)
7184470f0f3SRui Paulo 		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq,
7194470f0f3SRui Paulo 		    sc->sc_irq);
7204470f0f3SRui Paulo 	if (sc->sc_ioport)
7214470f0f3SRui Paulo 		bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
7224470f0f3SRui Paulo 		    sc->sc_ioport);
72332a8088fSRui Paulo 	mtx_destroy(&sc->sc_mtx);
72432a8088fSRui Paulo 
72532a8088fSRui Paulo 	return (0);
72632a8088fSRui Paulo }
72732a8088fSRui Paulo 
728108e3076SAdrian Chadd static int
729108e3076SAdrian Chadd asmc_resume(device_t dev)
730108e3076SAdrian Chadd {
731108e3076SAdrian Chadd     uint8_t buf[2];
732108e3076SAdrian Chadd     buf[0] = light_control;
733108e3076SAdrian Chadd     buf[1] = 0x00;
734108e3076SAdrian Chadd     asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof buf);
735108e3076SAdrian Chadd     return (0);
736108e3076SAdrian Chadd }
737108e3076SAdrian Chadd 
7381269f4d4SRui Paulo #ifdef DEBUG
7391269f4d4SRui Paulo void asmc_dumpall(device_t dev)
7401269f4d4SRui Paulo {
7411269f4d4SRui Paulo 	int i;
7421269f4d4SRui Paulo 
7431269f4d4SRui Paulo 	/* XXX magic number */
7441269f4d4SRui Paulo 	for (i=0; i < 0x100; i++)
7451269f4d4SRui Paulo 		asmc_key_dump(dev, i);
7461269f4d4SRui Paulo }
7471269f4d4SRui Paulo #endif
7481269f4d4SRui Paulo 
74932a8088fSRui Paulo static int
75032a8088fSRui Paulo asmc_init(device_t dev)
75132a8088fSRui Paulo {
75232a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
75332a8088fSRui Paulo 	int i, error = 1;
75432a8088fSRui Paulo 	uint8_t buf[4];
75532a8088fSRui Paulo 
75632a8088fSRui Paulo 	if (sc->sc_model->smc_sms_x == NULL)
75732a8088fSRui Paulo 		goto nosms;
75832a8088fSRui Paulo 
75932a8088fSRui Paulo 	/*
760453130d9SPedro F. Giffuni 	 * We are ready to receive interrupts from the SMS.
76132a8088fSRui Paulo 	 */
76232a8088fSRui Paulo 	buf[0] = 0x01;
7634470f0f3SRui Paulo 	ASMC_DPRINTF(("intok key\n"));
76432a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_INTOK, buf, 1);
76532a8088fSRui Paulo 	DELAY(50);
76632a8088fSRui Paulo 
76732a8088fSRui Paulo 	/*
76832a8088fSRui Paulo 	 * Initiate the polling intervals.
76932a8088fSRui Paulo 	 */
77032a8088fSRui Paulo 	buf[0] = 20; /* msecs */
7714470f0f3SRui Paulo 	ASMC_DPRINTF(("low int key\n"));
77232a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_LOW_INT, buf, 1);
77332a8088fSRui Paulo 	DELAY(200);
77432a8088fSRui Paulo 
77532a8088fSRui Paulo 	buf[0] = 20; /* msecs */
7764470f0f3SRui Paulo 	ASMC_DPRINTF(("high int key\n"));
77732a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_HIGH_INT, buf, 1);
77832a8088fSRui Paulo 	DELAY(200);
77932a8088fSRui Paulo 
78032a8088fSRui Paulo 	buf[0] = 0x00;
78132a8088fSRui Paulo 	buf[1] = 0x60;
7824470f0f3SRui Paulo 	ASMC_DPRINTF(("sms low key\n"));
78332a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_LOW, buf, 2);
78432a8088fSRui Paulo 	DELAY(200);
78532a8088fSRui Paulo 
78632a8088fSRui Paulo 	buf[0] = 0x01;
78732a8088fSRui Paulo 	buf[1] = 0xc0;
7884470f0f3SRui Paulo 	ASMC_DPRINTF(("sms high key\n"));
78932a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_HIGH, buf, 2);
79032a8088fSRui Paulo 	DELAY(200);
79132a8088fSRui Paulo 
79232a8088fSRui Paulo 	/*
79332a8088fSRui Paulo 	 * I'm not sure what this key does, but it seems to be
79432a8088fSRui Paulo 	 * required.
79532a8088fSRui Paulo 	 */
79632a8088fSRui Paulo 	buf[0] = 0x01;
7974470f0f3SRui Paulo 	ASMC_DPRINTF(("sms flag key\n"));
79832a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_FLAG, buf, 1);
799b75dfbe8SRui Paulo 	DELAY(100);
80032a8088fSRui Paulo 
8011269f4d4SRui Paulo 	sc->sc_sms_intr_works = 0;
8021269f4d4SRui Paulo 
80332a8088fSRui Paulo 	/*
8041269f4d4SRui Paulo 	 * Retry SMS initialization 1000 times
8051269f4d4SRui Paulo 	 * (takes approx. 2 seconds in worst case)
80632a8088fSRui Paulo 	 */
8071269f4d4SRui Paulo 	for (i = 0; i < 1000; i++) {
80832a8088fSRui Paulo 		if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 &&
8091269f4d4SRui Paulo 		    (buf[0] == ASMC_SMS_INIT1 && buf[1] == ASMC_SMS_INIT2)) {
81032a8088fSRui Paulo 			error = 0;
8111269f4d4SRui Paulo 			sc->sc_sms_intr_works = 1;
8124fb9bf66SRui Paulo 			goto out;
81332a8088fSRui Paulo 		}
81432a8088fSRui Paulo 		buf[0] = ASMC_SMS_INIT1;
81532a8088fSRui Paulo 		buf[1] = ASMC_SMS_INIT2;
8164470f0f3SRui Paulo 		ASMC_DPRINTF(("sms key\n"));
81732a8088fSRui Paulo 		asmc_key_write(dev, ASMC_KEY_SMS, buf, 2);
81832a8088fSRui Paulo 		DELAY(50);
81932a8088fSRui Paulo 	}
8204fb9bf66SRui Paulo 	device_printf(dev, "WARNING: Sudden Motion Sensor not initialized!\n");
82132a8088fSRui Paulo 
8224fb9bf66SRui Paulo out:
82332a8088fSRui Paulo 	asmc_sms_calibrate(dev);
82432a8088fSRui Paulo nosms:
82532a8088fSRui Paulo 	sc->sc_nfan = asmc_fan_count(dev);
82632a8088fSRui Paulo 	if (sc->sc_nfan > ASMC_MAXFANS) {
82732a8088fSRui Paulo 		device_printf(dev, "more than %d fans were detected. Please "
82832a8088fSRui Paulo 		    "report this.\n", ASMC_MAXFANS);
82932a8088fSRui Paulo 		sc->sc_nfan = ASMC_MAXFANS;
83032a8088fSRui Paulo 	}
83132a8088fSRui Paulo 
83232a8088fSRui Paulo 	if (bootverbose) {
83332a8088fSRui Paulo 		/*
834447666f0SRui Paulo 		 * The number of keys is a 32 bit buffer
83532a8088fSRui Paulo 		 */
83632a8088fSRui Paulo 		asmc_key_read(dev, ASMC_NKEYS, buf, 4);
837447666f0SRui Paulo 		device_printf(dev, "number of keys: %d\n", ntohl(*(uint32_t*)buf));
83832a8088fSRui Paulo 	}
83932a8088fSRui Paulo 
8401269f4d4SRui Paulo #ifdef DEBUG
8411269f4d4SRui Paulo 	asmc_dumpall(dev);
8421269f4d4SRui Paulo #endif
8431269f4d4SRui Paulo 
84432a8088fSRui Paulo 	return (error);
84532a8088fSRui Paulo }
84632a8088fSRui Paulo 
84732a8088fSRui Paulo /*
84832a8088fSRui Paulo  * We need to make sure that the SMC acks the byte sent.
849be80e49aSRui Paulo  * Just wait up to (amount * 10)  ms.
85032a8088fSRui Paulo  */
85132a8088fSRui Paulo static int
852be80e49aSRui Paulo asmc_wait_ack(device_t dev, uint8_t val, int amount)
85332a8088fSRui Paulo {
8544470f0f3SRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
85532a8088fSRui Paulo 	u_int i;
85632a8088fSRui Paulo 
85732a8088fSRui Paulo 	val = val & ASMC_STATUS_MASK;
85832a8088fSRui Paulo 
859be80e49aSRui Paulo 	for (i = 0; i < amount; i++) {
8604470f0f3SRui Paulo 		if ((ASMC_CMDPORT_READ(sc) & ASMC_STATUS_MASK) == val)
86132a8088fSRui Paulo 			return (0);
86232a8088fSRui Paulo 		DELAY(10);
86332a8088fSRui Paulo 	}
86432a8088fSRui Paulo 
865be80e49aSRui Paulo 	return (1);
866be80e49aSRui Paulo }
867be80e49aSRui Paulo 
868be80e49aSRui Paulo /*
869be80e49aSRui Paulo  * We need to make sure that the SMC acks the byte sent.
870be80e49aSRui Paulo  * Just wait up to 100 ms.
871be80e49aSRui Paulo  */
872be80e49aSRui Paulo static int
873be80e49aSRui Paulo asmc_wait(device_t dev, uint8_t val)
874be80e49aSRui Paulo {
875e02d0cabSMateusz Guzik #ifdef DEBUG
876be80e49aSRui Paulo 	struct asmc_softc *sc;
877e02d0cabSMateusz Guzik #endif
878be80e49aSRui Paulo 
879be80e49aSRui Paulo 	if (asmc_wait_ack(dev, val, 1000) == 0)
880be80e49aSRui Paulo 		return (0);
881be80e49aSRui Paulo 
882e02d0cabSMateusz Guzik #ifdef DEBUG
883be80e49aSRui Paulo 	sc = device_get_softc(dev);
884e02d0cabSMateusz Guzik #endif
885be80e49aSRui Paulo 	val = val & ASMC_STATUS_MASK;
886be80e49aSRui Paulo 
887be80e49aSRui Paulo #ifdef DEBUG
88832a8088fSRui Paulo 	device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, val,
8894470f0f3SRui Paulo 	    ASMC_CMDPORT_READ(sc));
890be80e49aSRui Paulo #endif
891be80e49aSRui Paulo 	return (1);
892be80e49aSRui Paulo }
89332a8088fSRui Paulo 
894be80e49aSRui Paulo /*
895be80e49aSRui Paulo  * Send the given command, retrying up to 10 times if
896be80e49aSRui Paulo  * the acknowledgement fails.
897be80e49aSRui Paulo  */
898be80e49aSRui Paulo static int
899be80e49aSRui Paulo asmc_command(device_t dev, uint8_t command) {
900be80e49aSRui Paulo 	int i;
901be80e49aSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
902be80e49aSRui Paulo 
903be80e49aSRui Paulo 	for (i=0; i < 10; i++) {
904be80e49aSRui Paulo 		ASMC_CMDPORT_WRITE(sc, command);
905be80e49aSRui Paulo 		if (asmc_wait_ack(dev, 0x0c, 100) == 0) {
906be80e49aSRui Paulo 			return (0);
907be80e49aSRui Paulo 		}
908be80e49aSRui Paulo 	}
909be80e49aSRui Paulo 
910be80e49aSRui Paulo #ifdef DEBUG
911be80e49aSRui Paulo 	device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, command,
912be80e49aSRui Paulo 	    ASMC_CMDPORT_READ(sc));
913be80e49aSRui Paulo #endif
91432a8088fSRui Paulo 	return (1);
91532a8088fSRui Paulo }
91632a8088fSRui Paulo 
91732a8088fSRui Paulo static int
91832a8088fSRui Paulo asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len)
91932a8088fSRui Paulo {
920be80e49aSRui Paulo 	int i, error = 1, try = 0;
92132a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
92232a8088fSRui Paulo 
92332a8088fSRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
92432a8088fSRui Paulo 
925be80e49aSRui Paulo begin:
926be80e49aSRui Paulo 	if (asmc_command(dev, ASMC_CMDREAD))
92732a8088fSRui Paulo 		goto out;
92832a8088fSRui Paulo 
92932a8088fSRui Paulo 	for (i = 0; i < 4; i++) {
9304470f0f3SRui Paulo 		ASMC_DATAPORT_WRITE(sc, key[i]);
93132a8088fSRui Paulo 		if (asmc_wait(dev, 0x04))
93232a8088fSRui Paulo 			goto out;
93332a8088fSRui Paulo 	}
93432a8088fSRui Paulo 
9354470f0f3SRui Paulo 	ASMC_DATAPORT_WRITE(sc, len);
93632a8088fSRui Paulo 
93732a8088fSRui Paulo 	for (i = 0; i < len; i++) {
93832a8088fSRui Paulo 		if (asmc_wait(dev, 0x05))
93932a8088fSRui Paulo 			goto out;
9404470f0f3SRui Paulo 		buf[i] = ASMC_DATAPORT_READ(sc);
94132a8088fSRui Paulo 	}
94232a8088fSRui Paulo 
94332a8088fSRui Paulo 	error = 0;
94432a8088fSRui Paulo out:
945be80e49aSRui Paulo 	if (error) {
946be80e49aSRui Paulo 		if (++try < 10) goto begin;
947be80e49aSRui Paulo 		device_printf(dev,"%s for key %s failed %d times, giving up\n",
948be80e49aSRui Paulo 			__func__, key, try);
949be80e49aSRui Paulo 	}
950be80e49aSRui Paulo 
95132a8088fSRui Paulo 	mtx_unlock_spin(&sc->sc_mtx);
95232a8088fSRui Paulo 
95332a8088fSRui Paulo 	return (error);
95432a8088fSRui Paulo }
95532a8088fSRui Paulo 
9561269f4d4SRui Paulo #ifdef DEBUG
9571269f4d4SRui Paulo static int
9581269f4d4SRui Paulo asmc_key_dump(device_t dev, int number)
9591269f4d4SRui Paulo {
9601269f4d4SRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
9611269f4d4SRui Paulo 	char key[5] = { 0 };
9621269f4d4SRui Paulo 	char type[7] = { 0 };
9631269f4d4SRui Paulo 	uint8_t index[4];
9641269f4d4SRui Paulo 	uint8_t v[32];
9651269f4d4SRui Paulo 	uint8_t maxlen;
9661269f4d4SRui Paulo 	int i, error = 1, try = 0;
9671269f4d4SRui Paulo 
9681269f4d4SRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
9691269f4d4SRui Paulo 
9701269f4d4SRui Paulo 	index[0] = (number >> 24) & 0xff;
9711269f4d4SRui Paulo 	index[1] = (number >> 16) & 0xff;
9721269f4d4SRui Paulo 	index[2] = (number >> 8) & 0xff;
9731269f4d4SRui Paulo 	index[3] = (number) & 0xff;
9741269f4d4SRui Paulo 
9751269f4d4SRui Paulo begin:
9761269f4d4SRui Paulo 	if (asmc_command(dev, 0x12))
9771269f4d4SRui Paulo 		goto out;
9781269f4d4SRui Paulo 
9791269f4d4SRui Paulo 	for (i = 0; i < 4; i++) {
9801269f4d4SRui Paulo 		ASMC_DATAPORT_WRITE(sc, index[i]);
9811269f4d4SRui Paulo 		if (asmc_wait(dev, 0x04))
9821269f4d4SRui Paulo 			goto out;
9831269f4d4SRui Paulo 	}
9841269f4d4SRui Paulo 
9851269f4d4SRui Paulo 	ASMC_DATAPORT_WRITE(sc, 4);
9861269f4d4SRui Paulo 
9871269f4d4SRui Paulo 	for (i = 0; i < 4; i++) {
9881269f4d4SRui Paulo 		if (asmc_wait(dev, 0x05))
9891269f4d4SRui Paulo 			goto out;
9901269f4d4SRui Paulo 		key[i] = ASMC_DATAPORT_READ(sc);
9911269f4d4SRui Paulo 	}
9921269f4d4SRui Paulo 
9931269f4d4SRui Paulo 	/* get type */
9941269f4d4SRui Paulo 	if (asmc_command(dev, 0x13))
9951269f4d4SRui Paulo 		goto out;
9961269f4d4SRui Paulo 
9971269f4d4SRui Paulo 	for (i = 0; i < 4; i++) {
9981269f4d4SRui Paulo 		ASMC_DATAPORT_WRITE(sc, key[i]);
9991269f4d4SRui Paulo 		if (asmc_wait(dev, 0x04))
10001269f4d4SRui Paulo 			goto out;
10011269f4d4SRui Paulo 	}
10021269f4d4SRui Paulo 
10031269f4d4SRui Paulo 	ASMC_DATAPORT_WRITE(sc, 6);
10041269f4d4SRui Paulo 
10051269f4d4SRui Paulo 	for (i = 0; i < 6; i++) {
10061269f4d4SRui Paulo 		if (asmc_wait(dev, 0x05))
10071269f4d4SRui Paulo 			goto out;
10081269f4d4SRui Paulo 		type[i] = ASMC_DATAPORT_READ(sc);
10091269f4d4SRui Paulo 	}
10101269f4d4SRui Paulo 
10111269f4d4SRui Paulo 	error = 0;
10121269f4d4SRui Paulo out:
10131269f4d4SRui Paulo 	if (error) {
10141269f4d4SRui Paulo 		if (++try < 10) goto begin;
10151269f4d4SRui Paulo 		device_printf(dev,"%s for key %s failed %d times, giving up\n",
10161269f4d4SRui Paulo 			__func__, key, try);
10171269f4d4SRui Paulo 		mtx_unlock_spin(&sc->sc_mtx);
10181269f4d4SRui Paulo 	}
10191269f4d4SRui Paulo 	else {
10201269f4d4SRui Paulo 		char buf[1024];
10211269f4d4SRui Paulo 		char buf2[8];
10221269f4d4SRui Paulo 		mtx_unlock_spin(&sc->sc_mtx);
10231269f4d4SRui Paulo 		maxlen = type[0];
10241269f4d4SRui Paulo 		type[0] = ' ';
10251269f4d4SRui Paulo 		type[5] = 0;
10261269f4d4SRui Paulo 		if (maxlen > sizeof(v)) {
1027f17bca82SRui Paulo 			device_printf(dev,
1028f17bca82SRui Paulo 			    "WARNING: cropping maxlen from %d to %zu\n",
1029f17bca82SRui Paulo 			    maxlen, sizeof(v));
10301269f4d4SRui Paulo 			maxlen = sizeof(v);
10311269f4d4SRui Paulo 		}
10321269f4d4SRui Paulo 		for (i = 0; i < sizeof(v); i++) {
10331269f4d4SRui Paulo 			v[i] = 0;
10341269f4d4SRui Paulo 		}
10351269f4d4SRui Paulo 		asmc_key_read(dev, key, v, maxlen);
10361269f4d4SRui Paulo 		snprintf(buf, sizeof(buf), "key %d is: %s, type %s "
10371269f4d4SRui Paulo 		    "(len %d), data", number, key, type, maxlen);
10381269f4d4SRui Paulo 		for (i = 0; i < maxlen; i++) {
1039108e3076SAdrian Chadd 			snprintf(buf2, sizeof(buf2), " %02x", v[i]);
10401269f4d4SRui Paulo 			strlcat(buf, buf2, sizeof(buf));
10411269f4d4SRui Paulo 		}
10421269f4d4SRui Paulo 		strlcat(buf, " \n", sizeof(buf));
104346c76550SRoman Divacky 		device_printf(dev, "%s", buf);
10441269f4d4SRui Paulo 	}
10451269f4d4SRui Paulo 
10461269f4d4SRui Paulo 	return (error);
10471269f4d4SRui Paulo }
10481269f4d4SRui Paulo #endif
10491269f4d4SRui Paulo 
105032a8088fSRui Paulo static int
105132a8088fSRui Paulo asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len)
105232a8088fSRui Paulo {
1053be80e49aSRui Paulo 	int i, error = -1, try = 0;
105432a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
105532a8088fSRui Paulo 
105632a8088fSRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
105732a8088fSRui Paulo 
1058be80e49aSRui Paulo begin:
10594470f0f3SRui Paulo 	ASMC_DPRINTF(("cmd port: cmd write\n"));
1060be80e49aSRui Paulo 	if (asmc_command(dev, ASMC_CMDWRITE))
106132a8088fSRui Paulo 		goto out;
106232a8088fSRui Paulo 
10634470f0f3SRui Paulo 	ASMC_DPRINTF(("data port: key\n"));
106432a8088fSRui Paulo 	for (i = 0; i < 4; i++) {
10654470f0f3SRui Paulo 		ASMC_DATAPORT_WRITE(sc, key[i]);
106632a8088fSRui Paulo 		if (asmc_wait(dev, 0x04))
106732a8088fSRui Paulo 			goto out;
106832a8088fSRui Paulo 	}
10694470f0f3SRui Paulo 	ASMC_DPRINTF(("data port: length\n"));
10704470f0f3SRui Paulo 	ASMC_DATAPORT_WRITE(sc, len);
107132a8088fSRui Paulo 
10724470f0f3SRui Paulo 	ASMC_DPRINTF(("data port: buffer\n"));
107332a8088fSRui Paulo 	for (i = 0; i < len; i++) {
107432a8088fSRui Paulo 		if (asmc_wait(dev, 0x04))
107532a8088fSRui Paulo 			goto out;
10764470f0f3SRui Paulo 		ASMC_DATAPORT_WRITE(sc, buf[i]);
107732a8088fSRui Paulo 	}
107832a8088fSRui Paulo 
107932a8088fSRui Paulo 	error = 0;
108032a8088fSRui Paulo out:
1081be80e49aSRui Paulo 	if (error) {
1082be80e49aSRui Paulo 		if (++try < 10) goto begin;
1083be80e49aSRui Paulo 		device_printf(dev,"%s for key %s failed %d times, giving up\n",
1084be80e49aSRui Paulo 			__func__, key, try);
1085be80e49aSRui Paulo 	}
1086be80e49aSRui Paulo 
108732a8088fSRui Paulo 	mtx_unlock_spin(&sc->sc_mtx);
108832a8088fSRui Paulo 
108932a8088fSRui Paulo 	return (error);
109032a8088fSRui Paulo 
109132a8088fSRui Paulo }
109232a8088fSRui Paulo 
109332a8088fSRui Paulo /*
109432a8088fSRui Paulo  * Fan control functions.
109532a8088fSRui Paulo  */
109632a8088fSRui Paulo static int
109732a8088fSRui Paulo asmc_fan_count(device_t dev)
109832a8088fSRui Paulo {
109932a8088fSRui Paulo 	uint8_t buf[1];
110032a8088fSRui Paulo 
11019c325393SMark Johnston 	if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, sizeof buf) != 0)
110232a8088fSRui Paulo 		return (-1);
110332a8088fSRui Paulo 
110432a8088fSRui Paulo 	return (buf[0]);
110532a8088fSRui Paulo }
110632a8088fSRui Paulo 
110732a8088fSRui Paulo static int
110832a8088fSRui Paulo asmc_fan_getvalue(device_t dev, const char *key, int fan)
110932a8088fSRui Paulo {
111032a8088fSRui Paulo 	int speed;
111132a8088fSRui Paulo 	uint8_t buf[2];
111232a8088fSRui Paulo 	char fankey[5];
111332a8088fSRui Paulo 
111432a8088fSRui Paulo 	snprintf(fankey, sizeof(fankey), key, fan);
11159c325393SMark Johnston 	if (asmc_key_read(dev, fankey, buf, sizeof buf) != 0)
111632a8088fSRui Paulo 		return (-1);
111732a8088fSRui Paulo 	speed = (buf[0] << 6) | (buf[1] >> 2);
111832a8088fSRui Paulo 
111932a8088fSRui Paulo 	return (speed);
112032a8088fSRui Paulo }
112132a8088fSRui Paulo 
1122447666f0SRui Paulo static char*
1123623534d6SUlrich Spörlein asmc_fan_getstring(device_t dev, const char *key, int fan, uint8_t *buf, uint8_t buflen)
1124447666f0SRui Paulo {
1125447666f0SRui Paulo 	char fankey[5];
1126447666f0SRui Paulo 	char* desc;
1127447666f0SRui Paulo 
1128447666f0SRui Paulo 	snprintf(fankey, sizeof(fankey), key, fan);
11299c325393SMark Johnston 	if (asmc_key_read(dev, fankey, buf, buflen) != 0)
1130447666f0SRui Paulo 		return (NULL);
1131447666f0SRui Paulo 	desc = buf+4;
1132447666f0SRui Paulo 
1133447666f0SRui Paulo 	return (desc);
1134447666f0SRui Paulo }
1135447666f0SRui Paulo 
1136447666f0SRui Paulo static int
1137447666f0SRui Paulo asmc_fan_setvalue(device_t dev, const char *key, int fan, int speed)
1138447666f0SRui Paulo {
1139447666f0SRui Paulo 	uint8_t buf[2];
1140447666f0SRui Paulo 	char fankey[5];
1141447666f0SRui Paulo 
1142447666f0SRui Paulo 	speed *= 4;
1143447666f0SRui Paulo 
1144447666f0SRui Paulo 	buf[0] = speed>>8;
1145447666f0SRui Paulo 	buf[1] = speed;
1146447666f0SRui Paulo 
1147447666f0SRui Paulo 	snprintf(fankey, sizeof(fankey), key, fan);
1148447666f0SRui Paulo 	if (asmc_key_write(dev, fankey, buf, sizeof buf) < 0)
1149447666f0SRui Paulo 		return (-1);
1150447666f0SRui Paulo 
1151447666f0SRui Paulo 	return (0);
1152447666f0SRui Paulo }
1153447666f0SRui Paulo 
115432a8088fSRui Paulo static int
115532a8088fSRui Paulo asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS)
115632a8088fSRui Paulo {
115732a8088fSRui Paulo 	device_t dev = (device_t) arg1;
115832a8088fSRui Paulo 	int fan = arg2;
115932a8088fSRui Paulo 	int error;
116032a8088fSRui Paulo 	int32_t v;
116132a8088fSRui Paulo 
116232a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSPEED, fan);
116332a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
116432a8088fSRui Paulo 
116532a8088fSRui Paulo 	return (error);
116632a8088fSRui Paulo }
116732a8088fSRui Paulo 
116832a8088fSRui Paulo static int
1169447666f0SRui Paulo asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS)
1170447666f0SRui Paulo {
1171623534d6SUlrich Spörlein 	uint8_t buf[16];
1172447666f0SRui Paulo 	device_t dev = (device_t) arg1;
1173447666f0SRui Paulo 	int fan = arg2;
1174447666f0SRui Paulo 	int error = true;
1175447666f0SRui Paulo 	char* desc;
1176447666f0SRui Paulo 
1177623534d6SUlrich Spörlein 	desc = asmc_fan_getstring(dev, ASMC_KEY_FANID, fan, buf, sizeof(buf));
1178447666f0SRui Paulo 
1179447666f0SRui Paulo 	if (desc != NULL)
1180447666f0SRui Paulo 		error = sysctl_handle_string(oidp, desc, 0, req);
1181447666f0SRui Paulo 
1182447666f0SRui Paulo 	return (error);
1183447666f0SRui Paulo }
1184447666f0SRui Paulo 
1185447666f0SRui Paulo static int
118632a8088fSRui Paulo asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS)
118732a8088fSRui Paulo {
118832a8088fSRui Paulo 	device_t dev = (device_t) arg1;
118932a8088fSRui Paulo 	int fan = arg2;
119032a8088fSRui Paulo 	int error;
119132a8088fSRui Paulo 	int32_t v;
119232a8088fSRui Paulo 
119332a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSAFESPEED, fan);
119432a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
119532a8088fSRui Paulo 
119632a8088fSRui Paulo 	return (error);
119732a8088fSRui Paulo }
119832a8088fSRui Paulo 
119932a8088fSRui Paulo static int
120032a8088fSRui Paulo asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS)
120132a8088fSRui Paulo {
120232a8088fSRui Paulo 	device_t dev = (device_t) arg1;
120332a8088fSRui Paulo 	int fan = arg2;
120432a8088fSRui Paulo 	int error;
120532a8088fSRui Paulo 	int32_t v;
120632a8088fSRui Paulo 
120732a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMINSPEED, fan);
120832a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
120932a8088fSRui Paulo 
1210447666f0SRui Paulo 	if (error == 0 && req->newptr != NULL) {
12110e1152fcSHans Petter Selasky 		unsigned int newspeed = v;
1212447666f0SRui Paulo 		asmc_fan_setvalue(dev, ASMC_KEY_FANMINSPEED, fan, newspeed);
1213447666f0SRui Paulo 	}
1214447666f0SRui Paulo 
121532a8088fSRui Paulo 	return (error);
121632a8088fSRui Paulo }
121732a8088fSRui Paulo 
121832a8088fSRui Paulo static int
121932a8088fSRui Paulo asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS)
122032a8088fSRui Paulo {
122132a8088fSRui Paulo 	device_t dev = (device_t) arg1;
122232a8088fSRui Paulo 	int fan = arg2;
122332a8088fSRui Paulo 	int error;
122432a8088fSRui Paulo 	int32_t v;
122532a8088fSRui Paulo 
122632a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMAXSPEED, fan);
122732a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
122832a8088fSRui Paulo 
1229447666f0SRui Paulo 	if (error == 0 && req->newptr != NULL) {
12300e1152fcSHans Petter Selasky 		unsigned int newspeed = v;
1231447666f0SRui Paulo 		asmc_fan_setvalue(dev, ASMC_KEY_FANMAXSPEED, fan, newspeed);
1232447666f0SRui Paulo 	}
1233447666f0SRui Paulo 
123432a8088fSRui Paulo 	return (error);
123532a8088fSRui Paulo }
123632a8088fSRui Paulo 
123732a8088fSRui Paulo static int
123832a8088fSRui Paulo asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS)
123932a8088fSRui Paulo {
124032a8088fSRui Paulo 	device_t dev = (device_t) arg1;
124132a8088fSRui Paulo 	int fan = arg2;
124232a8088fSRui Paulo 	int error;
124332a8088fSRui Paulo 	int32_t v;
124432a8088fSRui Paulo 
124532a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANTARGETSPEED, fan);
124632a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
124732a8088fSRui Paulo 
1248447666f0SRui Paulo 	if (error == 0 && req->newptr != NULL) {
12490e1152fcSHans Petter Selasky 		unsigned int newspeed = v;
1250447666f0SRui Paulo 		asmc_fan_setvalue(dev, ASMC_KEY_FANTARGETSPEED, fan, newspeed);
1251447666f0SRui Paulo 	}
1252447666f0SRui Paulo 
125332a8088fSRui Paulo 	return (error);
125432a8088fSRui Paulo }
125532a8088fSRui Paulo 
125632a8088fSRui Paulo /*
125732a8088fSRui Paulo  * Temperature functions.
125832a8088fSRui Paulo  */
125932a8088fSRui Paulo static int
126032a8088fSRui Paulo asmc_temp_getvalue(device_t dev, const char *key)
126132a8088fSRui Paulo {
126232a8088fSRui Paulo 	uint8_t buf[2];
126332a8088fSRui Paulo 
126432a8088fSRui Paulo 	/*
126532a8088fSRui Paulo 	 * Check for invalid temperatures.
126632a8088fSRui Paulo 	 */
12679c325393SMark Johnston 	if (asmc_key_read(dev, key, buf, sizeof buf) != 0)
126832a8088fSRui Paulo 		return (-1);
126932a8088fSRui Paulo 
127032a8088fSRui Paulo 	return (buf[0]);
127132a8088fSRui Paulo }
127232a8088fSRui Paulo 
127332a8088fSRui Paulo static int
127432a8088fSRui Paulo asmc_temp_sysctl(SYSCTL_HANDLER_ARGS)
127532a8088fSRui Paulo {
127632a8088fSRui Paulo 	device_t dev = (device_t) arg1;
127732a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
127832a8088fSRui Paulo 	int error, val;
127932a8088fSRui Paulo 
128032a8088fSRui Paulo 	val = asmc_temp_getvalue(dev, sc->sc_model->smc_temps[arg2]);
128132a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &val, 0, req);
128232a8088fSRui Paulo 
128332a8088fSRui Paulo 	return (error);
128432a8088fSRui Paulo }
128532a8088fSRui Paulo 
128632a8088fSRui Paulo /*
128732a8088fSRui Paulo  * Sudden Motion Sensor functions.
128832a8088fSRui Paulo  */
128932a8088fSRui Paulo static int
129032a8088fSRui Paulo asmc_sms_read(device_t dev, const char *key, int16_t *val)
129132a8088fSRui Paulo {
129232a8088fSRui Paulo 	uint8_t buf[2];
129332a8088fSRui Paulo 	int error;
129432a8088fSRui Paulo 
129532a8088fSRui Paulo 	/* no need to do locking here as asmc_key_read() already does it */
129632a8088fSRui Paulo 	switch (key[3]) {
129732a8088fSRui Paulo 	case 'X':
129832a8088fSRui Paulo 	case 'Y':
129932a8088fSRui Paulo 	case 'Z':
1300447666f0SRui Paulo 		error =	asmc_key_read(dev, key, buf, sizeof buf);
130132a8088fSRui Paulo 		break;
130232a8088fSRui Paulo 	default:
130332a8088fSRui Paulo 		device_printf(dev, "%s called with invalid argument %s\n",
130432a8088fSRui Paulo 			      __func__, key);
130532a8088fSRui Paulo 		error = 1;
130632a8088fSRui Paulo 		goto out;
130732a8088fSRui Paulo 	}
130832a8088fSRui Paulo 	*val = ((int16_t)buf[0] << 8) | buf[1];
130932a8088fSRui Paulo out:
131032a8088fSRui Paulo 	return (error);
131132a8088fSRui Paulo }
131232a8088fSRui Paulo 
131332a8088fSRui Paulo static void
131432a8088fSRui Paulo asmc_sms_calibrate(device_t dev)
131532a8088fSRui Paulo {
131632a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
131732a8088fSRui Paulo 
131832a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_X, &sc->sms_rest_x);
131932a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &sc->sms_rest_y);
132032a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &sc->sms_rest_z);
132132a8088fSRui Paulo }
132232a8088fSRui Paulo 
132332a8088fSRui Paulo static int
132432a8088fSRui Paulo asmc_sms_intrfast(void *arg)
132532a8088fSRui Paulo {
132632a8088fSRui Paulo 	uint8_t type;
132732a8088fSRui Paulo 	device_t dev = (device_t) arg;
132832a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
13291269f4d4SRui Paulo 	if (!sc->sc_sms_intr_works)
13301269f4d4SRui Paulo 		return (FILTER_HANDLED);
133132a8088fSRui Paulo 
133232a8088fSRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
13334470f0f3SRui Paulo 	type = ASMC_INTPORT_READ(sc);
133432a8088fSRui Paulo 	mtx_unlock_spin(&sc->sc_mtx);
133532a8088fSRui Paulo 
133632a8088fSRui Paulo 	sc->sc_sms_intrtype = type;
133732a8088fSRui Paulo 	asmc_sms_printintr(dev, type);
133832a8088fSRui Paulo 
133932a8088fSRui Paulo 	taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task);
134032a8088fSRui Paulo 	return (FILTER_HANDLED);
134132a8088fSRui Paulo }
134232a8088fSRui Paulo 
134332a8088fSRui Paulo static void
134432a8088fSRui Paulo asmc_sms_printintr(device_t dev, uint8_t type)
134532a8088fSRui Paulo {
134632a8088fSRui Paulo 
134732a8088fSRui Paulo 	switch (type) {
134832a8088fSRui Paulo 	case ASMC_SMS_INTFF:
134932a8088fSRui Paulo 		device_printf(dev, "WARNING: possible free fall!\n");
135032a8088fSRui Paulo 		break;
135132a8088fSRui Paulo 	case ASMC_SMS_INTHA:
135232a8088fSRui Paulo 		device_printf(dev, "WARNING: high acceleration detected!\n");
135332a8088fSRui Paulo 		break;
135432a8088fSRui Paulo 	case ASMC_SMS_INTSH:
135532a8088fSRui Paulo 		device_printf(dev, "WARNING: possible shock!\n");
135632a8088fSRui Paulo 		break;
135732a8088fSRui Paulo 	default:
135832a8088fSRui Paulo 		device_printf(dev, "%s unknown interrupt\n", __func__);
135932a8088fSRui Paulo 	}
136032a8088fSRui Paulo }
136132a8088fSRui Paulo 
136232a8088fSRui Paulo static void
136332a8088fSRui Paulo asmc_sms_task(void *arg, int pending)
136432a8088fSRui Paulo {
136532a8088fSRui Paulo 	struct asmc_softc *sc = (struct asmc_softc *)arg;
136632a8088fSRui Paulo 	char notify[16];
136732a8088fSRui Paulo 	int type;
136832a8088fSRui Paulo 
136932a8088fSRui Paulo 	switch (sc->sc_sms_intrtype) {
137032a8088fSRui Paulo 	case ASMC_SMS_INTFF:
137132a8088fSRui Paulo 		type = 2;
137232a8088fSRui Paulo 		break;
137332a8088fSRui Paulo 	case ASMC_SMS_INTHA:
137432a8088fSRui Paulo 		type = 1;
137532a8088fSRui Paulo 		break;
137632a8088fSRui Paulo 	case ASMC_SMS_INTSH:
137732a8088fSRui Paulo 		type = 0;
137832a8088fSRui Paulo 		break;
137932a8088fSRui Paulo 	default:
138032a8088fSRui Paulo 		type = 255;
138132a8088fSRui Paulo 	}
138232a8088fSRui Paulo 
138332a8088fSRui Paulo 	snprintf(notify, sizeof(notify), " notify=0x%x", type);
13844470f0f3SRui Paulo 	devctl_notify("ACPI", "asmc", "SMS", notify);
138532a8088fSRui Paulo }
138632a8088fSRui Paulo 
138732a8088fSRui Paulo static int
138832a8088fSRui Paulo asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS)
138932a8088fSRui Paulo {
139032a8088fSRui Paulo 	device_t dev = (device_t) arg1;
139132a8088fSRui Paulo 	int error;
139232a8088fSRui Paulo 	int16_t val;
139332a8088fSRui Paulo 	int32_t v;
139432a8088fSRui Paulo 
139532a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_X, &val);
139632a8088fSRui Paulo 	v = (int32_t) val;
139732a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
139832a8088fSRui Paulo 
139932a8088fSRui Paulo 	return (error);
140032a8088fSRui Paulo }
140132a8088fSRui Paulo 
140232a8088fSRui Paulo static int
140332a8088fSRui Paulo asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS)
140432a8088fSRui Paulo {
140532a8088fSRui Paulo 	device_t dev = (device_t) arg1;
140632a8088fSRui Paulo 	int error;
140732a8088fSRui Paulo 	int16_t val;
140832a8088fSRui Paulo 	int32_t v;
140932a8088fSRui Paulo 
141032a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val);
141132a8088fSRui Paulo 	v = (int32_t) val;
141232a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
141332a8088fSRui Paulo 
141432a8088fSRui Paulo 	return (error);
141532a8088fSRui Paulo }
141632a8088fSRui Paulo 
141732a8088fSRui Paulo static int
141832a8088fSRui Paulo asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS)
141932a8088fSRui Paulo {
142032a8088fSRui Paulo 	device_t dev = (device_t) arg1;
142132a8088fSRui Paulo 	int error;
142232a8088fSRui Paulo 	int16_t val;
142332a8088fSRui Paulo 	int32_t v;
142432a8088fSRui Paulo 
142532a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val);
142632a8088fSRui Paulo 	v = (int32_t) val;
14270e1152fcSHans Petter Selasky 	error = sysctl_handle_int(oidp, &v, 0, req);
142832a8088fSRui Paulo 
142932a8088fSRui Paulo 	return (error);
143032a8088fSRui Paulo }
143132a8088fSRui Paulo 
143232a8088fSRui Paulo static int
143332a8088fSRui Paulo asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS)
143432a8088fSRui Paulo {
143532a8088fSRui Paulo 	device_t dev = (device_t) arg1;
143632a8088fSRui Paulo 	uint8_t buf[6];
143732a8088fSRui Paulo 	int error;
143832a8088fSRui Paulo 	int32_t v;
143932a8088fSRui Paulo 
1440447666f0SRui Paulo 	asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, sizeof buf);
144132a8088fSRui Paulo 	v = buf[2];
14420e1152fcSHans Petter Selasky 	error = sysctl_handle_int(oidp, &v, 0, req);
144332a8088fSRui Paulo 
144432a8088fSRui Paulo 	return (error);
144532a8088fSRui Paulo }
144632a8088fSRui Paulo 
144732a8088fSRui Paulo static int
144832a8088fSRui Paulo asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS)
144932a8088fSRui Paulo {
145032a8088fSRui Paulo 	device_t dev = (device_t) arg1;
145132a8088fSRui Paulo 	uint8_t buf[6];
145232a8088fSRui Paulo 	int error;
145332a8088fSRui Paulo 	int32_t v;
145432a8088fSRui Paulo 
1455447666f0SRui Paulo 	asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, sizeof buf);
145632a8088fSRui Paulo 	v = buf[2];
14570e1152fcSHans Petter Selasky 	error = sysctl_handle_int(oidp, &v, 0, req);
1458be80e49aSRui Paulo 
1459be80e49aSRui Paulo 	return (error);
1460be80e49aSRui Paulo }
1461be80e49aSRui Paulo 
1462be80e49aSRui Paulo static int
1463be80e49aSRui Paulo asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS)
1464be80e49aSRui Paulo {
1465be80e49aSRui Paulo 	device_t dev = (device_t) arg1;
1466be80e49aSRui Paulo 	uint8_t buf[2];
1467be80e49aSRui Paulo 	int error;
14680e1152fcSHans Petter Selasky 	int v;
1469be80e49aSRui Paulo 
1470108e3076SAdrian Chadd 	v = light_control;
14710e1152fcSHans Petter Selasky 	error = sysctl_handle_int(oidp, &v, 0, req);
14720e1152fcSHans Petter Selasky 
14730e1152fcSHans Petter Selasky 	if (error == 0 && req->newptr != NULL) {
14740e1152fcSHans Petter Selasky 		if (v < 0 || v > 255)
14750e1152fcSHans Petter Selasky 			return (EINVAL);
1476108e3076SAdrian Chadd 		light_control = v;
1477108e3076SAdrian Chadd 		buf[0] = light_control;
147832a8088fSRui Paulo 		buf[1] = 0x00;
1479447666f0SRui Paulo 		asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof buf);
148032a8088fSRui Paulo 	}
148132a8088fSRui Paulo 	return (error);
148232a8088fSRui Paulo }
1483