1 /* $OpenBSD: kb3310.c,v 1.23 2017/01/23 10:54:51 fcambus Exp $ */ 2 /* 3 * Copyright (c) 2010 Otto Moerbeek <otto@drijf.net> 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 #include <sys/param.h> 19 #include <sys/kernel.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 #include <sys/sensors.h> 23 #include <sys/timeout.h> 24 25 #include <machine/apmvar.h> 26 #include <machine/autoconf.h> 27 #include <machine/bus.h> 28 #include <dev/isa/isavar.h> 29 30 #include <dev/pci/glxreg.h> 31 32 #include <loongson/dev/bonitoreg.h> 33 #include <loongson/dev/kb3310var.h> 34 35 #include "apm.h" 36 #include "pckbd.h" 37 #include "hidkbd.h" 38 39 #if NPCKBD > 0 || NHIDKBD > 0 40 #include <dev/ic/pckbcvar.h> 41 #include <dev/pckbc/pckbdvar.h> 42 #include <dev/hid/hidkbdvar.h> 43 #endif 44 45 struct cfdriver ykbec_cd = { 46 NULL, "ykbec", DV_DULL, 47 }; 48 49 #ifdef KB3310_DEBUG 50 #define DPRINTF(x) printf x 51 #else 52 #define DPRINTF(x) 53 #endif 54 55 #define IO_YKBEC 0x381 56 #define IO_YKBECSIZE 0x3 57 58 static const struct { 59 const char *desc; 60 int type; 61 } ykbec_table[] = { 62 #define YKBEC_FAN 0 63 { NULL, SENSOR_FANRPM }, 64 #define YKBEC_ITEMP 1 65 { "Internal temperature", SENSOR_TEMP }, 66 #define YKBEC_FCAP 2 67 { "Battery full charge capacity", SENSOR_AMPHOUR }, 68 #define YKBEC_BCURRENT 3 69 { "Battery current", SENSOR_AMPS }, 70 #define YKBEC_BVOLT 4 71 { "Battery voltage", SENSOR_VOLTS_DC }, 72 #define YKBEC_BTEMP 5 73 { "Battery temperature", SENSOR_TEMP }, 74 #define YKBEC_CAP 6 75 { "Battery capacity", SENSOR_PERCENT }, 76 #define YKBEC_CHARGING 7 77 { "Battery charging", SENSOR_INDICATOR }, 78 #define YKBEC_AC 8 79 { "AC-Power", SENSOR_INDICATOR }, 80 #define YKBEC_LID 9 81 { "Lid open", SENSOR_INDICATOR } 82 #define YKBEC_NSENSORS 10 83 }; 84 85 struct ykbec_softc { 86 struct device sc_dev; 87 bus_space_tag_t sc_iot; 88 bus_space_handle_t sc_ioh; 89 struct ksensor sc_sensor[YKBEC_NSENSORS]; 90 struct ksensordev sc_sensordev; 91 #if NPCKBD > 0 || NHIDKBD > 0 92 struct timeout sc_bell_tmo; 93 #endif 94 }; 95 96 static struct ykbec_softc *ykbec_sc; 97 static int ykbec_chip_config; 98 99 extern void loongson_set_isa_imr(uint); 100 101 int ykbec_match(struct device *, void *, void *); 102 void ykbec_attach(struct device *, struct device *, void *); 103 104 const struct cfattach ykbec_ca = { 105 sizeof(struct ykbec_softc), ykbec_match, ykbec_attach 106 }; 107 108 int ykbec_apminfo(struct apm_power_info *); 109 void ykbec_bell(void *, u_int, u_int, u_int, int); 110 void ykbec_bell_stop(void *); 111 void ykbec_print_bat_info(struct ykbec_softc *); 112 u_int ykbec_read(struct ykbec_softc *, u_int); 113 u_int ykbec_read16(struct ykbec_softc *, u_int); 114 void ykbec_refresh(void *arg); 115 void ykbec_write(struct ykbec_softc *, u_int, u_int); 116 117 #if NAPM > 0 118 struct apm_power_info ykbec_apmdata; 119 const char *ykbec_batstate[] = { 120 "high", 121 "low", 122 "critical", 123 "charging", 124 "unknown" 125 }; 126 #define BATTERY_STRING(x) ((x) < nitems(ykbec_batstate) ? \ 127 ykbec_batstate[x] : ykbec_batstate[4]) 128 #endif 129 130 int 131 ykbec_match(struct device *parent, void *match, void *aux) 132 { 133 struct isa_attach_args *ia = aux; 134 bus_space_handle_t ioh; 135 136 /* XXX maybe allow LOONGSON_EBT700 ??? */ 137 if (sys_platform->system_type != LOONGSON_YEELOONG) 138 return (0); 139 140 if ((ia->ia_iobase != IOBASEUNK && ia->ia_iobase != IO_YKBEC) || 141 /* (ia->ia_iosize != 0 && ia->ia_iosize != IO_YKBECSIZE) || XXX isa.c */ 142 ia->ia_maddr != MADDRUNK || ia->ia_msize != 0 || 143 ia->ia_irq != IRQUNK || ia->ia_drq != DRQUNK) 144 return (0); 145 146 if (bus_space_map(ia->ia_iot, IO_YKBEC, IO_YKBECSIZE, 0, &ioh)) 147 return (0); 148 149 bus_space_unmap(ia->ia_iot, ioh, IO_YKBECSIZE); 150 151 ia->ia_iobase = IO_YKBEC; 152 ia->ia_iosize = IO_YKBECSIZE; 153 154 return (1); 155 } 156 157 void 158 ykbec_attach(struct device *parent, struct device *self, void *aux) 159 { 160 struct isa_attach_args *ia = aux; 161 struct ykbec_softc *sc = (struct ykbec_softc *)self; 162 int i; 163 164 sc->sc_iot = ia->ia_iot; 165 if (bus_space_map(sc->sc_iot, ia->ia_iobase, ia->ia_iosize, 0, 166 &sc->sc_ioh)) { 167 printf(": couldn't map I/O space"); 168 return; 169 } 170 171 /* Initialize sensor data. */ 172 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 173 sizeof(sc->sc_sensordev.xname)); 174 if (sensor_task_register(sc, ykbec_refresh, 5) == NULL) { 175 printf(", unable to register update task\n"); 176 return; 177 } 178 179 #ifdef DEBUG 180 ykbec_print_bat_info(sc); 181 #endif 182 printf("\n"); 183 184 for (i = 0; i < YKBEC_NSENSORS; i++) { 185 sc->sc_sensor[i].type = ykbec_table[i].type; 186 if (ykbec_table[i].desc) 187 strlcpy(sc->sc_sensor[i].desc, ykbec_table[i].desc, 188 sizeof(sc->sc_sensor[i].desc)); 189 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]); 190 } 191 192 sensordev_install(&sc->sc_sensordev); 193 194 #if NAPM > 0 195 /* make sure we have the apm state initialized before apm attaches */ 196 ykbec_refresh(sc); 197 apm_setinfohook(ykbec_apminfo); 198 #endif 199 #if NPCKBD > 0 || NHIDKBD > 0 200 timeout_set(&sc->sc_bell_tmo, ykbec_bell_stop, sc); 201 #if NPCKBD > 0 202 pckbd_hookup_bell(ykbec_bell, sc); 203 #endif 204 #if NHIDKBD > 0 205 hidkbd_hookup_bell(ykbec_bell, sc); 206 #endif 207 #endif 208 ykbec_sc = sc; 209 } 210 211 void 212 ykbec_write(struct ykbec_softc *mcsc, u_int reg, u_int datum) 213 { 214 struct ykbec_softc *sc = (struct ykbec_softc *)mcsc; 215 bus_space_tag_t iot = sc->sc_iot; 216 bus_space_handle_t ioh = sc->sc_ioh; 217 218 bus_space_write_1(iot, ioh, 0, (reg >> 8) & 0xff); 219 bus_space_write_1(iot, ioh, 1, (reg >> 0) & 0xff); 220 bus_space_write_1(iot, ioh, 2, datum); 221 } 222 223 u_int 224 ykbec_read(struct ykbec_softc *mcsc, u_int reg) 225 { 226 struct ykbec_softc *sc = (struct ykbec_softc *)mcsc; 227 bus_space_tag_t iot = sc->sc_iot; 228 bus_space_handle_t ioh = sc->sc_ioh; 229 230 bus_space_write_1(iot, ioh, 0, (reg >> 8) & 0xff); 231 bus_space_write_1(iot, ioh, 1, (reg >> 0) & 0xff); 232 return bus_space_read_1(iot, ioh, 2); 233 } 234 235 u_int 236 ykbec_read16(struct ykbec_softc *mcsc, u_int reg) 237 { 238 u_int val; 239 240 val = ykbec_read(mcsc, reg); 241 return (val << 8) | ykbec_read(mcsc, reg + 1); 242 } 243 244 #define KB3310_FAN_SPEED_DIVIDER 480000 245 246 #define ECTEMP_CURRENT_REG 0xf458 247 #define REG_FAN_SPEED_HIGH 0xfe22 248 #define REG_FAN_SPEED_LOW 0xfe23 249 250 #define REG_DESIGN_CAP_HIGH 0xf77d 251 #define REG_DESIGN_CAP_LOW 0xf77e 252 #define REG_FULLCHG_CAP_HIGH 0xf780 253 #define REG_FULLCHG_CAP_LOW 0xf781 254 255 #define REG_DESIGN_VOL_HIGH 0xf782 256 #define REG_DESIGN_VOL_LOW 0xf783 257 #define REG_CURRENT_HIGH 0xf784 258 #define REG_CURRENT_LOW 0xf785 259 #define REG_VOLTAGE_HIGH 0xf786 260 #define REG_VOLTAGE_LOW 0xf787 261 #define REG_TEMPERATURE_HIGH 0xf788 262 #define REG_TEMPERATURE_LOW 0xf789 263 #define REG_RELATIVE_CAT_HIGH 0xf492 264 #define REG_RELATIVE_CAT_LOW 0xf493 265 #define REG_BAT_VENDOR 0xf4c4 266 #define REG_BAT_CELL_COUNT 0xf4c6 267 268 #define REG_BAT_CHARGE 0xf4a2 269 #define BAT_CHARGE_AC 0x00 270 #define BAT_CHARGE_DISCHARGE 0x01 271 #define BAT_CHARGE_CHARGE 0x02 272 273 #define REG_POWER_FLAG 0xf440 274 #define POWER_FLAG_ADAPTER_IN (1<<0) 275 #define POWER_FLAG_POWER_ON (1<<1) 276 #define POWER_FLAG_ENTER_SUS (1<<2) 277 278 #define REG_BAT_STATUS 0xf4b0 279 #define BAT_STATUS_BAT_EXISTS (1<<0) 280 #define BAT_STATUS_BAT_FULL (1<<1) 281 #define BAT_STATUS_BAT_DESTROY (1<<2) 282 #define BAT_STATUS_BAT_LOW (1<<5) 283 284 #define REG_CHARGE_STATUS 0xf4b1 285 #define CHARGE_STATUS_PRECHARGE (1<<1) 286 #define CHARGE_STATUS_OVERHEAT (1<<2) 287 288 #define REG_BAT_STATE 0xf482 289 #define BAT_STATE_DISCHARGING (1<<0) 290 #define BAT_STATE_CHARGING (1<<1) 291 292 #define REG_BEEP_CONTROL 0xf4d0 293 #define BEEP_ENABLE (1<<0) 294 295 #define REG_PMUCFG 0xff0c 296 #define PMUCFG_STOP_MODE (1<<7) 297 #define PMUCFG_IDLE_MODE (1<<6) 298 #define PMUCFG_LPC_WAKEUP (1<<5) 299 #define PMUCFG_RESET_8051 (1<<4) 300 #define PMUCFG_SCI_WAKEUP (1<<3) 301 #define PMUCFG_WDT_WAKEUP (1<<2) 302 #define PMUCFG_GPWU_WAKEUP (1<<1) 303 #define PMUCFG_IRQ_IDLE (1<<0) 304 305 #define REG_USB0 0xf461 306 #define REG_USB1 0xf462 307 #define REG_USB2 0xf463 308 #define USB_FLAG_ON 1 309 #define USB_FLAG_OFF 0 310 311 #define REG_FAN_CONTROL 0xf4d2 312 #define REG_FAN_ON 1 313 #define REG_FAN_OFF 0 314 315 #define REG_LID_STATE 0xf4bd 316 #define LID_OPEN 1 317 #define LID_CLOSED 0 318 319 #define YKBEC_SCI_IRQ 0xa 320 321 #ifdef DEBUG 322 void 323 ykbec_print_bat_info(struct ykbec_softc *sc) 324 { 325 uint bat_status, count, dvolt, dcap; 326 327 printf(": battery "); 328 bat_status = ykbec_read(sc, REG_BAT_STATUS); 329 if (!ISSET(bat_status, BAT_STATUS_BAT_EXISTS)) { 330 printf("absent"); 331 return; 332 } 333 334 count = ykbec_read(sc, REG_BAT_CELL_COUNT); 335 dvolt = ykbec_read16(sc, REG_DESIGN_VOL_HIGH); 336 dcap = ykbec_read16(sc, REG_DESIGN_CAP_HIGH); 337 printf("%d cells, design capacity %dmV %dmAh", count, dvolt, dcap); 338 } 339 #endif 340 341 void 342 ykbec_refresh(void *arg) 343 { 344 struct ykbec_softc *sc = (struct ykbec_softc *)arg; 345 u_int val, bat_charge, bat_status, charge_status, bat_state, power_flag; 346 u_int lid_state, cap_pct, fullcap; 347 int current; 348 #if NAPM > 0 349 struct apm_power_info old; 350 #endif 351 352 val = ykbec_read16(sc, REG_FAN_SPEED_HIGH) & 0xfffff; 353 if (val != 0) { 354 val = KB3310_FAN_SPEED_DIVIDER / val; 355 sc->sc_sensor[YKBEC_FAN].value = val; 356 CLR(sc->sc_sensor[YKBEC_FAN].flags, SENSOR_FINVALID); 357 } else 358 SET(sc->sc_sensor[YKBEC_FAN].flags, SENSOR_FINVALID); 359 360 val = ykbec_read(sc, ECTEMP_CURRENT_REG); 361 sc->sc_sensor[YKBEC_ITEMP].value = val * 1000000 + 273150000; 362 363 fullcap = ykbec_read16(sc, REG_FULLCHG_CAP_HIGH); 364 sc->sc_sensor[YKBEC_FCAP].value = fullcap * 1000; 365 366 current = ykbec_read16(sc, REG_CURRENT_HIGH); 367 /* sign extend short -> int, int -> int64 will be done next statement */ 368 current |= -(current & 0x8000); 369 sc->sc_sensor[YKBEC_BCURRENT].value = -1000 * current; 370 371 sc->sc_sensor[YKBEC_BVOLT].value = ykbec_read16(sc, REG_VOLTAGE_HIGH) * 372 1000; 373 374 val = ykbec_read16(sc, REG_TEMPERATURE_HIGH); 375 sc->sc_sensor[YKBEC_BTEMP].value = val * 1000000 + 273150000; 376 377 cap_pct = ykbec_read16(sc, REG_RELATIVE_CAT_HIGH); 378 sc->sc_sensor[YKBEC_CAP].value = cap_pct * 1000; 379 380 bat_charge = ykbec_read(sc, REG_BAT_CHARGE); 381 bat_status = ykbec_read(sc, REG_BAT_STATUS); 382 charge_status = ykbec_read(sc, REG_CHARGE_STATUS); 383 bat_state = ykbec_read(sc, REG_BAT_STATE); 384 power_flag = ykbec_read(sc, REG_POWER_FLAG); 385 lid_state = ykbec_read(sc, REG_LID_STATE); 386 387 sc->sc_sensor[YKBEC_CHARGING].value = !!ISSET(bat_state, 388 BAT_STATE_CHARGING); 389 sc->sc_sensor[YKBEC_AC].value = !!ISSET(power_flag, 390 POWER_FLAG_ADAPTER_IN); 391 392 sc->sc_sensor[YKBEC_LID].value = !!ISSET(lid_state, LID_OPEN); 393 394 sc->sc_sensor[YKBEC_CAP].status = ISSET(bat_status, BAT_STATUS_BAT_LOW) ? 395 SENSOR_S_CRIT : SENSOR_S_OK; 396 397 #if NAPM > 0 398 bcopy(&ykbec_apmdata, &old, sizeof(old)); 399 ykbec_apmdata.battery_life = cap_pct; 400 ykbec_apmdata.ac_state = ISSET(power_flag, POWER_FLAG_ADAPTER_IN) ? 401 APM_AC_ON : APM_AC_OFF; 402 if (!ISSET(bat_status, BAT_STATUS_BAT_EXISTS)) { 403 ykbec_apmdata.battery_state = APM_BATTERY_ABSENT; 404 ykbec_apmdata.minutes_left = 0; 405 ykbec_apmdata.battery_life = 0; 406 } else { 407 if (ISSET(bat_state, BAT_STATE_CHARGING)) 408 ykbec_apmdata.battery_state = APM_BATT_CHARGING; 409 else if (ISSET(bat_status, BAT_STATUS_BAT_LOW)) 410 ykbec_apmdata.battery_state = APM_BATT_CRITICAL; 411 else if (cap_pct > 50) 412 ykbec_apmdata.battery_state = APM_BATT_HIGH; 413 else 414 ykbec_apmdata.battery_state = APM_BATT_LOW; 415 416 /* if charging, current is positive */ 417 if (ISSET(bat_state, BAT_STATE_CHARGING)) 418 current = 0; 419 else 420 current = -current; 421 /* XXX Yeeloong draw is about 1A */ 422 if (current <= 0) 423 current = 1000; 424 /* XXX at 5?%, the Yeeloong shuts down */ 425 if (cap_pct <= 5) 426 cap_pct = 0; 427 else 428 cap_pct -= 5; 429 fullcap = cap_pct * 60 * fullcap / 100; 430 ykbec_apmdata.minutes_left = fullcap / current; 431 432 } 433 if (old.ac_state != ykbec_apmdata.ac_state) 434 apm_record_event(APM_POWER_CHANGE, "AC power", 435 ykbec_apmdata.ac_state ? "restored" : "lost"); 436 if (old.battery_state != ykbec_apmdata.battery_state) 437 apm_record_event(APM_POWER_CHANGE, "battery", 438 BATTERY_STRING(ykbec_apmdata.battery_state)); 439 #endif 440 } 441 442 #if NAPM > 0 443 int 444 ykbec_apminfo(struct apm_power_info *info) 445 { 446 bcopy(&ykbec_apmdata, info, sizeof(struct apm_power_info)); 447 return 0; 448 } 449 450 int 451 ykbec_suspend() 452 { 453 struct ykbec_softc *sc = ykbec_sc; 454 int ctrl; 455 456 /* 457 * Set up wakeup sources: currently only the internal keyboard. 458 */ 459 loongson_set_isa_imr(1 << 1); 460 461 /* USB */ 462 DPRINTF(("USB\n")); 463 ykbec_write(sc, REG_USB0, USB_FLAG_OFF); 464 ykbec_write(sc, REG_USB1, USB_FLAG_OFF); 465 ykbec_write(sc, REG_USB2, USB_FLAG_OFF); 466 467 /* EC */ 468 DPRINTF(("REG_PMUCFG\n")); 469 ctrl = PMUCFG_SCI_WAKEUP | PMUCFG_WDT_WAKEUP | PMUCFG_GPWU_WAKEUP | 470 PMUCFG_LPC_WAKEUP | PMUCFG_STOP_MODE | PMUCFG_RESET_8051; 471 ykbec_write(sc, REG_PMUCFG, ctrl); 472 473 /* FAN */ 474 DPRINTF(("FAN\n")); 475 ykbec_write(sc, REG_FAN_CONTROL, REG_FAN_OFF); 476 477 /* CPU */ 478 DPRINTF(("CPU\n")); 479 ykbec_chip_config = REGVAL(LOONGSON_CHIP_CONFIG0); 480 enableintr(); 481 REGVAL(LOONGSON_CHIP_CONFIG0) = ykbec_chip_config & ~0x7; 482 (void)REGVAL(LOONGSON_CHIP_CONFIG0); 483 484 /* 485 * When a resume interrupt fires, we will enter the interrupt 486 * dispatcher, which will do nothing because we are at splhigh, 487 * and execution flow will return here and continue. 488 */ 489 (void)disableintr(); 490 491 return 0; 492 } 493 494 int 495 ykbec_resume() 496 { 497 struct ykbec_softc *sc = ykbec_sc; 498 499 /* CPU */ 500 DPRINTF(("CPU\n")); 501 REGVAL(LOONGSON_CHIP_CONFIG0) = ykbec_chip_config; 502 (void)REGVAL(LOONGSON_CHIP_CONFIG0); 503 504 /* FAN */ 505 DPRINTF(("FAN\n")); 506 ykbec_write(sc, REG_FAN_CONTROL, REG_FAN_ON); 507 508 /* USB */ 509 DPRINTF(("USB\n")); 510 ykbec_write(sc, REG_USB0, USB_FLAG_ON); 511 ykbec_write(sc, REG_USB1, USB_FLAG_ON); 512 ykbec_write(sc, REG_USB2, USB_FLAG_ON); 513 514 ykbec_refresh(sc); 515 516 return 0; 517 } 518 #endif 519 520 #if NPCKBD > 0 || NHIDKBD > 0 521 void 522 ykbec_bell(void *arg, u_int pitch, u_int period, u_int volume, int poll) 523 { 524 struct ykbec_softc *sc = (struct ykbec_softc *)arg; 525 int bctrl; 526 int s; 527 528 s = spltty(); 529 bctrl = ykbec_read(sc, REG_BEEP_CONTROL); 530 if (timeout_del(&sc->sc_bell_tmo) || volume == 0) { 531 /* inline ykbec_bell_stop(arg); */ 532 ykbec_write(sc, REG_BEEP_CONTROL, bctrl & ~BEEP_ENABLE); 533 } 534 535 if (volume != 0) { 536 ykbec_write(sc, REG_BEEP_CONTROL, bctrl | BEEP_ENABLE); 537 if (poll) { 538 delay(period * 1000); 539 ykbec_write(sc, REG_BEEP_CONTROL, bctrl & ~BEEP_ENABLE); 540 } else { 541 timeout_add_msec(&sc->sc_bell_tmo, period); 542 } 543 } 544 splx(s); 545 } 546 547 void 548 ykbec_bell_stop(void *arg) 549 { 550 struct ykbec_softc *sc = (struct ykbec_softc *)arg; 551 int s; 552 553 s = spltty(); 554 ykbec_write(sc, REG_BEEP_CONTROL, 555 ykbec_read(sc, REG_BEEP_CONTROL) & ~BEEP_ENABLE); 556 splx(s); 557 } 558 #endif 559