Annotation of sys/arch/zaurus/dev/zaurus_scoop.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: zaurus_scoop.c,v 1.16 2007/03/29 18:42:38 uwe Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2005 Uwe Stuehler <uwe@bsdx.de>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: #include <sys/param.h>
! 20: #include <sys/device.h>
! 21: #include <sys/kernel.h>
! 22: #include <sys/systm.h>
! 23: #include <sys/conf.h>
! 24: #include <sys/disk.h>
! 25: #include <sys/timeout.h>
! 26: #include <sys/gpio.h>
! 27:
! 28: #include <machine/bus.h>
! 29:
! 30: #include <arm/xscale/pxa2x0var.h>
! 31:
! 32: #include <machine/zaurus_reg.h>
! 33: #include <machine/zaurus_var.h>
! 34:
! 35: #include <zaurus/dev/zaurus_scoopreg.h>
! 36: #include <zaurus/dev/zaurus_scoopvar.h>
! 37:
! 38: struct scoop_softc {
! 39: struct device sc_dev;
! 40: bus_space_tag_t sc_iot;
! 41: bus_space_handle_t sc_ioh;
! 42: u_int16_t sc_gpwr; /* GPIO state before suspend */
! 43: void *sc_powerhook;
! 44: int sc_suspended;
! 45: };
! 46:
! 47: int scoopmatch(struct device *, void *, void *);
! 48: void scoopattach(struct device *, struct device *, void *);
! 49:
! 50: struct cfattach scoop_ca = {
! 51: sizeof (struct scoop_softc), scoopmatch, scoopattach
! 52: };
! 53:
! 54: struct cfdriver scoop_cd = {
! 55: NULL, "scoop", DV_DULL
! 56: };
! 57:
! 58: enum card {
! 59: SD_CARD,
! 60: CF_CARD /* socket 0 (external) */
! 61: };
! 62:
! 63: int scoop_gpio_pin_read(struct scoop_softc *sc, int);
! 64: void scoop_gpio_pin_write(struct scoop_softc *sc, int, int);
! 65: void scoop_gpio_pin_ctl(struct scoop_softc *sc, int, int);
! 66: void scoop0_set_card_power(enum card, int);
! 67:
! 68: struct timeout scoop_checkdisk;
! 69: void scoop_timeout(void *);
! 70: void scoop_power(int, void *);
! 71:
! 72: int
! 73: scoopmatch(struct device *parent, void *match, void *aux)
! 74: {
! 75: struct cfdata *cf = match;
! 76:
! 77: /*
! 78: * Only C3000-like models are known to have two SCOOPs.
! 79: */
! 80: if (ZAURUS_ISC3000)
! 81: return (cf->cf_unit < 2);
! 82:
! 83: return (cf->cf_unit == 0);
! 84: }
! 85:
! 86: void
! 87: scoopattach(struct device *parent, struct device *self, void *aux)
! 88: {
! 89: struct pxaip_attach_args *pxa = aux;
! 90: struct scoop_softc *sc = (struct scoop_softc *)self;
! 91: bus_addr_t addr;
! 92: bus_size_t size;
! 93:
! 94: sc->sc_iot = pxa->pxa_iot;
! 95:
! 96: if (pxa->pxa_addr != -1)
! 97: addr = pxa->pxa_addr;
! 98: else if (sc->sc_dev.dv_unit == 0)
! 99: addr = C3000_SCOOP0_BASE;
! 100: else
! 101: addr = C3000_SCOOP1_BASE;
! 102:
! 103: size = pxa->pxa_size < SCOOP_SIZE ? SCOOP_SIZE : pxa->pxa_size;
! 104:
! 105: if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh) != 0) {
! 106: printf(": failed to map %s\n", sc->sc_dev.dv_xname);
! 107: return;
! 108: }
! 109:
! 110: if (ZAURUS_ISC3000 && sc->sc_dev.dv_unit == 1) {
! 111: scoop_gpio_pin_ctl(sc, SCOOP1_AKIN_PULLUP, GPIO_PIN_OUTPUT);
! 112: scoop_gpio_pin_write(sc, SCOOP1_AKIN_PULLUP, GPIO_PIN_LOW);
! 113: } else if (!ZAURUS_ISC3000) {
! 114: scoop_gpio_pin_ctl(sc, SCOOP0_AKIN_PULLUP, GPIO_PIN_OUTPUT);
! 115: scoop_gpio_pin_write(sc, SCOOP0_AKIN_PULLUP, GPIO_PIN_LOW);
! 116: }
! 117:
! 118: if (sc->sc_dev.dv_unit == 0)
! 119: timeout_set(&scoop_checkdisk, scoop_timeout, sc);
! 120:
! 121: printf(": PCMCIA/GPIO controller\n");
! 122:
! 123: sc->sc_powerhook = powerhook_establish(scoop_power, sc);
! 124: if (sc->sc_powerhook == NULL)
! 125: panic("Unable to establish %s powerhook",
! 126: sc->sc_dev.dv_xname);
! 127: }
! 128:
! 129: int
! 130: scoop_gpio_pin_read(struct scoop_softc *sc, int pin)
! 131: {
! 132: u_int16_t rv;
! 133: u_int16_t bit = (1 << pin);
! 134:
! 135: rv = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR);
! 136: return (rv & bit) != 0 ? 1 : 0;
! 137: }
! 138:
! 139: void
! 140: scoop_gpio_pin_write(struct scoop_softc *sc, int pin, int level)
! 141: {
! 142: u_int16_t rv;
! 143: u_int16_t bit = (1 << pin);
! 144: int s;
! 145:
! 146: s = splhigh();
! 147: rv = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR);
! 148: bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR,
! 149: level == GPIO_PIN_LOW ? (rv & ~bit) : (rv | bit));
! 150: splx(s);
! 151: }
! 152:
! 153: void
! 154: scoop_gpio_pin_ctl(struct scoop_softc *sc, int pin, int flags)
! 155: {
! 156: u_int16_t rv;
! 157: u_int16_t bit = (1 << pin);
! 158:
! 159: rv = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPCR);
! 160: switch (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
! 161: case GPIO_PIN_INPUT:
! 162: rv &= ~bit;
! 163: break;
! 164: case GPIO_PIN_OUTPUT:
! 165: rv |= bit;
! 166: break;
! 167: }
! 168: bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPCR, rv);
! 169: }
! 170:
! 171: /*
! 172: * Turn the LCD background light and contrast signal on or off.
! 173: */
! 174: void
! 175: scoop_set_backlight(int on, int cont)
! 176: {
! 177:
! 178: if (scoop_cd.cd_ndevs > 1 && scoop_cd.cd_devs[1] != NULL) {
! 179: /* C3000 */
! 180: scoop_gpio_pin_write(scoop_cd.cd_devs[1],
! 181: SCOOP1_BACKLIGHT_CONT, !cont);
! 182: scoop_gpio_pin_write(scoop_cd.cd_devs[1],
! 183: SCOOP1_BACKLIGHT_ON, on);
! 184: }
! 185: #if 0
! 186: else if (scoop_cd.cd_ndevs > 0 && scoop_cd.cd_devs[0] != NULL) {
! 187: scoop_gpio_pin_write(scoop_cd.cd_devs[0],
! 188: SCOOP0_BACKLIGHT_CONT, cont);
! 189: }
! 190: #endif
! 191: }
! 192:
! 193: /*
! 194: * Turn the infrared LED on or off (must be on while transmitting).
! 195: */
! 196: void
! 197: scoop_set_irled(int on)
! 198: {
! 199: if (scoop_cd.cd_ndevs > 1 && scoop_cd.cd_devs[1] != NULL)
! 200: /* IR_ON is inverted */
! 201: scoop_gpio_pin_write(scoop_cd.cd_devs[1],
! 202: SCOOP1_IR_ON, !on);
! 203: }
! 204:
! 205: /*
! 206: * Turn the green and orange LEDs on or off. If the orange LED is on,
! 207: * then it is wired to indicate if A/C is connected. The green LED has
! 208: * no such predefined function.
! 209: */
! 210: void
! 211: scoop_led_set(int led, int on)
! 212: {
! 213:
! 214: if (scoop_cd.cd_ndevs > 0 && scoop_cd.cd_devs[0] != NULL) {
! 215: if ((led & SCOOP_LED_GREEN) != 0)
! 216: scoop_gpio_pin_write(scoop_cd.cd_devs[0],
! 217: SCOOP0_LED_GREEN, on);
! 218: if (scoop_cd.cd_ndevs > 1 && (led & SCOOP_LED_ORANGE) != 0)
! 219: scoop_gpio_pin_write(scoop_cd.cd_devs[0],
! 220: SCOOP0_LED_ORANGE_C3000, on);
! 221: }
! 222: }
! 223:
! 224: /*
! 225: * Enable or disable the headphone output connection.
! 226: */
! 227: void
! 228: scoop_set_headphone(int on)
! 229: {
! 230: if (scoop_cd.cd_ndevs < 1 || scoop_cd.cd_devs[0] == NULL)
! 231: return;
! 232:
! 233: scoop_gpio_pin_ctl(scoop_cd.cd_devs[0], SCOOP0_MUTE_L,
! 234: GPIO_PIN_OUTPUT);
! 235: scoop_gpio_pin_ctl(scoop_cd.cd_devs[0], SCOOP0_MUTE_R,
! 236: GPIO_PIN_OUTPUT);
! 237:
! 238: if (on) {
! 239: scoop_gpio_pin_write(scoop_cd.cd_devs[0], SCOOP0_MUTE_L,
! 240: GPIO_PIN_HIGH);
! 241: scoop_gpio_pin_write(scoop_cd.cd_devs[0], SCOOP0_MUTE_R,
! 242: GPIO_PIN_HIGH);
! 243: } else {
! 244: scoop_gpio_pin_write(scoop_cd.cd_devs[0], SCOOP0_MUTE_L,
! 245: GPIO_PIN_LOW);
! 246: scoop_gpio_pin_write(scoop_cd.cd_devs[0], SCOOP0_MUTE_R,
! 247: GPIO_PIN_LOW);
! 248: }
! 249: }
! 250:
! 251: /*
! 252: * Enable or disable 3.3V power to the SD/MMC card slot.
! 253: */
! 254: void
! 255: scoop_set_sdmmc_power(int on)
! 256: {
! 257: scoop0_set_card_power(SD_CARD, on ? SCP_CPR_SD_3V : SCP_CPR_OFF);
! 258: }
! 259:
! 260: /*
! 261: * The Card Power Register of the first SCOOP unit controls the power
! 262: * for the first CompactFlash slot and the SD/MMC card slot as well.
! 263: */
! 264: void
! 265: scoop0_set_card_power(enum card slot, int new_cpr)
! 266: {
! 267: struct scoop_softc *sc = scoop_cd.cd_devs[0];
! 268: u_int16_t cpr;
! 269:
! 270: cpr = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SCOOP_CPR);
! 271: if (new_cpr & SCP_CPR_VOLTAGE_MSK) {
! 272: if (slot == CF_CARD)
! 273: cpr |= SCP_CPR_5V;
! 274: else if (slot == SD_CARD)
! 275: cpr |= SCP_CPR_SD_3V;
! 276:
! 277: scoop_gpio_pin_write(sc, SCOOP0_CF_POWER_C3000, 1);
! 278: if (!ISSET(cpr, SCP_CPR_5V) && !ISSET(cpr, SCP_CPR_SD_3V))
! 279: delay(5000);
! 280: bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_CPR,
! 281: cpr | new_cpr);
! 282: } else {
! 283: if (slot == CF_CARD)
! 284: cpr &= ~SCP_CPR_5V;
! 285: else if (slot == SD_CARD)
! 286: cpr &= ~SCP_CPR_SD_3V;
! 287:
! 288: if (!ISSET(cpr, SCP_CPR_5V) && !ISSET(cpr, SCP_CPR_SD_3V)) {
! 289: bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_CPR,
! 290: SCP_CPR_OFF);
! 291: delay(1000);
! 292: scoop_gpio_pin_write(sc, SCOOP0_CF_POWER_C3000, 0);
! 293: } else
! 294: bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_CPR,
! 295: cpr | new_cpr);
! 296: }
! 297: }
! 298:
! 299: /*
! 300: * Turn on pullup resistor while not reading the remote control.
! 301: */
! 302: void
! 303: scoop_akin_pullup(int enable)
! 304: {
! 305: if (scoop_cd.cd_ndevs > 1 && scoop_cd.cd_devs[1] != NULL)
! 306: scoop_gpio_pin_write(scoop_cd.cd_devs[1],
! 307: SCOOP1_AKIN_PULLUP, enable);
! 308: else
! 309: scoop_gpio_pin_write(scoop_cd.cd_devs[0],
! 310: SCOOP0_AKIN_PULLUP, enable);
! 311: }
! 312:
! 313: void
! 314: scoop_battery_temp_adc(int enable)
! 315: {
! 316:
! 317: if (scoop_cd.cd_ndevs > 0 && scoop_cd.cd_devs[0] != NULL)
! 318: scoop_gpio_pin_write(scoop_cd.cd_devs[0],
! 319: SCOOP0_ADC_TEMP_ON_C3000, enable);
! 320: }
! 321:
! 322: void
! 323: scoop_charge_battery(int enable, int voltage_high)
! 324: {
! 325:
! 326: if (scoop_cd.cd_ndevs > 0 && scoop_cd.cd_devs[0] != NULL) {
! 327: scoop_gpio_pin_write(scoop_cd.cd_devs[0],
! 328: SCOOP0_JK_B_C3000, voltage_high);
! 329: scoop_gpio_pin_write(scoop_cd.cd_devs[0],
! 330: SCOOP0_CHARGE_OFF_C3000, !enable);
! 331: }
! 332: }
! 333:
! 334: void
! 335: scoop_discharge_battery(int enable)
! 336: {
! 337:
! 338: if (scoop_cd.cd_ndevs > 0 && scoop_cd.cd_devs[0] != NULL)
! 339: scoop_gpio_pin_write(scoop_cd.cd_devs[0],
! 340: SCOOP0_JK_A_C3000, enable);
! 341: }
! 342:
! 343: /* XXX */
! 344: void scoop_check_mcr(void);
! 345: void
! 346: scoop_check_mcr(void)
! 347: {
! 348: struct scoop_softc *sc;
! 349:
! 350: /* C3000 */
! 351: if (scoop_cd.cd_ndevs > 1 && scoop_cd.cd_devs[1] != NULL) {
! 352:
! 353: sc = scoop_cd.cd_devs[0];
! 354: if ((bus_space_read_2(sc->sc_iot, sc->sc_ioh, SCOOP_MCR) &
! 355: 0x100) == 0)
! 356: bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_MCR,
! 357: 0x0101);
! 358:
! 359: sc = scoop_cd.cd_devs[1];
! 360: if ((bus_space_read_2(sc->sc_iot, sc->sc_ioh, SCOOP_MCR) &
! 361: 0x100) == 0)
! 362: bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_MCR,
! 363: 0x0101);
! 364: }
! 365: }
! 366:
! 367: void
! 368: scoop_suspend(void)
! 369: {
! 370: struct scoop_softc *sc;
! 371: u_int32_t rv;
! 372:
! 373: scoop_check_mcr();
! 374:
! 375: if (scoop_cd.cd_ndevs > 0 && scoop_cd.cd_devs[0] != NULL) {
! 376: sc = scoop_cd.cd_devs[0];
! 377: sc->sc_gpwr = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
! 378: SCOOP_GPWR);
! 379: /* C3000 */
! 380: bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR,
! 381: sc->sc_gpwr & ~((1<<SCOOP0_MUTE_L) | (1<<SCOOP0_MUTE_R) |
! 382: (1<<SCOOP0_JK_A_C3000) | (1<<SCOOP0_ADC_TEMP_ON_C3000) |
! 383: (1<<SCOOP0_LED_GREEN)));
! 384: }
! 385:
! 386: /* C3000 */
! 387: if (scoop_cd.cd_ndevs > 1 && scoop_cd.cd_devs[1] != NULL) {
! 388: sc = scoop_cd.cd_devs[1];
! 389: sc->sc_gpwr = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
! 390: SCOOP_GPWR);
! 391: bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR,
! 392: sc->sc_gpwr & ~((1<<SCOOP1_RESERVED_4) |
! 393: (1<<SCOOP1_RESERVED_5) | (1<<SCOOP1_RESERVED_6) |
! 394: (1<<SCOOP1_BACKLIGHT_CONT) | (1<<SCOOP1_BACKLIGHT_ON) |
! 395: (1<<SCOOP1_MIC_BIAS)));
! 396: rv = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR);
! 397: bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR,
! 398: rv | ((1<<SCOOP1_IR_ON) | (1<<SCOOP1_RESERVED_3)));
! 399: }
! 400: }
! 401:
! 402: void
! 403: scoop_resume(void)
! 404: {
! 405: struct scoop_softc *sc;
! 406:
! 407: scoop_check_mcr();
! 408:
! 409: if (scoop_cd.cd_ndevs > 0 && scoop_cd.cd_devs[0] != NULL) {
! 410: sc = scoop_cd.cd_devs[0];
! 411: bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR,
! 412: sc->sc_gpwr);
! 413: }
! 414:
! 415: if (scoop_cd.cd_ndevs > 1 && scoop_cd.cd_devs[1] != NULL) {
! 416: sc = scoop_cd.cd_devs[1];
! 417: bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCOOP_GPWR,
! 418: sc->sc_gpwr);
! 419: }
! 420: }
! 421:
! 422: void
! 423: scoop_timeout(void *v)
! 424: {
! 425: extern struct disklist_head disklist;
! 426: struct scoop_softc *sc = v;
! 427: static struct disk *dk;
! 428: static int state = 0;
! 429:
! 430: if (dk == NULL) {
! 431: for (dk = TAILQ_FIRST(&disklist); dk;
! 432: dk = TAILQ_NEXT(dk, dk_link))
! 433: if (dk->dk_name &&
! 434: strcmp(dk->dk_name, "wd0") == 0)
! 435: break;
! 436: }
! 437:
! 438: if (sc->sc_suspended)
! 439: state = -1;
! 440: else if (dk) {
! 441: int newstate = (dk->dk_busy ? 1 : 0);
! 442:
! 443: if (newstate != state) {
! 444: state = newstate;
! 445: scoop_led_set(SCOOP_LED_GREEN, newstate);
! 446: }
! 447: }
! 448: timeout_add(&scoop_checkdisk, hz/25);
! 449: }
! 450:
! 451: void
! 452: scoop_power(int why, void *arg)
! 453: {
! 454: struct scoop_softc *sc = arg;
! 455:
! 456: switch (why) {
! 457: case PWR_STANDBY:
! 458: case PWR_SUSPEND:
! 459: /*
! 460: * Nothing should use the scoop from this point on.
! 461: * No timeouts, no interrupts (even though interrupts
! 462: * are still enabled). scoop_timeout() respects the
! 463: * sc_suspended flag.
! 464: */
! 465: if (sc->sc_dev.dv_unit == 0) {
! 466: sc->sc_suspended = 1;
! 467: scoop_suspend();
! 468: }
! 469: break;
! 470: case PWR_RESUME:
! 471: if (sc->sc_dev.dv_unit == 0) {
! 472: scoop_resume();
! 473: sc->sc_suspended = 0;
! 474: }
! 475: break;
! 476: }
! 477: }
CVSweb