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