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