Annotation of sys/arch/i386/pci/elan520.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: elan520.c,v 1.13 2007/05/23 11:55:11 markus Exp $ */
! 2: /* $NetBSD: elan520.c,v 1.4 2002/10/02 05:47:15 thorpej Exp $ */
! 3:
! 4: /*-
! 5: * Copyright (c) 2002 The NetBSD Foundation, Inc.
! 6: * All rights reserved.
! 7: *
! 8: * This code is derived from software contributed to The NetBSD Foundation
! 9: * by Jason R. Thorpe.
! 10: *
! 11: * Redistribution and use in source and binary forms, with or without
! 12: * modification, are permitted provided that the following conditions
! 13: * are met:
! 14: * 1. Redistributions of source code must retain the above copyright
! 15: * notice, this list of conditions and the following disclaimer.
! 16: * 2. Redistributions in binary form must reproduce the above copyright
! 17: * notice, this list of conditions and the following disclaimer in the
! 18: * documentation and/or other materials provided with the distribution.
! 19: * 3. All advertising materials mentioning features or use of this software
! 20: * must display the following acknowledgement:
! 21: * This product includes software developed by the NetBSD
! 22: * Foundation, Inc. and its contributors.
! 23: * 4. Neither the name of The NetBSD Foundation nor the names of its
! 24: * contributors may be used to endorse or promote products derived
! 25: * from this software without specific prior written permission.
! 26: *
! 27: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
! 28: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
! 29: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
! 30: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
! 31: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
! 32: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
! 33: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
! 34: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
! 35: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
! 36: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
! 37: * POSSIBILITY OF SUCH DAMAGE.
! 38: */
! 39:
! 40: /*
! 41: * Device driver for the AMD Elan SC520 System Controller. This attaches
! 42: * where the "pchb" driver might normally attach, and provides support for
! 43: * extra features on the SC520, such as the watchdog timer and GPIO.
! 44: */
! 45:
! 46: #include <sys/param.h>
! 47: #include <sys/systm.h>
! 48: #include <sys/device.h>
! 49: #include <sys/gpio.h>
! 50: #include <sys/sysctl.h>
! 51: #include <sys/time.h>
! 52: #include <sys/timetc.h>
! 53:
! 54: #include <machine/bus.h>
! 55:
! 56: #include <dev/pci/pcivar.h>
! 57: #include <dev/pci/pcidevs.h>
! 58:
! 59: #include <dev/gpio/gpiovar.h>
! 60:
! 61: #include <arch/i386/pci/elan520reg.h>
! 62:
! 63: struct elansc_softc {
! 64: struct device sc_dev;
! 65: bus_space_tag_t sc_memt;
! 66: bus_space_handle_t sc_memh;
! 67:
! 68: /* GPIO interface */
! 69: struct gpio_chipset_tag sc_gpio_gc;
! 70: gpio_pin_t sc_gpio_pins[ELANSC_PIO_NPINS];
! 71:
! 72: /* GP timer */
! 73: struct timecounter sc_tc;
! 74: } *elansc;
! 75:
! 76: int elansc_match(struct device *, void *, void *);
! 77: void elansc_attach(struct device *, struct device *, void *);
! 78: void elansc_update_cpuspeed(void);
! 79: void elansc_setperf(int);
! 80: int elansc_cpuspeed(int *);
! 81:
! 82: void elansc_wdogctl(struct elansc_softc *, int, uint16_t);
! 83: #define elansc_wdogctl_reset(sc) elansc_wdogctl(sc, 1, 0)
! 84: #define elansc_wdogctl_write(sc, val) elansc_wdogctl(sc, 0, val)
! 85: int elansc_wdogctl_cb(void *, int);
! 86:
! 87: int elansc_gpio_pin_read(void *, int);
! 88: void elansc_gpio_pin_write(void *, int, int);
! 89: void elansc_gpio_pin_ctl(void *, int, int);
! 90:
! 91: u_int elansc_tc_read(struct timecounter *);
! 92:
! 93: struct cfattach elansc_ca = {
! 94: sizeof(struct elansc_softc), elansc_match, elansc_attach
! 95: };
! 96:
! 97: struct cfdriver elansc_cd = {
! 98: NULL, "elansc", DV_DULL
! 99: };
! 100:
! 101: static int cpuspeed;
! 102:
! 103: int
! 104: elansc_match(struct device *parent, void *match, void *aux)
! 105: {
! 106: struct pci_attach_args *pa = aux;
! 107:
! 108: if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD &&
! 109: PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_ELANSC520)
! 110: return (10); /* beat pchb */
! 111:
! 112: return (0);
! 113: }
! 114:
! 115: static const char *elansc_speeds[] = {
! 116: "(reserved 00)",
! 117: "100MHz",
! 118: "133MHz",
! 119: "(reserved 11)",
! 120: };
! 121:
! 122: #define RSTBITS "\20\x07SCP\x06HRST\x05SRST\x04WDT\x03SD\x02PRGRST\x01PWRGOOD"
! 123:
! 124: void
! 125: elansc_attach(struct device *parent, struct device *self, void *aux)
! 126: {
! 127: struct elansc_softc *sc = (void *) self;
! 128: struct pci_attach_args *pa = aux;
! 129: struct gpiobus_attach_args gba;
! 130: struct timecounter *tc;
! 131: uint16_t rev, data;
! 132: uint8_t ressta, cpuctl, tmr;
! 133: int pin, reg, shift;
! 134:
! 135: sc->sc_memt = pa->pa_memt;
! 136: if (bus_space_map(sc->sc_memt, MMCR_BASE_ADDR, NBPG, 0,
! 137: &sc->sc_memh) != 0) {
! 138: printf(": unable to map registers\n");
! 139: return;
! 140: }
! 141:
! 142: rev = bus_space_read_2(sc->sc_memt, sc->sc_memh, MMCR_REVID);
! 143: cpuctl = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_CPUCTL);
! 144: ressta = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_RESSTA);
! 145:
! 146: printf(": product %d stepping %d.%d, CPU clock %s, reset %b\n",
! 147: (rev & REVID_PRODID) >> REVID_PRODID_SHIFT,
! 148: (rev & REVID_MAJSTEP) >> REVID_MAJSTEP_SHIFT,
! 149: (rev & REVID_MINSTEP),
! 150: elansc_speeds[cpuctl & CPUCTL_CPU_CLK_SPD_MASK],
! 151: ressta, RSTBITS);
! 152:
! 153: /*
! 154: * Determine cause of the last reset, and issue a warning if it
! 155: * was due to watchdog expiry.
! 156: */
! 157: if (ressta & RESSTA_WDT_RST_DET)
! 158: printf("%s: WARNING: LAST RESET DUE TO WATCHDOG EXPIRATION!\n",
! 159: sc->sc_dev.dv_xname);
! 160: bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_RESSTA, ressta);
! 161:
! 162: /* Set up the watchdog registers with some defaults. */
! 163: elansc_wdogctl_write(sc, WDTMRCTL_WRST_ENB | WDTMRCTL_EXP_SEL30);
! 164:
! 165: /* ...and clear it. */
! 166: elansc_wdogctl_reset(sc);
! 167:
! 168: wdog_register(sc, elansc_wdogctl_cb);
! 169: elansc = sc;
! 170: cpu_setperf = elansc_setperf;
! 171: cpu_cpuspeed = elansc_cpuspeed;
! 172: elansc_update_cpuspeed();
! 173:
! 174: /* Initialize GPIO pins array */
! 175: for (pin = 0; pin < ELANSC_PIO_NPINS; pin++) {
! 176: sc->sc_gpio_pins[pin].pin_num = pin;
! 177: sc->sc_gpio_pins[pin].pin_caps = GPIO_PIN_INPUT |
! 178: GPIO_PIN_OUTPUT;
! 179:
! 180: /* Read initial state */
! 181: reg = (pin < 16 ? MMCR_PIODIR15_0 : MMCR_PIODIR31_16);
! 182: shift = pin % 16;
! 183: data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg);
! 184: if ((data & (1 << shift)) == 0)
! 185: sc->sc_gpio_pins[pin].pin_flags = GPIO_PIN_INPUT;
! 186: else
! 187: sc->sc_gpio_pins[pin].pin_flags = GPIO_PIN_OUTPUT;
! 188: if (elansc_gpio_pin_read(sc, pin) == 0)
! 189: sc->sc_gpio_pins[pin].pin_state = GPIO_PIN_LOW;
! 190: else
! 191: sc->sc_gpio_pins[pin].pin_state = GPIO_PIN_HIGH;
! 192: }
! 193:
! 194: /* Create controller tag */
! 195: sc->sc_gpio_gc.gp_cookie = sc;
! 196: sc->sc_gpio_gc.gp_pin_read = elansc_gpio_pin_read;
! 197: sc->sc_gpio_gc.gp_pin_write = elansc_gpio_pin_write;
! 198: sc->sc_gpio_gc.gp_pin_ctl = elansc_gpio_pin_ctl;
! 199:
! 200: gba.gba_name = "gpio";
! 201: gba.gba_gc = &sc->sc_gpio_gc;
! 202: gba.gba_pins = sc->sc_gpio_pins;
! 203: gba.gba_npins = ELANSC_PIO_NPINS;
! 204:
! 205: /* Attach GPIO framework */
! 206: config_found(&sc->sc_dev, &gba, gpiobus_print);
! 207:
! 208: /* Disable GP1/2, clear the current count, and set the period to max */
! 209: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR1CTL,
! 210: GPTMRCTL_ENB_WR | GPTMRCTL_CONT_CMP |
! 211: GPTMRCTL_PSC_SEL | GPTMRCTL_RTG);
! 212: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR1CNT, 0);
! 213: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR1MAXCMPA, 0);
! 214:
! 215: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR2CTL,
! 216: GPTMRCTL_ENB_WR | GPTMRCTL_CONT_CMP);
! 217: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR2CNT, 0);
! 218: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR2MAXCMPA, 0);
! 219:
! 220: tmr = bus_space_read_1(sc->sc_memt, sc->sc_memh, SWTMRCFG);
! 221:
! 222: /* Enable GP1/2 */
! 223: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR1CTL,
! 224: GPTMRCTL_ENB | GPTMRCTL_ENB_WR | GPTMRCTL_CONT_CMP |
! 225: GPTMRCTL_PSC_SEL | GPTMRCTL_RTG);
! 226: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR2CTL,
! 227: GPTMRCTL_ENB | GPTMRCTL_ENB_WR | GPTMRCTL_CONT_CMP);
! 228:
! 229: /* Attach timer */
! 230: tc = &sc->sc_tc;
! 231: tc->tc_get_timecount = elansc_tc_read;
! 232: tc->tc_poll_pps = NULL;
! 233: tc->tc_counter_mask = ~0;
! 234: tc->tc_frequency = (tmr & 1) ? (33000000 / 4) : (33333333 / 4);
! 235: tc->tc_name = sc->sc_dev.dv_xname;
! 236: tc->tc_quality = 1000;
! 237: tc->tc_priv = sc;
! 238: tc_init(tc);
! 239: }
! 240:
! 241: u_int
! 242: elansc_tc_read(struct timecounter *tc)
! 243: {
! 244: struct elansc_softc *sc = tc->tc_priv;
! 245: u_int32_t m1, m2, l;
! 246:
! 247: do {
! 248: m1 = bus_space_read_2(sc->sc_memt, sc->sc_memh, GPTMR1CNT);
! 249: l = bus_space_read_2(sc->sc_memt, sc->sc_memh, GPTMR2CNT);
! 250: m2 = bus_space_read_2(sc->sc_memt, sc->sc_memh, GPTMR1CNT);
! 251: } while (m1 != m2);
! 252:
! 253: return ((m1 << 16) | l);
! 254: }
! 255:
! 256: void
! 257: elansc_wdogctl(struct elansc_softc *sc, int do_reset, uint16_t val)
! 258: {
! 259: int s;
! 260: uint8_t echo_mode;
! 261:
! 262: s = splhigh();
! 263:
! 264: /* Switch off GP bus echo mode. */
! 265: echo_mode = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_GPECHO);
! 266: bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_GPECHO,
! 267: echo_mode & ~GPECHO_GP_ECHO_ENB);
! 268:
! 269: if (do_reset) {
! 270: /* Reset the watchdog. */
! 271: bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL,
! 272: WDTMRCTL_RESET1);
! 273: bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL,
! 274: WDTMRCTL_RESET2);
! 275: } else {
! 276: /* Unlock the register. */
! 277: bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL,
! 278: WDTMRCTL_UNLOCK1);
! 279: bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL,
! 280: WDTMRCTL_UNLOCK2);
! 281:
! 282: /* Write the value. */
! 283: bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL,
! 284: val);
! 285: }
! 286:
! 287: /* Switch GP bus echo mode back. */
! 288: bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_GPECHO, echo_mode);
! 289:
! 290: splx(s);
! 291: }
! 292:
! 293: static const struct {
! 294: int period; /* whole seconds */
! 295: uint16_t exp; /* exponent select */
! 296: } elansc_wdog_periods[] = {
! 297: { 1, WDTMRCTL_EXP_SEL25 },
! 298: { 2, WDTMRCTL_EXP_SEL26 },
! 299: { 4, WDTMRCTL_EXP_SEL27 },
! 300: { 8, WDTMRCTL_EXP_SEL28 },
! 301: { 16, WDTMRCTL_EXP_SEL29 },
! 302: { 32, WDTMRCTL_EXP_SEL30 },
! 303: };
! 304:
! 305: int
! 306: elansc_wdogctl_cb(void *self, int period)
! 307: {
! 308: struct elansc_softc *sc = self;
! 309: int i;
! 310:
! 311: if (period == 0) {
! 312: elansc_wdogctl_write(sc,
! 313: WDTMRCTL_WRST_ENB | WDTMRCTL_EXP_SEL30);
! 314: } else {
! 315: for (i = 0; i < (sizeof(elansc_wdog_periods) /
! 316: sizeof(elansc_wdog_periods[0])) - 1; i++)
! 317: if (elansc_wdog_periods[i].period >= period)
! 318: break;
! 319: period = elansc_wdog_periods[i].period;
! 320: elansc_wdogctl_write(sc, WDTMRCTL_ENB |
! 321: WDTMRCTL_WRST_ENB | elansc_wdog_periods[i].exp);
! 322: elansc_wdogctl_reset(sc);
! 323: }
! 324: return (period);
! 325: }
! 326:
! 327: void
! 328: elansc_update_cpuspeed(void)
! 329: {
! 330: #ifdef I586_CPU
! 331: static const int elansc_mhz[] = { 0, 100, 133, 999 };
! 332: #endif
! 333: uint8_t cpuctl;
! 334:
! 335: cpuctl = bus_space_read_1(elansc->sc_memt, elansc->sc_memh,
! 336: MMCR_CPUCTL);
! 337: #ifdef I586_CPU
! 338: cpuspeed = elansc_mhz[cpuctl & CPUCTL_CPU_CLK_SPD_MASK];
! 339: #endif
! 340: }
! 341:
! 342: void
! 343: elansc_setperf(int level)
! 344: {
! 345: uint32_t eflags;
! 346: uint8_t cpuctl, speed;
! 347:
! 348: level = (level > 50) ? 100 : 0;
! 349:
! 350: cpuctl = bus_space_read_1(elansc->sc_memt, elansc->sc_memh,
! 351: MMCR_CPUCTL);
! 352: speed = (level == 100) ? 2 : 1;
! 353: if ((cpuctl & CPUCTL_CPU_CLK_SPD_MASK) == speed)
! 354: return;
! 355:
! 356: eflags = read_eflags();
! 357: disable_intr();
! 358: bus_space_write_1(elansc->sc_memt, elansc->sc_memh, MMCR_CPUCTL,
! 359: (cpuctl & ~CPUCTL_CPU_CLK_SPD_MASK) | speed);
! 360: enable_intr();
! 361: write_eflags(eflags);
! 362:
! 363: elansc_update_cpuspeed();
! 364: }
! 365:
! 366: int
! 367: elansc_cpuspeed(int *freq)
! 368: {
! 369: *freq = cpuspeed;
! 370: return (0);
! 371: }
! 372:
! 373: int
! 374: elansc_gpio_pin_read(void *arg, int pin)
! 375: {
! 376: struct elansc_softc *sc = arg;
! 377: int reg, shift;
! 378: u_int16_t data;
! 379:
! 380: reg = (pin < 16 ? MMCR_PIODATA15_0 : MMCR_PIODATA31_16);
! 381: shift = pin % 16;
! 382: data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg);
! 383:
! 384: return ((data >> shift) & 0x1);
! 385: }
! 386:
! 387: void
! 388: elansc_gpio_pin_write(void *arg, int pin, int value)
! 389: {
! 390: struct elansc_softc *sc = arg;
! 391: int reg, shift;
! 392: u_int16_t data;
! 393:
! 394: reg = (pin < 16 ? MMCR_PIODATA15_0 : MMCR_PIODATA31_16);
! 395: shift = pin % 16;
! 396: data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg);
! 397: if (value == 0)
! 398: data &= ~(1 << shift);
! 399: else if (value == 1)
! 400: data |= (1 << shift);
! 401:
! 402: bus_space_write_2(sc->sc_memt, sc->sc_memh, reg, data);
! 403: }
! 404:
! 405: void
! 406: elansc_gpio_pin_ctl(void *arg, int pin, int flags)
! 407: {
! 408: struct elansc_softc *sc = arg;
! 409: int reg, shift;
! 410: u_int16_t data;
! 411:
! 412: reg = (pin < 16 ? MMCR_PIODIR15_0 : MMCR_PIODIR31_16);
! 413: shift = pin % 16;
! 414: data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg);
! 415: if (flags & GPIO_PIN_INPUT)
! 416: data &= ~(1 << shift);
! 417: if (flags & GPIO_PIN_OUTPUT)
! 418: data |= (1 << shift);
! 419:
! 420: bus_space_write_2(sc->sc_memt, sc->sc_memh, reg, data);
! 421: }
CVSweb