[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     ! 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