xref: /openbsd/sys/dev/acpi/asmc.c (revision 142d0649)
1 /*	$OpenBSD: asmc.c,v 1.5 2022/10/20 16:08:13 kn 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 	const 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 const 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
asmc_match(struct device * parent,void * match,void * aux)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 	if (aa->aaa_naddr < 1)
246 		return 0;
247 	return acpi_matchhids(aa, asmc_hids, cf->cf_driver->cd_name);
248 }
249 
250 void
asmc_attach(struct device * parent,struct device * self,void * aux)251 asmc_attach(struct device *parent, struct device *self, void *aux)
252 {
253 	struct asmc_softc *sc = (struct asmc_softc *)self;
254 	struct acpi_attach_args *aaa = aux;
255 	struct aml_value res;
256 	int64_t sta;
257 	uint8_t buf[6];
258 	int i, r;
259 
260 	if (!hw_vendor || !hw_prod || strncmp(hw_vendor, "Apple", 5))
261 		return;
262 
263 	for (i = 0; asmc_prods[i].pr_name && !sc->sc_prod; i++)
264 		if (!strncasecmp(asmc_prods[i].pr_name, hw_prod,
265 		    strlen(asmc_prods[i].pr_name)))
266 			sc->sc_prod = &asmc_prods[i];
267 	if (!sc->sc_prod)
268 		return;
269 
270 	sc->sc_acpi = (struct acpi_softc *)parent;
271 	sc->sc_devnode = aaa->aaa_node;
272 
273 	printf(": %s", sc->sc_devnode->name);
274 
275 	sta = acpi_getsta(sc->sc_acpi, sc->sc_devnode);
276 	if ((sta & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) !=
277 	    (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) {
278 		printf(": not enabled\n");
279 		return;
280 	}
281 
282 	if (!(aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CID", 0, NULL, &res)))
283 		printf(" (%s)", res.v_string);
284 
285 	printf (" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]);
286 
287 	sc->sc_iot = aaa->aaa_bst[0];
288 	if (bus_space_map(sc->sc_iot, aaa->aaa_addr[0], aaa->aaa_size[0], 0,
289 	    &sc->sc_ioh)) {
290 		printf(": can't map registers\n");
291 		return;
292 	}
293 
294 	rw_init(&sc->sc_lock, sc->sc_dev.dv_xname);
295 
296 	if ((r = asmc_try(sc, ASMC_READ, "REV ", buf, 6))) {
297 		printf(": revision failed (0x%x)\n", r);
298 		bus_space_unmap(sc->sc_iot, aaa->aaa_addr[0], aaa->aaa_size[0]);
299 		return;
300 	}
301 	printf(": rev %x.%x%x%x", buf[0], buf[1], buf[2],
302 	    ntohs(*(uint16_t *)buf + 4));
303 
304 	if ((r = asmc_try(sc, ASMC_READ, "#KEY", buf, 4))) {
305 		printf(", no of keys failed (0x%x)\n", r);
306 		bus_space_unmap(sc->sc_iot, aaa->aaa_addr[0], aaa->aaa_size[0]);
307 		return;
308 	}
309 	printf(", %u key%s\n", ntohl(*(uint32_t *)buf),
310 	    (ntohl(*(uint32_t *)buf) == 1) ? "" : "s");
311 
312 	/* keyboard backlight led is optional */
313 	sc->sc_backlight = buf[0] = 127, buf[1] = 0;
314 	if ((r = asmc_try(sc, ASMC_WRITE, "LKSB", buf, 2))) {
315 		if (r != ASMC_NOTFOUND)
316 			printf("%s: keyboard backlight failed (0x%x)\n",
317 			    sc->sc_dev.dv_xname, r);
318 	} else {
319 		wskbd_get_backlight = asmc_get_backlight;
320 		wskbd_set_backlight = asmc_set_backlight;
321 	}
322 	task_set(&sc->sc_task_backlight, asmc_backlight, sc);
323 
324 	strlcpy(sc->sc_sensor_dev.xname, sc->sc_dev.dv_xname,
325 	    sizeof(sc->sc_sensor_dev.xname));
326 	for (i = 0; i < ASMC_MAXTEMP; i++) {
327 		sc->sc_sensor_temp[i].flags |= SENSOR_FINVALID;
328 		sc->sc_sensor_temp[i].flags |= SENSOR_FUNKNOWN;
329 	}
330 	for (i = 0; i < ASMC_MAXFAN; i++) {
331 		sc->sc_sensor_fan[i].flags |= SENSOR_FINVALID;
332 		sc->sc_sensor_fan[i].flags |= SENSOR_FUNKNOWN;
333 	}
334 	for (i = 0; i < ASMC_MAXLIGHT; i++) {
335 		sc->sc_sensor_light[i].flags |= SENSOR_FINVALID;
336 		sc->sc_sensor_light[i].flags |= SENSOR_FUNKNOWN;
337 	}
338 	for (i = 0; i < ASMC_MAXMOTION; i++) {
339 		sc->sc_sensor_motion[i].flags |= SENSOR_FINVALID;
340 		sc->sc_sensor_motion[i].flags |= SENSOR_FUNKNOWN;
341 	}
342 	asmc_init(sc);
343 
344 	if (!(sc->sc_sensor_task = sensor_task_register(sc, asmc_update, 5))) {
345 		printf("%s: unable to register task\n", sc->sc_dev.dv_xname);
346 		bus_space_unmap(sc->sc_iot, aaa->aaa_addr[0], aaa->aaa_size[0]);
347 		return;
348 	}
349 	sensordev_install(&sc->sc_sensor_dev);
350 }
351 
352 int
asmc_detach(struct device * self,int flags)353 asmc_detach(struct device *self, int flags)
354 {
355 	struct asmc_softc *sc = (struct asmc_softc *)self;
356 	uint8_t buf[2] = { (sc->sc_backlight = 0), 0 };
357 	int i;
358 
359 	if (sc->sc_sensor_task) {
360 		sensor_task_unregister(sc->sc_sensor_task);
361 		sc->sc_sensor_task = NULL;
362 	}
363 	sensordev_deinstall(&sc->sc_sensor_dev);
364 	for (i = 0; i < ASMC_MAXMOTION; i++)
365 		sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_motion[i]);
366 	for (i = 0; i < ASMC_MAXLIGHT; i++)
367 		sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_light[i]);
368 	for (i = 0; i < ASMC_MAXFAN; i++)
369 		sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_fan[i]);
370 	for (i = 0; i < ASMC_MAXTEMP; i++)
371 		sensor_detach(&sc->sc_sensor_dev, &sc->sc_sensor_temp[i]);
372 
373 	task_del(systq, &sc->sc_task_backlight);
374 	asmc_try(sc, ASMC_WRITE, "LKSB", buf, 2);
375 	return 0;
376 }
377 
378 int
asmc_activate(struct device * self,int act)379 asmc_activate(struct device *self, int act)
380 {
381 	struct asmc_softc *sc = (struct asmc_softc *)self;
382 
383 	switch (act) {
384 	case DVACT_WAKEUP:
385 		asmc_backlight(sc);
386 		break;
387 	}
388 
389 	return 0;
390 }
391 
392 void
asmc_backlight(void * arg)393 asmc_backlight(void *arg)
394 {
395 	struct asmc_softc *sc = arg;
396 	uint8_t buf[2] = { sc->sc_backlight, 0 };
397 	int r;
398 
399 	if ((r = asmc_try(sc, ASMC_WRITE, "LKSB", buf, 2)))
400 		printf("%s: keyboard backlight failed (0x%x)\n",
401 		    sc->sc_dev.dv_xname, r);
402 }
403 
404 int
asmc_get_backlight(struct wskbd_backlight * kbl)405 asmc_get_backlight(struct wskbd_backlight *kbl)
406 {
407 	struct asmc_softc *sc = asmc_cd.cd_devs[0];
408 
409 	KASSERT(sc != NULL);
410 	kbl->min = 0;
411 	kbl->max = 0xff;
412 	kbl->curval = sc->sc_backlight;
413 	return 0;
414 }
415 
416 int
asmc_set_backlight(struct wskbd_backlight * kbl)417 asmc_set_backlight(struct wskbd_backlight *kbl)
418 {
419 	struct asmc_softc *sc = asmc_cd.cd_devs[0];
420 
421 	KASSERT(sc != NULL);
422 	if (kbl->curval > 0xff)
423 		return EINVAL;
424 	sc->sc_backlight = kbl->curval;
425 	task_add(systq, &sc->sc_task_backlight);
426 	return 0;
427 }
428 
429 static uint8_t
asmc_status(struct asmc_softc * sc)430 asmc_status(struct asmc_softc *sc)
431 {
432 	return bus_space_read_1(sc->sc_iot, sc->sc_ioh, ASMC_STATUS);
433 }
434 
435 static int
asmc_write(struct asmc_softc * sc,uint8_t off,uint8_t val)436 asmc_write(struct asmc_softc *sc, uint8_t off, uint8_t val)
437 {
438 	int i;
439 	uint8_t status;
440 
441 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, off, val);
442 	for (i = 0; i < ASMC_DELAY_LOOP; i++) {
443 		status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ASMC_COMMAND);
444 		if (status & ASMC_IBF)
445 			continue;
446 		if (status & ASMC_ACCEPT)
447 			return 0;
448 		delay(10);
449 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, off, val);
450 	}
451 
452 	return ETIMEDOUT;
453 }
454 
455 static int
asmc_read(struct asmc_softc * sc,uint8_t off,uint8_t * buf)456 asmc_read(struct asmc_softc *sc, uint8_t off, uint8_t *buf)
457 {
458 	int i;
459 	uint8_t status;
460 
461 	for (i = 0; i < ASMC_DELAY_LOOP; i++) {
462 		status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ASMC_COMMAND);
463 		if (status & ASMC_OBF)
464 			break;
465 		delay(10);
466 	}
467 	if (i == ASMC_DELAY_LOOP)
468 		return ETIMEDOUT;
469 	*buf = bus_space_read_1(sc->sc_iot, sc->sc_ioh, off);
470 
471 	return 0;
472 }
473 
474 static int
asmc_command(struct asmc_softc * sc,int cmd,const char * key,uint8_t * buf,uint8_t len)475 asmc_command(struct asmc_softc *sc, int cmd, const char *key, uint8_t *buf,
476     uint8_t len)
477 {
478 	int i;
479 
480 	if (len > ASMC_MAXLEN)
481 		return 1;
482 	if (asmc_write(sc, ASMC_COMMAND, cmd))
483 		return 1;
484 	for (i = 0; i < 4; i++)
485 		if (asmc_write(sc, ASMC_DATA, key[i]))
486 			return 1;
487 	if (asmc_write(sc, ASMC_DATA, len))
488 		return 1;
489 	if (cmd == ASMC_READ || cmd == ASMC_INFO) {
490 		for (i = 0; i < len; i++)
491 			if (asmc_read(sc, ASMC_DATA, &buf[i]))
492 				return 1;
493 	} else if (cmd == ASMC_WRITE) {
494 		for (i = 0; i < len; i++)
495 			if (asmc_write(sc, ASMC_DATA, buf[i]))
496 				return 1;
497 	} else
498 		return 1;
499 	return 0;
500 }
501 
502 int
asmc_try(struct asmc_softc * sc,int cmd,const char * key,uint8_t * buf,uint8_t len)503 asmc_try(struct asmc_softc *sc, int cmd, const char *key, uint8_t *buf,
504     uint8_t len)
505 {
506 	uint8_t s;
507 	int i, r;
508 
509 	rw_enter_write(&sc->sc_lock);
510 	for (i = 0; i < ASMC_RETRY; i++)
511 		if (!(r = asmc_command(sc, cmd, key, buf, len)))
512 			break;
513 	if (r && (s = asmc_status(sc)))
514 		r = s;
515 	rw_exit_write(&sc->sc_lock);
516 
517 	return r;
518 }
519 
520 static uint32_t
asmc_uk(uint8_t * buf)521 asmc_uk(uint8_t *buf)
522 {
523 	/* spe78: floating point, signed, 7 bits exponent, 8 bits fraction */
524 	return (((int16_t)ntohs(*(uint16_t *)buf)) >> 8) * 1000000 + 273150000;
525 }
526 
527 static uint16_t
asmc_rpm(uint8_t * buf)528 asmc_rpm(uint8_t *buf)
529 {
530 	/* fpe2: floating point, unsigned, 14 bits exponent, 2 bits fraction */
531 	return ntohs(*(uint16_t *)buf) >> 2;
532 }
533 
534 static uint32_t
asmc_lux(uint8_t * buf,uint8_t lightlen)535 asmc_lux(uint8_t *buf, uint8_t lightlen)
536 {
537 	/* newer macbooks report a 10 bit big endian value */
538 	return (lightlen == 10) ?
539 	    /* fp18.14: floating point, 18 bits exponent, 14 bits fraction */
540 	    (ntohl(*(uint32_t *)(buf + 6)) >> 14) * 1000000 :
541 	    /*
542 	     * todo: calculate lux from ADC raw data
543 	     * buf[1] true/false for high/low gain chan reads
544 	     * chan 0: ntohs(*(uint16_t *)(buf + 2));
545 	     * chan 1: ntohs(*(uint16_t *)(buf + 4));
546 	     */
547 	    ntohs(*(uint16_t *)(buf + 2)) * 1000000;
548 }
549 
550 static int
asmc_temp(struct asmc_softc * sc,uint8_t idx,int init)551 asmc_temp(struct asmc_softc *sc, uint8_t idx, int init)
552 {
553 	uint8_t buf[2];
554 	uint32_t uk;
555 	int i, r;
556 
557 	if ((r = asmc_try(sc, ASMC_READ, sc->sc_prod->pr_temp[idx], buf, 2)))
558 		return r;
559 	if ((uk = asmc_uk(buf)) < 253150000) /* ignore unlikely values */
560 		return 0;
561 	sc->sc_sensor_temp[idx].value = uk;
562 	sc->sc_sensor_temp[idx].flags &= ~SENSOR_FUNKNOWN;
563 
564 	if (!init)
565 		return 0;
566 
567 	strlcpy(sc->sc_sensor_temp[idx].desc, sc->sc_prod->pr_temp[idx],
568 	    sizeof(sc->sc_sensor_temp[idx].desc));
569 	for (i = 0; asmc_temp_desc[i][0]; i++)
570 		if (!strcmp(asmc_temp_desc[i][0], sc->sc_prod->pr_temp[idx]))
571 			break;
572 	if (asmc_temp_desc[i][0]) {
573 		strlcat(sc->sc_sensor_temp[idx].desc, " ",
574 		    sizeof(sc->sc_sensor_temp[idx].desc));
575 		strlcat(sc->sc_sensor_temp[idx].desc, asmc_temp_desc[i][1],
576 		    sizeof(sc->sc_sensor_temp[idx].desc));
577 	}
578 	sc->sc_sensor_temp[idx].type = SENSOR_TEMP;
579 	sc->sc_sensor_temp[idx].flags &= ~SENSOR_FINVALID;
580 	sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_temp[idx]);
581 	return 0;
582 }
583 
584 static int
asmc_fan(struct asmc_softc * sc,uint8_t idx,int init)585 asmc_fan(struct asmc_softc *sc, uint8_t idx, int init)
586 {
587 	char key[5];
588 	uint8_t buf[17], *end;
589 	int r;
590 
591 	snprintf(key, sizeof(key), "F%dAc", idx);
592 	if ((r = asmc_try(sc, ASMC_READ, key, buf, 2)))
593 		return r;
594 	sc->sc_sensor_fan[idx].value = asmc_rpm(buf);
595 	sc->sc_sensor_fan[idx].flags &= ~SENSOR_FUNKNOWN;
596 
597 	if (!init)
598 		return 0;
599 
600 	snprintf(key, sizeof(key), "F%dID", idx);
601 	if ((r = asmc_try(sc, ASMC_READ, key, buf, 16)))
602 		return r;
603 	buf[16] = '\0';
604 	end = buf + 4 + strlen((char *)buf + 4) - 1;
605 	while (buf + 4 < end && *end == ' ') /* trim trailing spaces */
606 		*end-- = '\0';
607 	strlcpy(sc->sc_sensor_fan[idx].desc, buf + 4,
608 	    sizeof(sc->sc_sensor_fan[idx].desc));
609 	if (buf[2] < nitems(asmc_fan_loc)) {
610 		strlcat(sc->sc_sensor_fan[idx].desc, ", ",
611 		    sizeof(sc->sc_sensor_fan[idx].desc));
612 		strlcat(sc->sc_sensor_fan[idx].desc, asmc_fan_loc[buf[2]],
613 		    sizeof(sc->sc_sensor_fan[idx].desc));
614 	}
615 	sc->sc_sensor_fan[idx].type = SENSOR_FANRPM;
616 	sc->sc_sensor_fan[idx].flags &= ~SENSOR_FINVALID;
617 	sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_fan[idx]);
618 	return 0;
619 }
620 
621 static int
asmc_light(struct asmc_softc * sc,uint8_t idx,int init)622 asmc_light(struct asmc_softc *sc, uint8_t idx, int init)
623 {
624 	char key[5];
625 	uint8_t buf[10];
626 	int r;
627 
628 	snprintf(key, sizeof(key), "ALV%d", idx);
629 	if (!sc->sc_lightlen) {
630 		if ((r = asmc_try(sc, ASMC_INFO, key, buf, 6)))
631 			return r;
632 		if ((sc->sc_lightlen = buf[0]) > 10)
633 			return 1;
634 	}
635 	if ((r = asmc_try(sc, ASMC_READ, key, buf, sc->sc_lightlen)))
636 		return r;
637 	if (!buf[0]) /* valid data? */
638 		return 0;
639 	sc->sc_sensor_light[idx].value = asmc_lux(buf, sc->sc_lightlen);
640 	sc->sc_sensor_light[idx].flags &= ~SENSOR_FUNKNOWN;
641 
642 	if (!init)
643 		return 0;
644 
645 	strlcpy(sc->sc_sensor_light[idx].desc, asmc_light_desc[idx],
646 	    sizeof(sc->sc_sensor_light[idx].desc));
647 	sc->sc_sensor_light[idx].type = SENSOR_LUX;
648 	sc->sc_sensor_light[idx].flags &= ~SENSOR_FINVALID;
649 	sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_light[idx]);
650 	return 0;
651 }
652 
653 #if 0 /* todo: implement motion sensors update and initialization */
654 static int
655 asmc_motion(struct asmc_softc *sc, uint8_t idx, int init)
656 {
657 	char key[5];
658 	uint8_t buf[2];
659 	int r;
660 
661 	snprintf(key, sizeof(key), "MO_%c", 88 + idx); /* X, Y, Z */
662 	if ((r = asmc_try(sc, ASMC_READ, key, buf, 2)))
663 		return r;
664 	sc->sc_sensor_motion[idx].value = 0;
665 	sc->sc_sensor_motion[idx].flags &= ~SENSOR_FUNKNOWN;
666 
667 	if (!init)
668 		return 0;
669 
670 	/* todo: setup and attach sensors and description */
671 	strlcpy(sc->sc_sensor_motion[idx].desc, 120 + idx, /* x, y, z */
672 	    sizeof(sc->sc_sensor_motion[idx].desc));
673 	strlcat(sc->sc_sensor_motion[idx].desc, "-axis",
674 	    sizeof(sc->sc_sensor_motion[idx].desc));
675 	sc->sc_sensor_motion[idx].type = SENSOR_ACCEL;
676 	sc->sc_sensor_motion[idx].flags &= ~SENSOR_FINVALID;
677 	sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor_motion[idx]);
678 	return 0;
679 }
680 #endif
681 
682 void
asmc_init(struct asmc_softc * sc)683 asmc_init(struct asmc_softc *sc)
684 {
685 	uint8_t buf[2];
686 	int i, r;
687 
688 	/* number of temperature sensors depends on product */
689 	for (i = 0; i < ASMC_MAXTEMP && sc->sc_prod->pr_temp[i]; i++)
690 		if ((r = asmc_temp(sc, i, 1)) && r != ASMC_NOTFOUND)
691 			printf("%s: read temp %d failed (0x%x)\n",
692 			    sc->sc_dev.dv_xname, i, r);
693 	/* number of fan sensors depends on product */
694 	if ((r = asmc_try(sc, ASMC_READ, "FNum", buf, 1)))
695 		printf("%s: read FNum failed (0x%x)\n",
696 		    sc->sc_dev.dv_xname, r);
697 	else
698 		sc->sc_nfans = buf[0];
699 	for (i = 0; i < sc->sc_nfans && i < ASMC_MAXFAN; i++)
700 		if ((r = asmc_fan(sc, i, 1)) && r != ASMC_NOTFOUND)
701 			printf("%s: read fan %d failed (0x%x)\n",
702 			    sc->sc_dev.dv_xname, i, r);
703 	/* left and right light sensors are optional */
704 	for (i = 0; sc->sc_prod->pr_light && i < ASMC_MAXLIGHT; i++)
705 		if ((r = asmc_light(sc, i, 1)) && r != ASMC_NOTFOUND)
706 			printf("%s: read light %d failed (0x%x)\n",
707 			    sc->sc_dev.dv_xname, i, r);
708 	/* motion sensors are optional */
709 	if ((r = asmc_try(sc, ASMC_READ, "MOCN", buf, 2)) &&
710 	    r != ASMC_NOTFOUND)
711 		printf("%s: read MOCN failed (0x%x)\n",
712 		    sc->sc_dev.dv_xname, r);
713 #if 0 /* todo: initialize sudden motion sensors and setup interrupt handling */
714 	buf[0] = 0xe0, buf[1] = 0xf8;
715 	if ((r = asmc_try(sc, ASMC_WRITE, "MOCN", buf, 2)))
716 		printf("%s write MOCN failed (0x%x)\n",
717 		    sc->sc_dev.dv_xname, r);
718 	for (i = 0; i < ASMC_MAXMOTION; i++)
719 		if ((r = asmc_motion(sc, i, 1)) && r != ASMC_NOTFOUND)
720 			printf("%s: read motion %d failed (0x%x)\n",
721 			    sc->sc_dev.dv_xname, i, r);
722 #endif
723 }
724 
725 void
asmc_update(void * arg)726 asmc_update(void *arg)
727 {
728 	struct asmc_softc *sc = arg;
729 	int i;
730 
731 	for (i = 0; i < ASMC_MAXTEMP && sc->sc_prod->pr_temp[i]; i++)
732 		if (!(sc->sc_sensor_temp[i].flags & SENSOR_FINVALID))
733 			asmc_temp(sc, i, 0);
734 	for (i = 0; i < sc->sc_nfans && i < ASMC_MAXFAN; i++)
735 		if (!(sc->sc_sensor_fan[i].flags & SENSOR_FINVALID))
736 			asmc_fan(sc, i, 0);
737 	for (i = 0; i < ASMC_MAXLIGHT; i++)
738 		if (!(sc->sc_sensor_light[i].flags & SENSOR_FINVALID))
739 			asmc_light(sc, i, 0);
740 #if 0
741 	for (i = 0; i < ASMC_MAXMOTION; i++)
742 		if (!(sc->sc_sensor_motion[i].flags & SENSOR_FINVALID))
743 			asmc_motion(sc, i, 0);
744 #endif
745 }
746