[BACK]Return to elan520.c CVS log [TXT][DIR] Up to [local] / sys / arch / i386 / pci

Annotation of sys/arch/i386/pci/elan520.c, Revision 1.1.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