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