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