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