Index: i386/i386/soekris_gpio.c =================================================================== --- i386/i386/soekris_gpio.c (revision 0) +++ i386/i386/soekris_gpio.c (working copy) @@ -0,0 +1,476 @@ +/*- + * Copyright (c) 2013 Oleksandr Tymoshenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpio_if.h" + +#define SUPERIO_INDEX 0x2e +#define SUPERIO_DATA 0x2f +#define SUPERIO_ID_ADD 0x20 +#define SUPERIO_ACTIVATE 0x30 +#define SUPERIO_ID 0xe9 +#define SUPERIO_DEVICE_REG 0x07 +#define SUPERIO_GPIO_DEV 0x07 +#define SUPERIO_IOPORT0HI 0x60 +#define SUPERIO_IOPORT0LO 0x61 +#define SUPERIO_GPIOPINSEL 0xf0 +#define SUPERIO_GPIOPINCONF 0xf1 +#define GPIOPINCONF_OUTPUT (1 << 0) +#define GPIOPINCONF_PUSHPULL (1 << 1) +#define GPIOPINCONF_PULLUP (1 << 2) + +#define _GPIO_PINS 29 + +#define GPIO_REG_DO0 0x00 +#define GPIO_REG_DI0 0x01 +#define GPIO_REG_DO1 0x04 +#define GPIO_REG_DI1 0x05 +#define GPIO_REG_DO2 0x08 +#define GPIO_REG_DI2 0x09 +#define GPIO_REG_DO3 0x0A +#define GPIO_REG_DI3 0x0B + +#define GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | \ + GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN | \ + GPIO_PIN_PUSHPULL | GPIO_PIN_PULLUP) + +#define GPIO_LOCK(sc) do { mtx_lock(&(sc)->gpio_mtx); } while(0) +#define GPIO_UNLOCK(sc) do { mtx_unlock(&(sc)->gpio_mtx); } while(0) + +#if 0 +static int super_io_pins[_GPIO_PINS] = + {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x04, 0x05, 0x13, 0x12}; +#endif + +#define GPIO_PORTNUM(pin) ((pin)/8) +#define GPIO_PORTPIN(pin) ((pin) & 7) +#define GPIO_PINSELECT(port, pin) ((port << 4) | (pin)) + +struct pc87366_gpio_softc { + device_t dev; + struct mtx gpio_mtx; + struct resource *gpio_io_res; + int gpio_reg_offset; +}; + +static int +pc87366_gpio_pin_max(device_t dev, int *maxpin) +{ + + *maxpin = _GPIO_PINS - 1; + return (0); +} + +static int +pc87366_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + + if (pin >= _GPIO_PINS) + return (EINVAL); + + *caps = GPIO_DEFAULT_CAPS; + + return (0); +} + +static int +pc87366_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct pc87366_gpio_softc *sc = device_get_softc(dev); + uint8_t pinselect, pinconf; + int portnum, portpin; + + if (pin >= _GPIO_PINS) + return (EINVAL); + + GPIO_LOCK(sc); + + portnum = GPIO_PORTNUM(pin); + portpin = GPIO_PORTPIN(pin); + pinselect = GPIO_PINSELECT(portnum, portpin); + + outb(SUPERIO_INDEX, SUPERIO_GPIOPINSEL); + outb(SUPERIO_DATA, pinselect); + outb(SUPERIO_INDEX, SUPERIO_GPIOPINCONF); + pinconf = inb(SUPERIO_DATA); + + if (pinconf & GPIOPINCONF_OUTPUT) + *flags = GPIO_PIN_OUTPUT; + else + *flags = GPIO_PIN_INPUT; + + if (pinconf & GPIOPINCONF_PUSHPULL) + *flags |= GPIO_PIN_PUSHPULL; + else + *flags |= GPIO_PIN_OPENDRAIN; + + if (pinconf & GPIOPINCONF_PULLUP) + *flags |= GPIO_PIN_PULLUP; + + GPIO_UNLOCK(sc); + + return (0); +} + +static int +pc87366_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + + if (pin >= _GPIO_PINS) + return (EINVAL); + + snprintf(name, GPIOMAXNAME - 1, "gpio%02d", pin); + + return (0); +} + +static int +pc87366_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct pc87366_gpio_softc *sc = device_get_softc(dev); + uint8_t pinconf; + uint8_t pinselect; + int portnum, portpin; + + if (pin >= _GPIO_PINS) + return (EINVAL); + + /* Filter out unwanted flags */ + if ((flags & GPIO_DEFAULT_CAPS) != flags) + return (EINVAL); + + /* Can't mix input/output together */ + if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == + (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) + return (EINVAL); + + /* Can't mix open-drain/push-pull */ + if ((flags & (GPIO_PIN_OPENDRAIN|GPIO_PIN_PUSHPULL)) == + (GPIO_PIN_OPENDRAIN|GPIO_PIN_PUSHPULL)) + return (EINVAL); + + GPIO_LOCK(sc); + + pinconf = 0; + + portnum = GPIO_PORTNUM(pin); + portpin = GPIO_PORTPIN(pin); + pinselect = GPIO_PINSELECT(portnum, portpin); + + if (flags & GPIO_PIN_OUTPUT) + pinconf |= GPIOPINCONF_OUTPUT; + if (flags & GPIO_PIN_PUSHPULL) + pinconf |= GPIOPINCONF_PUSHPULL; + if (flags & GPIO_PIN_PULLUP) + pinconf |= GPIOPINCONF_PULLUP; + + outb(SUPERIO_INDEX, SUPERIO_GPIOPINSEL); + outb(SUPERIO_DATA, pinselect); + outb(SUPERIO_INDEX, SUPERIO_GPIOPINCONF); + outb(SUPERIO_DATA, pinconf); + + GPIO_UNLOCK(sc); + + return (0); +} + +static int +pc87366_gpio_pin_set(device_t dev, uint32_t pin, unsigned int val) +{ + struct pc87366_gpio_softc *sc = device_get_softc(dev); + int portnum, portpin; + int offset; + uint8_t reg; + + if (pin >= _GPIO_PINS) + return (EINVAL); + + portnum = GPIO_PORTNUM(pin); + portpin = GPIO_PORTPIN(pin); + + /* Get port offsetess */ + switch (portnum) { + case 0: + offset = GPIO_REG_DO0; + break; + + case 1: + offset = GPIO_REG_DO1; + break; + + case 2: + offset = GPIO_REG_DO2; + break; + + case 3: + offset = GPIO_REG_DO3; + break; + } + + GPIO_LOCK(sc); + reg = inb(sc->gpio_reg_offset + offset); + if (val == GPIO_PIN_HIGH) + reg |= (1 << portpin); + else + reg &= ~(1 << portpin); + outb(sc->gpio_reg_offset + offset, reg); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +pc87366_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) +{ + struct pc87366_gpio_softc *sc = device_get_softc(dev); + int portnum, portpin; + int offset; + uint8_t reg; + + if (pin >= _GPIO_PINS) + return (EINVAL); + + portnum = GPIO_PORTNUM(pin); + portpin = GPIO_PORTPIN(pin); + + /* Get port offsetess */ + switch (portnum) { + case 0: + offset = GPIO_REG_DI0; + break; + + case 1: + offset = GPIO_REG_DI1; + break; + + case 2: + offset = GPIO_REG_DI2; + break; + + case 3: + offset = GPIO_REG_DI3; + break; + } + + GPIO_LOCK(sc); + reg = inb(sc->gpio_reg_offset + offset); + + if (reg & (1 << portpin)) + *val = GPIO_PIN_HIGH; + else + *val = GPIO_PIN_LOW; + GPIO_UNLOCK(sc); + + return (0); +} + +static int +pc87366_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct pc87366_gpio_softc *sc = device_get_softc(dev); + int portnum, portpin; + int offset; + uint8_t reg; + + if (pin >= _GPIO_PINS) + return (EINVAL); + + portnum = GPIO_PORTNUM(pin); + portpin = GPIO_PORTPIN(pin); + + /* Get port offsetess */ + switch (portnum) { + case 0: + offset = GPIO_REG_DO0; + break; + + case 1: + offset = GPIO_REG_DO1; + break; + + case 2: + offset = GPIO_REG_DO2; + break; + + case 3: + offset = GPIO_REG_DO3; + break; + } + + GPIO_LOCK(sc); + reg = inb(sc->gpio_reg_offset + offset); + if (reg & (1 << portpin)) + reg &= ~(1 << portpin); + else + reg |= (1 << portpin); + outb(sc->gpio_reg_offset + offset, reg); + GPIO_UNLOCK(sc); + + return (0); +} + + + +static int +pc87366_gpio_probe(device_t dev) +{ + + printf("Trying: %08x\n", pci_get_devid(dev)); + if (pci_get_devid(dev) == 0x0510100b) { + device_set_desc(dev, "PCM87366 GPIO"); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +pc87366_gpio_attach(device_t dev) +{ + struct pc87366_gpio_softc *sc = device_get_softc(dev); + int rid; + uint8_t reg; + + sc->dev = dev; + mtx_init(&sc->gpio_mtx, "PCM87366 GPIO", "gpio", MTX_DEF); + + rid = 0; + sc->gpio_io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, + RF_ACTIVE); + + + if (!sc->gpio_io_res) { + device_printf(dev, "cannot allocate I/O resource\n"); + goto fail; + } + + device_printf(dev, "IO resource: %lu (%lu)\n", + rman_get_start(sc->gpio_io_res), + rman_get_size(sc->gpio_io_res)); + + outb(SUPERIO_INDEX, SUPERIO_ID_ADD); + reg = inb(SUPERIO_DATA); + if (reg != SUPERIO_ID) { + printf("Invalid SuperIO ID: %02x\n", reg); + goto fail; + } + + outb(SUPERIO_INDEX, SUPERIO_DEVICE_REG); + /* Point to GPIO Device */ + outb(SUPERIO_DATA, SUPERIO_GPIO_DEV); + /* get GPIO address */ + outb(SUPERIO_INDEX, SUPERIO_IOPORT0HI); + sc->gpio_reg_offset = inb(SUPERIO_DATA) << 8; + outb(SUPERIO_INDEX, SUPERIO_IOPORT0LO); + sc->gpio_reg_offset |= inb(SUPERIO_DATA); + device_printf(dev, "Geode SuperIO GPIO at %x\n",sc->gpio_reg_offset); + + outb(SUPERIO_INDEX, SUPERIO_ACTIVATE); + reg = inb(SUPERIO_DATA); + + if(!(reg & 0x01)) { + device_printf(dev, "GPIO is not enabled\n"); + goto fail; + } + + device_add_child(dev, "gpioc", device_get_unit(dev)); + device_add_child(dev, "gpiobus", device_get_unit(dev)); + + return (bus_generic_attach(dev)); + +fail: + if (sc->gpio_io_res != NULL) + bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->gpio_io_res); + mtx_destroy(&sc->gpio_mtx); + + return (ENXIO); +} + +static int +pc87366_gpio_detach(device_t dev) +{ + struct pc87366_gpio_softc *sc = device_get_softc(dev); + + bus_generic_detach(dev); + + /* Release the IO resource */ + if (sc->gpio_io_res != NULL) + bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->gpio_io_res); + + mtx_destroy(&sc->gpio_mtx); + + return (0); +} + + +static device_method_t pc87366_gpio_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pc87366_gpio_probe), + DEVMETHOD(device_attach, pc87366_gpio_attach), + DEVMETHOD(device_detach, pc87366_gpio_detach), + + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* GPIO protocol */ + DEVMETHOD(gpio_pin_max, pc87366_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, pc87366_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, pc87366_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, pc87366_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, pc87366_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, pc87366_gpio_pin_get), + DEVMETHOD(gpio_pin_set, pc87366_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, pc87366_gpio_pin_toggle), + + {0, 0} +}; + +static driver_t pc87366_gpio_driver = { + "gpio", + pc87366_gpio_methods, + sizeof(struct pc87366_gpio_softc), +}; + +static devclass_t pc87366_gpio_devclass; + +DRIVER_MODULE(gpio, pci, pc87366_gpio_driver, pc87366_gpio_devclass, 0, 0); Property changes on: i386/i386/soekris_gpio.c ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: conf/files.i386 =================================================================== --- conf/files.i386 (revision 249215) +++ conf/files.i386 (working copy) @@ -421,6 +421,7 @@ i386/xen/exception.s optional xen i386/i386/gdb_machdep.c optional gdb i386/i386/geode.c optional cpu_geode +i386/i386/soekris_gpio.c optional gpio i386/i386/i686_mem.c optional mem i386/i386/identcpu.c standard i386/i386/in_cksum.c optional inet | inet6