xref: /freebsd/sys/dev/asmc/asmc.c (revision 5efca36fbda65afe7726d685dcc43a707ef76447)
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 
15432a8088fSRui Paulo struct asmc_model asmc_models[] = {
15532a8088fSRui Paulo 	{
15632a8088fSRui Paulo 	  "MacBook1,1", "Apple SMC MacBook Core Duo",
157be80e49aSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
15832a8088fSRui Paulo 	  ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
15932a8088fSRui Paulo 	},
16032a8088fSRui Paulo 
16132a8088fSRui Paulo 	{
16232a8088fSRui Paulo 	  "MacBook2,1", "Apple SMC MacBook Core 2 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 	{
168108e3076SAdrian Chadd 	  "MacBook3,1", "Apple SMC MacBook Core 2 Duo",
169108e3076SAdrian Chadd 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
170108e3076SAdrian Chadd 	  ASMC_MB31_TEMPS, ASMC_MB31_TEMPNAMES, ASMC_MB31_TEMPDESCS
171108e3076SAdrian Chadd 	},
172108e3076SAdrian Chadd 
173108e3076SAdrian Chadd 	{
17432a8088fSRui Paulo 	  "MacBookPro1,1", "Apple SMC MacBook Pro Core Duo (15-inch)",
17532a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
17632a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
17732a8088fSRui Paulo 	},
17832a8088fSRui Paulo 
17932a8088fSRui Paulo 	{
18032a8088fSRui Paulo 	  "MacBookPro1,2", "Apple SMC MacBook Pro Core Duo (17-inch)",
18132a8088fSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
18232a8088fSRui Paulo 	  ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
18332a8088fSRui Paulo 	},
18432a8088fSRui Paulo 
18532a8088fSRui Paulo 	{
18632a8088fSRui Paulo 	  "MacBookPro2,1", "Apple SMC MacBook Pro Core 2 Duo (17-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 	  "MacBookPro2,2", "Apple SMC MacBook Pro Core 2 Duo (15-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 	  "MacBookPro3,1", "Apple SMC MacBook Pro Core 2 Duo (15-inch LED)",
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 	  "MacBookPro3,2", "Apple SMC MacBook Pro Core 2 Duo (17-inch HD)",
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 
209be80e49aSRui Paulo 	{
210be80e49aSRui Paulo 	  "MacBookPro4,1", "Apple SMC MacBook Pro Core 2 Duo (Penryn)",
211be80e49aSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
212be80e49aSRui Paulo 	  ASMC_MBP4_TEMPS, ASMC_MBP4_TEMPNAMES, ASMC_MBP4_TEMPDESCS
213be80e49aSRui Paulo 	},
214be80e49aSRui Paulo 
215447666f0SRui Paulo 	{
21631ae3b07SAdrian Chadd 	  "MacBookPro5,1", "Apple SMC MacBook Pro Core 2 Duo (2008/2009)",
21731ae3b07SAdrian Chadd 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
21831ae3b07SAdrian Chadd 	  ASMC_MBP5_TEMPS, ASMC_MBP5_TEMPNAMES, ASMC_MBP5_TEMPDESCS
21931ae3b07SAdrian Chadd 	},
22031ae3b07SAdrian Chadd 
22131ae3b07SAdrian Chadd 	{
222447666f0SRui Paulo 	  "MacBookPro8,2", "Apple SMC MacBook Pro (early 2011)",
223447666f0SRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
224447666f0SRui Paulo 	  ASMC_MBP8_TEMPS, ASMC_MBP8_TEMPNAMES, ASMC_MBP8_TEMPDESCS
225447666f0SRui Paulo 	},
226447666f0SRui Paulo 
227447666f0SRui Paulo 	{
228109e2d29SAdrian Chadd 	  "MacBookPro11,2", "Apple SMC MacBook Pro Retina Core i7 (2013/2014)",
229109e2d29SAdrian Chadd 	  ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS2, ASMC_LIGHT_FUNCS,
230109e2d29SAdrian Chadd 	  ASMC_MBP112_TEMPS, ASMC_MBP112_TEMPNAMES, ASMC_MBP112_TEMPDESCS
231109e2d29SAdrian Chadd 	},
232109e2d29SAdrian Chadd 
233109e2d29SAdrian Chadd 	{
234447666f0SRui Paulo 	  "MacBookPro11,3", "Apple SMC MacBook Pro Retina Core i7 (2013/2014)",
235447666f0SRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
236109e2d29SAdrian Chadd 	  ASMC_MBP113_TEMPS, ASMC_MBP113_TEMPNAMES, ASMC_MBP113_TEMPDESCS
237447666f0SRui Paulo 	},
238447666f0SRui Paulo 
23932a8088fSRui Paulo 	/* The Mac Mini has no SMS */
24032a8088fSRui Paulo 	{
24132a8088fSRui Paulo 	  "Macmini1,1", "Apple SMC Mac Mini",
24232a8088fSRui Paulo 	  NULL, NULL, NULL,
24332a8088fSRui Paulo 	  ASMC_FAN_FUNCS,
244be80e49aSRui Paulo 	  NULL, NULL, NULL,
24532a8088fSRui Paulo 	  ASMC_MM_TEMPS, ASMC_MM_TEMPNAMES, ASMC_MM_TEMPDESCS
24632a8088fSRui Paulo 	},
24732a8088fSRui Paulo 
248764442e0SGavin Atkinson 	/* The Mac Mini 3,1 has no SMS */
249764442e0SGavin Atkinson 	{
250764442e0SGavin Atkinson 	  "Macmini3,1", "Apple SMC Mac Mini 3,1",
251764442e0SGavin Atkinson 	  NULL, NULL, NULL,
252764442e0SGavin Atkinson 	  ASMC_FAN_FUNCS,
253764442e0SGavin Atkinson 	  NULL, NULL, NULL,
254764442e0SGavin Atkinson 	  ASMC_MM31_TEMPS, ASMC_MM31_TEMPNAMES, ASMC_MM31_TEMPDESCS
255764442e0SGavin Atkinson 	},
256764442e0SGavin Atkinson 
257d8246db0SRui Paulo 	/* Idem for the MacPro */
258d8246db0SRui Paulo 	{
259d8246db0SRui Paulo 	  "MacPro2", "Apple SMC Mac Pro (8-core)",
260d8246db0SRui Paulo 	  NULL, NULL, NULL,
261d8246db0SRui Paulo 	  ASMC_FAN_FUNCS,
262be80e49aSRui Paulo 	  NULL, NULL, NULL,
263d8246db0SRui Paulo 	  ASMC_MP_TEMPS, ASMC_MP_TEMPNAMES, ASMC_MP_TEMPDESCS
264d8246db0SRui Paulo 	},
265941f9f10SRui Paulo 
266447666f0SRui Paulo 	/* Idem for the MacPro  2010*/
267447666f0SRui Paulo 	{
268447666f0SRui Paulo 	  "MacPro5,1", "Apple SMC MacPro (2010)",
269447666f0SRui Paulo 	  NULL, NULL, NULL,
270447666f0SRui Paulo 	  ASMC_FAN_FUNCS,
271447666f0SRui Paulo 	  NULL, NULL, NULL,
272447666f0SRui Paulo 	  ASMC_MP5_TEMPS, ASMC_MP5_TEMPNAMES, ASMC_MP5_TEMPDESCS
273447666f0SRui Paulo 	},
274447666f0SRui Paulo 
275941f9f10SRui Paulo 	{
276941f9f10SRui Paulo 	  "MacBookAir1,1", "Apple SMC MacBook Air",
277be80e49aSRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
278941f9f10SRui Paulo 	  ASMC_MBA_TEMPS, ASMC_MBA_TEMPNAMES, ASMC_MBA_TEMPDESCS
279941f9f10SRui Paulo 	},
280941f9f10SRui Paulo 
281447666f0SRui Paulo 	{
282447666f0SRui Paulo 	  "MacBookAir3,1", "Apple SMC MacBook Air Core 2 Duo (Late 2010)",
283447666f0SRui Paulo 	  ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
284447666f0SRui Paulo 	  ASMC_MBA3_TEMPS, ASMC_MBA3_TEMPNAMES, ASMC_MBA3_TEMPDESCS
285447666f0SRui Paulo 	},
286447666f0SRui Paulo 
287108e3076SAdrian Chadd 	{
288108e3076SAdrian Chadd 	  "MacBookAir5,1", "Apple SMC MacBook Air 11-inch (Mid 2012)",
289108e3076SAdrian Chadd 	  ASMC_SMS_FUNCS_DISABLED,
290108e3076SAdrian Chadd 	  ASMC_FAN_FUNCS2,
291108e3076SAdrian Chadd 	  ASMC_LIGHT_FUNCS,
292108e3076SAdrian Chadd 	  ASMC_MBA5_TEMPS, ASMC_MBA5_TEMPNAMES, ASMC_MBA5_TEMPDESCS
293108e3076SAdrian Chadd 	},
294108e3076SAdrian Chadd 
295108e3076SAdrian Chadd 	{
296108e3076SAdrian Chadd 	  "MacBookAir5,2", "Apple SMC MacBook Air 13-inch (Mid 2012)",
297108e3076SAdrian Chadd 	  ASMC_SMS_FUNCS_DISABLED,
298108e3076SAdrian Chadd 	  ASMC_FAN_FUNCS2,
299108e3076SAdrian Chadd 	  ASMC_LIGHT_FUNCS,
300108e3076SAdrian Chadd 	  ASMC_MBA5_TEMPS, ASMC_MBA5_TEMPNAMES, ASMC_MBA5_TEMPDESCS
301108e3076SAdrian Chadd 	},
302108e3076SAdrian Chadd 
303d8246db0SRui Paulo 
30432a8088fSRui Paulo 	{ NULL, NULL }
30532a8088fSRui Paulo };
30632a8088fSRui Paulo 
30732a8088fSRui Paulo #undef ASMC_SMS_FUNCS
308108e3076SAdrian Chadd #undef ASMC_SMS_FUNCS_DISABLED
30932a8088fSRui Paulo #undef ASMC_FAN_FUNCS
310108e3076SAdrian Chadd #undef ASMC_FAN_FUNCS2
31132a8088fSRui Paulo #undef ASMC_LIGHT_FUNCS
31232a8088fSRui Paulo 
31332a8088fSRui Paulo /*
31432a8088fSRui Paulo  * Driver methods.
31532a8088fSRui Paulo  */
31632a8088fSRui Paulo static device_method_t	asmc_methods[] = {
31732a8088fSRui Paulo 	DEVMETHOD(device_probe,		asmc_probe),
31832a8088fSRui Paulo 	DEVMETHOD(device_attach,	asmc_attach),
31932a8088fSRui Paulo 	DEVMETHOD(device_detach,	asmc_detach),
320108e3076SAdrian Chadd 	DEVMETHOD(device_resume,	asmc_resume),
32132a8088fSRui Paulo 
32232a8088fSRui Paulo 	{ 0, 0 }
32332a8088fSRui Paulo };
32432a8088fSRui Paulo 
32532a8088fSRui Paulo static driver_t	asmc_driver = {
32632a8088fSRui Paulo 	"asmc",
32732a8088fSRui Paulo 	asmc_methods,
32832a8088fSRui Paulo 	sizeof(struct asmc_softc)
32932a8088fSRui Paulo };
33032a8088fSRui Paulo 
3314470f0f3SRui Paulo /*
3324470f0f3SRui Paulo  * Debugging
3334470f0f3SRui Paulo  */
3344470f0f3SRui Paulo #define	_COMPONENT	ACPI_OEM
3354470f0f3SRui Paulo ACPI_MODULE_NAME("ASMC")
3364470f0f3SRui Paulo #ifdef DEBUG
3374470f0f3SRui Paulo #define ASMC_DPRINTF(str)	device_printf(dev, str)
3384fb9bf66SRui Paulo #else
3394fb9bf66SRui Paulo #define ASMC_DPRINTF(str)
3404470f0f3SRui Paulo #endif
3414470f0f3SRui Paulo 
342be80e49aSRui Paulo /* NB: can't be const */
3434470f0f3SRui Paulo static char *asmc_ids[] = { "APP0001", NULL };
3444470f0f3SRui Paulo 
34532a8088fSRui Paulo static devclass_t asmc_devclass;
34632a8088fSRui Paulo 
347108e3076SAdrian Chadd static unsigned int light_control = 0;
348108e3076SAdrian Chadd 
3494470f0f3SRui Paulo DRIVER_MODULE(asmc, acpi, asmc_driver, asmc_devclass, NULL, NULL);
3504470f0f3SRui Paulo MODULE_DEPEND(asmc, acpi, 1, 1, 1);
35132a8088fSRui Paulo 
35232a8088fSRui Paulo static struct asmc_model *
35332a8088fSRui Paulo asmc_match(device_t dev)
35432a8088fSRui Paulo {
35532a8088fSRui Paulo 	int i;
35632a8088fSRui Paulo 	char *model;
35732a8088fSRui Paulo 
3582be111bfSDavide Italiano 	model = kern_getenv("smbios.system.product");
35947105877SRui Paulo 	if (model == NULL)
36047105877SRui Paulo 		return (NULL);
36147105877SRui Paulo 
36232a8088fSRui Paulo 	for (i = 0; asmc_models[i].smc_model; i++) {
36332a8088fSRui Paulo 		if (!strncmp(model, asmc_models[i].smc_model, strlen(model))) {
36432a8088fSRui Paulo 			freeenv(model);
36532a8088fSRui Paulo 			return (&asmc_models[i]);
36632a8088fSRui Paulo 		}
36732a8088fSRui Paulo 	}
36832a8088fSRui Paulo 	freeenv(model);
36932a8088fSRui Paulo 
37032a8088fSRui Paulo 	return (NULL);
37132a8088fSRui Paulo }
37232a8088fSRui Paulo 
37332a8088fSRui Paulo static int
37432a8088fSRui Paulo asmc_probe(device_t dev)
37532a8088fSRui Paulo {
37632a8088fSRui Paulo 	struct asmc_model *model;
377*5efca36fSTakanori Watanabe 	int rv;
37832a8088fSRui Paulo 
379a8de37b0SEitan Adler 	if (resource_disabled("asmc", 0))
380a8de37b0SEitan Adler 		return (ENXIO);
381*5efca36fSTakanori Watanabe 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, asmc_ids, NULL);
382*5efca36fSTakanori Watanabe 	if (rv > 0)
383*5efca36fSTakanori Watanabe 		return (rv);
3844470f0f3SRui Paulo 
38532a8088fSRui Paulo 	model = asmc_match(dev);
3864470f0f3SRui Paulo 	if (!model) {
3874470f0f3SRui Paulo 		device_printf(dev, "model not recognized\n");
38832a8088fSRui Paulo 		return (ENXIO);
3894470f0f3SRui Paulo 	}
39032a8088fSRui Paulo 	device_set_desc(dev, model->smc_desc);
39132a8088fSRui Paulo 
392*5efca36fSTakanori Watanabe 	return (rv);
39332a8088fSRui Paulo }
39432a8088fSRui Paulo 
39532a8088fSRui Paulo static int
39632a8088fSRui Paulo asmc_attach(device_t dev)
39732a8088fSRui Paulo {
39832a8088fSRui Paulo 	int i, j;
39932a8088fSRui Paulo 	int ret;
40032a8088fSRui Paulo 	char name[2];
40132a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
40232a8088fSRui Paulo 	struct sysctl_ctx_list *sysctlctx;
40332a8088fSRui Paulo 	struct sysctl_oid *sysctlnode;
40432a8088fSRui Paulo 	struct asmc_model *model;
40532a8088fSRui Paulo 
4064470f0f3SRui Paulo 	sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
4074470f0f3SRui Paulo 	    &sc->sc_rid_port, RF_ACTIVE);
4084470f0f3SRui Paulo 	if (sc->sc_ioport == NULL) {
4094470f0f3SRui Paulo 		device_printf(dev, "unable to allocate IO port\n");
4104470f0f3SRui Paulo 		return (ENOMEM);
4114470f0f3SRui Paulo 	}
4124470f0f3SRui Paulo 
41332a8088fSRui Paulo 	sysctlctx  = device_get_sysctl_ctx(dev);
41432a8088fSRui Paulo 	sysctlnode = device_get_sysctl_tree(dev);
41532a8088fSRui Paulo 
41632a8088fSRui Paulo 	model = asmc_match(dev);
41732a8088fSRui Paulo 
41832a8088fSRui Paulo 	mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN);
41932a8088fSRui Paulo 
42032a8088fSRui Paulo 	sc->sc_model = model;
42132a8088fSRui Paulo 	asmc_init(dev);
42232a8088fSRui Paulo 
42332a8088fSRui Paulo 	/*
42432a8088fSRui Paulo 	 * dev.asmc.n.fan.* tree.
42532a8088fSRui Paulo 	 */
42632a8088fSRui Paulo 	sc->sc_fan_tree[0] = SYSCTL_ADD_NODE(sysctlctx,
42732a8088fSRui Paulo 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "fan",
42832a8088fSRui Paulo 	    CTLFLAG_RD, 0, "Fan Root Tree");
42932a8088fSRui Paulo 
43032a8088fSRui Paulo 	for (i = 1; i <= sc->sc_nfan; i++) {
43132a8088fSRui Paulo 		j = i - 1;
43232a8088fSRui Paulo 		name[0] = '0' + j;
43332a8088fSRui Paulo 		name[1] = 0;
43432a8088fSRui Paulo 		sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx,
43532a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[0]),
43632a8088fSRui Paulo 		    OID_AUTO, name, CTLFLAG_RD, 0,
43732a8088fSRui Paulo 		    "Fan Subtree");
43832a8088fSRui Paulo 
43932a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
44032a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
441447666f0SRui Paulo 		    OID_AUTO, "id", CTLTYPE_STRING | CTLFLAG_RD,
442447666f0SRui Paulo 		    dev, j, model->smc_fan_id, "I",
443447666f0SRui Paulo 		    "Fan ID");
444447666f0SRui Paulo 
445447666f0SRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
446447666f0SRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
44732a8088fSRui Paulo 		    OID_AUTO, "speed", CTLTYPE_INT | CTLFLAG_RD,
44832a8088fSRui Paulo 		    dev, j, model->smc_fan_speed, "I",
44932a8088fSRui Paulo 		    "Fan speed in RPM");
45032a8088fSRui Paulo 
45132a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
45232a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
45332a8088fSRui Paulo 		    OID_AUTO, "safespeed",
45432a8088fSRui Paulo 		    CTLTYPE_INT | CTLFLAG_RD,
45532a8088fSRui Paulo 		    dev, j, model->smc_fan_safespeed, "I",
45632a8088fSRui Paulo 		    "Fan safe speed in RPM");
45732a8088fSRui Paulo 
45832a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
45932a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
46032a8088fSRui Paulo 		    OID_AUTO, "minspeed",
461447666f0SRui Paulo 		    CTLTYPE_INT | CTLFLAG_RW,
46232a8088fSRui Paulo 		    dev, j, model->smc_fan_minspeed, "I",
46332a8088fSRui Paulo 		    "Fan minimum speed in RPM");
46432a8088fSRui Paulo 
46532a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
46632a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
46732a8088fSRui Paulo 		    OID_AUTO, "maxspeed",
468447666f0SRui Paulo 		    CTLTYPE_INT | CTLFLAG_RW,
46932a8088fSRui Paulo 		    dev, j, model->smc_fan_maxspeed, "I",
47032a8088fSRui Paulo 		    "Fan maximum speed in RPM");
47132a8088fSRui Paulo 
47232a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
47332a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
47432a8088fSRui Paulo 		    OID_AUTO, "targetspeed",
475447666f0SRui Paulo 		    CTLTYPE_INT | CTLFLAG_RW,
47632a8088fSRui Paulo 		    dev, j, model->smc_fan_targetspeed, "I",
47732a8088fSRui Paulo 		    "Fan target speed in RPM");
47832a8088fSRui Paulo 	}
47932a8088fSRui Paulo 
48032a8088fSRui Paulo 	/*
48132a8088fSRui Paulo 	 * dev.asmc.n.temp tree.
48232a8088fSRui Paulo 	 */
48332a8088fSRui Paulo 	sc->sc_temp_tree = SYSCTL_ADD_NODE(sysctlctx,
48432a8088fSRui Paulo 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp",
48532a8088fSRui Paulo 	    CTLFLAG_RD, 0, "Temperature sensors");
48632a8088fSRui Paulo 
48732a8088fSRui Paulo 	for (i = 0; model->smc_temps[i]; i++) {
48832a8088fSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
48932a8088fSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_temp_tree),
49032a8088fSRui Paulo 		    OID_AUTO, model->smc_tempnames[i],
49132a8088fSRui Paulo 		    CTLTYPE_INT | CTLFLAG_RD,
49232a8088fSRui Paulo 		    dev, i, asmc_temp_sysctl, "I",
49332a8088fSRui Paulo 		    model->smc_tempdescs[i]);
49432a8088fSRui Paulo 	}
49532a8088fSRui Paulo 
496be80e49aSRui Paulo 	/*
497be80e49aSRui Paulo 	 * dev.asmc.n.light
498be80e49aSRui Paulo 	 */
499be80e49aSRui Paulo 	if (model->smc_light_left) {
500be80e49aSRui Paulo 		sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx,
501be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light",
502be80e49aSRui Paulo 		    CTLFLAG_RD, 0, "Keyboard backlight sensors");
503be80e49aSRui Paulo 
504be80e49aSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
505be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_light_tree),
506be80e49aSRui Paulo 		    OID_AUTO, "left", CTLTYPE_INT | CTLFLAG_RD,
507be80e49aSRui Paulo 		    dev, 0, model->smc_light_left, "I",
508be80e49aSRui Paulo 		    "Keyboard backlight left sensor");
509be80e49aSRui Paulo 
510be80e49aSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
511be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_light_tree),
512be80e49aSRui Paulo 		    OID_AUTO, "right", CTLTYPE_INT | CTLFLAG_RD,
513be80e49aSRui Paulo 		    dev, 0, model->smc_light_right, "I",
514be80e49aSRui Paulo 		    "Keyboard backlight right sensor");
515be80e49aSRui Paulo 
516be80e49aSRui Paulo 		SYSCTL_ADD_PROC(sysctlctx,
517be80e49aSRui Paulo 		    SYSCTL_CHILDREN(sc->sc_light_tree),
5183471c35dSRui Paulo 		    OID_AUTO, "control",
5193471c35dSRui Paulo 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
520be80e49aSRui Paulo 		    dev, 0, model->smc_light_control, "I",
521be80e49aSRui Paulo 		    "Keyboard backlight brightness control");
522be80e49aSRui Paulo 	}
523be80e49aSRui Paulo 
52432a8088fSRui Paulo 	if (model->smc_sms_x == NULL)
52532a8088fSRui Paulo 		goto nosms;
52632a8088fSRui Paulo 
52732a8088fSRui Paulo 	/*
52832a8088fSRui Paulo 	 * dev.asmc.n.sms tree.
52932a8088fSRui Paulo 	 */
53032a8088fSRui Paulo 	sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx,
53132a8088fSRui Paulo 	    SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "sms",
53232a8088fSRui Paulo 	    CTLFLAG_RD, 0, "Sudden Motion Sensor");
53332a8088fSRui Paulo 
53432a8088fSRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
53532a8088fSRui Paulo 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
53632a8088fSRui Paulo 	    OID_AUTO, "x", CTLTYPE_INT | CTLFLAG_RD,
53732a8088fSRui Paulo 	    dev, 0, model->smc_sms_x, "I",
53832a8088fSRui Paulo 	    "Sudden Motion Sensor X value");
53932a8088fSRui Paulo 
54032a8088fSRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
54132a8088fSRui Paulo 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
54232a8088fSRui Paulo 	    OID_AUTO, "y", CTLTYPE_INT | CTLFLAG_RD,
54332a8088fSRui Paulo 	    dev, 0, model->smc_sms_y, "I",
54432a8088fSRui Paulo 	    "Sudden Motion Sensor Y value");
54532a8088fSRui Paulo 
54632a8088fSRui Paulo 	SYSCTL_ADD_PROC(sysctlctx,
54732a8088fSRui Paulo 	    SYSCTL_CHILDREN(sc->sc_sms_tree),
54832a8088fSRui Paulo 	    OID_AUTO, "z", CTLTYPE_INT | CTLFLAG_RD,
54932a8088fSRui Paulo 	    dev, 0, model->smc_sms_z, "I",
55032a8088fSRui Paulo 	    "Sudden Motion Sensor Z value");
55132a8088fSRui Paulo 
55232a8088fSRui Paulo 	/*
55332a8088fSRui Paulo 	 * Need a taskqueue to send devctl_notify() events
55432a8088fSRui Paulo 	 * when the SMS interrupt us.
55532a8088fSRui Paulo 	 *
55632a8088fSRui Paulo 	 * PI_REALTIME is used due to the sensitivity of the
55732a8088fSRui Paulo 	 * interrupt. An interrupt from the SMS means that the
55832a8088fSRui Paulo 	 * disk heads should be turned off as quickly as possible.
55932a8088fSRui Paulo 	 *
56032a8088fSRui Paulo 	 * We only need to do this for the non INTR_FILTER case.
56132a8088fSRui Paulo 	 */
56232a8088fSRui Paulo 	sc->sc_sms_tq = NULL;
56332a8088fSRui Paulo 	TASK_INIT(&sc->sc_sms_task, 0, asmc_sms_task, sc);
56432a8088fSRui Paulo 	sc->sc_sms_tq = taskqueue_create_fast("asmc_taskq", M_WAITOK,
56532a8088fSRui Paulo 	    taskqueue_thread_enqueue, &sc->sc_sms_tq);
56632a8088fSRui Paulo 	taskqueue_start_threads(&sc->sc_sms_tq, 1, PI_REALTIME, "%s sms taskq",
56732a8088fSRui Paulo 	    device_get_nameunit(dev));
56832a8088fSRui Paulo 	/*
56932a8088fSRui Paulo 	 * Allocate an IRQ for the SMS.
57032a8088fSRui Paulo 	 */
5714470f0f3SRui Paulo 	sc->sc_rid_irq = 0;
5724470f0f3SRui Paulo 	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
5734470f0f3SRui Paulo 	    &sc->sc_rid_irq, RF_ACTIVE);
5744470f0f3SRui Paulo 	if (sc->sc_irq == NULL) {
57532a8088fSRui Paulo 		device_printf(dev, "unable to allocate IRQ resource\n");
57632a8088fSRui Paulo 		ret = ENXIO;
57732a8088fSRui Paulo 		goto err2;
57832a8088fSRui Paulo 	}
57932a8088fSRui Paulo 
5804470f0f3SRui Paulo 	ret = bus_setup_intr(dev, sc->sc_irq,
58132a8088fSRui Paulo 	          INTR_TYPE_MISC | INTR_MPSAFE,
58232a8088fSRui Paulo 	    asmc_sms_intrfast, NULL,
58332a8088fSRui Paulo 	    dev, &sc->sc_cookie);
58432a8088fSRui Paulo 
58532a8088fSRui Paulo 	if (ret) {
58632a8088fSRui Paulo 		device_printf(dev, "unable to setup SMS IRQ\n");
58732a8088fSRui Paulo 		goto err1;
58832a8088fSRui Paulo 	}
58932a8088fSRui Paulo nosms:
59032a8088fSRui Paulo 	return (0);
59132a8088fSRui Paulo err1:
5924470f0f3SRui Paulo 	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq, sc->sc_irq);
59332a8088fSRui Paulo err2:
5944470f0f3SRui Paulo 	bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
5954470f0f3SRui Paulo 	    sc->sc_ioport);
59632a8088fSRui Paulo 	mtx_destroy(&sc->sc_mtx);
59732a8088fSRui Paulo 	if (sc->sc_sms_tq)
59832a8088fSRui Paulo 		taskqueue_free(sc->sc_sms_tq);
59932a8088fSRui Paulo 
60032a8088fSRui Paulo 	return (ret);
60132a8088fSRui Paulo }
60232a8088fSRui Paulo 
60332a8088fSRui Paulo static int
60432a8088fSRui Paulo asmc_detach(device_t dev)
60532a8088fSRui Paulo {
60632a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
60732a8088fSRui Paulo 
60832a8088fSRui Paulo 	if (sc->sc_sms_tq) {
60932a8088fSRui Paulo 		taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task);
61032a8088fSRui Paulo 		taskqueue_free(sc->sc_sms_tq);
61132a8088fSRui Paulo 	}
61232a8088fSRui Paulo 	if (sc->sc_cookie)
6134470f0f3SRui Paulo 		bus_teardown_intr(dev, sc->sc_irq, sc->sc_cookie);
6144470f0f3SRui Paulo 	if (sc->sc_irq)
6154470f0f3SRui Paulo 		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq,
6164470f0f3SRui Paulo 		    sc->sc_irq);
6174470f0f3SRui Paulo 	if (sc->sc_ioport)
6184470f0f3SRui Paulo 		bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
6194470f0f3SRui Paulo 		    sc->sc_ioport);
62032a8088fSRui Paulo 	mtx_destroy(&sc->sc_mtx);
62132a8088fSRui Paulo 
62232a8088fSRui Paulo 	return (0);
62332a8088fSRui Paulo }
62432a8088fSRui Paulo 
625108e3076SAdrian Chadd static int
626108e3076SAdrian Chadd asmc_resume(device_t dev)
627108e3076SAdrian Chadd {
628108e3076SAdrian Chadd     uint8_t buf[2];
629108e3076SAdrian Chadd     buf[0] = light_control;
630108e3076SAdrian Chadd     buf[1] = 0x00;
631108e3076SAdrian Chadd     asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof buf);
632108e3076SAdrian Chadd     return (0);
633108e3076SAdrian Chadd }
634108e3076SAdrian Chadd 
635108e3076SAdrian Chadd 
6361269f4d4SRui Paulo #ifdef DEBUG
6371269f4d4SRui Paulo void asmc_dumpall(device_t dev)
6381269f4d4SRui Paulo {
6391269f4d4SRui Paulo 	int i;
6401269f4d4SRui Paulo 
6411269f4d4SRui Paulo 	/* XXX magic number */
6421269f4d4SRui Paulo 	for (i=0; i < 0x100; i++)
6431269f4d4SRui Paulo 		asmc_key_dump(dev, i);
6441269f4d4SRui Paulo }
6451269f4d4SRui Paulo #endif
6461269f4d4SRui Paulo 
64732a8088fSRui Paulo static int
64832a8088fSRui Paulo asmc_init(device_t dev)
64932a8088fSRui Paulo {
65032a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
65132a8088fSRui Paulo 	int i, error = 1;
65232a8088fSRui Paulo 	uint8_t buf[4];
65332a8088fSRui Paulo 
65432a8088fSRui Paulo 	if (sc->sc_model->smc_sms_x == NULL)
65532a8088fSRui Paulo 		goto nosms;
65632a8088fSRui Paulo 
65732a8088fSRui Paulo 	/*
658453130d9SPedro F. Giffuni 	 * We are ready to receive interrupts from the SMS.
65932a8088fSRui Paulo 	 */
66032a8088fSRui Paulo 	buf[0] = 0x01;
6614470f0f3SRui Paulo 	ASMC_DPRINTF(("intok key\n"));
66232a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_INTOK, buf, 1);
66332a8088fSRui Paulo 	DELAY(50);
66432a8088fSRui Paulo 
66532a8088fSRui Paulo 	/*
66632a8088fSRui Paulo 	 * Initiate the polling intervals.
66732a8088fSRui Paulo 	 */
66832a8088fSRui Paulo 	buf[0] = 20; /* msecs */
6694470f0f3SRui Paulo 	ASMC_DPRINTF(("low int key\n"));
67032a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_LOW_INT, buf, 1);
67132a8088fSRui Paulo 	DELAY(200);
67232a8088fSRui Paulo 
67332a8088fSRui Paulo 	buf[0] = 20; /* msecs */
6744470f0f3SRui Paulo 	ASMC_DPRINTF(("high int key\n"));
67532a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_HIGH_INT, buf, 1);
67632a8088fSRui Paulo 	DELAY(200);
67732a8088fSRui Paulo 
67832a8088fSRui Paulo 	buf[0] = 0x00;
67932a8088fSRui Paulo 	buf[1] = 0x60;
6804470f0f3SRui Paulo 	ASMC_DPRINTF(("sms low key\n"));
68132a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_LOW, buf, 2);
68232a8088fSRui Paulo 	DELAY(200);
68332a8088fSRui Paulo 
68432a8088fSRui Paulo 	buf[0] = 0x01;
68532a8088fSRui Paulo 	buf[1] = 0xc0;
6864470f0f3SRui Paulo 	ASMC_DPRINTF(("sms high key\n"));
68732a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_HIGH, buf, 2);
68832a8088fSRui Paulo 	DELAY(200);
68932a8088fSRui Paulo 
69032a8088fSRui Paulo 	/*
69132a8088fSRui Paulo 	 * I'm not sure what this key does, but it seems to be
69232a8088fSRui Paulo 	 * required.
69332a8088fSRui Paulo 	 */
69432a8088fSRui Paulo 	buf[0] = 0x01;
6954470f0f3SRui Paulo 	ASMC_DPRINTF(("sms flag key\n"));
69632a8088fSRui Paulo 	asmc_key_write(dev, ASMC_KEY_SMS_FLAG, buf, 1);
697b75dfbe8SRui Paulo 	DELAY(100);
69832a8088fSRui Paulo 
6991269f4d4SRui Paulo 	sc->sc_sms_intr_works = 0;
7001269f4d4SRui Paulo 
70132a8088fSRui Paulo 	/*
7021269f4d4SRui Paulo 	 * Retry SMS initialization 1000 times
7031269f4d4SRui Paulo 	 * (takes approx. 2 seconds in worst case)
70432a8088fSRui Paulo 	 */
7051269f4d4SRui Paulo 	for (i = 0; i < 1000; i++) {
70632a8088fSRui Paulo 		if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 &&
7071269f4d4SRui Paulo 		    (buf[0] == ASMC_SMS_INIT1 && buf[1] == ASMC_SMS_INIT2)) {
70832a8088fSRui Paulo 			error = 0;
7091269f4d4SRui Paulo 			sc->sc_sms_intr_works = 1;
7104fb9bf66SRui Paulo 			goto out;
71132a8088fSRui Paulo 		}
71232a8088fSRui Paulo 		buf[0] = ASMC_SMS_INIT1;
71332a8088fSRui Paulo 		buf[1] = ASMC_SMS_INIT2;
7144470f0f3SRui Paulo 		ASMC_DPRINTF(("sms key\n"));
71532a8088fSRui Paulo 		asmc_key_write(dev, ASMC_KEY_SMS, buf, 2);
71632a8088fSRui Paulo 		DELAY(50);
71732a8088fSRui Paulo 	}
7184fb9bf66SRui Paulo 	device_printf(dev, "WARNING: Sudden Motion Sensor not initialized!\n");
71932a8088fSRui Paulo 
7204fb9bf66SRui Paulo out:
72132a8088fSRui Paulo 	asmc_sms_calibrate(dev);
72232a8088fSRui Paulo nosms:
72332a8088fSRui Paulo 	sc->sc_nfan = asmc_fan_count(dev);
72432a8088fSRui Paulo 	if (sc->sc_nfan > ASMC_MAXFANS) {
72532a8088fSRui Paulo 		device_printf(dev, "more than %d fans were detected. Please "
72632a8088fSRui Paulo 		    "report this.\n", ASMC_MAXFANS);
72732a8088fSRui Paulo 		sc->sc_nfan = ASMC_MAXFANS;
72832a8088fSRui Paulo 	}
72932a8088fSRui Paulo 
73032a8088fSRui Paulo 	if (bootverbose) {
73132a8088fSRui Paulo 		/*
732447666f0SRui Paulo 		 * The number of keys is a 32 bit buffer
73332a8088fSRui Paulo 		 */
73432a8088fSRui Paulo 		asmc_key_read(dev, ASMC_NKEYS, buf, 4);
735447666f0SRui Paulo 		device_printf(dev, "number of keys: %d\n", ntohl(*(uint32_t*)buf));
73632a8088fSRui Paulo 	}
73732a8088fSRui Paulo 
7381269f4d4SRui Paulo #ifdef DEBUG
7391269f4d4SRui Paulo 	asmc_dumpall(dev);
7401269f4d4SRui Paulo #endif
7411269f4d4SRui Paulo 
74232a8088fSRui Paulo 	return (error);
74332a8088fSRui Paulo }
74432a8088fSRui Paulo 
74532a8088fSRui Paulo /*
74632a8088fSRui Paulo  * We need to make sure that the SMC acks the byte sent.
747be80e49aSRui Paulo  * Just wait up to (amount * 10)  ms.
74832a8088fSRui Paulo  */
74932a8088fSRui Paulo static int
750be80e49aSRui Paulo asmc_wait_ack(device_t dev, uint8_t val, int amount)
75132a8088fSRui Paulo {
7524470f0f3SRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
75332a8088fSRui Paulo 	u_int i;
75432a8088fSRui Paulo 
75532a8088fSRui Paulo 	val = val & ASMC_STATUS_MASK;
75632a8088fSRui Paulo 
757be80e49aSRui Paulo 	for (i = 0; i < amount; i++) {
7584470f0f3SRui Paulo 		if ((ASMC_CMDPORT_READ(sc) & ASMC_STATUS_MASK) == val)
75932a8088fSRui Paulo 			return (0);
76032a8088fSRui Paulo 		DELAY(10);
76132a8088fSRui Paulo 	}
76232a8088fSRui Paulo 
763be80e49aSRui Paulo 	return (1);
764be80e49aSRui Paulo }
765be80e49aSRui Paulo 
766be80e49aSRui Paulo /*
767be80e49aSRui Paulo  * We need to make sure that the SMC acks the byte sent.
768be80e49aSRui Paulo  * Just wait up to 100 ms.
769be80e49aSRui Paulo  */
770be80e49aSRui Paulo static int
771be80e49aSRui Paulo asmc_wait(device_t dev, uint8_t val)
772be80e49aSRui Paulo {
773be80e49aSRui Paulo 	struct asmc_softc *sc;
774be80e49aSRui Paulo 
775be80e49aSRui Paulo 	if (asmc_wait_ack(dev, val, 1000) == 0)
776be80e49aSRui Paulo 		return (0);
777be80e49aSRui Paulo 
778be80e49aSRui Paulo 	sc = device_get_softc(dev);
779be80e49aSRui Paulo 	val = val & ASMC_STATUS_MASK;
780be80e49aSRui Paulo 
781be80e49aSRui Paulo #ifdef DEBUG
78232a8088fSRui Paulo 	device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, val,
7834470f0f3SRui Paulo 	    ASMC_CMDPORT_READ(sc));
784be80e49aSRui Paulo #endif
785be80e49aSRui Paulo 	return (1);
786be80e49aSRui Paulo }
78732a8088fSRui Paulo 
788be80e49aSRui Paulo /*
789be80e49aSRui Paulo  * Send the given command, retrying up to 10 times if
790be80e49aSRui Paulo  * the acknowledgement fails.
791be80e49aSRui Paulo  */
792be80e49aSRui Paulo static int
793be80e49aSRui Paulo asmc_command(device_t dev, uint8_t command) {
794be80e49aSRui Paulo 
795be80e49aSRui Paulo 	int i;
796be80e49aSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
797be80e49aSRui Paulo 
798be80e49aSRui Paulo 	for (i=0; i < 10; i++) {
799be80e49aSRui Paulo 		ASMC_CMDPORT_WRITE(sc, command);
800be80e49aSRui Paulo 		if (asmc_wait_ack(dev, 0x0c, 100) == 0) {
801be80e49aSRui Paulo 			return (0);
802be80e49aSRui Paulo 		}
803be80e49aSRui Paulo 	}
804be80e49aSRui Paulo 
805be80e49aSRui Paulo #ifdef DEBUG
806be80e49aSRui Paulo 	device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, command,
807be80e49aSRui Paulo 	    ASMC_CMDPORT_READ(sc));
808be80e49aSRui Paulo #endif
80932a8088fSRui Paulo 	return (1);
81032a8088fSRui Paulo }
81132a8088fSRui Paulo 
81232a8088fSRui Paulo static int
81332a8088fSRui Paulo asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len)
81432a8088fSRui Paulo {
815be80e49aSRui Paulo 	int i, error = 1, try = 0;
81632a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
81732a8088fSRui Paulo 
81832a8088fSRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
81932a8088fSRui Paulo 
820be80e49aSRui Paulo begin:
821be80e49aSRui Paulo 	if (asmc_command(dev, ASMC_CMDREAD))
82232a8088fSRui Paulo 		goto out;
82332a8088fSRui Paulo 
82432a8088fSRui Paulo 	for (i = 0; i < 4; i++) {
8254470f0f3SRui Paulo 		ASMC_DATAPORT_WRITE(sc, key[i]);
82632a8088fSRui Paulo 		if (asmc_wait(dev, 0x04))
82732a8088fSRui Paulo 			goto out;
82832a8088fSRui Paulo 	}
82932a8088fSRui Paulo 
8304470f0f3SRui Paulo 	ASMC_DATAPORT_WRITE(sc, len);
83132a8088fSRui Paulo 
83232a8088fSRui Paulo 	for (i = 0; i < len; i++) {
83332a8088fSRui Paulo 		if (asmc_wait(dev, 0x05))
83432a8088fSRui Paulo 			goto out;
8354470f0f3SRui Paulo 		buf[i] = ASMC_DATAPORT_READ(sc);
83632a8088fSRui Paulo 	}
83732a8088fSRui Paulo 
83832a8088fSRui Paulo 	error = 0;
83932a8088fSRui Paulo out:
840be80e49aSRui Paulo 	if (error) {
841be80e49aSRui Paulo 		if (++try < 10) goto begin;
842be80e49aSRui Paulo 		device_printf(dev,"%s for key %s failed %d times, giving up\n",
843be80e49aSRui Paulo 			__func__, key, try);
844be80e49aSRui Paulo 	}
845be80e49aSRui Paulo 
84632a8088fSRui Paulo 	mtx_unlock_spin(&sc->sc_mtx);
84732a8088fSRui Paulo 
84832a8088fSRui Paulo 	return (error);
84932a8088fSRui Paulo }
85032a8088fSRui Paulo 
8511269f4d4SRui Paulo #ifdef DEBUG
8521269f4d4SRui Paulo static int
8531269f4d4SRui Paulo asmc_key_dump(device_t dev, int number)
8541269f4d4SRui Paulo {
8551269f4d4SRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
8561269f4d4SRui Paulo 	char key[5] = { 0 };
8571269f4d4SRui Paulo 	char type[7] = { 0 };
8581269f4d4SRui Paulo 	uint8_t index[4];
8591269f4d4SRui Paulo 	uint8_t v[32];
8601269f4d4SRui Paulo 	uint8_t maxlen;
8611269f4d4SRui Paulo 	int i, error = 1, try = 0;
8621269f4d4SRui Paulo 
8631269f4d4SRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
8641269f4d4SRui Paulo 
8651269f4d4SRui Paulo 	index[0] = (number >> 24) & 0xff;
8661269f4d4SRui Paulo 	index[1] = (number >> 16) & 0xff;
8671269f4d4SRui Paulo 	index[2] = (number >> 8) & 0xff;
8681269f4d4SRui Paulo 	index[3] = (number) & 0xff;
8691269f4d4SRui Paulo 
8701269f4d4SRui Paulo begin:
8711269f4d4SRui Paulo 	if (asmc_command(dev, 0x12))
8721269f4d4SRui Paulo 		goto out;
8731269f4d4SRui Paulo 
8741269f4d4SRui Paulo 	for (i = 0; i < 4; i++) {
8751269f4d4SRui Paulo 		ASMC_DATAPORT_WRITE(sc, index[i]);
8761269f4d4SRui Paulo 		if (asmc_wait(dev, 0x04))
8771269f4d4SRui Paulo 			goto out;
8781269f4d4SRui Paulo 	}
8791269f4d4SRui Paulo 
8801269f4d4SRui Paulo 	ASMC_DATAPORT_WRITE(sc, 4);
8811269f4d4SRui Paulo 
8821269f4d4SRui Paulo 	for (i = 0; i < 4; i++) {
8831269f4d4SRui Paulo 		if (asmc_wait(dev, 0x05))
8841269f4d4SRui Paulo 			goto out;
8851269f4d4SRui Paulo 		key[i] = ASMC_DATAPORT_READ(sc);
8861269f4d4SRui Paulo 	}
8871269f4d4SRui Paulo 
8881269f4d4SRui Paulo 	/* get type */
8891269f4d4SRui Paulo 	if (asmc_command(dev, 0x13))
8901269f4d4SRui Paulo 		goto out;
8911269f4d4SRui Paulo 
8921269f4d4SRui Paulo 	for (i = 0; i < 4; i++) {
8931269f4d4SRui Paulo 		ASMC_DATAPORT_WRITE(sc, key[i]);
8941269f4d4SRui Paulo 		if (asmc_wait(dev, 0x04))
8951269f4d4SRui Paulo 			goto out;
8961269f4d4SRui Paulo 	}
8971269f4d4SRui Paulo 
8981269f4d4SRui Paulo 	ASMC_DATAPORT_WRITE(sc, 6);
8991269f4d4SRui Paulo 
9001269f4d4SRui Paulo 	for (i = 0; i < 6; i++) {
9011269f4d4SRui Paulo 		if (asmc_wait(dev, 0x05))
9021269f4d4SRui Paulo 			goto out;
9031269f4d4SRui Paulo 		type[i] = ASMC_DATAPORT_READ(sc);
9041269f4d4SRui Paulo 	}
9051269f4d4SRui Paulo 
9061269f4d4SRui Paulo 	error = 0;
9071269f4d4SRui Paulo out:
9081269f4d4SRui Paulo 	if (error) {
9091269f4d4SRui Paulo 		if (++try < 10) goto begin;
9101269f4d4SRui Paulo 		device_printf(dev,"%s for key %s failed %d times, giving up\n",
9111269f4d4SRui Paulo 			__func__, key, try);
9121269f4d4SRui Paulo 		mtx_unlock_spin(&sc->sc_mtx);
9131269f4d4SRui Paulo 	}
9141269f4d4SRui Paulo 	else {
9151269f4d4SRui Paulo 		char buf[1024];
9161269f4d4SRui Paulo 		char buf2[8];
9171269f4d4SRui Paulo 		mtx_unlock_spin(&sc->sc_mtx);
9181269f4d4SRui Paulo 		maxlen = type[0];
9191269f4d4SRui Paulo 		type[0] = ' ';
9201269f4d4SRui Paulo 		type[5] = 0;
9211269f4d4SRui Paulo 		if (maxlen > sizeof(v)) {
922f17bca82SRui Paulo 			device_printf(dev,
923f17bca82SRui Paulo 			    "WARNING: cropping maxlen from %d to %zu\n",
924f17bca82SRui Paulo 			    maxlen, sizeof(v));
9251269f4d4SRui Paulo 			maxlen = sizeof(v);
9261269f4d4SRui Paulo 		}
9271269f4d4SRui Paulo 		for (i = 0; i < sizeof(v); i++) {
9281269f4d4SRui Paulo 			v[i] = 0;
9291269f4d4SRui Paulo 		}
9301269f4d4SRui Paulo 		asmc_key_read(dev, key, v, maxlen);
9311269f4d4SRui Paulo 		snprintf(buf, sizeof(buf), "key %d is: %s, type %s "
9321269f4d4SRui Paulo 		    "(len %d), data", number, key, type, maxlen);
9331269f4d4SRui Paulo 		for (i = 0; i < maxlen; i++) {
934108e3076SAdrian Chadd 			snprintf(buf2, sizeof(buf2), " %02x", v[i]);
9351269f4d4SRui Paulo 			strlcat(buf, buf2, sizeof(buf));
9361269f4d4SRui Paulo 		}
9371269f4d4SRui Paulo 		strlcat(buf, " \n", sizeof(buf));
93846c76550SRoman Divacky 		device_printf(dev, "%s", buf);
9391269f4d4SRui Paulo 	}
9401269f4d4SRui Paulo 
9411269f4d4SRui Paulo 	return (error);
9421269f4d4SRui Paulo }
9431269f4d4SRui Paulo #endif
9441269f4d4SRui Paulo 
94532a8088fSRui Paulo static int
94632a8088fSRui Paulo asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len)
94732a8088fSRui Paulo {
948be80e49aSRui Paulo 	int i, error = -1, try = 0;
94932a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
95032a8088fSRui Paulo 
95132a8088fSRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
95232a8088fSRui Paulo 
953be80e49aSRui Paulo begin:
9544470f0f3SRui Paulo 	ASMC_DPRINTF(("cmd port: cmd write\n"));
955be80e49aSRui Paulo 	if (asmc_command(dev, ASMC_CMDWRITE))
95632a8088fSRui Paulo 		goto out;
95732a8088fSRui Paulo 
9584470f0f3SRui Paulo 	ASMC_DPRINTF(("data port: key\n"));
95932a8088fSRui Paulo 	for (i = 0; i < 4; i++) {
9604470f0f3SRui Paulo 		ASMC_DATAPORT_WRITE(sc, key[i]);
96132a8088fSRui Paulo 		if (asmc_wait(dev, 0x04))
96232a8088fSRui Paulo 			goto out;
96332a8088fSRui Paulo 	}
9644470f0f3SRui Paulo 	ASMC_DPRINTF(("data port: length\n"));
9654470f0f3SRui Paulo 	ASMC_DATAPORT_WRITE(sc, len);
96632a8088fSRui Paulo 
9674470f0f3SRui Paulo 	ASMC_DPRINTF(("data port: buffer\n"));
96832a8088fSRui Paulo 	for (i = 0; i < len; i++) {
96932a8088fSRui Paulo 		if (asmc_wait(dev, 0x04))
97032a8088fSRui Paulo 			goto out;
9714470f0f3SRui Paulo 		ASMC_DATAPORT_WRITE(sc, buf[i]);
97232a8088fSRui Paulo 	}
97332a8088fSRui Paulo 
97432a8088fSRui Paulo 	error = 0;
97532a8088fSRui Paulo out:
976be80e49aSRui Paulo 	if (error) {
977be80e49aSRui Paulo 		if (++try < 10) goto begin;
978be80e49aSRui Paulo 		device_printf(dev,"%s for key %s failed %d times, giving up\n",
979be80e49aSRui Paulo 			__func__, key, try);
980be80e49aSRui Paulo 	}
981be80e49aSRui Paulo 
98232a8088fSRui Paulo 	mtx_unlock_spin(&sc->sc_mtx);
98332a8088fSRui Paulo 
98432a8088fSRui Paulo 	return (error);
98532a8088fSRui Paulo 
98632a8088fSRui Paulo }
98732a8088fSRui Paulo 
98832a8088fSRui Paulo /*
98932a8088fSRui Paulo  * Fan control functions.
99032a8088fSRui Paulo  */
99132a8088fSRui Paulo static int
99232a8088fSRui Paulo asmc_fan_count(device_t dev)
99332a8088fSRui Paulo {
99432a8088fSRui Paulo 	uint8_t buf[1];
99532a8088fSRui Paulo 
996447666f0SRui Paulo 	if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, sizeof buf) < 0)
99732a8088fSRui Paulo 		return (-1);
99832a8088fSRui Paulo 
99932a8088fSRui Paulo 	return (buf[0]);
100032a8088fSRui Paulo }
100132a8088fSRui Paulo 
100232a8088fSRui Paulo static int
100332a8088fSRui Paulo asmc_fan_getvalue(device_t dev, const char *key, int fan)
100432a8088fSRui Paulo {
100532a8088fSRui Paulo 	int speed;
100632a8088fSRui Paulo 	uint8_t buf[2];
100732a8088fSRui Paulo 	char fankey[5];
100832a8088fSRui Paulo 
100932a8088fSRui Paulo 	snprintf(fankey, sizeof(fankey), key, fan);
1010447666f0SRui Paulo 	if (asmc_key_read(dev, fankey, buf, sizeof buf) < 0)
101132a8088fSRui Paulo 		return (-1);
101232a8088fSRui Paulo 	speed = (buf[0] << 6) | (buf[1] >> 2);
101332a8088fSRui Paulo 
101432a8088fSRui Paulo 	return (speed);
101532a8088fSRui Paulo }
101632a8088fSRui Paulo 
1017447666f0SRui Paulo static char*
1018623534d6SUlrich Spörlein asmc_fan_getstring(device_t dev, const char *key, int fan, uint8_t *buf, uint8_t buflen)
1019447666f0SRui Paulo {
1020447666f0SRui Paulo 	char fankey[5];
1021447666f0SRui Paulo 	char* desc;
1022447666f0SRui Paulo 
1023447666f0SRui Paulo 	snprintf(fankey, sizeof(fankey), key, fan);
1024623534d6SUlrich Spörlein 	if (asmc_key_read(dev, fankey, buf, buflen) < 0)
1025447666f0SRui Paulo 		return (NULL);
1026447666f0SRui Paulo 	desc = buf+4;
1027447666f0SRui Paulo 
1028447666f0SRui Paulo 	return (desc);
1029447666f0SRui Paulo }
1030447666f0SRui Paulo 
1031447666f0SRui Paulo static int
1032447666f0SRui Paulo asmc_fan_setvalue(device_t dev, const char *key, int fan, int speed)
1033447666f0SRui Paulo {
1034447666f0SRui Paulo 	uint8_t buf[2];
1035447666f0SRui Paulo 	char fankey[5];
1036447666f0SRui Paulo 
1037447666f0SRui Paulo 	speed *= 4;
1038447666f0SRui Paulo 
1039447666f0SRui Paulo 	buf[0] = speed>>8;
1040447666f0SRui Paulo 	buf[1] = speed;
1041447666f0SRui Paulo 
1042447666f0SRui Paulo 	snprintf(fankey, sizeof(fankey), key, fan);
1043447666f0SRui Paulo 	if (asmc_key_write(dev, fankey, buf, sizeof buf) < 0)
1044447666f0SRui Paulo 		return (-1);
1045447666f0SRui Paulo 
1046447666f0SRui Paulo 	return (0);
1047447666f0SRui Paulo }
1048447666f0SRui Paulo 
104932a8088fSRui Paulo static int
105032a8088fSRui Paulo asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS)
105132a8088fSRui Paulo {
105232a8088fSRui Paulo 	device_t dev = (device_t) arg1;
105332a8088fSRui Paulo 	int fan = arg2;
105432a8088fSRui Paulo 	int error;
105532a8088fSRui Paulo 	int32_t v;
105632a8088fSRui Paulo 
105732a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSPEED, fan);
105832a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
105932a8088fSRui Paulo 
106032a8088fSRui Paulo 	return (error);
106132a8088fSRui Paulo }
106232a8088fSRui Paulo 
106332a8088fSRui Paulo static int
1064447666f0SRui Paulo asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS)
1065447666f0SRui Paulo {
1066623534d6SUlrich Spörlein 	uint8_t buf[16];
1067447666f0SRui Paulo 	device_t dev = (device_t) arg1;
1068447666f0SRui Paulo 	int fan = arg2;
1069447666f0SRui Paulo 	int error = true;
1070447666f0SRui Paulo 	char* desc;
1071447666f0SRui Paulo 
1072623534d6SUlrich Spörlein 	desc = asmc_fan_getstring(dev, ASMC_KEY_FANID, fan, buf, sizeof(buf));
1073447666f0SRui Paulo 
1074447666f0SRui Paulo 	if (desc != NULL)
1075447666f0SRui Paulo 		error = sysctl_handle_string(oidp, desc, 0, req);
1076447666f0SRui Paulo 
1077447666f0SRui Paulo 	return (error);
1078447666f0SRui Paulo }
1079447666f0SRui Paulo 
1080447666f0SRui Paulo static int
108132a8088fSRui Paulo asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS)
108232a8088fSRui Paulo {
108332a8088fSRui Paulo 	device_t dev = (device_t) arg1;
108432a8088fSRui Paulo 	int fan = arg2;
108532a8088fSRui Paulo 	int error;
108632a8088fSRui Paulo 	int32_t v;
108732a8088fSRui Paulo 
108832a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANSAFESPEED, fan);
108932a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
109032a8088fSRui Paulo 
109132a8088fSRui Paulo 	return (error);
109232a8088fSRui Paulo }
109332a8088fSRui Paulo 
109432a8088fSRui Paulo 
109532a8088fSRui Paulo static int
109632a8088fSRui Paulo asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS)
109732a8088fSRui Paulo {
109832a8088fSRui Paulo 	device_t dev = (device_t) arg1;
109932a8088fSRui Paulo 	int fan = arg2;
110032a8088fSRui Paulo 	int error;
110132a8088fSRui Paulo 	int32_t v;
110232a8088fSRui Paulo 
110332a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMINSPEED, fan);
110432a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
110532a8088fSRui Paulo 
1106447666f0SRui Paulo 	if (error == 0 && req->newptr != NULL) {
11070e1152fcSHans Petter Selasky 		unsigned int newspeed = v;
1108447666f0SRui Paulo 		asmc_fan_setvalue(dev, ASMC_KEY_FANMINSPEED, fan, newspeed);
1109447666f0SRui Paulo 	}
1110447666f0SRui Paulo 
111132a8088fSRui Paulo 	return (error);
111232a8088fSRui Paulo }
111332a8088fSRui Paulo 
111432a8088fSRui Paulo static int
111532a8088fSRui Paulo asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS)
111632a8088fSRui Paulo {
111732a8088fSRui Paulo 	device_t dev = (device_t) arg1;
111832a8088fSRui Paulo 	int fan = arg2;
111932a8088fSRui Paulo 	int error;
112032a8088fSRui Paulo 	int32_t v;
112132a8088fSRui Paulo 
112232a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANMAXSPEED, fan);
112332a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
112432a8088fSRui Paulo 
1125447666f0SRui Paulo 	if (error == 0 && req->newptr != NULL) {
11260e1152fcSHans Petter Selasky 		unsigned int newspeed = v;
1127447666f0SRui Paulo 		asmc_fan_setvalue(dev, ASMC_KEY_FANMAXSPEED, fan, newspeed);
1128447666f0SRui Paulo 	}
1129447666f0SRui Paulo 
113032a8088fSRui Paulo 	return (error);
113132a8088fSRui Paulo }
113232a8088fSRui Paulo 
113332a8088fSRui Paulo static int
113432a8088fSRui Paulo asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS)
113532a8088fSRui Paulo {
113632a8088fSRui Paulo 	device_t dev = (device_t) arg1;
113732a8088fSRui Paulo 	int fan = arg2;
113832a8088fSRui Paulo 	int error;
113932a8088fSRui Paulo 	int32_t v;
114032a8088fSRui Paulo 
114132a8088fSRui Paulo 	v = asmc_fan_getvalue(dev, ASMC_KEY_FANTARGETSPEED, fan);
114232a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
114332a8088fSRui Paulo 
1144447666f0SRui Paulo 	if (error == 0 && req->newptr != NULL) {
11450e1152fcSHans Petter Selasky 		unsigned int newspeed = v;
1146447666f0SRui Paulo 		asmc_fan_setvalue(dev, ASMC_KEY_FANTARGETSPEED, fan, newspeed);
1147447666f0SRui Paulo 	}
1148447666f0SRui Paulo 
114932a8088fSRui Paulo 	return (error);
115032a8088fSRui Paulo }
115132a8088fSRui Paulo 
115232a8088fSRui Paulo /*
115332a8088fSRui Paulo  * Temperature functions.
115432a8088fSRui Paulo  */
115532a8088fSRui Paulo static int
115632a8088fSRui Paulo asmc_temp_getvalue(device_t dev, const char *key)
115732a8088fSRui Paulo {
115832a8088fSRui Paulo 	uint8_t buf[2];
115932a8088fSRui Paulo 
116032a8088fSRui Paulo 	/*
116132a8088fSRui Paulo 	 * Check for invalid temperatures.
116232a8088fSRui Paulo 	 */
1163447666f0SRui Paulo 	if (asmc_key_read(dev, key, buf, sizeof buf) < 0)
116432a8088fSRui Paulo 		return (-1);
116532a8088fSRui Paulo 
116632a8088fSRui Paulo 	return (buf[0]);
116732a8088fSRui Paulo }
116832a8088fSRui Paulo 
116932a8088fSRui Paulo static int
117032a8088fSRui Paulo asmc_temp_sysctl(SYSCTL_HANDLER_ARGS)
117132a8088fSRui Paulo {
117232a8088fSRui Paulo 	device_t dev = (device_t) arg1;
117332a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
117432a8088fSRui Paulo 	int error, val;
117532a8088fSRui Paulo 
117632a8088fSRui Paulo 	val = asmc_temp_getvalue(dev, sc->sc_model->smc_temps[arg2]);
117732a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &val, 0, req);
117832a8088fSRui Paulo 
117932a8088fSRui Paulo 	return (error);
118032a8088fSRui Paulo }
118132a8088fSRui Paulo 
118232a8088fSRui Paulo /*
118332a8088fSRui Paulo  * Sudden Motion Sensor functions.
118432a8088fSRui Paulo  */
118532a8088fSRui Paulo static int
118632a8088fSRui Paulo asmc_sms_read(device_t dev, const char *key, int16_t *val)
118732a8088fSRui Paulo {
118832a8088fSRui Paulo 	uint8_t buf[2];
118932a8088fSRui Paulo 	int error;
119032a8088fSRui Paulo 
119132a8088fSRui Paulo 	/* no need to do locking here as asmc_key_read() already does it */
119232a8088fSRui Paulo 	switch (key[3]) {
119332a8088fSRui Paulo 	case 'X':
119432a8088fSRui Paulo 	case 'Y':
119532a8088fSRui Paulo 	case 'Z':
1196447666f0SRui Paulo 		error =	asmc_key_read(dev, key, buf, sizeof buf);
119732a8088fSRui Paulo 		break;
119832a8088fSRui Paulo 	default:
119932a8088fSRui Paulo 		device_printf(dev, "%s called with invalid argument %s\n",
120032a8088fSRui Paulo 			      __func__, key);
120132a8088fSRui Paulo 		error = 1;
120232a8088fSRui Paulo 		goto out;
120332a8088fSRui Paulo 	}
120432a8088fSRui Paulo 	*val = ((int16_t)buf[0] << 8) | buf[1];
120532a8088fSRui Paulo out:
120632a8088fSRui Paulo 	return (error);
120732a8088fSRui Paulo }
120832a8088fSRui Paulo 
120932a8088fSRui Paulo static void
121032a8088fSRui Paulo asmc_sms_calibrate(device_t dev)
121132a8088fSRui Paulo {
121232a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
121332a8088fSRui Paulo 
121432a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_X, &sc->sms_rest_x);
121532a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &sc->sms_rest_y);
121632a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &sc->sms_rest_z);
121732a8088fSRui Paulo }
121832a8088fSRui Paulo 
121932a8088fSRui Paulo static int
122032a8088fSRui Paulo asmc_sms_intrfast(void *arg)
122132a8088fSRui Paulo {
122232a8088fSRui Paulo 	uint8_t type;
122332a8088fSRui Paulo 	device_t dev = (device_t) arg;
122432a8088fSRui Paulo 	struct asmc_softc *sc = device_get_softc(dev);
12251269f4d4SRui Paulo 	if (!sc->sc_sms_intr_works)
12261269f4d4SRui Paulo 		return (FILTER_HANDLED);
122732a8088fSRui Paulo 
122832a8088fSRui Paulo 	mtx_lock_spin(&sc->sc_mtx);
12294470f0f3SRui Paulo 	type = ASMC_INTPORT_READ(sc);
123032a8088fSRui Paulo 	mtx_unlock_spin(&sc->sc_mtx);
123132a8088fSRui Paulo 
123232a8088fSRui Paulo 	sc->sc_sms_intrtype = type;
123332a8088fSRui Paulo 	asmc_sms_printintr(dev, type);
123432a8088fSRui Paulo 
123532a8088fSRui Paulo 	taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task);
123632a8088fSRui Paulo 	return (FILTER_HANDLED);
123732a8088fSRui Paulo }
123832a8088fSRui Paulo 
123932a8088fSRui Paulo 
124032a8088fSRui Paulo 
124132a8088fSRui Paulo static void
124232a8088fSRui Paulo asmc_sms_printintr(device_t dev, uint8_t type)
124332a8088fSRui Paulo {
124432a8088fSRui Paulo 
124532a8088fSRui Paulo 	switch (type) {
124632a8088fSRui Paulo 	case ASMC_SMS_INTFF:
124732a8088fSRui Paulo 		device_printf(dev, "WARNING: possible free fall!\n");
124832a8088fSRui Paulo 		break;
124932a8088fSRui Paulo 	case ASMC_SMS_INTHA:
125032a8088fSRui Paulo 		device_printf(dev, "WARNING: high acceleration detected!\n");
125132a8088fSRui Paulo 		break;
125232a8088fSRui Paulo 	case ASMC_SMS_INTSH:
125332a8088fSRui Paulo 		device_printf(dev, "WARNING: possible shock!\n");
125432a8088fSRui Paulo 		break;
125532a8088fSRui Paulo 	default:
125632a8088fSRui Paulo 		device_printf(dev, "%s unknown interrupt\n", __func__);
125732a8088fSRui Paulo 	}
125832a8088fSRui Paulo }
125932a8088fSRui Paulo 
126032a8088fSRui Paulo static void
126132a8088fSRui Paulo asmc_sms_task(void *arg, int pending)
126232a8088fSRui Paulo {
126332a8088fSRui Paulo 	struct asmc_softc *sc = (struct asmc_softc *)arg;
126432a8088fSRui Paulo 	char notify[16];
126532a8088fSRui Paulo 	int type;
126632a8088fSRui Paulo 
126732a8088fSRui Paulo 	switch (sc->sc_sms_intrtype) {
126832a8088fSRui Paulo 	case ASMC_SMS_INTFF:
126932a8088fSRui Paulo 		type = 2;
127032a8088fSRui Paulo 		break;
127132a8088fSRui Paulo 	case ASMC_SMS_INTHA:
127232a8088fSRui Paulo 		type = 1;
127332a8088fSRui Paulo 		break;
127432a8088fSRui Paulo 	case ASMC_SMS_INTSH:
127532a8088fSRui Paulo 		type = 0;
127632a8088fSRui Paulo 		break;
127732a8088fSRui Paulo 	default:
127832a8088fSRui Paulo 		type = 255;
127932a8088fSRui Paulo 	}
128032a8088fSRui Paulo 
128132a8088fSRui Paulo 	snprintf(notify, sizeof(notify), " notify=0x%x", type);
12824470f0f3SRui Paulo 	devctl_notify("ACPI", "asmc", "SMS", notify);
128332a8088fSRui Paulo }
128432a8088fSRui Paulo 
128532a8088fSRui Paulo static int
128632a8088fSRui Paulo asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS)
128732a8088fSRui Paulo {
128832a8088fSRui Paulo 	device_t dev = (device_t) arg1;
128932a8088fSRui Paulo 	int error;
129032a8088fSRui Paulo 	int16_t val;
129132a8088fSRui Paulo 	int32_t v;
129232a8088fSRui Paulo 
129332a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_X, &val);
129432a8088fSRui Paulo 	v = (int32_t) val;
129532a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
129632a8088fSRui Paulo 
129732a8088fSRui Paulo 	return (error);
129832a8088fSRui Paulo }
129932a8088fSRui Paulo 
130032a8088fSRui Paulo static int
130132a8088fSRui Paulo asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS)
130232a8088fSRui Paulo {
130332a8088fSRui Paulo 	device_t dev = (device_t) arg1;
130432a8088fSRui Paulo 	int error;
130532a8088fSRui Paulo 	int16_t val;
130632a8088fSRui Paulo 	int32_t v;
130732a8088fSRui Paulo 
130832a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val);
130932a8088fSRui Paulo 	v = (int32_t) val;
131032a8088fSRui Paulo 	error = sysctl_handle_int(oidp, &v, 0, req);
131132a8088fSRui Paulo 
131232a8088fSRui Paulo 	return (error);
131332a8088fSRui Paulo }
131432a8088fSRui Paulo 
131532a8088fSRui Paulo static int
131632a8088fSRui Paulo asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS)
131732a8088fSRui Paulo {
131832a8088fSRui Paulo 	device_t dev = (device_t) arg1;
131932a8088fSRui Paulo 	int error;
132032a8088fSRui Paulo 	int16_t val;
132132a8088fSRui Paulo 	int32_t v;
132232a8088fSRui Paulo 
132332a8088fSRui Paulo 	asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val);
132432a8088fSRui Paulo 	v = (int32_t) val;
13250e1152fcSHans Petter Selasky 	error = sysctl_handle_int(oidp, &v, 0, req);
132632a8088fSRui Paulo 
132732a8088fSRui Paulo 	return (error);
132832a8088fSRui Paulo }
132932a8088fSRui Paulo 
133032a8088fSRui Paulo static int
133132a8088fSRui Paulo asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS)
133232a8088fSRui Paulo {
133332a8088fSRui Paulo 	device_t dev = (device_t) arg1;
133432a8088fSRui Paulo 	uint8_t buf[6];
133532a8088fSRui Paulo 	int error;
133632a8088fSRui Paulo 	int32_t v;
133732a8088fSRui Paulo 
1338447666f0SRui Paulo 	asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, sizeof buf);
133932a8088fSRui Paulo 	v = buf[2];
13400e1152fcSHans Petter Selasky 	error = sysctl_handle_int(oidp, &v, 0, req);
134132a8088fSRui Paulo 
134232a8088fSRui Paulo 	return (error);
134332a8088fSRui Paulo }
134432a8088fSRui Paulo 
134532a8088fSRui Paulo static int
134632a8088fSRui Paulo asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS)
134732a8088fSRui Paulo {
134832a8088fSRui Paulo 	device_t dev = (device_t) arg1;
134932a8088fSRui Paulo 	uint8_t buf[6];
135032a8088fSRui Paulo 	int error;
135132a8088fSRui Paulo 	int32_t v;
135232a8088fSRui Paulo 
1353447666f0SRui Paulo 	asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, sizeof buf);
135432a8088fSRui Paulo 	v = buf[2];
13550e1152fcSHans Petter Selasky 	error = sysctl_handle_int(oidp, &v, 0, req);
1356be80e49aSRui Paulo 
1357be80e49aSRui Paulo 	return (error);
1358be80e49aSRui Paulo }
1359be80e49aSRui Paulo 
1360be80e49aSRui Paulo static int
1361be80e49aSRui Paulo asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS)
1362be80e49aSRui Paulo {
1363be80e49aSRui Paulo 	device_t dev = (device_t) arg1;
1364be80e49aSRui Paulo 	uint8_t buf[2];
1365be80e49aSRui Paulo 	int error;
13660e1152fcSHans Petter Selasky 	int v;
1367be80e49aSRui Paulo 
1368108e3076SAdrian Chadd 	v = light_control;
13690e1152fcSHans Petter Selasky 	error = sysctl_handle_int(oidp, &v, 0, req);
13700e1152fcSHans Petter Selasky 
13710e1152fcSHans Petter Selasky 	if (error == 0 && req->newptr != NULL) {
13720e1152fcSHans Petter Selasky 		if (v < 0 || v > 255)
13730e1152fcSHans Petter Selasky 			return (EINVAL);
1374108e3076SAdrian Chadd 		light_control = v;
1375108e3076SAdrian Chadd 		buf[0] = light_control;
137632a8088fSRui Paulo 		buf[1] = 0x00;
1377447666f0SRui Paulo 		asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof buf);
137832a8088fSRui Paulo 	}
137932a8088fSRui Paulo 	return (error);
138032a8088fSRui Paulo }
1381