root/arch/i386/pci/elan520.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. elansc_match
  2. elansc_attach
  3. elansc_tc_read
  4. elansc_wdogctl
  5. elansc_wdogctl_cb
  6. elansc_update_cpuspeed
  7. elansc_setperf
  8. elansc_cpuspeed
  9. elansc_gpio_pin_read
  10. elansc_gpio_pin_write
  11. elansc_gpio_pin_ctl

    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 }

/* [<][>][^][v][top][bottom][index][help] */