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