xref: /freebsd/sys/dev/asmc/asmc.c (revision 49a5fe1a017fc179c79b74f409440e070511a0ab)
132a8088fSRui Paulo /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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/param.h>
3832a8088fSRui Paulo #include <sys/bus.h>
3932a8088fSRui Paulo #include <sys/conf.h>
402e9d05fdSAdrian Chadd #include <sys/endian.h>
4132a8088fSRui Paulo #include <sys/kernel.h>
4232a8088fSRui Paulo #include <sys/lock.h>
4332a8088fSRui Paulo #include <sys/malloc.h>
4432a8088fSRui Paulo #include <sys/module.h>
4532a8088fSRui Paulo #include <sys/mutex.h>
4632a8088fSRui Paulo #include <sys/sysctl.h>
4732a8088fSRui Paulo #include <sys/systm.h>
4832a8088fSRui Paulo #include <sys/taskqueue.h>
4932a8088fSRui Paulo #include <sys/rman.h>
504c061448SRui Paulo 
5132a8088fSRui Paulo #include <machine/resource.h>
52129d3046SJung-uk Kim 
53129d3046SJung-uk Kim #include <contrib/dev/acpica/include/acpi.h>
54129d3046SJung-uk Kim 
554470f0f3SRui Paulo #include <dev/acpica/acpivar.h>
5632a8088fSRui Paulo #include <dev/asmc/asmcvar.h>
5732a8088fSRui Paulo 
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);
64108e3076SAdrian Chadd static int 	asmc_resume(device_t dev);
6532a8088fSRui Paulo 
6632a8088fSRui Paulo /*
6732a8088fSRui Paulo  * SMC functions.
6832a8088fSRui Paulo  */
6932a8088fSRui Paulo static int 	asmc_init(device_t dev);
70be80e49aSRui Paulo static int 	asmc_command(device_t dev, uint8_t command);
7132a8088fSRui Paulo static int 	asmc_wait(device_t dev, uint8_t val);
72be80e49aSRui Paulo static int 	asmc_wait_ack(device_t dev, uint8_t val, int amount);
7332a8088fSRui Paulo static int 	asmc_key_write(device_t dev, const char *key, uint8_t *buf,
7432a8088fSRui Paulo     uint8_t len);
7532a8088fSRui Paulo static int 	asmc_key_read(device_t dev, const char *key, uint8_t *buf,
7632a8088fSRui Paulo     uint8_t);
7732a8088fSRui Paulo static int 	asmc_fan_count(device_t dev);
7832a8088fSRui Paulo static int 	asmc_fan_getvalue(device_t dev, const char *key, int fan);
79447666f0SRui Paulo static int 	asmc_fan_setvalue(device_t dev, const char *key, int fan, int speed);
8032a8088fSRui Paulo static int 	asmc_temp_getvalue(device_t dev, const char *key);
8132a8088fSRui Paulo static int 	asmc_sms_read(device_t, const char *key, int16_t *val);
8232a8088fSRui Paulo static void 	asmc_sms_calibrate(device_t dev);
8332a8088fSRui Paulo static int 	asmc_sms_intrfast(void *arg);
8432a8088fSRui Paulo static void 	asmc_sms_printintr(device_t dev, uint8_t);
8532a8088fSRui Paulo static void 	asmc_sms_task(void *arg, int pending);
861269f4d4SRui Paulo #ifdef DEBUG
871269f4d4SRui Paulo void		asmc_dumpall(device_t);
881269f4d4SRui Paulo static int	asmc_key_dump(device_t, int);
891269f4d4SRui Paulo #endif
9032a8088fSRui Paulo 
9132a8088fSRui Paulo /*
9232a8088fSRui Paulo  * Model functions.
9332a8088fSRui Paulo  */
94447666f0SRui Paulo static int 	asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS);
9532a8088fSRui Paulo static int 	asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS);
9632a8088fSRui Paulo static int 	asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS);
9732a8088fSRui Paulo static int 	asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS);
9832a8088fSRui Paulo static int 	asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS);
9932a8088fSRui Paulo static int 	asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS);
10032a8088fSRui Paulo static int 	asmc_temp_sysctl(SYSCTL_HANDLER_ARGS);
10132a8088fSRui Paulo static int 	asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS);
10232a8088fSRui Paulo static int 	asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS);
10332a8088fSRui Paulo static int 	asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS);
10432a8088fSRui Paulo static int 	asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS);
10532a8088fSRui Paulo static int 	asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS);
106be80e49aSRui Paulo static int 	asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS);
1072e9d05fdSAdrian Chadd static int 	asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS);
10832a8088fSRui Paulo 
10932a8088fSRui Paulo struct asmc_model {
11032a8088fSRui Paulo 	const char 	 *smc_model;	/* smbios.system.product env var. */
11132a8088fSRui Paulo 	const char 	 *smc_desc;	/* driver description */
11232a8088fSRui Paulo 
11332a8088fSRui Paulo 	/* Helper functions */
11432a8088fSRui Paulo 	int (*smc_sms_x)(SYSCTL_HANDLER_ARGS);
11532a8088fSRui Paulo 	int (*smc_sms_y)(SYSCTL_HANDLER_ARGS);
11632a8088fSRui Paulo 	int (*smc_sms_z)(SYSCTL_HANDLER_ARGS);
117447666f0SRui Paulo 	int (*smc_fan_id)(SYSCTL_HANDLER_ARGS);
11832a8088fSRui Paulo 	int (*smc_fan_speed)(SYSCTL_HANDLER_ARGS);
11932a8088fSRui Paulo 	int (*smc_fan_safespeed)(SYSCTL_HANDLER_ARGS);
12032a8088fSRui Paulo 	int (*smc_fan_minspeed)(SYSCTL_HANDLER_ARGS);
12132a8088fSRui Paulo 	int (*smc_fan_maxspeed)(SYSCTL_HANDLER_ARGS);
12232a8088fSRui Paulo 	int (*smc_fan_targetspeed)(SYSCTL_HANDLER_ARGS);
12332a8088fSRui Paulo 	int (*smc_light_left)(SYSCTL_HANDLER_ARGS);
12432a8088fSRui Paulo 	int (*smc_light_right)(SYSCTL_HANDLER_ARGS);
125be80e49aSRui Paulo 	int (*smc_light_control)(SYSCTL_HANDLER_ARGS);
12632a8088fSRui Paulo 
127d8246db0SRui Paulo 	const char 	*smc_temps[ASMC_TEMP_MAX];
128d8246db0SRui Paulo 	const char 	*smc_tempnames[ASMC_TEMP_MAX];
129d8246db0SRui Paulo 	const char 	*smc_tempdescs[ASMC_TEMP_MAX];
13032a8088fSRui Paulo };
13132a8088fSRui Paulo 
13227d4c6f8SMark Johnston static const struct asmc_model *asmc_match(device_t dev);
13332a8088fSRui Paulo 
13432a8088fSRui Paulo #define ASMC_SMS_FUNCS	asmc_mb_sysctl_sms_x, asmc_mb_sysctl_sms_y, \
13532a8088fSRui Paulo 			asmc_mb_sysctl_sms_z
13632a8088fSRui Paulo 
137108e3076SAdrian Chadd #define ASMC_SMS_FUNCS_DISABLED NULL,NULL,NULL
138108e3076SAdrian Chadd 
139447666f0SRui Paulo #define ASMC_FAN_FUNCS	asmc_mb_sysctl_fanid, asmc_mb_sysctl_fanspeed, asmc_mb_sysctl_fansafespeed, \
14032a8088fSRui Paulo 			asmc_mb_sysctl_fanminspeed, \
14132a8088fSRui Paulo 			asmc_mb_sysctl_fanmaxspeed, \
14232a8088fSRui Paulo 			asmc_mb_sysctl_fantargetspeed
143108e3076SAdrian Chadd 
144108e3076SAdrian Chadd #define ASMC_FAN_FUNCS2	asmc_mb_sysctl_fanid, asmc_mb_sysctl_fanspeed, NULL, \
145108e3076SAdrian Chadd 			asmc_mb_sysctl_fanminspeed, \
146108e3076SAdrian Chadd 			asmc_mb_sysctl_fanmaxspeed, \
147108e3076SAdrian Chadd 			asmc_mb_sysctl_fantargetspeed
148108e3076SAdrian Chadd 
14932a8088fSRui Paulo #define ASMC_LIGHT_FUNCS asmc_mbp_sysctl_light_left, \
150be80e49aSRui Paulo 			 asmc_mbp_sysctl_light_right, \
151be80e49aSRui Paulo 			 asmc_mbp_sysctl_light_control
15232a8088fSRui Paulo 
1532e9d05fdSAdrian Chadd #define ASMC_LIGHT_FUNCS_10BYTE \
1542e9d05fdSAdrian Chadd 			 asmc_mbp_sysctl_light_left_10byte, \
1552e9d05fdSAdrian Chadd 			 NULL, \
1562e9d05fdSAdrian Chadd 			 asmc_mbp_sysctl_light_control
1572e9d05fdSAdrian Chadd 
1589f0bfc51SDavid Bright #define ASMC_LIGHT_FUNCS_DISABLED NULL, NULL, NULL
1599f0bfc51SDavid Bright 
16027d4c6f8SMark Johnston static const struct asmc_model asmc_models[] = {
16132a8088fSRui Paulo 	{
16232a8088fSRui Paulo 	  "MacBook1,1", "Apple SMC MacBook Core Duo",
163be80e49aSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
16432a8088fSRui Paulo 	  ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
16532a8088fSRui Paulo 	},
16632a8088fSRui Paulo 
16732a8088fSRui Paulo 	{
16832a8088fSRui Paulo 	  "MacBook2,1", "Apple SMC MacBook Core 2 Duo",
169be80e49aSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
17032a8088fSRui Paulo 	  ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
17132a8088fSRui Paulo 	},
17232a8088fSRui Paulo 
17332a8088fSRui Paulo 	{
174108e3076SAdrian Chadd 	  "MacBook3,1", "Apple SMC MacBook Core 2 Duo",
175108e3076SAdrian Chadd 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
176108e3076SAdrian Chadd 	  ASMC_MB31_TEMPS, ASMC_MB31_TEMPNAMES, ASMC_MB31_TEMPDESCS
177108e3076SAdrian Chadd 	},
178108e3076SAdrian Chadd 
179108e3076SAdrian Chadd 	{
180e4a14ce7SMark Johnston 	  "MacBook7,1", "Apple SMC MacBook Core 2 Duo (mid 2010)",
181e4a14ce7SMark Johnston 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS2, ASMC_LIGHT_FUNCS_DISABLED,
182e4a14ce7SMark Johnston 	  ASMC_MB71_TEMPS, ASMC_MB71_TEMPNAMES, ASMC_MB71_TEMPDESCS
183e4a14ce7SMark Johnston 	},
184e4a14ce7SMark Johnston 
185e4a14ce7SMark Johnston 	{
18632a8088fSRui Paulo 	  "MacBookPro1,1", "Apple SMC MacBook Pro Core Duo (15-inch)",
18732a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
18832a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
18932a8088fSRui Paulo 	},
19032a8088fSRui Paulo 
19132a8088fSRui Paulo 	{
19232a8088fSRui Paulo 	  "MacBookPro1,2", "Apple SMC MacBook Pro Core Duo (17-inch)",
19332a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
19432a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
19532a8088fSRui Paulo 	},
19632a8088fSRui Paulo 
19732a8088fSRui Paulo 	{
19832a8088fSRui Paulo 	  "MacBookPro2,1", "Apple SMC MacBook Pro Core 2 Duo (17-inch)",
19932a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
20032a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
20132a8088fSRui Paulo 	},
20232a8088fSRui Paulo 
20332a8088fSRui Paulo 	{
20432a8088fSRui Paulo 	  "MacBookPro2,2", "Apple SMC MacBook Pro Core 2 Duo (15-inch)",
20532a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
20632a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
20732a8088fSRui Paulo 	},
20832a8088fSRui Paulo 
20932a8088fSRui Paulo 	{
21032a8088fSRui Paulo 	  "MacBookPro3,1", "Apple SMC MacBook Pro Core 2 Duo (15-inch LED)",
21132a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
21232a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
21332a8088fSRui Paulo 	},
21432a8088fSRui Paulo 
21532a8088fSRui Paulo 	{
21632a8088fSRui Paulo 	  "MacBookPro3,2", "Apple SMC MacBook Pro Core 2 Duo (17-inch HD)",
21732a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
21832a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
21932a8088fSRui Paulo 	},
22032a8088fSRui Paulo 
221be80e49aSRui Paulo 	{
222be80e49aSRui Paulo 	  "MacBookPro4,1", "Apple SMC MacBook Pro Core 2 Duo (Penryn)",
223be80e49aSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
224be80e49aSRui Paulo 	  ASMC_MBP4_TEMPS, ASMC_MBP4_TEMPNAMES, ASMC_MBP4_TEMPDESCS
225be80e49aSRui Paulo 	},
226be80e49aSRui Paulo 
227447666f0SRui Paulo 	{
22831ae3b07SAdrian Chadd 	  "MacBookPro5,1", "Apple SMC MacBook Pro Core 2 Duo (2008/2009)",
22931ae3b07SAdrian Chadd 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
230638937d4SMichael Gmelin 	  ASMC_MBP51_TEMPS, ASMC_MBP51_TEMPNAMES, ASMC_MBP51_TEMPDESCS
231638937d4SMichael Gmelin 	},
232638937d4SMichael Gmelin 
233638937d4SMichael Gmelin 	{
234638937d4SMichael Gmelin 	  "MacBookPro5,5", "Apple SMC MacBook Pro Core 2 Duo (Mid 2009)",
235638937d4SMichael Gmelin 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS2, ASMC_LIGHT_FUNCS,
236638937d4SMichael Gmelin 	  ASMC_MBP55_TEMPS, ASMC_MBP55_TEMPNAMES, ASMC_MBP55_TEMPDESCS
23731ae3b07SAdrian Chadd 	},
23831ae3b07SAdrian Chadd 
23931ae3b07SAdrian Chadd 	{
2403416f5cdSed crowe 	  "MacBookPro6,2", "Apple SMC MacBook Pro (Mid 2010, 15-inch)",
2413416f5cdSed crowe 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
2423416f5cdSed crowe 	  ASMC_MBP62_TEMPS, ASMC_MBP62_TEMPNAMES, ASMC_MBP62_TEMPDESCS
2433416f5cdSed crowe 	},
2443416f5cdSed crowe 
2453416f5cdSed crowe 	{
24609ff71d3SDavid Bright 	  "MacBookPro8,1", "Apple SMC MacBook Pro (early 2011, 13-inch)",
24709ff71d3SDavid Bright 	  ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS2, ASMC_LIGHT_FUNCS,
24809ff71d3SDavid Bright 	  ASMC_MBP81_TEMPS, ASMC_MBP81_TEMPNAMES, ASMC_MBP81_TEMPDESCS
24909ff71d3SDavid Bright 	},
25009ff71d3SDavid Bright 
25109ff71d3SDavid Bright 	{
252447666f0SRui Paulo 	  "MacBookPro8,2", "Apple SMC MacBook Pro (early 2011)",
253447666f0SRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
25409ff71d3SDavid Bright 	  ASMC_MBP82_TEMPS, ASMC_MBP82_TEMPNAMES, ASMC_MBP82_TEMPDESCS
255447666f0SRui Paulo 	},
256447666f0SRui Paulo 
257447666f0SRui Paulo 	{
25879291c9bSDaniel W. Delâtre 	  "MacBookPro9,1", "Apple SMC MacBook Pro (mid 2012, 15-inch)",
25998ae4866SDavid Bright 	  ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
26079291c9bSDaniel W. Delâtre 	  ASMC_MBP91_TEMPS, ASMC_MBP91_TEMPNAMES, ASMC_MBP91_TEMPDESCS
26179291c9bSDaniel W. Delâtre 	},
26279291c9bSDaniel W. Delâtre 
26379291c9bSDaniel W. Delâtre 	{
26479291c9bSDaniel W. Delâtre 	 "MacBookPro9,2", "Apple SMC MacBook Pro (mid 2012, 13-inch)",
26579291c9bSDaniel W. Delâtre 	  ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
26679291c9bSDaniel W. Delâtre 	  ASMC_MBP92_TEMPS, ASMC_MBP92_TEMPNAMES, ASMC_MBP92_TEMPDESCS
26798ae4866SDavid Bright 	},
26898ae4866SDavid Bright 
26998ae4866SDavid Bright 	{
270109e2d29SAdrian Chadd 	  "MacBookPro11,2", "Apple SMC MacBook Pro Retina Core i7 (2013/2014)",
271109e2d29SAdrian Chadd 	  ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS2, ASMC_LIGHT_FUNCS,
272109e2d29SAdrian Chadd 	  ASMC_MBP112_TEMPS, ASMC_MBP112_TEMPNAMES, ASMC_MBP112_TEMPDESCS
273109e2d29SAdrian Chadd 	},
274109e2d29SAdrian Chadd 
275109e2d29SAdrian Chadd 	{
276447666f0SRui Paulo 	  "MacBookPro11,3", "Apple SMC MacBook Pro Retina Core i7 (2013/2014)",
277447666f0SRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
278109e2d29SAdrian Chadd 	  ASMC_MBP113_TEMPS, ASMC_MBP113_TEMPNAMES, ASMC_MBP113_TEMPDESCS
279447666f0SRui Paulo 	},
280447666f0SRui Paulo 
281*49a5fe1aSJoshua Rogers 	{
282*49a5fe1aSJoshua Rogers 	  "MacBookPro11,4", "Apple SMC MacBook Pro Retina Core i7 (mid 2015, 15-inch)",
283*49a5fe1aSJoshua Rogers 	  ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
284*49a5fe1aSJoshua Rogers 	  ASMC_MBP114_TEMPS, ASMC_MBP114_TEMPNAMES, ASMC_MBP114_TEMPDESCS
285*49a5fe1aSJoshua Rogers 	},
286*49a5fe1aSJoshua Rogers 
28732a8088fSRui Paulo 	/* The Mac Mini has no SMS */
28832a8088fSRui Paulo 	{
28932a8088fSRui Paulo 	  "Macmini1,1", "Apple SMC Mac Mini",
29032a8088fSRui Paulo 	  NULL, NULL, NULL,
29132a8088fSRui Paulo 	  ASMC_FAN_FUNCS,
292be80e49aSRui Paulo 	  NULL, NULL, NULL,
29332a8088fSRui Paulo 	  ASMC_MM_TEMPS, ASMC_MM_TEMPNAMES, ASMC_MM_TEMPDESCS
29432a8088fSRui Paulo 	},
29532a8088fSRui Paulo 
2969e33968bSDavid Bright 	/* The Mac Mini 2,1 has no SMS */
2979e33968bSDavid Bright 	{
2989e33968bSDavid Bright 	  "Macmini2,1", "Apple SMC Mac Mini 2,1",
2999e33968bSDavid Bright 	  ASMC_SMS_FUNCS_DISABLED,
3009e33968bSDavid Bright 	  ASMC_FAN_FUNCS,
3019e33968bSDavid Bright 	  ASMC_LIGHT_FUNCS_DISABLED,
3029e33968bSDavid Bright 	  ASMC_MM21_TEMPS, ASMC_MM21_TEMPNAMES, ASMC_MM21_TEMPDESCS
3039e33968bSDavid Bright 	},
3049e33968bSDavid Bright 
305764442e0SGavin Atkinson 	/* The Mac Mini 3,1 has no SMS */
306764442e0SGavin Atkinson 	{
307764442e0SGavin Atkinson 	  "Macmini3,1", "Apple SMC Mac Mini 3,1",
308764442e0SGavin Atkinson 	  NULL, NULL, NULL,
309764442e0SGavin Atkinson 	  ASMC_FAN_FUNCS,
310764442e0SGavin Atkinson 	  NULL, NULL, NULL,
311764442e0SGavin Atkinson 	  ASMC_MM31_TEMPS, ASMC_MM31_TEMPNAMES, ASMC_MM31_TEMPDESCS
312764442e0SGavin Atkinson 	},
313764442e0SGavin Atkinson 
3149f0bfc51SDavid Bright 	/* The Mac Mini 4,1 (Mid-2010) has no SMS */
3159f0bfc51SDavid Bright 	{
3169f0bfc51SDavid Bright 	  "Macmini4,1", "Apple SMC Mac mini 4,1 (Mid-2010)",
3179f0bfc51SDavid Bright 	  ASMC_SMS_FUNCS_DISABLED,
3189f0bfc51SDavid Bright 	  ASMC_FAN_FUNCS,
3199f0bfc51SDavid Bright 	  ASMC_LIGHT_FUNCS_DISABLED,
3209f0bfc51SDavid Bright 	  ASMC_MM41_TEMPS, ASMC_MM41_TEMPNAMES, ASMC_MM41_TEMPDESCS
3219f0bfc51SDavid Bright 	},
3229f0bfc51SDavid Bright 
323601abb30STrev 	/* The Mac Mini 5,1 has no SMS */
324601abb30STrev 	/* - same sensors as Mac Mini 5,2 */
325601abb30STrev 	{
326601abb30STrev 	  "Macmini5,1", "Apple SMC Mac Mini 5,1",
327601abb30STrev 	  NULL, NULL, NULL,
328601abb30STrev 	  ASMC_FAN_FUNCS2,
329601abb30STrev 	  NULL, NULL, NULL,
330601abb30STrev 	  ASMC_MM52_TEMPS, ASMC_MM52_TEMPNAMES, ASMC_MM52_TEMPDESCS
331601abb30STrev 	},
332601abb30STrev 
33371b475e7SDavid Bright 	/* The Mac Mini 5,2 has no SMS */
33471b475e7SDavid Bright 	{
33571b475e7SDavid Bright 	  "Macmini5,2", "Apple SMC Mac Mini 5,2",
33671b475e7SDavid Bright 	  NULL, NULL, NULL,
33771b475e7SDavid Bright 	  ASMC_FAN_FUNCS2,
33871b475e7SDavid Bright 	  NULL, NULL, NULL,
33971b475e7SDavid Bright 	  ASMC_MM52_TEMPS, ASMC_MM52_TEMPNAMES, ASMC_MM52_TEMPDESCS
34071b475e7SDavid Bright 	},
34171b475e7SDavid Bright 
342601abb30STrev 	/* The Mac Mini 5,3 has no SMS */
343601abb30STrev 	/* - same sensors as Mac Mini 5,2 */
344601abb30STrev 	{
345601abb30STrev 	  "Macmini5,3", "Apple SMC Mac Mini 5,3",
346601abb30STrev 	  NULL, NULL, NULL,
347601abb30STrev 	  ASMC_FAN_FUNCS2,
348601abb30STrev 	  NULL, NULL, NULL,
349601abb30STrev 	  ASMC_MM52_TEMPS, ASMC_MM52_TEMPNAMES, ASMC_MM52_TEMPDESCS
350601abb30STrev 	},
351601abb30STrev 
352601abb30STrev 	/* The Mac Mini 7,1 has no SMS */
353601abb30STrev 	{
354601abb30STrev 	  "Macmini7,1", "Apple SMC Mac Mini 7,1",
355601abb30STrev 	  NULL, NULL, NULL,
356601abb30STrev 	  ASMC_FAN_FUNCS2,
357601abb30STrev 	  NULL, NULL, NULL,
358601abb30STrev 	  ASMC_MM71_TEMPS, ASMC_MM71_TEMPNAMES, ASMC_MM71_TEMPDESCS
359601abb30STrev 	},
360601abb30STrev 
36139a8ee13SDavid Bright 	/* Idem for the Mac Pro "Quad Core" (original) */
36239a8ee13SDavid Bright 	{
36339a8ee13SDavid Bright 	  "MacPro1,1", "Apple SMC Mac Pro (Quad Core)",
36439a8ee13SDavid Bright 	  NULL, NULL, NULL,
36539a8ee13SDavid Bright 	  ASMC_FAN_FUNCS,
36639a8ee13SDavid Bright 	  NULL, NULL, NULL,
36739a8ee13SDavid Bright 	  ASMC_MP1_TEMPS, ASMC_MP1_TEMPNAMES, ASMC_MP1_TEMPDESCS
36839a8ee13SDavid Bright 	},
36939a8ee13SDavid Bright 
37039a8ee13SDavid Bright 	/* Idem for the Mac Pro (8-core) */
371d8246db0SRui Paulo 	{
372d8246db0SRui Paulo 	  "MacPro2", "Apple SMC Mac Pro (8-core)",
373d8246db0SRui Paulo 	  NULL, NULL, NULL,
374d8246db0SRui Paulo 	  ASMC_FAN_FUNCS,
375be80e49aSRui Paulo 	  NULL, NULL, NULL,
37639a8ee13SDavid Bright 	  ASMC_MP2_TEMPS, ASMC_MP2_TEMPNAMES, ASMC_MP2_TEMPDESCS
377d8246db0SRui Paulo 	},
378941f9f10SRui Paulo 
379447666f0SRui Paulo 	/* Idem for the MacPro  2010*/
380447666f0SRui Paulo 	{
381447666f0SRui Paulo 	  "MacPro5,1", "Apple SMC MacPro (2010)",
382447666f0SRui Paulo 	  NULL, NULL, NULL,
383447666f0SRui Paulo 	  ASMC_FAN_FUNCS,
384447666f0SRui Paulo 	  NULL, NULL, NULL,
385447666f0SRui Paulo 	  ASMC_MP5_TEMPS, ASMC_MP5_TEMPNAMES, ASMC_MP5_TEMPDESCS
386447666f0SRui Paulo 	},
387447666f0SRui Paulo 
3887d5fef18SAdam S 	/* Idem for the Mac Pro 2013 (cylinder) */
3897d5fef18SAdam S 	{
3907d5fef18SAdam S 	  "MacPro6,1", "Apple SMC Mac Pro (2013)",
3917d5fef18SAdam S 	  ASMC_SMS_FUNCS_DISABLED,
392dc484aedSAdam S 	  ASMC_FAN_FUNCS2,
3937d5fef18SAdam S 	  ASMC_LIGHT_FUNCS_DISABLED,
3947d5fef18SAdam S 	  ASMC_MP6_TEMPS, ASMC_MP6_TEMPNAMES, ASMC_MP6_TEMPDESCS
3957d5fef18SAdam S 	},
3967d5fef18SAdam S 
397941f9f10SRui Paulo 	{
398941f9f10SRui Paulo 	  "MacBookAir1,1", "Apple SMC MacBook Air",
399be80e49aSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
400941f9f10SRui Paulo 	  ASMC_MBA_TEMPS, ASMC_MBA_TEMPNAMES, ASMC_MBA_TEMPDESCS
401941f9f10SRui Paulo 	},
402941f9f10SRui Paulo 
403447666f0SRui Paulo 	{
404447666f0SRui Paulo 	  "MacBookAir3,1", "Apple SMC MacBook Air Core 2 Duo (Late 2010)",
405447666f0SRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
406447666f0SRui Paulo 	  ASMC_MBA3_TEMPS, ASMC_MBA3_TEMPNAMES, ASMC_MBA3_TEMPDESCS
407447666f0SRui Paulo 	},
408447666f0SRui Paulo 
409108e3076SAdrian Chadd 	{
410308340ccSMark Johnston 	  "MacBookAir4,1", "Apple SMC Macbook Air 11-inch (Mid 2011)",
411308340ccSMark Johnston 	  ASMC_SMS_FUNCS_DISABLED,
412308340ccSMark Johnston 	  ASMC_FAN_FUNCS2,
413308340ccSMark Johnston 	  ASMC_LIGHT_FUNCS,
414308340ccSMark Johnston 	  ASMC_MBA4_TEMPS, ASMC_MBA4_TEMPNAMES, ASMC_MBA4_TEMPDESCS
415308340ccSMark Johnston 	},
416308340ccSMark Johnston 
417308340ccSMark Johnston 	{
418308340ccSMark Johnston 	  "MacBookAir4,2", "Apple SMC Macbook Air 13-inch (Mid 2011)",
419308340ccSMark Johnston 	  ASMC_SMS_FUNCS_DISABLED,
420308340ccSMark Johnston 	  ASMC_FAN_FUNCS2,
421308340ccSMark Johnston 	  ASMC_LIGHT_FUNCS,
422308340ccSMark Johnston 	  ASMC_MBA4_TEMPS, ASMC_MBA4_TEMPNAMES, ASMC_MBA4_TEMPDESCS
423308340ccSMark Johnston 	},
424308340ccSMark Johnston 
425308340ccSMark Johnston 	{
426108e3076SAdrian Chadd 	  "MacBookAir5,1", "Apple SMC MacBook Air 11-inch (Mid 2012)",
427108e3076SAdrian Chadd 	  ASMC_SMS_FUNCS_DISABLED,
428108e3076SAdrian Chadd 	  ASMC_FAN_FUNCS2,
429108e3076SAdrian Chadd 	  ASMC_LIGHT_FUNCS,
430108e3076SAdrian Chadd 	  ASMC_MBA5_TEMPS, ASMC_MBA5_TEMPNAMES, ASMC_MBA5_TEMPDESCS
431108e3076SAdrian Chadd 	},
432108e3076SAdrian Chadd 
433108e3076SAdrian Chadd 	{
434108e3076SAdrian Chadd 	  "MacBookAir5,2", "Apple SMC MacBook Air 13-inch (Mid 2012)",
435108e3076SAdrian Chadd 	  ASMC_SMS_FUNCS_DISABLED,
436108e3076SAdrian Chadd 	  ASMC_FAN_FUNCS2,
437108e3076SAdrian Chadd 	  ASMC_LIGHT_FUNCS,
438108e3076SAdrian Chadd 	  ASMC_MBA5_TEMPS, ASMC_MBA5_TEMPNAMES, ASMC_MBA5_TEMPDESCS
439108e3076SAdrian Chadd 	},
440ffc58e2cSAdrian Chadd 	{
4412e9d05fdSAdrian Chadd 	  "MacBookAir6,1", "Apple SMC MacBook Air 11-inch (Early 2013)",
4422e9d05fdSAdrian Chadd 	  ASMC_SMS_FUNCS_DISABLED,
4432e9d05fdSAdrian Chadd 	  ASMC_FAN_FUNCS2,
4442e9d05fdSAdrian Chadd 	  ASMC_LIGHT_FUNCS_10BYTE,
4452e9d05fdSAdrian Chadd 	  ASMC_MBA6_TEMPS, ASMC_MBA6_TEMPNAMES, ASMC_MBA6_TEMPDESCS
4462e9d05fdSAdrian Chadd 	},
4472e9d05fdSAdrian Chadd 	{
448ffc58e2cSAdrian Chadd 	  "MacBookAir6,2", "Apple SMC MacBook Air 13-inch (Early 2013)",
449ffc58e2cSAdrian Chadd 	  ASMC_SMS_FUNCS_DISABLED,
450ffc58e2cSAdrian Chadd 	  ASMC_FAN_FUNCS2,
4512e9d05fdSAdrian Chadd 	  ASMC_LIGHT_FUNCS_10BYTE,
452ffc58e2cSAdrian Chadd 	  ASMC_MBA6_TEMPS, ASMC_MBA6_TEMPNAMES, ASMC_MBA6_TEMPDESCS
453ffc58e2cSAdrian Chadd 	},
454081954d3SDavid Bright 	{
455081954d3SDavid Bright 	  "MacBookAir7,1", "Apple SMC MacBook Air 11-inch (Early 2015)",
456081954d3SDavid Bright 	  ASMC_SMS_FUNCS_DISABLED,
457081954d3SDavid Bright 	  ASMC_FAN_FUNCS2,
458081954d3SDavid Bright 	  ASMC_LIGHT_FUNCS,
459081954d3SDavid Bright 	  ASMC_MBA7_TEMPS, ASMC_MBA7_TEMPNAMES, ASMC_MBA7_TEMPDESCS
460081954d3SDavid Bright 	},
461081954d3SDavid Bright 	{
462081954d3SDavid Bright 	  "MacBookAir7,2", "Apple SMC MacBook Air 13-inch (Early 2015)",
463081954d3SDavid Bright 	  ASMC_SMS_FUNCS_DISABLED,
464081954d3SDavid Bright 	  ASMC_FAN_FUNCS2,
465081954d3SDavid Bright 	  ASMC_LIGHT_FUNCS,
466081954d3SDavid Bright 	  ASMC_MBA7_TEMPS, ASMC_MBA7_TEMPNAMES, ASMC_MBA7_TEMPDESCS
467081954d3SDavid Bright 	},
46832a8088fSRui Paulo 	{ NULL, NULL }
46932a8088fSRui Paulo };
47032a8088fSRui Paulo 
47132a8088fSRui Paulo #undef ASMC_SMS_FUNCS
472108e3076SAdrian Chadd #undef ASMC_SMS_FUNCS_DISABLED
47332a8088fSRui Paulo #undef ASMC_FAN_FUNCS
474108e3076SAdrian Chadd #undef ASMC_FAN_FUNCS2
47532a8088fSRui Paulo #undef ASMC_LIGHT_FUNCS
47632a8088fSRui Paulo 
47732a8088fSRui Paulo /*
47832a8088fSRui Paulo  * Driver methods.
47932a8088fSRui Paulo  */
48032a8088fSRui Paulo static device_method_t	asmc_methods[] = {
48132a8088fSRui Paulo 	DEVMETHOD(device_probe,		asmc_probe),
48232a8088fSRui Paulo 	DEVMETHOD(device_attach,	asmc_attach),
48332a8088fSRui Paulo 	DEVMETHOD(device_detach,	asmc_detach),
484108e3076SAdrian Chadd 	DEVMETHOD(device_resume,	asmc_resume),
48532a8088fSRui Paulo 	{ 0, 0 }
48632a8088fSRui Paulo };
48732a8088fSRui Paulo 
48832a8088fSRui Paulo static driver_t	asmc_driver = {
48932a8088fSRui Paulo 	"asmc",
49032a8088fSRui Paulo 	asmc_methods,
49132a8088fSRui Paulo 	sizeof(struct asmc_softc)
49232a8088fSRui Paulo };
49332a8088fSRui Paulo 
4944470f0f3SRui Paulo /*
4954470f0f3SRui Paulo  * Debugging
4964470f0f3SRui Paulo  */
4974470f0f3SRui Paulo #define	_COMPONENT	ACPI_OEM
4984470f0f3SRui Paulo ACPI_MODULE_NAME("ASMC")
4994470f0f3SRui Paulo #ifdef DEBUG
5004470f0f3SRui Paulo #define ASMC_DPRINTF(str)	device_printf(dev, str)
5014fb9bf66SRui Paulo #else
5024fb9bf66SRui Paulo #define ASMC_DPRINTF(str)
5034470f0f3SRui Paulo #endif
5044470f0f3SRui Paulo 
505be80e49aSRui Paulo /* NB: can't be const */
5064470f0f3SRui Paulo static char *asmc_ids[] = { "APP0001", NULL };
5074470f0f3SRui Paulo 
508108e3076SAdrian Chadd static unsigned int light_control = 0;
509108e3076SAdrian Chadd 
510867864a2SJohn Baldwin DRIVER_MODULE(asmc, acpi, asmc_driver, NULL, NULL);
5114470f0f3SRui Paulo MODULE_DEPEND(asmc, acpi, 1, 1, 1);
51232a8088fSRui Paulo 
51327d4c6f8SMark Johnston static const struct asmc_model *
asmc_match(device_t dev)51432a8088fSRui Paulo asmc_match(device_t dev)
51532a8088fSRui Paulo {
51632a8088fSRui Paulo 	int i;
51732a8088fSRui Paulo 	char *model;
51832a8088fSRui Paulo 
5192be111bfSDavide Italiano 	model = kern_getenv("smbios.system.product");
52047105877SRui Paulo 	if (model == NULL)
52147105877SRui Paulo 		return (NULL);
52247105877SRui Paulo 
52332a8088fSRui Paulo 	for (i = 0; asmc_models[i].smc_model; i++) {
52432a8088fSRui Paulo 		if (!strncmp(model, asmc_models[i].smc_model, strlen(model))) {
52532a8088fSRui Paulo 			freeenv(model);
52632a8088fSRui Paulo 			return (&asmc_models[i]);
52732a8088fSRui Paulo 		}
52832a8088fSRui Paulo 	}
52932a8088fSRui Paulo 	freeenv(model);
53032a8088fSRui Paulo 
53132a8088fSRui Paulo 	return (NULL);
53232a8088fSRui Paulo }
53332a8088fSRui Paulo 
53432a8088fSRui Paulo static int
asmc_probe(device_t dev)53532a8088fSRui Paulo asmc_probe(device_t dev)
53632a8088fSRui Paulo {
53727d4c6f8SMark Johnston 	const struct asmc_model *model;
5385efca36fSTakanori Watanabe 	int rv;
53932a8088fSRui Paulo 
540a8de37b0SEitan Adler 	if (resource_disabled("asmc", 0))
541a8de37b0SEitan Adler 		return (ENXIO);
5425efca36fSTakanori Watanabe 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, asmc_ids, NULL);
5435efca36fSTakanori Watanabe 	if (rv > 0)
5445efca36fSTakanori Watanabe 		return (rv);
5454470f0f3SRui Paulo 
54632a8088fSRui Paulo 	model = asmc_match(dev);
5474470f0f3SRui Paulo 	if (!model) {
5484470f0f3SRui Paulo 		device_printf(dev, "model not recognized\n");
54932a8088fSRui Paulo 		return (ENXIO);
5504470f0f3SRui Paulo 	}
55132a8088fSRui Paulo 	device_set_desc(dev, model->smc_desc);
55232a8088fSRui Paulo 
5535efca36fSTakanori Watanabe 	return (rv);
55432a8088fSRui Paulo }
55532a8088fSRui Paulo 
55632a8088fSRui Paulo static int
asmc_attach(device_t dev)55732a8088fSRui Paulo asmc_attach(device_t dev)
55832a8088fSRui Paulo {
55932a8088fSRui Paulo 	int i, j;
56032a8088fSRui Paulo 	int ret;
56132a8088fSRui Paulo 	char name[2];
56232a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
56332a8088fSRui Paulo 	struct sysctl_ctx_list *sysctlctx;
56432a8088fSRui Paulo 	struct sysctl_oid *sysctlnode;
56527d4c6f8SMark Johnston 	const struct asmc_model *model;
56632a8088fSRui Paulo 
5674470f0f3SRui Paulo 	sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
5684470f0f3SRui Paulo 	    &sc->sc_rid_port, RF_ACTIVE);
5694470f0f3SRui Paulo 	if (sc->sc_ioport == NULL) {
5704470f0f3SRui Paulo 		device_printf(dev, "unable to allocate IO port\n");
5714470f0f3SRui Paulo 		return (ENOMEM);
5724470f0f3SRui Paulo 	}
5734470f0f3SRui Paulo 
57432a8088fSRui Paulo 	sysctlctx  = device_get_sysctl_ctx(dev);
57532a8088fSRui Paulo 	sysctlnode = device_get_sysctl_tree(dev);
57632a8088fSRui Paulo 
57732a8088fSRui Paulo 	model = asmc_match(dev);
57832a8088fSRui Paulo 
57932a8088fSRui Paulo 	mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN);
58032a8088fSRui Paulo 
58132a8088fSRui Paulo 	sc->sc_model = model;
58232a8088fSRui Paulo 	asmc_init(dev);
58332a8088fSRui Paulo 
58432a8088fSRui Paulo 	/*
58532a8088fSRui Paulo 	 * dev.asmc.n.fan.* tree.
58632a8088fSRui Paulo 	 */
58732a8088fSRui Paulo 	sc->sc_fan_tree[0] = SYSCTL_ADD_NODE(sysctlctx,
58832a8088fSRui Paulo 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "fan",
5897029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Fan Root Tree");
59032a8088fSRui Paulo 
59132a8088fSRui Paulo 	for (i = 1; i <= sc->sc_nfan; i++) {
59232a8088fSRui Paulo 		j = i - 1;
59332a8088fSRui Paulo 		name[0] = '0' + j;
59432a8088fSRui Paulo 		name[1] = 0;
59532a8088fSRui Paulo 		sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx,
59632a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[0]),
5977029da5cSPawel Biernacki 		    OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
59832a8088fSRui Paulo 		    "Fan Subtree");
59932a8088fSRui Paulo 
60032a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
60132a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
6027029da5cSPawel Biernacki 		    OID_AUTO, "id",
6037029da5cSPawel Biernacki 		    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
604447666f0SRui Paulo 		    dev, j, model->smc_fan_id, "I",
605447666f0SRui Paulo 		    "Fan ID");
606447666f0SRui Paulo 
607447666f0SRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
608447666f0SRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
6097029da5cSPawel Biernacki 		    OID_AUTO, "speed",
6107029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
61132a8088fSRui Paulo 		    dev, j, model->smc_fan_speed, "I",
61232a8088fSRui Paulo 		    "Fan speed in RPM");
61332a8088fSRui Paulo 
61432a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
61532a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
61632a8088fSRui Paulo 		    OID_AUTO, "safespeed",
6177029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
61832a8088fSRui Paulo 		    dev, j, model->smc_fan_safespeed, "I",
61932a8088fSRui Paulo 		    "Fan safe speed in RPM");
62032a8088fSRui Paulo 
62132a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
62232a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
62332a8088fSRui Paulo 		    OID_AUTO, "minspeed",
6247029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
62532a8088fSRui Paulo 		    dev, j, model->smc_fan_minspeed, "I",
62632a8088fSRui Paulo 		    "Fan minimum speed in RPM");
62732a8088fSRui Paulo 
62832a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
62932a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
63032a8088fSRui Paulo 		    OID_AUTO, "maxspeed",
6317029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
63232a8088fSRui Paulo 		    dev, j, model->smc_fan_maxspeed, "I",
63332a8088fSRui Paulo 		    "Fan maximum speed in RPM");
63432a8088fSRui Paulo 
63532a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
63632a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
63732a8088fSRui Paulo 		    OID_AUTO, "targetspeed",
6387029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
63932a8088fSRui Paulo 		    dev, j, model->smc_fan_targetspeed, "I",
64032a8088fSRui Paulo 		    "Fan target speed in RPM");
64132a8088fSRui Paulo 	}
64232a8088fSRui Paulo 
64332a8088fSRui Paulo 	/*
64432a8088fSRui Paulo 	 * dev.asmc.n.temp tree.
64532a8088fSRui Paulo 	 */
64632a8088fSRui Paulo 	sc->sc_temp_tree = SYSCTL_ADD_NODE(sysctlctx,
64732a8088fSRui Paulo 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp",
6487029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Temperature sensors");
64932a8088fSRui Paulo 
65032a8088fSRui Paulo 	for (i = 0; model->smc_temps[i]; i++) {
65132a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
65232a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_temp_tree),
65332a8088fSRui Paulo 		    OID_AUTO, model->smc_tempnames[i],
6547029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
65532a8088fSRui Paulo 		    dev, i, asmc_temp_sysctl, "I",
65632a8088fSRui Paulo 		    model->smc_tempdescs[i]);
65732a8088fSRui Paulo 	}
65832a8088fSRui Paulo 
659be80e49aSRui Paulo 	/*
660be80e49aSRui Paulo 	 * dev.asmc.n.light
661be80e49aSRui Paulo 	 */
662be80e49aSRui Paulo 	if (model->smc_light_left) {
663be80e49aSRui Paulo 		sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx,
664be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light",
6657029da5cSPawel Biernacki 		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
6667029da5cSPawel Biernacki 		    "Keyboard backlight sensors");
667be80e49aSRui Paulo 
668be80e49aSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
669be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_light_tree),
6707029da5cSPawel Biernacki 		    OID_AUTO, "left",
6717029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
672be80e49aSRui Paulo 		    dev, 0, model->smc_light_left, "I",
673be80e49aSRui Paulo 		    "Keyboard backlight left sensor");
674be80e49aSRui Paulo 
675be80e49aSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
676be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_light_tree),
6777029da5cSPawel Biernacki 		    OID_AUTO, "right",
6787029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
679be80e49aSRui Paulo 		    dev, 0, model->smc_light_right, "I",
680be80e49aSRui Paulo 		    "Keyboard backlight right sensor");
681be80e49aSRui Paulo 
682be80e49aSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
683be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_light_tree),
6843471c35dSRui Paulo 		    OID_AUTO, "control",
6857029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY |
6867029da5cSPawel Biernacki 		    CTLFLAG_NEEDGIANT, dev, 0,
6877029da5cSPawel Biernacki 		    model->smc_light_control, "I",
688be80e49aSRui Paulo 		    "Keyboard backlight brightness control");
689be80e49aSRui Paulo 	}
690be80e49aSRui Paulo 
69132a8088fSRui Paulo 	if (model->smc_sms_x == NULL)
69232a8088fSRui Paulo 		goto nosms;
69332a8088fSRui Paulo 
69432a8088fSRui Paulo 	/*
69532a8088fSRui Paulo 	 * dev.asmc.n.sms tree.
69632a8088fSRui Paulo 	 */
69732a8088fSRui Paulo 	sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx,
69832a8088fSRui Paulo 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "sms",
6997029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Sudden Motion Sensor");
70032a8088fSRui Paulo 
70132a8088fSRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
70232a8088fSRui Paulo 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
7037029da5cSPawel Biernacki 	    OID_AUTO, "x",
7047029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
70532a8088fSRui Paulo 	    dev, 0, model->smc_sms_x, "I",
70632a8088fSRui Paulo 	    "Sudden Motion Sensor X value");
70732a8088fSRui Paulo 
70832a8088fSRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
70932a8088fSRui Paulo 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
7107029da5cSPawel Biernacki 	    OID_AUTO, "y",
7117029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
71232a8088fSRui Paulo 	    dev, 0, model->smc_sms_y, "I",
71332a8088fSRui Paulo 	    "Sudden Motion Sensor Y value");
71432a8088fSRui Paulo 
71532a8088fSRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
71632a8088fSRui Paulo 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
7177029da5cSPawel Biernacki 	    OID_AUTO, "z",
7187029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
71932a8088fSRui Paulo 	    dev, 0, model->smc_sms_z, "I",
72032a8088fSRui Paulo 	    "Sudden Motion Sensor Z value");
72132a8088fSRui Paulo 
72232a8088fSRui Paulo 	/*
72332a8088fSRui Paulo 	 * Need a taskqueue to send devctl_notify() events
72432a8088fSRui Paulo 	 * when the SMS interrupt us.
72532a8088fSRui Paulo 	 *
72632a8088fSRui Paulo 	 * PI_REALTIME is used due to the sensitivity of the
72732a8088fSRui Paulo 	 * interrupt. An interrupt from the SMS means that the
72832a8088fSRui Paulo 	 * disk heads should be turned off as quickly as possible.
72932a8088fSRui Paulo 	 *
73032a8088fSRui Paulo 	 * We only need to do this for the non INTR_FILTER case.
73132a8088fSRui Paulo 	 */
73232a8088fSRui Paulo 	sc->sc_sms_tq = NULL;
73332a8088fSRui Paulo 	TASK_INIT(&sc->sc_sms_task, 0, asmc_sms_task, sc);
73432a8088fSRui Paulo 	sc->sc_sms_tq = taskqueue_create_fast("asmc_taskq", M_WAITOK,
73532a8088fSRui Paulo 	    taskqueue_thread_enqueue, &sc->sc_sms_tq);
73632a8088fSRui Paulo 	taskqueue_start_threads(&sc->sc_sms_tq, 1, PI_REALTIME, "%s sms taskq",
73732a8088fSRui Paulo 	    device_get_nameunit(dev));
73832a8088fSRui Paulo 	/*
73932a8088fSRui Paulo 	 * Allocate an IRQ for the SMS.
74032a8088fSRui Paulo 	 */
7414470f0f3SRui Paulo 	sc->sc_rid_irq = 0;
7424470f0f3SRui Paulo 	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
7434470f0f3SRui Paulo 	    &sc->sc_rid_irq, RF_ACTIVE);
7444470f0f3SRui Paulo 	if (sc->sc_irq == NULL) {
74532a8088fSRui Paulo 		device_printf(dev, "unable to allocate IRQ resource\n");
74632a8088fSRui Paulo 		ret = ENXIO;
74732a8088fSRui Paulo 		goto err2;
74832a8088fSRui Paulo 	}
74932a8088fSRui Paulo 
7504470f0f3SRui Paulo 	ret = bus_setup_intr(dev, sc->sc_irq,
75132a8088fSRui Paulo 	          INTR_TYPE_MISC | INTR_MPSAFE,
75232a8088fSRui Paulo 	    asmc_sms_intrfast, NULL,
75332a8088fSRui Paulo 	    dev, &sc->sc_cookie);
75432a8088fSRui Paulo 
75532a8088fSRui Paulo 	if (ret) {
75632a8088fSRui Paulo 		device_printf(dev, "unable to setup SMS IRQ\n");
75732a8088fSRui Paulo 		goto err1;
75832a8088fSRui Paulo 	}
75932a8088fSRui Paulo nosms:
76032a8088fSRui Paulo 	return (0);
76132a8088fSRui Paulo err1:
7624470f0f3SRui Paulo 	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, sc->sc_irq);
76332a8088fSRui Paulo err2:
7644470f0f3SRui Paulo 	bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
7654470f0f3SRui Paulo 	    sc->sc_ioport);
76632a8088fSRui Paulo 	mtx_destroy(&sc->sc_mtx);
76732a8088fSRui Paulo 	if (sc->sc_sms_tq)
76832a8088fSRui Paulo 		taskqueue_free(sc->sc_sms_tq);
76932a8088fSRui Paulo 
77032a8088fSRui Paulo 	return (ret);
77132a8088fSRui Paulo }
77232a8088fSRui Paulo 
77332a8088fSRui Paulo static int
asmc_detach(device_t dev)77432a8088fSRui Paulo asmc_detach(device_t dev)
77532a8088fSRui Paulo {
77632a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
77732a8088fSRui Paulo 
77832a8088fSRui Paulo 	if (sc->sc_sms_tq) {
77932a8088fSRui Paulo 		taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task);
78032a8088fSRui Paulo 		taskqueue_free(sc->sc_sms_tq);
78132a8088fSRui Paulo 	}
78232a8088fSRui Paulo 	if (sc->sc_cookie)
7834470f0f3SRui Paulo 		bus_teardown_intr(dev, sc->sc_irq, sc->sc_cookie);
7844470f0f3SRui Paulo 	if (sc->sc_irq)
7854470f0f3SRui Paulo 		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq,
7864470f0f3SRui Paulo 		    sc->sc_irq);
7874470f0f3SRui Paulo 	if (sc->sc_ioport)
7884470f0f3SRui Paulo 		bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
7894470f0f3SRui Paulo 		    sc->sc_ioport);
79032a8088fSRui Paulo 	mtx_destroy(&sc->sc_mtx);
79132a8088fSRui Paulo 
79232a8088fSRui Paulo 	return (0);
79332a8088fSRui Paulo }
79432a8088fSRui Paulo 
795108e3076SAdrian Chadd static int
asmc_resume(device_t dev)796108e3076SAdrian Chadd asmc_resume(device_t dev)
797108e3076SAdrian Chadd {
798108e3076SAdrian Chadd     uint8_t buf[2];
799108e3076SAdrian Chadd     buf[0] = light_control;
800108e3076SAdrian Chadd     buf[1] = 0x00;
801108e3076SAdrian Chadd     asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof buf);
802108e3076SAdrian Chadd     return (0);
803108e3076SAdrian Chadd }
804108e3076SAdrian Chadd 
8051269f4d4SRui Paulo #ifdef DEBUG
asmc_dumpall(device_t dev)8061269f4d4SRui Paulo void asmc_dumpall(device_t dev)
8071269f4d4SRui Paulo {
8081269f4d4SRui Paulo 	int i;
8091269f4d4SRui Paulo 
8101269f4d4SRui Paulo 	/* XXX magic number */
8111269f4d4SRui Paulo 	for (i=0; i < 0x100; i++)
8121269f4d4SRui Paulo 		asmc_key_dump(dev, i);
8131269f4d4SRui Paulo }
8141269f4d4SRui Paulo #endif
8151269f4d4SRui Paulo 
81632a8088fSRui Paulo static int
asmc_init(device_t dev)81732a8088fSRui Paulo asmc_init(device_t dev)
81832a8088fSRui Paulo {
81932a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
82032a8088fSRui Paulo 	int i, error = 1;
82132a8088fSRui Paulo 	uint8_t buf[4];
82232a8088fSRui Paulo 
82332a8088fSRui Paulo 	if (sc->sc_model->smc_sms_x == NULL)
82432a8088fSRui Paulo 		goto nosms;
82532a8088fSRui Paulo 
82632a8088fSRui Paulo 	/*
827453130d9SPedro F. Giffuni 	 * We are ready to receive interrupts from the SMS.
82832a8088fSRui Paulo 	 */
82932a8088fSRui Paulo 	buf[0] = 0x01;
8304470f0f3SRui Paulo 	ASMC_DPRINTF(("intok key\n"));
83132a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_INTOK, buf, 1);
83232a8088fSRui Paulo 	DELAY(50);
83332a8088fSRui Paulo 
83432a8088fSRui Paulo 	/*
83532a8088fSRui Paulo 	 * Initiate the polling intervals.
83632a8088fSRui Paulo 	 */
83732a8088fSRui Paulo 	buf[0] = 20; /* msecs */
8384470f0f3SRui Paulo 	ASMC_DPRINTF(("low int key\n"));
83932a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_LOW_INT, buf, 1);
84032a8088fSRui Paulo 	DELAY(200);
84132a8088fSRui Paulo 
84232a8088fSRui Paulo 	buf[0] = 20; /* msecs */
8434470f0f3SRui Paulo 	ASMC_DPRINTF(("high int key\n"));
84432a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_HIGH_INT, buf, 1);
84532a8088fSRui Paulo 	DELAY(200);
84632a8088fSRui Paulo 
84732a8088fSRui Paulo 	buf[0] = 0x00;
84832a8088fSRui Paulo 	buf[1] = 0x60;
8494470f0f3SRui Paulo 	ASMC_DPRINTF(("sms low key\n"));
85032a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_LOW, buf, 2);
85132a8088fSRui Paulo 	DELAY(200);
85232a8088fSRui Paulo 
85332a8088fSRui Paulo 	buf[0] = 0x01;
85432a8088fSRui Paulo 	buf[1] = 0xc0;
8554470f0f3SRui Paulo 	ASMC_DPRINTF(("sms high key\n"));
85632a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_HIGH, buf, 2);
85732a8088fSRui Paulo 	DELAY(200);
85832a8088fSRui Paulo 
85932a8088fSRui Paulo 	/*
86032a8088fSRui Paulo 	 * I'm not sure what this key does, but it seems to be
86132a8088fSRui Paulo 	 * required.
86232a8088fSRui Paulo 	 */
86332a8088fSRui Paulo 	buf[0] = 0x01;
8644470f0f3SRui Paulo 	ASMC_DPRINTF(("sms flag key\n"));
86532a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_FLAG, buf, 1);
866b75dfbe8SRui Paulo 	DELAY(100);
86732a8088fSRui Paulo 
8681269f4d4SRui Paulo 	sc->sc_sms_intr_works = 0;
8691269f4d4SRui Paulo 
87032a8088fSRui Paulo 	/*
8711269f4d4SRui Paulo 	 * Retry SMS initialization 1000 times
8721269f4d4SRui Paulo 	 * (takes approx. 2 seconds in worst case)
87332a8088fSRui Paulo 	 */
8741269f4d4SRui Paulo 	for (i = 0; i < 1000; i++) {
87532a8088fSRui Paulo 		if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 &&
8761269f4d4SRui Paulo 		    (buf[0] == ASMC_SMS_INIT1 && buf[1] == ASMC_SMS_INIT2)) {
87732a8088fSRui Paulo 			error = 0;
8781269f4d4SRui Paulo 			sc->sc_sms_intr_works = 1;
8794fb9bf66SRui Paulo 			goto out;
88032a8088fSRui Paulo 		}
88132a8088fSRui Paulo 		buf[0] = ASMC_SMS_INIT1;
88232a8088fSRui Paulo 		buf[1] = ASMC_SMS_INIT2;
8834470f0f3SRui Paulo 		ASMC_DPRINTF(("sms key\n"));
88432a8088fSRui Paulo 		asmc_key_write(dev, ASMC_KEY_SMS, buf, 2);
88532a8088fSRui Paulo 		DELAY(50);
88632a8088fSRui Paulo 	}
8874fb9bf66SRui Paulo 	device_printf(dev, "WARNING: Sudden Motion Sensor not initialized!\n");
88832a8088fSRui Paulo 
8894fb9bf66SRui Paulo out:
89032a8088fSRui Paulo 	asmc_sms_calibrate(dev);
89132a8088fSRui Paulo nosms:
89232a8088fSRui Paulo 	sc->sc_nfan = asmc_fan_count(dev);
89332a8088fSRui Paulo 	if (sc->sc_nfan > ASMC_MAXFANS) {
89432a8088fSRui Paulo 		device_printf(dev, "more than %d fans were detected. Please "
89532a8088fSRui Paulo 		    "report this.\n", ASMC_MAXFANS);
89632a8088fSRui Paulo 		sc->sc_nfan = ASMC_MAXFANS;
89732a8088fSRui Paulo 	}
89832a8088fSRui Paulo 
89932a8088fSRui Paulo 	if (bootverbose) {
90032a8088fSRui Paulo 		/*
901447666f0SRui Paulo 		 * The number of keys is a 32 bit buffer
90232a8088fSRui Paulo 		 */
90332a8088fSRui Paulo 		asmc_key_read(dev, ASMC_NKEYS, buf, 4);
904447666f0SRui Paulo 		device_printf(dev, "number of keys: %d\n", ntohl(*(uint32_t*)buf));
90532a8088fSRui Paulo 	}
90632a8088fSRui Paulo 
9071269f4d4SRui Paulo #ifdef DEBUG
9081269f4d4SRui Paulo 	asmc_dumpall(dev);
9091269f4d4SRui Paulo #endif
9101269f4d4SRui Paulo 
91132a8088fSRui Paulo 	return (error);
91232a8088fSRui Paulo }
91332a8088fSRui Paulo 
91432a8088fSRui Paulo /*
91532a8088fSRui Paulo  * We need to make sure that the SMC acks the byte sent.
916be80e49aSRui Paulo  * Just wait up to (amount * 10)  ms.
91732a8088fSRui Paulo  */
91832a8088fSRui Paulo static int
asmc_wait_ack(device_t dev,uint8_t val,int amount)919be80e49aSRui Paulo asmc_wait_ack(device_t dev, uint8_t val, int amount)
92032a8088fSRui Paulo {
9214470f0f3SRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
92232a8088fSRui Paulo 	u_int i;
92332a8088fSRui Paulo 
92432a8088fSRui Paulo 	val = val & ASMC_STATUS_MASK;
92532a8088fSRui Paulo 
926be80e49aSRui Paulo 	for (i = 0; i < amount; i++) {
9274470f0f3SRui Paulo 		if ((ASMC_CMDPORT_READ(sc) & ASMC_STATUS_MASK) == val)
92832a8088fSRui Paulo 			return (0);
92932a8088fSRui Paulo 		DELAY(10);
93032a8088fSRui Paulo 	}
93132a8088fSRui Paulo 
932be80e49aSRui Paulo 	return (1);
933be80e49aSRui Paulo }
934be80e49aSRui Paulo 
935be80e49aSRui Paulo /*
936be80e49aSRui Paulo  * We need to make sure that the SMC acks the byte sent.
937be80e49aSRui Paulo  * Just wait up to 100 ms.
938be80e49aSRui Paulo  */
939be80e49aSRui Paulo static int
asmc_wait(device_t dev,uint8_t val)940be80e49aSRui Paulo asmc_wait(device_t dev, uint8_t val)
941be80e49aSRui Paulo {
942e02d0cabSMateusz Guzik #ifdef DEBUG
943be80e49aSRui Paulo 	struct asmc_softc *sc;
944e02d0cabSMateusz Guzik #endif
945be80e49aSRui Paulo 
946be80e49aSRui Paulo 	if (asmc_wait_ack(dev, val, 1000) == 0)
947be80e49aSRui Paulo 		return (0);
948be80e49aSRui Paulo 
949e02d0cabSMateusz Guzik #ifdef DEBUG
950be80e49aSRui Paulo 	sc = device_get_softc(dev);
951e02d0cabSMateusz Guzik #endif
952be80e49aSRui Paulo 	val = val & ASMC_STATUS_MASK;
953be80e49aSRui Paulo 
954be80e49aSRui Paulo #ifdef DEBUG
95532a8088fSRui Paulo 	device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, val,
9564470f0f3SRui Paulo 	    ASMC_CMDPORT_READ(sc));
957be80e49aSRui Paulo #endif
958be80e49aSRui Paulo 	return (1);
959be80e49aSRui Paulo }
96032a8088fSRui Paulo 
961be80e49aSRui Paulo /*
962be80e49aSRui Paulo  * Send the given command, retrying up to 10 times if
963be80e49aSRui Paulo  * the acknowledgement fails.
964be80e49aSRui Paulo  */
965be80e49aSRui Paulo static int
asmc_command(device_t dev,uint8_t command)966be80e49aSRui Paulo asmc_command(device_t dev, uint8_t command) {
967be80e49aSRui Paulo 	int i;
968be80e49aSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
969be80e49aSRui Paulo 
970be80e49aSRui Paulo 	for (i=0; i < 10; i++) {
971be80e49aSRui Paulo 		ASMC_CMDPORT_WRITE(sc, command);
972be80e49aSRui Paulo 		if (asmc_wait_ack(dev, 0x0c, 100) == 0) {
973be80e49aSRui Paulo 			return (0);
974be80e49aSRui Paulo 		}
975be80e49aSRui Paulo 	}
976be80e49aSRui Paulo 
977be80e49aSRui Paulo #ifdef DEBUG
978be80e49aSRui Paulo 	device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, command,
979be80e49aSRui Paulo 	    ASMC_CMDPORT_READ(sc));
980be80e49aSRui Paulo #endif
98132a8088fSRui Paulo 	return (1);
98232a8088fSRui Paulo }
98332a8088fSRui Paulo 
98432a8088fSRui Paulo static int
asmc_key_read(device_t dev,const char * key,uint8_t * buf,uint8_t len)98532a8088fSRui Paulo asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len)
98632a8088fSRui Paulo {
987be80e49aSRui Paulo 	int i, error = 1, try = 0;
98832a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
98932a8088fSRui Paulo 
99032a8088fSRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
99132a8088fSRui Paulo 
992be80e49aSRui Paulo begin:
993be80e49aSRui Paulo 	if (asmc_command(dev, ASMC_CMDREAD))
99432a8088fSRui Paulo 		goto out;
99532a8088fSRui Paulo 
99632a8088fSRui Paulo 	for (i = 0; i < 4; i++) {
9974470f0f3SRui Paulo 		ASMC_DATAPORT_WRITE(sc, key[i]);
99832a8088fSRui Paulo 		if (asmc_wait(dev, 0x04))
99932a8088fSRui Paulo 			goto out;
100032a8088fSRui Paulo 	}
100132a8088fSRui Paulo 
10024470f0f3SRui Paulo 	ASMC_DATAPORT_WRITE(sc, len);
100332a8088fSRui Paulo 
100432a8088fSRui Paulo 	for (i = 0; i < len; i++) {
100532a8088fSRui Paulo 		if (asmc_wait(dev, 0x05))
100632a8088fSRui Paulo 			goto out;
10074470f0f3SRui Paulo 		buf[i] = ASMC_DATAPORT_READ(sc);
100832a8088fSRui Paulo 	}
100932a8088fSRui Paulo 
101032a8088fSRui Paulo 	error = 0;
101132a8088fSRui Paulo out:
1012be80e49aSRui Paulo 	if (error) {
1013be80e49aSRui Paulo 		if (++try < 10) goto begin;
1014be80e49aSRui Paulo 		device_printf(dev,"%s for key %s failed %d times, giving up\n",
1015be80e49aSRui Paulo 			__func__, key, try);
1016be80e49aSRui Paulo 	}
1017be80e49aSRui Paulo 
101832a8088fSRui Paulo 	mtx_unlock_spin(&sc->sc_mtx);
101932a8088fSRui Paulo 
102032a8088fSRui Paulo 	return (error);
102132a8088fSRui Paulo }
102232a8088fSRui Paulo 
10231269f4d4SRui Paulo #ifdef DEBUG
10241269f4d4SRui Paulo static int
asmc_key_dump(device_t dev,int number)10251269f4d4SRui Paulo asmc_key_dump(device_t dev, int number)
10261269f4d4SRui Paulo {
10271269f4d4SRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
10281269f4d4SRui Paulo 	char key[5] = { 0 };
10291269f4d4SRui Paulo 	char type[7] = { 0 };
10301269f4d4SRui Paulo 	uint8_t index[4];
10311269f4d4SRui Paulo 	uint8_t v[32];
10321269f4d4SRui Paulo 	uint8_t maxlen;
10331269f4d4SRui Paulo 	int i, error = 1, try = 0;
10341269f4d4SRui Paulo 
10351269f4d4SRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
10361269f4d4SRui Paulo 
10371269f4d4SRui Paulo 	index[0] = (number >> 24) & 0xff;
10381269f4d4SRui Paulo 	index[1] = (number >> 16) & 0xff;
10391269f4d4SRui Paulo 	index[2] = (number >> 8) & 0xff;
10401269f4d4SRui Paulo 	index[3] = (number) & 0xff;
10411269f4d4SRui Paulo 
10421269f4d4SRui Paulo begin:
10431269f4d4SRui Paulo 	if (asmc_command(dev, 0x12))
10441269f4d4SRui Paulo 		goto out;
10451269f4d4SRui Paulo 
10461269f4d4SRui Paulo 	for (i = 0; i < 4; i++) {
10471269f4d4SRui Paulo 		ASMC_DATAPORT_WRITE(sc, index[i]);
10481269f4d4SRui Paulo 		if (asmc_wait(dev, 0x04))
10491269f4d4SRui Paulo 			goto out;
10501269f4d4SRui Paulo 	}
10511269f4d4SRui Paulo 
10521269f4d4SRui Paulo 	ASMC_DATAPORT_WRITE(sc, 4);
10531269f4d4SRui Paulo 
10541269f4d4SRui Paulo 	for (i = 0; i < 4; i++) {
10551269f4d4SRui Paulo 		if (asmc_wait(dev, 0x05))
10561269f4d4SRui Paulo 			goto out;
10571269f4d4SRui Paulo 		key[i] = ASMC_DATAPORT_READ(sc);
10581269f4d4SRui Paulo 	}
10591269f4d4SRui Paulo 
10601269f4d4SRui Paulo 	/* get type */
10611269f4d4SRui Paulo 	if (asmc_command(dev, 0x13))
10621269f4d4SRui Paulo 		goto out;
10631269f4d4SRui Paulo 
10641269f4d4SRui Paulo 	for (i = 0; i < 4; i++) {
10651269f4d4SRui Paulo 		ASMC_DATAPORT_WRITE(sc, key[i]);
10661269f4d4SRui Paulo 		if (asmc_wait(dev, 0x04))
10671269f4d4SRui Paulo 			goto out;
10681269f4d4SRui Paulo 	}
10691269f4d4SRui Paulo 
10701269f4d4SRui Paulo 	ASMC_DATAPORT_WRITE(sc, 6);
10711269f4d4SRui Paulo 
10721269f4d4SRui Paulo 	for (i = 0; i < 6; i++) {
10731269f4d4SRui Paulo 		if (asmc_wait(dev, 0x05))
10741269f4d4SRui Paulo 			goto out;
10751269f4d4SRui Paulo 		type[i] = ASMC_DATAPORT_READ(sc);
10761269f4d4SRui Paulo 	}
10771269f4d4SRui Paulo 
10781269f4d4SRui Paulo 	error = 0;
10791269f4d4SRui Paulo out:
10801269f4d4SRui Paulo 	if (error) {
10811269f4d4SRui Paulo 		if (++try < 10) goto begin;
10821269f4d4SRui Paulo 		device_printf(dev,"%s for key %s failed %d times, giving up\n",
10831269f4d4SRui Paulo 			__func__, key, try);
10841269f4d4SRui Paulo 		mtx_unlock_spin(&sc->sc_mtx);
10851269f4d4SRui Paulo 	}
10861269f4d4SRui Paulo 	else {
10871269f4d4SRui Paulo 		char buf[1024];
10881269f4d4SRui Paulo 		char buf2[8];
10891269f4d4SRui Paulo 		mtx_unlock_spin(&sc->sc_mtx);
10901269f4d4SRui Paulo 		maxlen = type[0];
10911269f4d4SRui Paulo 		type[0] = ' ';
10921269f4d4SRui Paulo 		type[5] = 0;
10931269f4d4SRui Paulo 		if (maxlen > sizeof(v)) {
1094f17bca82SRui Paulo 			device_printf(dev,
1095f17bca82SRui Paulo 			    "WARNING: cropping maxlen from %d to %zu\n",
1096f17bca82SRui Paulo 			    maxlen, sizeof(v));
10971269f4d4SRui Paulo 			maxlen = sizeof(v);
10981269f4d4SRui Paulo 		}
10991269f4d4SRui Paulo 		for (i = 0; i < sizeof(v); i++) {
11001269f4d4SRui Paulo 			v[i] = 0;
11011269f4d4SRui Paulo 		}
11021269f4d4SRui Paulo 		asmc_key_read(dev, key, v, maxlen);
11031269f4d4SRui Paulo 		snprintf(buf, sizeof(buf), "key %d is: %s, type %s "
11041269f4d4SRui Paulo 		    "(len %d), data", number, key, type, maxlen);
11051269f4d4SRui Paulo 		for (i = 0; i < maxlen; i++) {
1106108e3076SAdrian Chadd 			snprintf(buf2, sizeof(buf2), " %02x", v[i]);
11071269f4d4SRui Paulo 			strlcat(buf, buf2, sizeof(buf));
11081269f4d4SRui Paulo 		}
11091269f4d4SRui Paulo 		strlcat(buf, " \n", sizeof(buf));
111046c76550SRoman Divacky 		device_printf(dev, "%s", buf);
11111269f4d4SRui Paulo 	}
11121269f4d4SRui Paulo 
11131269f4d4SRui Paulo 	return (error);
11141269f4d4SRui Paulo }
11151269f4d4SRui Paulo #endif
11161269f4d4SRui Paulo 
111732a8088fSRui Paulo static int
asmc_key_write(device_t dev,const char * key,uint8_t * buf,uint8_t len)111832a8088fSRui Paulo asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len)
111932a8088fSRui Paulo {
1120be80e49aSRui Paulo 	int i, error = -1, try = 0;
112132a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
112232a8088fSRui Paulo 
112332a8088fSRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
112432a8088fSRui Paulo 
1125be80e49aSRui Paulo begin:
11264470f0f3SRui Paulo 	ASMC_DPRINTF(("cmd port: cmd write\n"));
1127be80e49aSRui Paulo 	if (asmc_command(dev, ASMC_CMDWRITE))
112832a8088fSRui Paulo 		goto out;
112932a8088fSRui Paulo 
11304470f0f3SRui Paulo 	ASMC_DPRINTF(("data port: key\n"));
113132a8088fSRui Paulo 	for (i = 0; i < 4; i++) {
11324470f0f3SRui Paulo 		ASMC_DATAPORT_WRITE(sc, key[i]);
113332a8088fSRui Paulo 		if (asmc_wait(dev, 0x04))
113432a8088fSRui Paulo 			goto out;
113532a8088fSRui Paulo 	}
11364470f0f3SRui Paulo 	ASMC_DPRINTF(("data port: length\n"));
11374470f0f3SRui Paulo 	ASMC_DATAPORT_WRITE(sc, len);
113832a8088fSRui Paulo 
11394470f0f3SRui Paulo 	ASMC_DPRINTF(("data port: buffer\n"));
114032a8088fSRui Paulo 	for (i = 0; i < len; i++) {
114132a8088fSRui Paulo 		if (asmc_wait(dev, 0x04))
114232a8088fSRui Paulo 			goto out;
11434470f0f3SRui Paulo 		ASMC_DATAPORT_WRITE(sc, buf[i]);
114432a8088fSRui Paulo 	}
114532a8088fSRui Paulo 
114632a8088fSRui Paulo 	error = 0;
114732a8088fSRui Paulo out:
1148be80e49aSRui Paulo 	if (error) {
1149be80e49aSRui Paulo 		if (++try < 10) goto begin;
1150be80e49aSRui Paulo 		device_printf(dev,"%s for key %s failed %d times, giving up\n",
1151be80e49aSRui Paulo 			__func__, key, try);
1152be80e49aSRui Paulo 	}
1153be80e49aSRui Paulo 
115432a8088fSRui Paulo 	mtx_unlock_spin(&sc->sc_mtx);
115532a8088fSRui Paulo 
115632a8088fSRui Paulo 	return (error);
115732a8088fSRui Paulo 
115832a8088fSRui Paulo }
115932a8088fSRui Paulo 
116032a8088fSRui Paulo /*
116132a8088fSRui Paulo  * Fan control functions.
116232a8088fSRui Paulo  */
116332a8088fSRui Paulo static int
asmc_fan_count(device_t dev)116432a8088fSRui Paulo asmc_fan_count(device_t dev)
116532a8088fSRui Paulo {
116632a8088fSRui Paulo 	uint8_t buf[1];
116732a8088fSRui Paulo 
11689c325393SMark Johnston 	if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, sizeof buf) != 0)
116932a8088fSRui Paulo 		return (-1);
117032a8088fSRui Paulo 
117132a8088fSRui Paulo 	return (buf[0]);
117232a8088fSRui Paulo }
117332a8088fSRui Paulo 
117432a8088fSRui Paulo static int
asmc_fan_getvalue(device_t dev,const char * key,int fan)117532a8088fSRui Paulo asmc_fan_getvalue(device_t dev, const char *key, int fan)
117632a8088fSRui Paulo {
117732a8088fSRui Paulo 	int speed;
117832a8088fSRui Paulo 	uint8_t buf[2];
117932a8088fSRui Paulo 	char fankey[5];
118032a8088fSRui Paulo 
118132a8088fSRui Paulo 	snprintf(fankey, sizeof(fankey), key, fan);
11829c325393SMark Johnston 	if (asmc_key_read(dev, fankey, buf, sizeof buf) != 0)
118332a8088fSRui Paulo 		return (-1);
118432a8088fSRui Paulo 	speed = (buf[0] << 6) | (buf[1] >> 2);
118532a8088fSRui Paulo 
118632a8088fSRui Paulo 	return (speed);
118732a8088fSRui Paulo }
118832a8088fSRui Paulo 
1189447666f0SRui Paulo static char*
asmc_fan_getstring(device_t dev,const char * key,int fan,uint8_t * buf,uint8_t buflen)1190623534d6SUlrich Spörlein asmc_fan_getstring(device_t dev, const char *key, int fan, uint8_t *buf, uint8_t buflen)
1191447666f0SRui Paulo {
1192447666f0SRui Paulo 	char fankey[5];
1193447666f0SRui Paulo 	char* desc;
1194447666f0SRui Paulo 
1195447666f0SRui Paulo 	snprintf(fankey, sizeof(fankey), key, fan);
11969c325393SMark Johnston 	if (asmc_key_read(dev, fankey, buf, buflen) != 0)
1197447666f0SRui Paulo 		return (NULL);
1198447666f0SRui Paulo 	desc = buf+4;
1199447666f0SRui Paulo 
1200447666f0SRui Paulo 	return (desc);
1201447666f0SRui Paulo }
1202447666f0SRui Paulo 
1203447666f0SRui Paulo static int
asmc_fan_setvalue(device_t dev,const char * key,int fan,int speed)1204447666f0SRui Paulo asmc_fan_setvalue(device_t dev, const char *key, int fan, int speed)
1205447666f0SRui Paulo {
1206447666f0SRui Paulo 	uint8_t buf[2];
1207447666f0SRui Paulo 	char fankey[5];
1208447666f0SRui Paulo 
1209447666f0SRui Paulo 	speed *= 4;
1210447666f0SRui Paulo 
1211447666f0SRui Paulo 	buf[0] = speed>>8;
1212447666f0SRui Paulo 	buf[1] = speed;
1213447666f0SRui Paulo 
1214447666f0SRui Paulo 	snprintf(fankey, sizeof(fankey), key, fan);
1215447666f0SRui Paulo 	if (asmc_key_write(dev, fankey, buf, sizeof buf) < 0)
1216447666f0SRui Paulo 		return (-1);
1217447666f0SRui Paulo 
1218447666f0SRui Paulo 	return (0);
1219447666f0SRui Paulo }
1220447666f0SRui Paulo 
122132a8088fSRui Paulo static int
asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS)122232a8088fSRui Paulo asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS)
122332a8088fSRui Paulo {
122432a8088fSRui Paulo 	device_t dev = (device_t) arg1;
122532a8088fSRui Paulo 	int fan = arg2;
122632a8088fSRui Paulo 	int error;
122732a8088fSRui Paulo 	int32_t v;
122832a8088fSRui Paulo 
122932a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSPEED, fan);
123032a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
123132a8088fSRui Paulo 
123232a8088fSRui Paulo 	return (error);
123332a8088fSRui Paulo }
123432a8088fSRui Paulo 
123532a8088fSRui Paulo static int
asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS)1236447666f0SRui Paulo asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS)
1237447666f0SRui Paulo {
1238623534d6SUlrich Spörlein 	uint8_t buf[16];
1239447666f0SRui Paulo 	device_t dev = (device_t) arg1;
1240447666f0SRui Paulo 	int fan = arg2;
1241447666f0SRui Paulo 	int error = true;
1242447666f0SRui Paulo 	char* desc;
1243447666f0SRui Paulo 
1244623534d6SUlrich Spörlein 	desc = asmc_fan_getstring(dev, ASMC_KEY_FANID, fan, buf, sizeof(buf));
1245447666f0SRui Paulo 
1246447666f0SRui Paulo 	if (desc != NULL)
1247447666f0SRui Paulo 		error = sysctl_handle_string(oidp, desc, 0, req);
1248447666f0SRui Paulo 
1249447666f0SRui Paulo 	return (error);
1250447666f0SRui Paulo }
1251447666f0SRui Paulo 
1252447666f0SRui Paulo static int
asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS)125332a8088fSRui Paulo asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS)
125432a8088fSRui Paulo {
125532a8088fSRui Paulo 	device_t dev = (device_t) arg1;
125632a8088fSRui Paulo 	int fan = arg2;
125732a8088fSRui Paulo 	int error;
125832a8088fSRui Paulo 	int32_t v;
125932a8088fSRui Paulo 
126032a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSAFESPEED, fan);
126132a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
126232a8088fSRui Paulo 
126332a8088fSRui Paulo 	return (error);
126432a8088fSRui Paulo }
126532a8088fSRui Paulo 
126632a8088fSRui Paulo static int
asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS)126732a8088fSRui Paulo asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS)
126832a8088fSRui Paulo {
126932a8088fSRui Paulo 	device_t dev = (device_t) arg1;
127032a8088fSRui Paulo 	int fan = arg2;
127132a8088fSRui Paulo 	int error;
127232a8088fSRui Paulo 	int32_t v;
127332a8088fSRui Paulo 
127432a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMINSPEED, fan);
127532a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
127632a8088fSRui Paulo 
1277447666f0SRui Paulo 	if (error == 0 && req->newptr != NULL) {
12780e1152fcSHans Petter Selasky 		unsigned int newspeed = v;
1279447666f0SRui Paulo 		asmc_fan_setvalue(dev, ASMC_KEY_FANMINSPEED, fan, newspeed);
1280447666f0SRui Paulo 	}
1281447666f0SRui Paulo 
128232a8088fSRui Paulo 	return (error);
128332a8088fSRui Paulo }
128432a8088fSRui Paulo 
128532a8088fSRui Paulo static int
asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS)128632a8088fSRui Paulo asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS)
128732a8088fSRui Paulo {
128832a8088fSRui Paulo 	device_t dev = (device_t) arg1;
128932a8088fSRui Paulo 	int fan = arg2;
129032a8088fSRui Paulo 	int error;
129132a8088fSRui Paulo 	int32_t v;
129232a8088fSRui Paulo 
129332a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMAXSPEED, fan);
129432a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
129532a8088fSRui Paulo 
1296447666f0SRui Paulo 	if (error == 0 && req->newptr != NULL) {
12970e1152fcSHans Petter Selasky 		unsigned int newspeed = v;
1298447666f0SRui Paulo 		asmc_fan_setvalue(dev, ASMC_KEY_FANMAXSPEED, fan, newspeed);
1299447666f0SRui Paulo 	}
1300447666f0SRui Paulo 
130132a8088fSRui Paulo 	return (error);
130232a8088fSRui Paulo }
130332a8088fSRui Paulo 
130432a8088fSRui Paulo static int
asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS)130532a8088fSRui Paulo asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS)
130632a8088fSRui Paulo {
130732a8088fSRui Paulo 	device_t dev = (device_t) arg1;
130832a8088fSRui Paulo 	int fan = arg2;
130932a8088fSRui Paulo 	int error;
131032a8088fSRui Paulo 	int32_t v;
131132a8088fSRui Paulo 
131232a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANTARGETSPEED, fan);
131332a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
131432a8088fSRui Paulo 
1315447666f0SRui Paulo 	if (error == 0 && req->newptr != NULL) {
13160e1152fcSHans Petter Selasky 		unsigned int newspeed = v;
1317447666f0SRui Paulo 		asmc_fan_setvalue(dev, ASMC_KEY_FANTARGETSPEED, fan, newspeed);
1318447666f0SRui Paulo 	}
1319447666f0SRui Paulo 
132032a8088fSRui Paulo 	return (error);
132132a8088fSRui Paulo }
132232a8088fSRui Paulo 
132332a8088fSRui Paulo /*
132432a8088fSRui Paulo  * Temperature functions.
132532a8088fSRui Paulo  */
132632a8088fSRui Paulo static int
asmc_temp_getvalue(device_t dev,const char * key)132732a8088fSRui Paulo asmc_temp_getvalue(device_t dev, const char *key)
132832a8088fSRui Paulo {
132932a8088fSRui Paulo 	uint8_t buf[2];
133032a8088fSRui Paulo 
133132a8088fSRui Paulo 	/*
133232a8088fSRui Paulo 	 * Check for invalid temperatures.
133332a8088fSRui Paulo 	 */
13349c325393SMark Johnston 	if (asmc_key_read(dev, key, buf, sizeof buf) != 0)
133532a8088fSRui Paulo 		return (-1);
133632a8088fSRui Paulo 
133732a8088fSRui Paulo 	return (buf[0]);
133832a8088fSRui Paulo }
133932a8088fSRui Paulo 
134032a8088fSRui Paulo static int
asmc_temp_sysctl(SYSCTL_HANDLER_ARGS)134132a8088fSRui Paulo asmc_temp_sysctl(SYSCTL_HANDLER_ARGS)
134232a8088fSRui Paulo {
134332a8088fSRui Paulo 	device_t dev = (device_t) arg1;
134432a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
134532a8088fSRui Paulo 	int error, val;
134632a8088fSRui Paulo 
134732a8088fSRui Paulo 	val = asmc_temp_getvalue(dev, sc->sc_model->smc_temps[arg2]);
134832a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &val, 0, req);
134932a8088fSRui Paulo 
135032a8088fSRui Paulo 	return (error);
135132a8088fSRui Paulo }
135232a8088fSRui Paulo 
135332a8088fSRui Paulo /*
135432a8088fSRui Paulo  * Sudden Motion Sensor functions.
135532a8088fSRui Paulo  */
135632a8088fSRui Paulo static int
asmc_sms_read(device_t dev,const char * key,int16_t * val)135732a8088fSRui Paulo asmc_sms_read(device_t dev, const char *key, int16_t *val)
135832a8088fSRui Paulo {
135932a8088fSRui Paulo 	uint8_t buf[2];
136032a8088fSRui Paulo 	int error;
136132a8088fSRui Paulo 
136232a8088fSRui Paulo 	/* no need to do locking here as asmc_key_read() already does it */
136332a8088fSRui Paulo 	switch (key[3]) {
136432a8088fSRui Paulo 	case 'X':
136532a8088fSRui Paulo 	case 'Y':
136632a8088fSRui Paulo 	case 'Z':
1367447666f0SRui Paulo 		error =	asmc_key_read(dev, key, buf, sizeof buf);
136832a8088fSRui Paulo 		break;
136932a8088fSRui Paulo 	default:
137032a8088fSRui Paulo 		device_printf(dev, "%s called with invalid argument %s\n",
137132a8088fSRui Paulo 			      __func__, key);
137232a8088fSRui Paulo 		error = 1;
137332a8088fSRui Paulo 		goto out;
137432a8088fSRui Paulo 	}
137532a8088fSRui Paulo 	*val = ((int16_t)buf[0] << 8) | buf[1];
137632a8088fSRui Paulo out:
137732a8088fSRui Paulo 	return (error);
137832a8088fSRui Paulo }
137932a8088fSRui Paulo 
138032a8088fSRui Paulo static void
asmc_sms_calibrate(device_t dev)138132a8088fSRui Paulo asmc_sms_calibrate(device_t dev)
138232a8088fSRui Paulo {
138332a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
138432a8088fSRui Paulo 
138532a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_X, &sc->sms_rest_x);
138632a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &sc->sms_rest_y);
138732a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &sc->sms_rest_z);
138832a8088fSRui Paulo }
138932a8088fSRui Paulo 
139032a8088fSRui Paulo static int
asmc_sms_intrfast(void * arg)139132a8088fSRui Paulo asmc_sms_intrfast(void *arg)
139232a8088fSRui Paulo {
139332a8088fSRui Paulo 	uint8_t type;
139432a8088fSRui Paulo 	device_t dev = (device_t) arg;
139532a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
13961269f4d4SRui Paulo 	if (!sc->sc_sms_intr_works)
13971269f4d4SRui Paulo 		return (FILTER_HANDLED);
139832a8088fSRui Paulo 
139932a8088fSRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
14004470f0f3SRui Paulo 	type = ASMC_INTPORT_READ(sc);
140132a8088fSRui Paulo 	mtx_unlock_spin(&sc->sc_mtx);
140232a8088fSRui Paulo 
140332a8088fSRui Paulo 	sc->sc_sms_intrtype = type;
140432a8088fSRui Paulo 	asmc_sms_printintr(dev, type);
140532a8088fSRui Paulo 
140632a8088fSRui Paulo 	taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task);
140732a8088fSRui Paulo 	return (FILTER_HANDLED);
140832a8088fSRui Paulo }
140932a8088fSRui Paulo 
141032a8088fSRui Paulo static void
asmc_sms_printintr(device_t dev,uint8_t type)141132a8088fSRui Paulo asmc_sms_printintr(device_t dev, uint8_t type)
141232a8088fSRui Paulo {
14133416f5cdSed crowe 	struct asmc_softc *sc = device_get_softc(dev);
141432a8088fSRui Paulo 
141532a8088fSRui Paulo 	switch (type) {
141632a8088fSRui Paulo 	case ASMC_SMS_INTFF:
141732a8088fSRui Paulo 		device_printf(dev, "WARNING: possible free fall!\n");
141832a8088fSRui Paulo 		break;
141932a8088fSRui Paulo 	case ASMC_SMS_INTHA:
142032a8088fSRui Paulo 		device_printf(dev, "WARNING: high acceleration detected!\n");
142132a8088fSRui Paulo 		break;
142232a8088fSRui Paulo 	case ASMC_SMS_INTSH:
142332a8088fSRui Paulo 		device_printf(dev, "WARNING: possible shock!\n");
142432a8088fSRui Paulo 		break;
14253416f5cdSed crowe 	case ASMC_ALSL_INT2A:
14263416f5cdSed crowe 		/*
14273416f5cdSed crowe 		 * This suppresses console and log messages for the ambient
1428638937d4SMichael Gmelin 		 * light sensor for models known to generate this interrupt.
14293416f5cdSed crowe 		 */
1430638937d4SMichael Gmelin 		if (strcmp(sc->sc_model->smc_model, "MacBookPro5,5") == 0 ||
1431638937d4SMichael Gmelin 		    strcmp(sc->sc_model->smc_model, "MacBookPro6,2") == 0)
14323416f5cdSed crowe 			break;
14333416f5cdSed crowe 		/* FALLTHROUGH */
143432a8088fSRui Paulo 	default:
14353416f5cdSed crowe 		device_printf(dev, "unknown interrupt: 0x%x\n", type);
143632a8088fSRui Paulo 	}
143732a8088fSRui Paulo }
143832a8088fSRui Paulo 
143932a8088fSRui Paulo static void
asmc_sms_task(void * arg,int pending)144032a8088fSRui Paulo asmc_sms_task(void *arg, int pending)
144132a8088fSRui Paulo {
144232a8088fSRui Paulo 	struct asmc_softc *sc = (struct asmc_softc *)arg;
144332a8088fSRui Paulo 	char notify[16];
144432a8088fSRui Paulo 	int type;
144532a8088fSRui Paulo 
144632a8088fSRui Paulo 	switch (sc->sc_sms_intrtype) {
144732a8088fSRui Paulo 	case ASMC_SMS_INTFF:
144832a8088fSRui Paulo 		type = 2;
144932a8088fSRui Paulo 		break;
145032a8088fSRui Paulo 	case ASMC_SMS_INTHA:
145132a8088fSRui Paulo 		type = 1;
145232a8088fSRui Paulo 		break;
145332a8088fSRui Paulo 	case ASMC_SMS_INTSH:
145432a8088fSRui Paulo 		type = 0;
145532a8088fSRui Paulo 		break;
145632a8088fSRui Paulo 	default:
145732a8088fSRui Paulo 		type = 255;
145832a8088fSRui Paulo 	}
145932a8088fSRui Paulo 
146032a8088fSRui Paulo 	snprintf(notify, sizeof(notify), " notify=0x%x", type);
14614470f0f3SRui Paulo 	devctl_notify("ACPI", "asmc", "SMS", notify);
146232a8088fSRui Paulo }
146332a8088fSRui Paulo 
146432a8088fSRui Paulo static int
asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS)146532a8088fSRui Paulo asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS)
146632a8088fSRui Paulo {
146732a8088fSRui Paulo 	device_t dev = (device_t) arg1;
146832a8088fSRui Paulo 	int error;
146932a8088fSRui Paulo 	int16_t val;
147032a8088fSRui Paulo 	int32_t v;
147132a8088fSRui Paulo 
147232a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_X, &val);
147332a8088fSRui Paulo 	v = (int32_t) val;
147432a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
147532a8088fSRui Paulo 
147632a8088fSRui Paulo 	return (error);
147732a8088fSRui Paulo }
147832a8088fSRui Paulo 
147932a8088fSRui Paulo static int
asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS)148032a8088fSRui Paulo asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS)
148132a8088fSRui Paulo {
148232a8088fSRui Paulo 	device_t dev = (device_t) arg1;
148332a8088fSRui Paulo 	int error;
148432a8088fSRui Paulo 	int16_t val;
148532a8088fSRui Paulo 	int32_t v;
148632a8088fSRui Paulo 
148732a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val);
148832a8088fSRui Paulo 	v = (int32_t) val;
148932a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
149032a8088fSRui Paulo 
149132a8088fSRui Paulo 	return (error);
149232a8088fSRui Paulo }
149332a8088fSRui Paulo 
149432a8088fSRui Paulo static int
asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS)149532a8088fSRui Paulo asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS)
149632a8088fSRui Paulo {
149732a8088fSRui Paulo 	device_t dev = (device_t) arg1;
149832a8088fSRui Paulo 	int error;
149932a8088fSRui Paulo 	int16_t val;
150032a8088fSRui Paulo 	int32_t v;
150132a8088fSRui Paulo 
150232a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val);
150332a8088fSRui Paulo 	v = (int32_t) val;
15040e1152fcSHans Petter Selasky 	error = sysctl_handle_int(oidp, &v, 0, req);
150532a8088fSRui Paulo 
150632a8088fSRui Paulo 	return (error);
150732a8088fSRui Paulo }
150832a8088fSRui Paulo 
150932a8088fSRui Paulo static int
asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS)151032a8088fSRui Paulo asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS)
151132a8088fSRui Paulo {
151232a8088fSRui Paulo 	device_t dev = (device_t) arg1;
151332a8088fSRui Paulo 	uint8_t buf[6];
151432a8088fSRui Paulo 	int error;
151532a8088fSRui Paulo 	int32_t v;
151632a8088fSRui Paulo 
1517447666f0SRui Paulo 	asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, sizeof buf);
151832a8088fSRui Paulo 	v = buf[2];
15190e1152fcSHans Petter Selasky 	error = sysctl_handle_int(oidp, &v, 0, req);
152032a8088fSRui Paulo 
152132a8088fSRui Paulo 	return (error);
152232a8088fSRui Paulo }
152332a8088fSRui Paulo 
152432a8088fSRui Paulo static int
asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS)152532a8088fSRui Paulo asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS)
152632a8088fSRui Paulo {
152732a8088fSRui Paulo 	device_t dev = (device_t) arg1;
152832a8088fSRui Paulo 	uint8_t buf[6];
152932a8088fSRui Paulo 	int error;
153032a8088fSRui Paulo 	int32_t v;
153132a8088fSRui Paulo 
1532447666f0SRui Paulo 	asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, sizeof buf);
153332a8088fSRui Paulo 	v = buf[2];
15340e1152fcSHans Petter Selasky 	error = sysctl_handle_int(oidp, &v, 0, req);
1535be80e49aSRui Paulo 
1536be80e49aSRui Paulo 	return (error);
1537be80e49aSRui Paulo }
1538be80e49aSRui Paulo 
1539be80e49aSRui Paulo static int
asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS)1540be80e49aSRui Paulo asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS)
1541be80e49aSRui Paulo {
1542be80e49aSRui Paulo 	device_t dev = (device_t) arg1;
1543be80e49aSRui Paulo 	uint8_t buf[2];
1544be80e49aSRui Paulo 	int error;
15450e1152fcSHans Petter Selasky 	int v;
1546be80e49aSRui Paulo 
1547108e3076SAdrian Chadd 	v = light_control;
15480e1152fcSHans Petter Selasky 	error = sysctl_handle_int(oidp, &v, 0, req);
15490e1152fcSHans Petter Selasky 
15500e1152fcSHans Petter Selasky 	if (error == 0 && req->newptr != NULL) {
15510e1152fcSHans Petter Selasky 		if (v < 0 || v > 255)
15520e1152fcSHans Petter Selasky 			return (EINVAL);
1553108e3076SAdrian Chadd 		light_control = v;
1554108e3076SAdrian Chadd 		buf[0] = light_control;
155532a8088fSRui Paulo 		buf[1] = 0x00;
1556447666f0SRui Paulo 		asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof buf);
155732a8088fSRui Paulo 	}
155832a8088fSRui Paulo 	return (error);
155932a8088fSRui Paulo }
15602e9d05fdSAdrian Chadd 
15612e9d05fdSAdrian Chadd static int
asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS)15622e9d05fdSAdrian Chadd asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS)
15632e9d05fdSAdrian Chadd {
15642e9d05fdSAdrian Chadd 	device_t dev = (device_t) arg1;
15652e9d05fdSAdrian Chadd 	uint8_t buf[10];
15662e9d05fdSAdrian Chadd 	int error;
15672e9d05fdSAdrian Chadd 	uint32_t v;
15682e9d05fdSAdrian Chadd 
15692e9d05fdSAdrian Chadd 	asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, sizeof buf);
15702e9d05fdSAdrian Chadd 
15712e9d05fdSAdrian Chadd 	/*
15722e9d05fdSAdrian Chadd 	 * This seems to be a 32 bit big endian value from buf[6] -> buf[9].
15732e9d05fdSAdrian Chadd 	 *
15742e9d05fdSAdrian Chadd 	 * Extract it out manually here, then shift/clamp it.
15752e9d05fdSAdrian Chadd 	 */
15762e9d05fdSAdrian Chadd 	v = be32dec(&buf[6]);
15772e9d05fdSAdrian Chadd 
15782e9d05fdSAdrian Chadd 	/*
15792e9d05fdSAdrian Chadd 	 * Shift out, clamp at 255; that way it looks like the
15802e9d05fdSAdrian Chadd 	 * earlier SMC firmware version responses.
15812e9d05fdSAdrian Chadd 	 */
15822e9d05fdSAdrian Chadd 	v = v >> 8;
15832e9d05fdSAdrian Chadd 	if (v > 255)
15842e9d05fdSAdrian Chadd 		v = 255;
15852e9d05fdSAdrian Chadd 
15862e9d05fdSAdrian Chadd 	error = sysctl_handle_int(oidp, &v, 0, req);
15872e9d05fdSAdrian Chadd 
15882e9d05fdSAdrian Chadd 	return (error);
15892e9d05fdSAdrian Chadd }
1590