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