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