xref: /openbsd/sys/dev/acpi/asmc.c (revision 142d0649)
1*142d0649Skn /*	$OpenBSD: asmc.c,v 1.5 2022/10/20 16:08:13 kn Exp $	*/
2b81f331cSmglocker /*
3b81f331cSmglocker  * Copyright (c) 2015 Joerg Jung <jung@openbsd.org>
4b81f331cSmglocker  *
5b81f331cSmglocker  * Permission to use, copy, modify, and distribute this software for any
6b81f331cSmglocker  * purpose with or without fee is hereby granted, provided that the above
7b81f331cSmglocker  * copyright notice and this permission notice appear in all copies.
8b81f331cSmglocker  *
9b81f331cSmglocker  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10b81f331cSmglocker  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11b81f331cSmglocker  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12b81f331cSmglocker  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13b81f331cSmglocker  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14b81f331cSmglocker  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15b81f331cSmglocker  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16b81f331cSmglocker  */
17b81f331cSmglocker 
18b81f331cSmglocker /*
19b81f331cSmglocker  * Driver for Apple's System Management Controller (SMC) an H8S/2117 chip
20b81f331cSmglocker  */
21b81f331cSmglocker 
22b81f331cSmglocker #include <sys/param.h>
23b81f331cSmglocker #include <sys/systm.h>
24b81f331cSmglocker #include <sys/device.h>
25b81f331cSmglocker #include <sys/kernel.h>
26b81f331cSmglocker #include <sys/rwlock.h>
27b81f331cSmglocker #include <sys/task.h>
28b81f331cSmglocker #include <sys/sensors.h>
29b81f331cSmglocker 
30b81f331cSmglocker #include <machine/bus.h>
31b81f331cSmglocker 
32b81f331cSmglocker #include <dev/acpi/acpivar.h>
33b81f331cSmglocker #include <dev/acpi/acpidev.h>
34b81f331cSmglocker #include <dev/acpi/amltypes.h>
35b81f331cSmglocker #include <dev/acpi/dsdt.h>
36b81f331cSmglocker 
37b81f331cSmglocker #include <dev/wscons/wsconsio.h>
38b81f331cSmglocker 
39b81f331cSmglocker #define ASMC_DATA	0x00	/* SMC data port offset */
40b81f331cSmglocker #define ASMC_COMMAND	0x04	/* SMC command port offset */
41b81f331cSmglocker #define ASMC_STATUS	0x1e	/* SMC status port offset */
42b81f331cSmglocker #define ASMC_INTERRUPT	0x1f	/* SMC interrupt port offset */
43b81f331cSmglocker 
44b81f331cSmglocker #define ASMC_READ	0x10	/* SMC read command */
45b81f331cSmglocker #define ASMC_WRITE	0x11	/* SMC write command */
46b81f331cSmglocker #define ASMC_INFO	0x13	/* SMC info/type command */
47b81f331cSmglocker 
48b81f331cSmglocker #define ASMC_OBF	0x01	/* Output buffer full */
49b81f331cSmglocker #define ASMC_IBF	0x02	/* Input buffer full */
50b81f331cSmglocker #define ASMC_ACCEPT	0x04
51b81f331cSmglocker 
52b81f331cSmglocker #define ASMC_RETRY	3
53b81f331cSmglocker #define ASMC_MAXLEN	32	/* SMC maximum data size len */
54b81f331cSmglocker #define ASMC_NOTFOUND	0x84	/* SMC status key not found */
55b81f331cSmglocker 
56b81f331cSmglocker #define ASMC_MAXTEMP	101	/* known asmc_prods temperature sensor keys */
57b81f331cSmglocker #define ASMC_MAXFAN	10	/* fan keys with digits 0-9 */
58b81f331cSmglocker #define ASMC_MAXLIGHT	2	/* left and right light sensor */
59b81f331cSmglocker #define ASMC_MAXMOTION	3	/* x y z axis motion sensors */
60b81f331cSmglocker 
612dbff3f4Smglocker #define ASMC_DELAY_LOOP	200	/* ASMC_DELAY_LOOP * 10us = 2ms */
622dbff3f4Smglocker 
63b81f331cSmglocker struct asmc_prod {
64b81f331cSmglocker 	const char	*pr_name;
65b81f331cSmglocker 	uint8_t		 pr_light;
66b81f331cSmglocker 	const char	*pr_temp[ASMC_MAXTEMP];
67b81f331cSmglocker };
68b81f331cSmglocker 
69b81f331cSmglocker struct asmc_softc {
70b81f331cSmglocker 	struct device		 sc_dev;
71b81f331cSmglocker 
72b81f331cSmglocker 	struct acpi_softc       *sc_acpi;
73b81f331cSmglocker 	struct aml_node         *sc_devnode;
74b81f331cSmglocker 
75b81f331cSmglocker 	bus_space_tag_t		 sc_iot;
76b81f331cSmglocker 	bus_space_handle_t	 sc_ioh;
77b81f331cSmglocker 
78*142d0649Skn 	const struct asmc_prod	*sc_prod;
79b81f331cSmglocker 	uint8_t			 sc_nfans;	/* number of fans */
80b81f331cSmglocker 	uint8_t			 sc_lightlen;	/* light data len */
81b81f331cSmglocker 	uint8_t			 sc_backlight;	/* keyboard backlight value */
82b81f331cSmglocker 
83b81f331cSmglocker 	struct rwlock		 sc_lock;
84b81f331cSmglocker 	struct task		 sc_task_backlight;
85b81f331cSmglocker 
86b81f331cSmglocker 	struct ksensor		 sc_sensor_temp[ASMC_MAXTEMP];
87b81f331cSmglocker 	struct ksensor		 sc_sensor_fan[ASMC_MAXFAN];
88b81f331cSmglocker 	struct ksensor		 sc_sensor_light[ASMC_MAXLIGHT];
89b81f331cSmglocker 	struct ksensor		 sc_sensor_motion[ASMC_MAXMOTION];
90b81f331cSmglocker 	struct ksensordev	 sc_sensor_dev;
91b81f331cSmglocker 	struct sensor_task	*sc_sensor_task;
92b81f331cSmglocker };
93b81f331cSmglocker 
94b81f331cSmglocker int	asmc_try(struct asmc_softc *, int, const char *, uint8_t *, uint8_t);
95b81f331cSmglocker void	asmc_init(struct asmc_softc *);
96b81f331cSmglocker void	asmc_update(void *);
97b81f331cSmglocker 
98b81f331cSmglocker int	asmc_match(struct device *, void *, void *);
99b81f331cSmglocker void	asmc_attach(struct device *, struct device *, void *);
100b81f331cSmglocker int 	asmc_detach(struct device *, int);
101b81f331cSmglocker int	asmc_activate(struct device *, int);
102b81f331cSmglocker 
103b81f331cSmglocker /* wskbd hook functions */
104b81f331cSmglocker void	asmc_backlight(void *);
105b81f331cSmglocker int	asmc_get_backlight(struct wskbd_backlight *);
106b81f331cSmglocker int	asmc_set_backlight(struct wskbd_backlight *);
107b81f331cSmglocker extern int (*wskbd_get_backlight)(struct wskbd_backlight *);
108b81f331cSmglocker extern int (*wskbd_set_backlight)(struct wskbd_backlight *);
109b81f331cSmglocker 
110b81f331cSmglocker const struct cfattach asmc_ca = {
111b81f331cSmglocker 	sizeof(struct asmc_softc), asmc_match, asmc_attach, NULL, asmc_activate
112b81f331cSmglocker };
113b81f331cSmglocker 
114b81f331cSmglocker struct cfdriver asmc_cd = {
115b81f331cSmglocker 	NULL, "asmc", DV_DULL
116b81f331cSmglocker };
117b81f331cSmglocker 
118b81f331cSmglocker const char *asmc_hids[] = {
119b81f331cSmglocker 	"APP0001", NULL
120b81f331cSmglocker };
121b81f331cSmglocker 
122*142d0649Skn static const struct asmc_prod asmc_prods[] = {
123b81f331cSmglocker 	{ "MacBookAir", 1, {
124b81f331cSmglocker 		"TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TBXT", "TC0C", "TC0D",
125b81f331cSmglocker 		"TC0E", "TC0F", "TC0P", "TC1C", "TC1E", "TC2C", "TCFP", "TCGC",
126b81f331cSmglocker 		"TCHP", "TCMX", "TCSA", "TCXC", "TCZ3", "TCZ4", "TCZ5", "TG0E",
127b81f331cSmglocker 		"TG1E", "TG2E", "TGZ3", "TGZ4", "TGZ5", "TH0A", "TH0B", "TH0V",
128b81f331cSmglocker 		"TH0a", "TH0b", "THSP", "TM0P", "TN0D", "TPCD", "TS2P", "TTF0",
129b81f331cSmglocker 		"TV0P", "TVFP", "TW0P", "Ta0P", "Th0H", "Th0P", "Th1H", "Tm0P",
130b81f331cSmglocker 		"Tm1P", "Tp0P", "Tp1P", "TpFP", "Ts0P", "Ts0S", NULL }
131b81f331cSmglocker 	},
132b81f331cSmglocker 	{ "MacBookPro", 1, {
133b81f331cSmglocker 		"TA0P", "TA1P", "TALP", "TB0T", "TB1T", "TB2T", "TB3T", "TBXT",
134b81f331cSmglocker 		"TC0C", "TC0D", "TC0E", "TC0F", "TC0P", "TC1C", "TC2C", "TC3C",
135b81f331cSmglocker 		"TC4C", "TCGC", "TCSA", "TCXC", "TG0D", "TG0F", "TG0H", "TG0P",
136b81f331cSmglocker 		"TG0T", "TG1D", "TG1F", "TG1H", "TG1d", "TH0A", "TH0B", "TH0F",
137b81f331cSmglocker 		"TH0R", "TH0V", "TH0a", "TH0b", "TH0c", "TH0x", "THSP", "TM0P",
138b81f331cSmglocker 		"TM0S", "TMCD", "TN0D", "TN0P", "TN0S", "TN1D", "TN1F", "TN1G",
139b81f331cSmglocker 		"TN1S", "TP0P", "TPCD", "TTF0", "TW0P", "Ta0P", "TaSP", "Th0H",
140b81f331cSmglocker 		"Th1H", "Th2H", "Tm0P", "Ts0P", "Ts0S", "Ts1P", "Ts1S", NULL }
141b81f331cSmglocker 	},
142b81f331cSmglocker 	{ "MacBook", 0, {
143b81f331cSmglocker 		"TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0P", "TM0P", "TN0D",
144b81f331cSmglocker 		"TN0P", "TN1P", "TTF0", "TW0P", "Th0H", "Th0S", "Th1H", "ThFH",
145b81f331cSmglocker 		"Ts0P", "Ts0S", NULL }
146b81f331cSmglocker 	},
147b81f331cSmglocker 	{ "MacPro", 0, {
148b81f331cSmglocker 		"TA0P", "TC0C", "TC0D", "TC0P", "TC1C", "TC1D", "TC2C", "TC2D",
149b81f331cSmglocker 		"TC3C", "TC3D", "TCAC", "TCAD", "TCAG", "TCAH", "TCAS", "TCBC",
150b81f331cSmglocker 		"TCBD", "TCBG", "TCBH", "TCBS", "TH0P", "TH1F", "TH1P", "TH1V",
151b81f331cSmglocker 		"TH2F", "TH2P", "TH2V", "TH3F", "TH3P", "TH3V", "TH4F", "TH4P",
152b81f331cSmglocker 		"TH4V", "THPS", "THTG", "TM0P", "TM0S", "TM1P", "TM1S", "TM2P",
153b81f331cSmglocker 		"TM2S", "TM2V", "TM3P", "TM3S", "TM3V", "TM4P", "TM5P", "TM6P",
154b81f331cSmglocker 		"TM6V", "TM7P", "TM7V", "TM8P", "TM8S", "TM8V", "TM9P", "TM9S",
155b81f331cSmglocker 		"TM9V", "TMA1", "TMA2", "TMA3", "TMA4", "TMAP", "TMAS", "TMB1",
156b81f331cSmglocker 		"TMB2", "TMB3", "TMB4", "TMBS", "TMHS", "TMLS", "TMPS", "TMPV",
157b81f331cSmglocker 		"TMTG", "TN0C", "TN0D", "TN0H", "TNTG", "TS0C", "Te1F", "Te1P",
158b81f331cSmglocker 		"Te1S", "Te2F", "Te2S", "Te3F", "Te3S", "Te4F", "Te4S", "Te5F",
159b81f331cSmglocker 		"Te5S", "TeGG", "TeGP", "TeRG", "TeRP", "TeRV", "Tp0C", "Tp1C",
160b81f331cSmglocker 		"TpPS", "TpTG", "Tv0S", "Tv1S", NULL }
161b81f331cSmglocker 	},
162b81f331cSmglocker 	{ "MacMini", 0, {
163b81f331cSmglocker 		"TC0D", "TC0H", "TC0P", "TH0P", "TN0D", "TN0P", "TN1P", "TW0P",
164b81f331cSmglocker 		NULL }
165b81f331cSmglocker 	},
166b81f331cSmglocker 	{ "iMac", 0, {
167b81f331cSmglocker 		"TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P",
168b81f331cSmglocker 		"TL0P", "TN0D", "TN0H", "TN0P", "TO0P", "TW0P", "Tm0P", "Tp0C",
169b81f331cSmglocker 		"Tp0P", NULL }
170b81f331cSmglocker 	},
171b81f331cSmglocker 	{ NULL, 0, { NULL } }
172b81f331cSmglocker };
173b81f331cSmglocker 
174b81f331cSmglocker static const char *asmc_temp_desc[][2] = {
175b81f331cSmglocker 	{ "TA0P", "ambient" }, { "TA0P", "hdd bay 1" },
176b81f331cSmglocker 	{ "TA0S", "pci slot 1 pos 1" }, { "TA1P", "ambient 2" },
177b81f331cSmglocker 	{ "TA1S", "pci slot 1 pos 2" }, { "TA2S", "pci slot 2 pos 1" },
178b81f331cSmglocker 	{ "TA3S", "pci slot 2 pos 2" },
179b81f331cSmglocker 	{ "TB0T", "enclosure bottom" }, { "TB1T", "enclosure bottom 2" },
180b81f331cSmglocker 	{ "TB2T", "enclosure bottom 3" }, { "TB3T", "enclosure bottom 4" },
181b81f331cSmglocker 	{ "TC0D", "cpu0 die core" }, { "TC0H", "cpu0 heatsink" },
182b81f331cSmglocker 	{ "TC0P", "cpu0 proximity" },
183b81f331cSmglocker 	{ "TC1D", "cpu1" }, { "TC2D", "cpu2" }, { "TC3D", "cpu3" },
184b81f331cSmglocker 	{ "TCAH", "cpu0" }, { "TCBH", "cpu1" }, { "TCCH", "cpu2" },
185b81f331cSmglocker 	{ "TCDH", "cpu3" },
186b81f331cSmglocker 	{ "TG0D", "gpu0 diode" }, { "TG0H", "gpu0 heatsink" },
187b81f331cSmglocker 	{ "TG0P", "gpu0 proximity" },
188b81f331cSmglocker 	{ "TG1H", "gpu heatsink 2" },
189b81f331cSmglocker 	{ "TH0P", "hdd bay 1" }, { "TH1P", "hdd bay 2" },
190b81f331cSmglocker 	{ "TH2P", "hdd bay 3" }, { "TH3P", "hdd bay 4" },
191b81f331cSmglocker 	{ "TL0P", "lcd proximity"},
192b81f331cSmglocker 	{ "TM0P", "mem bank a1" }, { "TM0S", "mem module a1" },
193b81f331cSmglocker 	{ "TM1P", "mem bank a2" }, { "TM1S", "mem module a2" },
194b81f331cSmglocker 	{ "TM2P", "mem bank a3" }, { "TM2S", "mem module a3" },
195b81f331cSmglocker 	{ "TM3P", "mem bank a4" }, { "TM3S", "mem module a4" },
196b81f331cSmglocker 	{ "TM4P", "mem bank a5" }, { "TM4S", "mem module a5" },
197b81f331cSmglocker 	{ "TM5P", "mem bank a6" }, { "TM5S", "mem module a6" },
198b81f331cSmglocker 	{ "TM6P", "mem bank a7" }, { "TM6S", "mem module a7" },
199b81f331cSmglocker 	{ "TM7P", "mem bank a8" }, { "TM7S", "mem module a8" },
200b81f331cSmglocker 	{ "TM8P", "mem bank b1" }, { "TM8S", "mem module b1" },
201b81f331cSmglocker 	{ "TM9P", "mem bank b2" }, { "TM9S", "mem module b2" },
202b81f331cSmglocker 	{ "TMA1", "ram a1" }, { "TMA2", "ram a2" },
203b81f331cSmglocker 	{ "TMA3", "ram a3" }, { "TMA4", "ram a4" },
204b81f331cSmglocker 	{ "TMB1", "ram b1" }, { "TMB2", "ram b2" },
205b81f331cSmglocker 	{ "TMB3", "ram b3" }, { "TMB4", "ram b4" },
206b81f331cSmglocker 	{ "TMAP", "mem bank b3" }, { "TMAS", "mem module b3" },
207b81f331cSmglocker 	{ "TMBP", "mem bank b4" }, { "TMBS", "mem module b4" },
208b81f331cSmglocker 	{ "TMCP", "mem bank b5" }, { "TMCS", "mem module b5" },
209b81f331cSmglocker 	{ "TMDP", "mem bank b6" }, { "TMDS", "mem module b6" },
210b81f331cSmglocker 	{ "TMEP", "mem bank b7" }, { "TMES", "mem module b7" },
211b81f331cSmglocker 	{ "TMFP", "mem bank b8" }, { "TMFS", "mem module b8" },
212b81f331cSmglocker 	{ "TN0D", "northbridge die core" }, { "TN0H", "northbridge" },
213b81f331cSmglocker 	{ "TN0P", "northbridge proximity" }, { "TN1P", "northbridge 2" },
214b81f331cSmglocker 	{ "TO0P", "optical drive" }, { "TS0C", "expansion slots" },
215b81f331cSmglocker 	{ "TW0P", "wireless airport card" },
216b81f331cSmglocker 	{ "Th0H", "main heatsink a" }, { "Th1H", "main heatsink b" },
217b81f331cSmglocker 	{ "Th2H", "main heatsink c" },
218b81f331cSmglocker 	{ "Tm0P", "memory controller" },
219b81f331cSmglocker 	{ "Tp0C", "power supply 1" }, { "Tp0P", "power supply 1" },
220b81f331cSmglocker 	{ "Tp1C", "power supply 2" }, { "Tp1P", "power supply 2" },
221b81f331cSmglocker 	{ "Tp2P", "power supply 3" }, { "Tp3P", "power supply 4" },
222b81f331cSmglocker 	{ "Tp4P", "power supply 5" }, { "Tp5P", "power supply 6" },
223b81f331cSmglocker 	{ NULL, NULL }
224b81f331cSmglocker };
225b81f331cSmglocker 
226b81f331cSmglocker static const char *asmc_fan_loc[] = {
227b81f331cSmglocker 	"left lower front", "center lower front", "right lower front",
228b81f331cSmglocker 	"left mid front",   "center mid front",   "right mid front",
229b81f331cSmglocker 	"left upper front", "center upper front", "right upper front",
230b81f331cSmglocker 	"left lower rear",  "center lower rear",  "right lower rear",
231b81f331cSmglocker 	"left mid rear",    "center mid rear",    "right mid rear",
232b81f331cSmglocker 	"left upper rear",  "center upper rear",  "right upper rear"
233b81f331cSmglocker };
234b81f331cSmglocker 
235b81f331cSmglocker static const char *asmc_light_desc[ASMC_MAXLIGHT] = {
236b81f331cSmglocker 	"left", "right"
237b81f331cSmglocker };
238b81f331cSmglocker 
239b81f331cSmglocker int
asmc_match(struct device * parent,void * match,void * aux)240b81f331cSmglocker asmc_match(struct device *parent, void *match, void *aux)
241b81f331cSmglocker {
242b81f331cSmglocker 	struct acpi_attach_args *aa = aux;
243b81f331cSmglocker 	struct cfdata *cf = match;
244b81f331cSmglocker 
24557ec0946Skettenis 	if (aa->aaa_naddr < 1)
24657ec0946Skettenis 		return 0;
247b81f331cSmglocker 	return acpi_matchhids(aa, asmc_hids, cf->cf_driver->cd_name);
248b81f331cSmglocker }
249b81f331cSmglocker 
250b81f331cSmglocker void
asmc_attach(struct device * parent,struct device * self,void * aux)251b81f331cSmglocker asmc_attach(struct device *parent, struct device *self, void *aux)
252b81f331cSmglocker {
253b81f331cSmglocker 	struct asmc_softc *sc = (struct asmc_softc *)self;
254b81f331cSmglocker 	struct acpi_attach_args *aaa = aux;
255b81f331cSmglocker 	struct aml_value res;
256b81f331cSmglocker 	int64_t sta;
257b81f331cSmglocker 	uint8_t buf[6];
258b81f331cSmglocker 	int i, r;
259b81f331cSmglocker 
260b81f331cSmglocker 	if (!hw_vendor || !hw_prod || strncmp(hw_vendor, "Apple", 5))
261b81f331cSmglocker 		return;
262b81f331cSmglocker 
263b81f331cSmglocker 	for (i = 0; asmc_prods[i].pr_name && !sc->sc_prod; i++)
264b81f331cSmglocker 		if (!strncasecmp(asmc_prods[i].pr_name, hw_prod,
265b81f331cSmglocker 		    strlen(asmc_prods[i].pr_name)))
266b81f331cSmglocker 			sc->sc_prod = &asmc_prods[i];
267b81f331cSmglocker 	if (!sc->sc_prod)
268b81f331cSmglocker 		return;
269b81f331cSmglocker 
270b81f331cSmglocker 	sc->sc_acpi = (struct acpi_softc *)parent;
271b81f331cSmglocker 	sc->sc_devnode = aaa->aaa_node;
272b81f331cSmglocker 
273b81f331cSmglocker 	printf(": %s", sc->sc_devnode->name);
274b81f331cSmglocker 
275b81f331cSmglocker 	sta = acpi_getsta(sc->sc_acpi, sc->sc_devnode);
276b81f331cSmglocker 	if ((sta & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) !=
277b81f331cSmglocker 	    (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) {
278b81f331cSmglocker 		printf(": not enabled\n");
279b81f331cSmglocker 		return;
280b81f331cSmglocker 	}
281b81f331cSmglocker 
282b81f331cSmglocker 	if (!(aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CID", 0, NULL, &res)))
283b81f331cSmglocker 		printf(" (%s)", res.v_string);
284b81f331cSmglocker 
285b81f331cSmglocker 	printf (" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]);
286b81f331cSmglocker 
287b81f331cSmglocker 	sc->sc_iot = aaa->aaa_bst[0];
288b81f331cSmglocker 	if (bus_space_map(sc->sc_iot, aaa->aaa_addr[0], aaa->aaa_size[0], 0,
289b81f331cSmglocker 	    &sc->sc_ioh)) {
290b81f331cSmglocker 		printf(": can't map registers\n");
291b81f331cSmglocker 		return;
292b81f331cSmglocker 	}
293b81f331cSmglocker 
294b81f331cSmglocker 	rw_init(&sc->sc_lock, sc->sc_dev.dv_xname);
295b81f331cSmglocker 
296b81f331cSmglocker 	if ((r = asmc_try(sc, ASMC_READ, "REV ", buf, 6))) {
297b81f331cSmglocker 		printf(": revision failed (0x%x)\n", r);
298b81f331cSmglocker 		bus_space_unmap(sc->sc_iot, aaa->aaa_addr[0], aaa->aaa_size[0]);
299b81f331cSmglocker 		return;
300b81f331cSmglocker 	}
301b81f331cSmglocker 	printf(": rev %x.%x%x%x", buf[0], buf[1], buf[2],
302b81f331cSmglocker 	    ntohs(*(uint16_t *)buf + 4));
303b81f331cSmglocker 
304b81f331cSmglocker 	if ((r = asmc_try(sc, ASMC_READ, "#KEY", buf, 4))) {
305b81f331cSmglocker 		printf(", no of keys failed (0x%x)\n", r);
306b81f331cSmglocker 		bus_space_unmap(sc->sc_iot, aaa->aaa_addr[0], aaa->aaa_size[0]);
307b81f331cSmglocker 		return;
308b81f331cSmglocker 	}
309b81f331cSmglocker 	printf(", %u key%s\n", ntohl(*(uint32_t *)buf),
310b81f331cSmglocker 	    (ntohl(*(uint32_t *)buf) == 1) ? "" : "s");
311b81f331cSmglocker 
312b81f331cSmglocker 	/* keyboard backlight led is optional */
313b81f331cSmglocker 	sc->sc_backlight = buf[0] = 127, buf[1] = 0;
314b81f331cSmglocker 	if ((r = asmc_try(sc, ASMC_WRITE, "LKSB", buf, 2))) {
315b81f331cSmglocker 		if (r != ASMC_NOTFOUND)
316b81f331cSmglocker 			printf("%s: keyboard backlight failed (0x%x)\n",
317b81f331cSmglocker 			    sc->sc_dev.dv_xname, r);
318b81f331cSmglocker 	} else {
319b81f331cSmglocker 		wskbd_get_backlight = asmc_get_backlight;
320b81f331cSmglocker 		wskbd_set_backlight = asmc_set_backlight;
321b81f331cSmglocker 	}
322b81f331cSmglocker 	task_set(&sc->sc_task_backlight, asmc_backlight, sc);
323b81f331cSmglocker 
324b81f331cSmglocker 	strlcpy(sc->sc_sensor_dev.xname, sc->sc_dev.dv_xname,
325b81f331cSmglocker 	    sizeof(sc->sc_sensor_dev.xname));
326b81f331cSmglocker 	for (i = 0; i < ASMC_MAXTEMP; i++) {
327b81f331cSmglocker 		sc->sc_sensor_temp[i].flags |= SENSOR_FINVALID;
328b81f331cSmglocker 		sc->sc_sensor_temp[i].flags |= SENSOR_FUNKNOWN;
329b81f331cSmglocker 	}
330b81f331cSmglocker 	for (i = 0; i < ASMC_MAXFAN; i++) {
331b81f331cSmglocker 		sc->sc_sensor_fan[i].flags |= SENSOR_FINVALID;
332b81f331cSmglocker 		sc->sc_sensor_fan[i].flags |= SENSOR_FUNKNOWN;
333b81f331cSmglocker 	}
334b81f331cSmglocker 	for (i = 0; i < ASMC_MAXLIGHT; i++) {
335b81f331cSmglocker 		sc->sc_sensor_light[i].flags |= SENSOR_FINVALID;
336b81f331cSmglocker 		sc->sc_sensor_light[i].flags |= SENSOR_FUNKNOWN;
337b81f331cSmglocker 	}
338b81f331cSmglocker 	for (i = 0; i < ASMC_MAXMOTION; i++) {
339b81f331cSmglocker 		sc->sc_sensor_motion[i].flags |= SENSOR_FINVALID;
340b81f331cSmglocker 		sc->sc_sensor_motion[i].flags |= SENSOR_FUNKNOWN;
341b81f331cSmglocker 	}
342b81f331cSmglocker 	asmc_init(sc);
343b81f331cSmglocker 
344b81f331cSmglocker 	if (!(sc->sc_sensor_task = sensor_task_register(sc, asmc_update, 5))) {
345b81f331cSmglocker 		printf("%s: unable to register task\n", sc->sc_dev.dv_xname);
346b81f331cSmglocker 		bus_space_unmap(sc->sc_iot, aaa->aaa_addr[0], aaa->aaa_size[0]);
347b81f331cSmglocker 		return;
348b81f331cSmglocker 	}
349b81f331cSmglocker 	sensordev_install(&sc->sc_sensor_dev);
350b81f331cSmglocker }
351b81f331cSmglocker 
352b81f331cSmglocker int
asmc_detach(struct device * self,int flags)353b81f331cSmglocker asmc_detach(struct device *self, int flags)
354b81f331cSmglocker {
355b81f331cSmglocker 	struct asmc_softc *sc = (struct asmc_softc *)self;
356b81f331cSmglocker 	uint8_t buf[2] = { (sc->sc_backlight = 0), 0 };
357b81f331cSmglocker 	int i;
358b81f331cSmglocker 
359b81f331cSmglocker 	if (sc->sc_sensor_task) {
360b81f331cSmglocker 		sensor_task_unregister(sc->sc_sensor_task);
361b81f331cSmglocker 		sc->sc_sensor_task = NULL;
362b81f331cSmglocker 	}
363b81f331cSmglocker 	sensordev_deinstall(&sc->sc_sensor_dev);
364b81f331cSmglocker 	for (i = 0; i < ASMC_MAXMOTION; i++)
365b81f331cSmglocker 		sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_motion[i]);
366b81f331cSmglocker 	for (i = 0; i < ASMC_MAXLIGHT; i++)
367b81f331cSmglocker 		sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_light[i]);
368b81f331cSmglocker 	for (i = 0; i < ASMC_MAXFAN; i++)
369b81f331cSmglocker 		sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_fan[i]);
370b81f331cSmglocker 	for (i = 0; i < ASMC_MAXTEMP; i++)
371b81f331cSmglocker 		sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_temp[i]);
372b81f331cSmglocker 
373b81f331cSmglocker 	task_del(systq, &sc->sc_task_backlight);
374b81f331cSmglocker 	asmc_try(sc, ASMC_WRITE, "LKSB", buf, 2);
375b81f331cSmglocker 	return 0;
376b81f331cSmglocker }
377b81f331cSmglocker 
378b81f331cSmglocker int
asmc_activate(struct device * self,int act)379b81f331cSmglocker asmc_activate(struct device *self, int act)
380b81f331cSmglocker {
381b81f331cSmglocker 	struct asmc_softc *sc = (struct asmc_softc *)self;
382b81f331cSmglocker 
383b81f331cSmglocker 	switch (act) {
384b81f331cSmglocker 	case DVACT_WAKEUP:
385b81f331cSmglocker 		asmc_backlight(sc);
386b81f331cSmglocker 		break;
387b81f331cSmglocker 	}
388b81f331cSmglocker 
389b81f331cSmglocker 	return 0;
390b81f331cSmglocker }
391b81f331cSmglocker 
392b81f331cSmglocker void
asmc_backlight(void * arg)393b81f331cSmglocker asmc_backlight(void *arg)
394b81f331cSmglocker {
395b81f331cSmglocker 	struct asmc_softc *sc = arg;
396b81f331cSmglocker 	uint8_t buf[2] = { sc->sc_backlight, 0 };
397b81f331cSmglocker 	int r;
398b81f331cSmglocker 
399b81f331cSmglocker 	if ((r = asmc_try(sc, ASMC_WRITE, "LKSB", buf, 2)))
400b81f331cSmglocker 		printf("%s: keyboard backlight failed (0x%x)\n",
401b81f331cSmglocker 		    sc->sc_dev.dv_xname, r);
402b81f331cSmglocker }
403b81f331cSmglocker 
404b81f331cSmglocker int
asmc_get_backlight(struct wskbd_backlight * kbl)405b81f331cSmglocker asmc_get_backlight(struct wskbd_backlight *kbl)
406b81f331cSmglocker {
407b81f331cSmglocker 	struct asmc_softc *sc = asmc_cd.cd_devs[0];
408b81f331cSmglocker 
409b81f331cSmglocker 	KASSERT(sc != NULL);
410b81f331cSmglocker 	kbl->min = 0;
411b81f331cSmglocker 	kbl->max = 0xff;
412b81f331cSmglocker 	kbl->curval = sc->sc_backlight;
413b81f331cSmglocker 	return 0;
414b81f331cSmglocker }
415b81f331cSmglocker 
416b81f331cSmglocker int
asmc_set_backlight(struct wskbd_backlight * kbl)417b81f331cSmglocker asmc_set_backlight(struct wskbd_backlight *kbl)
418b81f331cSmglocker {
419b81f331cSmglocker 	struct asmc_softc *sc = asmc_cd.cd_devs[0];
420b81f331cSmglocker 
421b81f331cSmglocker 	KASSERT(sc != NULL);
422b81f331cSmglocker 	if (kbl->curval > 0xff)
423b81f331cSmglocker 		return EINVAL;
424b81f331cSmglocker 	sc->sc_backlight = kbl->curval;
425b81f331cSmglocker 	task_add(systq, &sc->sc_task_backlight);
426b81f331cSmglocker 	return 0;
427b81f331cSmglocker }
428b81f331cSmglocker 
429b81f331cSmglocker static uint8_t
asmc_status(struct asmc_softc * sc)430b81f331cSmglocker asmc_status(struct asmc_softc *sc)
431b81f331cSmglocker {
432b81f331cSmglocker 	return bus_space_read_1(sc->sc_iot, sc->sc_ioh, ASMC_STATUS);
433b81f331cSmglocker }
434b81f331cSmglocker 
435b81f331cSmglocker static int
asmc_write(struct asmc_softc * sc,uint8_t off,uint8_t val)436b81f331cSmglocker asmc_write(struct asmc_softc *sc, uint8_t off, uint8_t val)
437b81f331cSmglocker {
4382dbff3f4Smglocker 	int i;
4392dbff3f4Smglocker 	uint8_t status;
4402dbff3f4Smglocker 
441b81f331cSmglocker 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, off, val);
4422dbff3f4Smglocker 	for (i = 0; i < ASMC_DELAY_LOOP; i++) {
4432dbff3f4Smglocker 		status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ASMC_COMMAND);
4442dbff3f4Smglocker 		if (status & ASMC_IBF)
4452dbff3f4Smglocker 			continue;
4462dbff3f4Smglocker 		if (status & ASMC_ACCEPT)
447b81f331cSmglocker 			return 0;
4482dbff3f4Smglocker 		delay(10);
4492dbff3f4Smglocker 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, off, val);
4502dbff3f4Smglocker 	}
4512dbff3f4Smglocker 
4522dbff3f4Smglocker 	return ETIMEDOUT;
453b81f331cSmglocker }
454b81f331cSmglocker 
455b81f331cSmglocker static int
asmc_read(struct asmc_softc * sc,uint8_t off,uint8_t * buf)456b81f331cSmglocker asmc_read(struct asmc_softc *sc, uint8_t off, uint8_t *buf)
457b81f331cSmglocker {
4582dbff3f4Smglocker 	int i;
4592dbff3f4Smglocker 	uint8_t status;
4602dbff3f4Smglocker 
4612dbff3f4Smglocker 	for (i = 0; i < ASMC_DELAY_LOOP; i++) {
4622dbff3f4Smglocker 		status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ASMC_COMMAND);
4632dbff3f4Smglocker 		if (status & ASMC_OBF)
4642dbff3f4Smglocker 			break;
4652dbff3f4Smglocker 		delay(10);
4662dbff3f4Smglocker 	}
4672dbff3f4Smglocker 	if (i == ASMC_DELAY_LOOP)
4682dbff3f4Smglocker 		return ETIMEDOUT;
469b81f331cSmglocker 	*buf = bus_space_read_1(sc->sc_iot, sc->sc_ioh, off);
4702dbff3f4Smglocker 
471b81f331cSmglocker 	return 0;
472b81f331cSmglocker }
473b81f331cSmglocker 
474b81f331cSmglocker static int
asmc_command(struct asmc_softc * sc,int cmd,const char * key,uint8_t * buf,uint8_t len)475b81f331cSmglocker asmc_command(struct asmc_softc *sc, int cmd, const char *key, uint8_t *buf,
476b81f331cSmglocker     uint8_t len)
477b81f331cSmglocker {
478b81f331cSmglocker 	int i;
479b81f331cSmglocker 
480b81f331cSmglocker 	if (len > ASMC_MAXLEN)
481b81f331cSmglocker 		return 1;
482b81f331cSmglocker 	if (asmc_write(sc, ASMC_COMMAND, cmd))
483b81f331cSmglocker 		return 1;
484b81f331cSmglocker 	for (i = 0; i < 4; i++)
485b81f331cSmglocker 		if (asmc_write(sc, ASMC_DATA, key[i]))
486b81f331cSmglocker 			return 1;
487b81f331cSmglocker 	if (asmc_write(sc, ASMC_DATA, len))
488b81f331cSmglocker 		return 1;
489b81f331cSmglocker 	if (cmd == ASMC_READ || cmd == ASMC_INFO) {
490b81f331cSmglocker 		for (i = 0; i < len; i++)
491b81f331cSmglocker 			if (asmc_read(sc, ASMC_DATA, &buf[i]))
492b81f331cSmglocker 				return 1;
493b81f331cSmglocker 	} else if (cmd == ASMC_WRITE) {
494b81f331cSmglocker 		for (i = 0; i < len; i++)
495b81f331cSmglocker 			if (asmc_write(sc, ASMC_DATA, buf[i]))
496b81f331cSmglocker 				return 1;
497b81f331cSmglocker 	} else
498b81f331cSmglocker 		return 1;
499b81f331cSmglocker 	return 0;
500b81f331cSmglocker }
501b81f331cSmglocker 
502b81f331cSmglocker int
asmc_try(struct asmc_softc * sc,int cmd,const char * key,uint8_t * buf,uint8_t len)503b81f331cSmglocker asmc_try(struct asmc_softc *sc, int cmd, const char *key, uint8_t *buf,
504b81f331cSmglocker     uint8_t len)
505b81f331cSmglocker {
506b81f331cSmglocker 	uint8_t s;
507b81f331cSmglocker 	int i, r;
508b81f331cSmglocker 
509b81f331cSmglocker 	rw_enter_write(&sc->sc_lock);
510b81f331cSmglocker 	for (i = 0; i < ASMC_RETRY; i++)
511b81f331cSmglocker 		if (!(r = asmc_command(sc, cmd, key, buf, len)))
512b81f331cSmglocker 			break;
513b81f331cSmglocker 	if (r && (s = asmc_status(sc)))
514b81f331cSmglocker 		r = s;
515b81f331cSmglocker 	rw_exit_write(&sc->sc_lock);
516b81f331cSmglocker 
517b81f331cSmglocker 	return r;
518b81f331cSmglocker }
519b81f331cSmglocker 
520b81f331cSmglocker static uint32_t
asmc_uk(uint8_t * buf)521b81f331cSmglocker asmc_uk(uint8_t *buf)
522b81f331cSmglocker {
523b81f331cSmglocker 	/* spe78: floating point, signed, 7 bits exponent, 8 bits fraction */
524b81f331cSmglocker 	return (((int16_t)ntohs(*(uint16_t *)buf)) >> 8) * 1000000 + 273150000;
525b81f331cSmglocker }
526b81f331cSmglocker 
527b81f331cSmglocker static uint16_t
asmc_rpm(uint8_t * buf)528b81f331cSmglocker asmc_rpm(uint8_t *buf)
529b81f331cSmglocker {
530b81f331cSmglocker 	/* fpe2: floating point, unsigned, 14 bits exponent, 2 bits fraction */
531b81f331cSmglocker 	return ntohs(*(uint16_t *)buf) >> 2;
532b81f331cSmglocker }
533b81f331cSmglocker 
534b81f331cSmglocker static uint32_t
asmc_lux(uint8_t * buf,uint8_t lightlen)535b81f331cSmglocker asmc_lux(uint8_t *buf, uint8_t lightlen)
536b81f331cSmglocker {
537b81f331cSmglocker 	/* newer macbooks report a 10 bit big endian value */
538b81f331cSmglocker 	return (lightlen == 10) ?
539b81f331cSmglocker 	    /* fp18.14: floating point, 18 bits exponent, 14 bits fraction */
540b81f331cSmglocker 	    (ntohl(*(uint32_t *)(buf + 6)) >> 14) * 1000000 :
541b81f331cSmglocker 	    /*
542b81f331cSmglocker 	     * todo: calculate lux from ADC raw data
543b81f331cSmglocker 	     * buf[1] true/false for high/low gain chan reads
544b81f331cSmglocker 	     * chan 0: ntohs(*(uint16_t *)(buf + 2));
545b81f331cSmglocker 	     * chan 1: ntohs(*(uint16_t *)(buf + 4));
546b81f331cSmglocker 	     */
547b81f331cSmglocker 	    ntohs(*(uint16_t *)(buf + 2)) * 1000000;
548b81f331cSmglocker }
549b81f331cSmglocker 
550b81f331cSmglocker static int
asmc_temp(struct asmc_softc * sc,uint8_t idx,int init)551b81f331cSmglocker asmc_temp(struct asmc_softc *sc, uint8_t idx, int init)
552b81f331cSmglocker {
553b81f331cSmglocker 	uint8_t buf[2];
554b81f331cSmglocker 	uint32_t uk;
555b81f331cSmglocker 	int i, r;
556b81f331cSmglocker 
557b81f331cSmglocker 	if ((r = asmc_try(sc, ASMC_READ, sc->sc_prod->pr_temp[idx], buf, 2)))
558b81f331cSmglocker 		return r;
559b81f331cSmglocker 	if ((uk = asmc_uk(buf)) < 253150000) /* ignore unlikely values */
560b81f331cSmglocker 		return 0;
561b81f331cSmglocker 	sc->sc_sensor_temp[idx].value = uk;
562b81f331cSmglocker 	sc->sc_sensor_temp[idx].flags &= ~SENSOR_FUNKNOWN;
563b81f331cSmglocker 
564b81f331cSmglocker 	if (!init)
565b81f331cSmglocker 		return 0;
566b81f331cSmglocker 
567b81f331cSmglocker 	strlcpy(sc->sc_sensor_temp[idx].desc, sc->sc_prod->pr_temp[idx],
568b81f331cSmglocker 	    sizeof(sc->sc_sensor_temp[idx].desc));
569b81f331cSmglocker 	for (i = 0; asmc_temp_desc[i][0]; i++)
570b81f331cSmglocker 		if (!strcmp(asmc_temp_desc[i][0], sc->sc_prod->pr_temp[idx]))
571b81f331cSmglocker 			break;
572b81f331cSmglocker 	if (asmc_temp_desc[i][0]) {
573b81f331cSmglocker 		strlcat(sc->sc_sensor_temp[idx].desc, " ",
574b81f331cSmglocker 		    sizeof(sc->sc_sensor_temp[idx].desc));
575b81f331cSmglocker 		strlcat(sc->sc_sensor_temp[idx].desc, asmc_temp_desc[i][1],
576b81f331cSmglocker 		    sizeof(sc->sc_sensor_temp[idx].desc));
577b81f331cSmglocker 	}
578b81f331cSmglocker 	sc->sc_sensor_temp[idx].type = SENSOR_TEMP;
579b81f331cSmglocker 	sc->sc_sensor_temp[idx].flags &= ~SENSOR_FINVALID;
580b81f331cSmglocker 	sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_temp[idx]);
581b81f331cSmglocker 	return 0;
582b81f331cSmglocker }
583b81f331cSmglocker 
584b81f331cSmglocker static int
asmc_fan(struct asmc_softc * sc,uint8_t idx,int init)585b81f331cSmglocker asmc_fan(struct asmc_softc *sc, uint8_t idx, int init)
586b81f331cSmglocker {
587b81f331cSmglocker 	char key[5];
588b81f331cSmglocker 	uint8_t buf[17], *end;
589b81f331cSmglocker 	int r;
590b81f331cSmglocker 
591b81f331cSmglocker 	snprintf(key, sizeof(key), "F%dAc", idx);
592b81f331cSmglocker 	if ((r = asmc_try(sc, ASMC_READ, key, buf, 2)))
593b81f331cSmglocker 		return r;
594b81f331cSmglocker 	sc->sc_sensor_fan[idx].value = asmc_rpm(buf);
595b81f331cSmglocker 	sc->sc_sensor_fan[idx].flags &= ~SENSOR_FUNKNOWN;
596b81f331cSmglocker 
597b81f331cSmglocker 	if (!init)
598b81f331cSmglocker 		return 0;
599b81f331cSmglocker 
600b81f331cSmglocker 	snprintf(key, sizeof(key), "F%dID", idx);
601b81f331cSmglocker 	if ((r = asmc_try(sc, ASMC_READ, key, buf, 16)))
602b81f331cSmglocker 		return r;
603b81f331cSmglocker 	buf[16] = '\0';
604b81f331cSmglocker 	end = buf + 4 + strlen((char *)buf + 4) - 1;
605b81f331cSmglocker 	while (buf + 4 < end && *end == ' ') /* trim trailing spaces */
606b81f331cSmglocker 		*end-- = '\0';
607b81f331cSmglocker 	strlcpy(sc->sc_sensor_fan[idx].desc, buf + 4,
608b81f331cSmglocker 	    sizeof(sc->sc_sensor_fan[idx].desc));
609b81f331cSmglocker 	if (buf[2] < nitems(asmc_fan_loc)) {
610b81f331cSmglocker 		strlcat(sc->sc_sensor_fan[idx].desc, ", ",
611b81f331cSmglocker 		    sizeof(sc->sc_sensor_fan[idx].desc));
612b81f331cSmglocker 		strlcat(sc->sc_sensor_fan[idx].desc, asmc_fan_loc[buf[2]],
613b81f331cSmglocker 		    sizeof(sc->sc_sensor_fan[idx].desc));
614b81f331cSmglocker 	}
615b81f331cSmglocker 	sc->sc_sensor_fan[idx].type = SENSOR_FANRPM;
616b81f331cSmglocker 	sc->sc_sensor_fan[idx].flags &= ~SENSOR_FINVALID;
617b81f331cSmglocker 	sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_fan[idx]);
618b81f331cSmglocker 	return 0;
619b81f331cSmglocker }
620b81f331cSmglocker 
621b81f331cSmglocker static int
asmc_light(struct asmc_softc * sc,uint8_t idx,int init)622b81f331cSmglocker asmc_light(struct asmc_softc *sc, uint8_t idx, int init)
623b81f331cSmglocker {
624b81f331cSmglocker 	char key[5];
625b81f331cSmglocker 	uint8_t buf[10];
626b81f331cSmglocker 	int r;
627b81f331cSmglocker 
628b81f331cSmglocker 	snprintf(key, sizeof(key), "ALV%d", idx);
629b81f331cSmglocker 	if (!sc->sc_lightlen) {
630b81f331cSmglocker 		if ((r = asmc_try(sc, ASMC_INFO, key, buf, 6)))
631b81f331cSmglocker 			return r;
632b81f331cSmglocker 		if ((sc->sc_lightlen = buf[0]) > 10)
633b81f331cSmglocker 			return 1;
634b81f331cSmglocker 	}
635b81f331cSmglocker 	if ((r = asmc_try(sc, ASMC_READ, key, buf, sc->sc_lightlen)))
636b81f331cSmglocker 		return r;
637b81f331cSmglocker 	if (!buf[0]) /* valid data? */
638b81f331cSmglocker 		return 0;
639b81f331cSmglocker 	sc->sc_sensor_light[idx].value = asmc_lux(buf, sc->sc_lightlen);
640b81f331cSmglocker 	sc->sc_sensor_light[idx].flags &= ~SENSOR_FUNKNOWN;
641b81f331cSmglocker 
642b81f331cSmglocker 	if (!init)
643b81f331cSmglocker 		return 0;
644b81f331cSmglocker 
645b81f331cSmglocker 	strlcpy(sc->sc_sensor_light[idx].desc, asmc_light_desc[idx],
646b81f331cSmglocker 	    sizeof(sc->sc_sensor_light[idx].desc));
647b81f331cSmglocker 	sc->sc_sensor_light[idx].type = SENSOR_LUX;
648b81f331cSmglocker 	sc->sc_sensor_light[idx].flags &= ~SENSOR_FINVALID;
649b81f331cSmglocker 	sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_light[idx]);
650b81f331cSmglocker 	return 0;
651b81f331cSmglocker }
652b81f331cSmglocker 
653b81f331cSmglocker #if 0 /* todo: implement motion sensors update and initialization */
654b81f331cSmglocker static int
655b81f331cSmglocker asmc_motion(struct asmc_softc *sc, uint8_t idx, int init)
656b81f331cSmglocker {
657b81f331cSmglocker 	char key[5];
658b81f331cSmglocker 	uint8_t buf[2];
659b81f331cSmglocker 	int r;
660b81f331cSmglocker 
661b81f331cSmglocker 	snprintf(key, sizeof(key), "MO_%c", 88 + idx); /* X, Y, Z */
662b81f331cSmglocker 	if ((r = asmc_try(sc, ASMC_READ, key, buf, 2)))
663b81f331cSmglocker 		return r;
664b81f331cSmglocker 	sc->sc_sensor_motion[idx].value = 0;
665b81f331cSmglocker 	sc->sc_sensor_motion[idx].flags &= ~SENSOR_FUNKNOWN;
666b81f331cSmglocker 
667b81f331cSmglocker 	if (!init)
668b81f331cSmglocker 		return 0;
669b81f331cSmglocker 
670b81f331cSmglocker 	/* todo: setup and attach sensors and description */
671b81f331cSmglocker 	strlcpy(sc->sc_sensor_motion[idx].desc, 120 + idx, /* x, y, z */
672b81f331cSmglocker 	    sizeof(sc->sc_sensor_motion[idx].desc));
673b81f331cSmglocker 	strlcat(sc->sc_sensor_motion[idx].desc, "-axis",
674b81f331cSmglocker 	    sizeof(sc->sc_sensor_motion[idx].desc));
675b81f331cSmglocker 	sc->sc_sensor_motion[idx].type = SENSOR_ACCEL;
676b81f331cSmglocker 	sc->sc_sensor_motion[idx].flags &= ~SENSOR_FINVALID;
677b81f331cSmglocker 	sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_motion[idx]);
678b81f331cSmglocker 	return 0;
679b81f331cSmglocker }
680b81f331cSmglocker #endif
681b81f331cSmglocker 
682b81f331cSmglocker void
asmc_init(struct asmc_softc * sc)683b81f331cSmglocker asmc_init(struct asmc_softc *sc)
684b81f331cSmglocker {
685b81f331cSmglocker 	uint8_t buf[2];
686b81f331cSmglocker 	int i, r;
687b81f331cSmglocker 
688b81f331cSmglocker 	/* number of temperature sensors depends on product */
689b81f331cSmglocker 	for (i = 0; i < ASMC_MAXTEMP && sc->sc_prod->pr_temp[i]; i++)
690b81f331cSmglocker 		if ((r = asmc_temp(sc, i, 1)) && r != ASMC_NOTFOUND)
691b81f331cSmglocker 			printf("%s: read temp %d failed (0x%x)\n",
692b81f331cSmglocker 			    sc->sc_dev.dv_xname, i, r);
693b81f331cSmglocker 	/* number of fan sensors depends on product */
694b81f331cSmglocker 	if ((r = asmc_try(sc, ASMC_READ, "FNum", buf, 1)))
695b81f331cSmglocker 		printf("%s: read FNum failed (0x%x)\n",
696b81f331cSmglocker 		    sc->sc_dev.dv_xname, r);
697b81f331cSmglocker 	else
698b81f331cSmglocker 		sc->sc_nfans = buf[0];
699b81f331cSmglocker 	for (i = 0; i < sc->sc_nfans && i < ASMC_MAXFAN; i++)
700b81f331cSmglocker 		if ((r = asmc_fan(sc, i, 1)) && r != ASMC_NOTFOUND)
701b81f331cSmglocker 			printf("%s: read fan %d failed (0x%x)\n",
702b81f331cSmglocker 			    sc->sc_dev.dv_xname, i, r);
703b81f331cSmglocker 	/* left and right light sensors are optional */
704b81f331cSmglocker 	for (i = 0; sc->sc_prod->pr_light && i < ASMC_MAXLIGHT; i++)
705b81f331cSmglocker 		if ((r = asmc_light(sc, i, 1)) && r != ASMC_NOTFOUND)
706b81f331cSmglocker 			printf("%s: read light %d failed (0x%x)\n",
707b81f331cSmglocker 			    sc->sc_dev.dv_xname, i, r);
708b81f331cSmglocker 	/* motion sensors are optional */
709b81f331cSmglocker 	if ((r = asmc_try(sc, ASMC_READ, "MOCN", buf, 2)) &&
710b81f331cSmglocker 	    r != ASMC_NOTFOUND)
711b81f331cSmglocker 		printf("%s: read MOCN failed (0x%x)\n",
712b81f331cSmglocker 		    sc->sc_dev.dv_xname, r);
713b81f331cSmglocker #if 0 /* todo: initialize sudden motion sensors and setup interrupt handling */
714b81f331cSmglocker 	buf[0] = 0xe0, buf[1] = 0xf8;
715b81f331cSmglocker 	if ((r = asmc_try(sc, ASMC_WRITE, "MOCN", buf, 2)))
716b81f331cSmglocker 		printf("%s write MOCN failed (0x%x)\n",
717b81f331cSmglocker 		    sc->sc_dev.dv_xname, r);
718b81f331cSmglocker 	for (i = 0; i < ASMC_MAXMOTION; i++)
719b81f331cSmglocker 		if ((r = asmc_motion(sc, i, 1)) && r != ASMC_NOTFOUND)
720b81f331cSmglocker 			printf("%s: read motion %d failed (0x%x)\n",
721b81f331cSmglocker 			    sc->sc_dev.dv_xname, i, r);
722b81f331cSmglocker #endif
723b81f331cSmglocker }
724b81f331cSmglocker 
725b81f331cSmglocker void
asmc_update(void * arg)726b81f331cSmglocker asmc_update(void *arg)
727b81f331cSmglocker {
728b81f331cSmglocker 	struct asmc_softc *sc = arg;
729b81f331cSmglocker 	int i;
730b81f331cSmglocker 
731b81f331cSmglocker 	for (i = 0; i < ASMC_MAXTEMP && sc->sc_prod->pr_temp[i]; i++)
732b81f331cSmglocker 		if (!(sc->sc_sensor_temp[i].flags & SENSOR_FINVALID))
733b81f331cSmglocker 			asmc_temp(sc, i, 0);
734b81f331cSmglocker 	for (i = 0; i < sc->sc_nfans && i < ASMC_MAXFAN; i++)
735b81f331cSmglocker 		if (!(sc->sc_sensor_fan[i].flags & SENSOR_FINVALID))
736b81f331cSmglocker 			asmc_fan(sc, i, 0);
737b81f331cSmglocker 	for (i = 0; i < ASMC_MAXLIGHT; i++)
738b81f331cSmglocker 		if (!(sc->sc_sensor_light[i].flags & SENSOR_FINVALID))
739b81f331cSmglocker 			asmc_light(sc, i, 0);
740b81f331cSmglocker #if 0
741b81f331cSmglocker 	for (i = 0; i < ASMC_MAXMOTION; i++)
742b81f331cSmglocker 		if (!(sc->sc_sensor_motion[i].flags & SENSOR_FINVALID))
743b81f331cSmglocker 			asmc_motion(sc, i, 0);
744b81f331cSmglocker #endif
745b81f331cSmglocker }
746