diff options
author | Paul Irofti <pirofti@cvs.openbsd.org> | 2010-10-14 21:23:06 +0000 |
---|---|---|
committer | Paul Irofti <pirofti@cvs.openbsd.org> | 2010-10-14 21:23:06 +0000 |
commit | 9c9cf5e82e30158f5b322404363bb7ad65ffbe6d (patch) | |
tree | b70fe0c98526bc9850ed7f97363efb6b6756a764 /sys/dev/pci/glxpcib.c | |
parent | db174ed465747271bae79ec96239d613fc347f3d (diff) |
Move glxpcib(4) to MI land unifying the two MD drivers.
Specific features of the companion chip will be handled in MD land.
Okay deraadt@.
Diffstat (limited to 'sys/dev/pci/glxpcib.c')
-rw-r--r-- | sys/dev/pci/glxpcib.c | 465 |
1 files changed, 465 insertions, 0 deletions
diff --git a/sys/dev/pci/glxpcib.c b/sys/dev/pci/glxpcib.c new file mode 100644 index 00000000000..2577e470199 --- /dev/null +++ b/sys/dev/pci/glxpcib.c @@ -0,0 +1,465 @@ +/* $OpenBSD: glxpcib.c,v 1.1 2010/10/14 21:23:04 pirofti Exp $ */ + +/* + * Copyright (c) 2007 Marc Balmer <mbalmer@openbsd.org> + * Copyright (c) 2007 Michael Shalayeff + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * AMD CS5536 series LPC bridge also containing timer, watchdog, and GPIO. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/device.h> +#include <sys/gpio.h> +#include <sys/timetc.h> + +#include <machine/bus.h> +#ifdef __i386__ +#include <machine/cpufunc.h> +#endif + +#include <dev/gpio/gpiovar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> + +#include <dev/pci/glxreg.h> +#include <dev/pci/glxvar.h> + +#include "gpio.h" + +#define AMD5536_REV GLCP_CHIP_REV_ID +#define AMD5536_REV_MASK 0xff +#define AMD5536_TMC PMC_LTMR + +#define MSR_LBAR_ENABLE 0x100000000ULL + +/* Multi-Functional General Purpose Timer */ +#define MSR_LBAR_MFGPT DIVIL_LBAR_MFGPT +#define MSR_MFGPT_SIZE 0x40 +#define MSR_MFGPT_ADDR_MASK 0xffc0 +#define AMD5536_MFGPT0_CMP1 0x00000000 +#define AMD5536_MFGPT0_CMP2 0x00000002 +#define AMD5536_MFGPT0_CNT 0x00000004 +#define AMD5536_MFGPT0_SETUP 0x00000006 +#define AMD5536_MFGPT_DIV_MASK 0x000f /* div = 1 << mask */ +#define AMD5536_MFGPT_CLKSEL 0x0010 +#define AMD5536_MFGPT_REV_EN 0x0020 +#define AMD5536_MFGPT_CMP1DIS 0x0000 +#define AMD5536_MFGPT_CMP1EQ 0x0040 +#define AMD5536_MFGPT_CMP1GE 0x0080 +#define AMD5536_MFGPT_CMP1EV 0x00c0 +#define AMD5536_MFGPT_CMP2DIS 0x0000 +#define AMD5536_MFGPT_CMP2EQ 0x0100 +#define AMD5536_MFGPT_CMP2GE 0x0200 +#define AMD5536_MFGPT_CMP2EV 0x0300 +#define AMD5536_MFGPT_STOP_EN 0x0800 +#define AMD5536_MFGPT_SET 0x1000 +#define AMD5536_MFGPT_CMP1 0x2000 +#define AMD5536_MFGPT_CMP2 0x4000 +#define AMD5536_MFGPT_CNT_EN 0x8000 +#define AMD5536_MFGPT_IRQ MFGPT_IRQ +#define AMD5536_MFGPT0_C1_IRQM 0x00000001 +#define AMD5536_MFGPT1_C1_IRQM 0x00000002 +#define AMD5536_MFGPT2_C1_IRQM 0x00000004 +#define AMD5536_MFGPT3_C1_IRQM 0x00000008 +#define AMD5536_MFGPT4_C1_IRQM 0x00000010 +#define AMD5536_MFGPT5_C1_IRQM 0x00000020 +#define AMD5536_MFGPT6_C1_IRQM 0x00000040 +#define AMD5536_MFGPT7_C1_IRQM 0x00000080 +#define AMD5536_MFGPT0_C2_IRQM 0x00000100 +#define AMD5536_MFGPT1_C2_IRQM 0x00000200 +#define AMD5536_MFGPT2_C2_IRQM 0x00000400 +#define AMD5536_MFGPT3_C2_IRQM 0x00000800 +#define AMD5536_MFGPT4_C2_IRQM 0x00001000 +#define AMD5536_MFGPT5_C2_IRQM 0x00002000 +#define AMD5536_MFGPT6_C2_IRQM 0x00004000 +#define AMD5536_MFGPT7_C2_IRQM 0x00008000 +#define AMD5536_MFGPT_NR MFGPT_NR +#define AMD5536_MFGPT0_C1_NMIM 0x00000001 +#define AMD5536_MFGPT1_C1_NMIM 0x00000002 +#define AMD5536_MFGPT2_C1_NMIM 0x00000004 +#define AMD5536_MFGPT3_C1_NMIM 0x00000008 +#define AMD5536_MFGPT4_C1_NMIM 0x00000010 +#define AMD5536_MFGPT5_C1_NMIM 0x00000020 +#define AMD5536_MFGPT6_C1_NMIM 0x00000040 +#define AMD5536_MFGPT7_C1_NMIM 0x00000080 +#define AMD5536_MFGPT0_C2_NMIM 0x00000100 +#define AMD5536_MFGPT1_C2_NMIM 0x00000200 +#define AMD5536_MFGPT2_C2_NMIM 0x00000400 +#define AMD5536_MFGPT3_C2_NMIM 0x00000800 +#define AMD5536_MFGPT4_C2_NMIM 0x00001000 +#define AMD5536_MFGPT5_C2_NMIM 0x00002000 +#define AMD5536_MFGPT6_C2_NMIM 0x00004000 +#define AMD5536_MFGPT7_C2_NMIM 0x00008000 +#define AMD5536_NMI_LEG 0x00010000 +#define AMD5536_MFGPT0_C2_RSTEN 0x01000000 +#define AMD5536_MFGPT1_C2_RSTEN 0x02000000 +#define AMD5536_MFGPT2_C2_RSTEN 0x04000000 +#define AMD5536_MFGPT3_C2_RSTEN 0x08000000 +#define AMD5536_MFGPT4_C2_RSTEN 0x10000000 +#define AMD5536_MFGPT5_C2_RSTEN 0x20000000 +#define AMD5536_MFGPT_SETUP MFGPT_SETUP + +/* GPIO */ +#define MSR_LBAR_GPIO DIVIL_LBAR_GPIO +#define MSR_GPIO_SIZE 0x100 +#define MSR_GPIO_ADDR_MASK 0xff00 +#define AMD5536_GPIO_NPINS 32 +#define AMD5536_GPIOH_OFFSET 0x80 /* high bank register offset */ +#define AMD5536_GPIO_OUT_VAL 0x00 /* output value */ +#define AMD5536_GPIO_OUT_EN 0x04 /* output enable */ +#define AMD5536_GPIO_OD_EN 0x08 /* open-drain enable */ +#define AMD5536_GPIO_OUT_INVRT_EN 0x0c /* invert output */ +#define AMD5536_GPIO_PU_EN 0x18 /* pull-up enable */ +#define AMD5536_GPIO_PD_EN 0x1c /* pull-down enable */ +#define AMD5536_GPIO_IN_EN 0x20 /* input enable */ +#define AMD5536_GPIO_IN_INVRT_EN 0x24 /* invert input */ +#define AMD5536_GPIO_READ_BACK 0x30 /* read back value */ + +/* + * MSR registers we want to preserve accross suspend/resume + */ +const uint32_t glxpcib_msrlist[] = { + GLIU_PAE, + GLCP_GLD_MSR_PM, + DIVIL_BALL_OPTS +}; + +struct glxpcib_softc { + struct device sc_dev; + + struct timecounter sc_timecounter; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + uint64_t sc_msrsave[nitems(glxpcib_msrlist)]; + +#if !defined(SMALL_KERNEL) && NGPIO > 0 + /* GPIO interface */ + bus_space_tag_t sc_gpio_iot; + bus_space_handle_t sc_gpio_ioh; + struct gpio_chipset_tag sc_gpio_gc; + gpio_pin_t sc_gpio_pins[AMD5536_GPIO_NPINS]; + int sc_wdog; + int sc_wdog_period; +#endif +}; + +struct cfdriver glxpcib_cd = { + NULL, "glxpcib", DV_DULL +}; + +int glxpcib_match(struct device *, void *, void *); +void glxpcib_attach(struct device *, struct device *, void *); +int glxpcib_activate(struct device *, int); + +struct cfattach glxpcib_ca = { + sizeof(struct glxpcib_softc), glxpcib_match, glxpcib_attach, + NULL, glxpcib_activate +}; + +/* from arch/<*>/pci/pcib.c */ +void pcibattach(struct device *parent, struct device *self, void *aux); + +u_int glxpcib_get_timecount(struct timecounter *tc); + +#if !defined(SMALL_KERNEL) && NGPIO > 0 +void glxpcib_gpio_pin_ctl(void *, int, int); +int glxpcib_gpio_pin_read(void *, int); +void glxpcib_gpio_pin_write(void *, int, int); +int glxpcib_wdogctl_cb(void *, int); +#endif + +const struct pci_matchid glxpcib_devices[] = { + { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_CS5536_PCIB } +}; + +int +glxpcib_match(struct device *parent, void *match, void *aux) +{ + if (pci_matchbyid((struct pci_attach_args *)aux, glxpcib_devices, + nitems(glxpcib_devices))) { + /* needs to win over pcib */ + return 2; + } + + return 0; +} + +void +glxpcib_attach(struct device *parent, struct device *self, void *aux) +{ + struct glxpcib_softc *sc = (struct glxpcib_softc *)self; + struct timecounter *tc = &sc->sc_timecounter; +#if !defined(SMALL_KERNEL) && NGPIO > 0 + struct pci_attach_args *pa = (struct pci_attach_args *)aux; + u_int64_t wa, ga; + struct gpiobus_attach_args gba; + int i, gpio = 0; +#endif + tc->tc_get_timecount = glxpcib_get_timecount; + tc->tc_counter_mask = 0xffffffff; + tc->tc_frequency = 3579545; + tc->tc_name = "CS5536"; +#ifdef __loongson__ + tc->tc_quality = 0; +#else + tc->tc_quality = 1000; +#endif + tc->tc_priv = sc; + tc_init(tc); + + printf(": rev %d, 32-bit %lluHz timer", + (int)rdmsr(AMD5536_REV) & AMD5536_REV_MASK, + tc->tc_frequency); + +#if !defined(SMALL_KERNEL) && NGPIO > 0 + /* Attach the watchdog timer */ + sc->sc_iot = pa->pa_iot; + wa = rdmsr(MSR_LBAR_MFGPT); + if (wa & MSR_LBAR_ENABLE && + !bus_space_map(sc->sc_iot, wa & MSR_MFGPT_ADDR_MASK, + MSR_MFGPT_SIZE, 0, &sc->sc_ioh)) { + /* count in seconds (as upper level desires) */ + bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT0_SETUP, + AMD5536_MFGPT_CNT_EN | AMD5536_MFGPT_CMP2EV | + AMD5536_MFGPT_CMP2 | AMD5536_MFGPT_DIV_MASK); + wdog_register(sc, glxpcib_wdogctl_cb); + sc->sc_wdog = 1; + printf(", watchdog"); + } + + /* map GPIO I/O space */ + sc->sc_gpio_iot = pa->pa_iot; + ga = rdmsr(MSR_LBAR_GPIO); + if (ga & MSR_LBAR_ENABLE && + !bus_space_map(sc->sc_gpio_iot, ga & MSR_GPIO_ADDR_MASK, + MSR_GPIO_SIZE, 0, &sc->sc_gpio_ioh)) { + printf(", gpio"); + + /* initialize pin array */ + for (i = 0; i < AMD5536_GPIO_NPINS; i++) { + sc->sc_gpio_pins[i].pin_num = i; + sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT | + GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN | + GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | + GPIO_PIN_INVIN | GPIO_PIN_INVOUT; + + /* read initial state */ + sc->sc_gpio_pins[i].pin_state = + glxpcib_gpio_pin_read(sc, i); + } + + /* create controller tag */ + sc->sc_gpio_gc.gp_cookie = sc; + sc->sc_gpio_gc.gp_pin_read = glxpcib_gpio_pin_read; + sc->sc_gpio_gc.gp_pin_write = glxpcib_gpio_pin_write; + sc->sc_gpio_gc.gp_pin_ctl = glxpcib_gpio_pin_ctl; + + gba.gba_name = "gpio"; + gba.gba_gc = &sc->sc_gpio_gc; + gba.gba_pins = sc->sc_gpio_pins; + gba.gba_npins = AMD5536_GPIO_NPINS; + gpio = 1; + + } +#endif + pcibattach(parent, self, aux); + +#if !defined(SMALL_KERNEL) && NGPIO > 0 + if (gpio) + config_found(&sc->sc_dev, &gba, gpiobus_print); +#endif +} + +int +glxpcib_activate(struct device *self, int act) +{ +#ifndef SMALL_KERNEL + struct glxpcib_softc *sc = (struct glxpcib_softc *)self; +#endif + int rv = 0; + uint i; + + switch (act) { + case DVACT_QUIESCE: + rv = config_activate_children(self, act); + break; + case DVACT_SUSPEND: +#ifndef SMALL_KERNEL + if (sc->sc_wdog) { + sc->sc_wdog_period = bus_space_read_2(sc->sc_iot, + sc->sc_ioh, AMD5536_MFGPT0_CMP2); + glxpcib_wdogctl_cb(sc, 0); + } +#endif + rv = config_activate_children(self, act); + for (i = 0; i < nitems(glxpcib_msrlist); i++) + sc->sc_msrsave[i] = rdmsr(glxpcib_msrlist[i]); + + break; + case DVACT_RESUME: +#ifndef SMALL_KERNEL + if (sc->sc_wdog) + glxpcib_wdogctl_cb(sc, sc->sc_wdog_period); +#endif + for (i = 0; i < nitems(glxpcib_msrlist); i++) + wrmsr(glxpcib_msrlist[i], sc->sc_msrsave[i]); + rv = config_activate_children(self, act); + break; + } + return (rv); +} + +u_int +glxpcib_get_timecount(struct timecounter *tc) +{ + return rdmsr(AMD5536_TMC); +} + +#if !defined(SMALL_KERNEL) && NGPIO > 0 +int +glxpcib_wdogctl_cb(void *v, int period) +{ + struct glxpcib_softc *sc = v; + + if (period > 0xffff) + period = 0xffff; + + bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT0_SETUP, + AMD5536_MFGPT_CNT_EN | AMD5536_MFGPT_CMP2); + bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT0_CNT, 0); + bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT0_CMP2, period); + + if (period) + wrmsr(AMD5536_MFGPT_NR, + rdmsr(AMD5536_MFGPT_NR) | AMD5536_MFGPT0_C2_RSTEN); + else + wrmsr(AMD5536_MFGPT_NR, + rdmsr(AMD5536_MFGPT_NR) & ~AMD5536_MFGPT0_C2_RSTEN); + + return period; +} + +int +glxpcib_gpio_pin_read(void *arg, int pin) +{ + struct glxpcib_softc *sc = arg; + u_int32_t data; + int reg, off = 0; + + reg = AMD5536_GPIO_IN_EN; + if (pin > 15) { + pin &= 0x0f; + off = AMD5536_GPIOH_OFFSET; + } + reg += off; + data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg); + + if (data & (1 << pin)) + reg = AMD5536_GPIO_READ_BACK + off; + else + reg = AMD5536_GPIO_OUT_VAL + off; + + data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg); + + return data & 1 << pin ? GPIO_PIN_HIGH : GPIO_PIN_LOW; +} + +void +glxpcib_gpio_pin_write(void *arg, int pin, int value) +{ + struct glxpcib_softc *sc = arg; + u_int32_t data; + int reg; + + reg = AMD5536_GPIO_OUT_VAL; + if (pin > 15) { + pin &= 0x0f; + reg += AMD5536_GPIOH_OFFSET; + } + if (value == 1) + data = 1 << pin; + else + data = 1 << (pin + 16); + + bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg, data); +} + +void +glxpcib_gpio_pin_ctl(void *arg, int pin, int flags) +{ + struct glxpcib_softc *sc = arg; + int n, reg[7], val[7], nreg = 0, off = 0; + + if (pin > 15) { + pin &= 0x0f; + off = AMD5536_GPIOH_OFFSET; + } + + reg[nreg] = AMD5536_GPIO_IN_EN + off; + if (flags & GPIO_PIN_INPUT) + val[nreg++] = 1 << pin; + else + val[nreg++] = 1 << (pin + 16); + + reg[nreg] = AMD5536_GPIO_OUT_EN + off; + if (flags & GPIO_PIN_OUTPUT) + val[nreg++] = 1 << pin; + else + val[nreg++] = 1 << (pin + 16); + + reg[nreg] = AMD5536_GPIO_OD_EN + off; + if (flags & GPIO_PIN_OPENDRAIN) + val[nreg++] = 1 << pin; + else + val[nreg++] = 1 << (pin + 16); + + reg[nreg] = AMD5536_GPIO_PU_EN + off; + if (flags & GPIO_PIN_PULLUP) + val[nreg++] = 1 << pin; + else + val[nreg++] = 1 << (pin + 16); + + reg[nreg] = AMD5536_GPIO_PD_EN + off; + if (flags & GPIO_PIN_PULLDOWN) + val[nreg++] = 1 << pin; + else + val[nreg++] = 1 << (pin + 16); + + reg[nreg] = AMD5536_GPIO_IN_INVRT_EN + off; + if (flags & GPIO_PIN_INVIN) + val[nreg++] = 1 << pin; + else + val[nreg++] = 1 << (pin + 16); + + reg[nreg] = AMD5536_GPIO_OUT_INVRT_EN + off; + if (flags & GPIO_PIN_INVOUT) + val[nreg++] = 1 << pin; + else + val[nreg++] = 1 << (pin + 16); + + /* set flags */ + for (n = 0; n < nreg; n++) + bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg[n], + val[n]); +} + +#endif |