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