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