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