diff --git a/sys/arm/conf/CAMBRIA b/sys/arm/conf/CAMBRIA index 174a7c785a8f..ed16ed83fab1 100644 --- a/sys/arm/conf/CAMBRIA +++ b/sys/arm/conf/CAMBRIA @@ -90,6 +90,10 @@ device ad7418 # AD7418 on I2C bus device cambria_fled # Font Panel LED on I2C bus device cambria_led # 8-LED latch +device gpio +device gpioled +device cambria_gpio # GPIO pins on J11 + device ata device atadisk # ATA disk drives device avila_ata # Gateworks CF/IDE support diff --git a/sys/arm/conf/CAMBRIA.hints b/sys/arm/conf/CAMBRIA.hints index 6a1f889349d2..f727c367ff99 100644 --- a/sys/arm/conf/CAMBRIA.hints +++ b/sys/arm/conf/CAMBRIA.hints @@ -54,6 +54,10 @@ hint.fled.0.addr=0x5a # Octal LED Latch hint.led_cambria.0.at="ixp0" +# GPIO pins +hint.gpio_cambria.0.at="iicbus0" +hint.gpio_cambria.0.addr=0x56 + # Analog Devices AD7418 temperature sensor hint.ad7418.0.at="iicbus0" hint.ad7418.0.addr=0x50 diff --git a/sys/arm/xscale/ixp425/cambria_gpio.c b/sys/arm/xscale/ixp425/cambria_gpio.c new file mode 100644 index 000000000000..89b07d852b7a --- /dev/null +++ b/sys/arm/xscale/ixp425/cambria_gpio.c @@ -0,0 +1,471 @@ +/*- + * Copyright (c) 2010, Andrew Thompson + * 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 unmodified, 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. + */ + +/* + * GPIO driver for Gateworks Cambria + * + * Note: + * The Cambria PLD does not set the i2c ack bit after each write, if we used the + * regular iicbus interface it would abort the xfer after the address byte + * times out and not write our latch. To get around this we grab the iicbus and + * then do our own bit banging. This is a comprimise to changing all the iicbb + * device methods to allow a flag to be passed down and is similir to how Linux + * does it. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "iicbb_if.h" +#include "gpio_if.h" + +#define IIC_M_WR 0 /* write operation */ +#define PLD_ADDR 0xac /* slave address */ + +#define I2C_DELAY 10 + +#define GPIO_CONF_CLR(sc, reg, mask) \ + GPIO_CONF_WRITE_4(sc, reg, GPIO_CONF_READ_4(sc, reg) &~ (mask)) +#define GPIO_CONF_SET(sc, reg, mask) \ + GPIO_CONF_WRITE_4(sc, reg, GPIO_CONF_READ_4(sc, reg) | (mask)) + +#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) + +#define GPIO_PINS 5 +struct cambria_gpio_softc { + device_t sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_gpio_ioh; + struct mtx sc_mtx; + struct gpio_pin sc_pins[GPIO_PINS]; + uint8_t sc_latch; +}; + +struct cambria_gpio_pin { + const char *name; + int pin; + int flags; +}; + +extern struct ixp425_softc *ixp425_softc; + +static struct cambria_gpio_pin cambria_gpio_pins[GPIO_PINS] = { + { "GPIO0", 0, GPIO_PIN_OUTPUT }, + { "GPIO1", 1, GPIO_PIN_OUTPUT }, + { "GPIO2", 2, GPIO_PIN_OUTPUT }, + { "GPIO3", 3, GPIO_PIN_OUTPUT }, + { "GPIO4", 4, GPIO_PIN_OUTPUT }, +}; + +/* + * Helpers + */ +static int cambria_gpio_read(struct cambria_gpio_softc *, uint32_t, unsigned int *); +static int cambria_gpio_write(struct cambria_gpio_softc *); + +/* + * Driver stuff + */ +static int cambria_gpio_probe(device_t dev); +static int cambria_gpio_attach(device_t dev); +static int cambria_gpio_detach(device_t dev); + +/* + * GPIO interface + */ +static int cambria_gpio_pin_max(device_t dev, int *maxpin); +static int cambria_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); +static int cambria_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t + *flags); +static int cambria_gpio_pin_getname(device_t dev, uint32_t pin, char *name); +static int cambria_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags); +static int cambria_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); +static int cambria_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val); +static int cambria_gpio_pin_toggle(device_t dev, uint32_t pin); + +static int +i2c_getsda(struct cambria_gpio_softc *sc) +{ + uint32_t reg; + + mtx_lock(&Giant); + GPIO_CONF_SET(sc, IXP425_GPIO_GPOER, GPIO_I2C_SDA_BIT); + + reg = GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR); + mtx_unlock(&Giant); + return (reg & GPIO_I2C_SDA_BIT); +} + +static void +i2c_setsda(struct cambria_gpio_softc *sc, int val) +{ + + mtx_lock(&Giant); + GPIO_CONF_CLR(sc, IXP425_GPIO_GPOUTR, GPIO_I2C_SDA_BIT); + if (val) + GPIO_CONF_SET(sc, IXP425_GPIO_GPOER, GPIO_I2C_SDA_BIT); + else + GPIO_CONF_CLR(sc, IXP425_GPIO_GPOER, GPIO_I2C_SDA_BIT); + mtx_unlock(&Giant); + DELAY(I2C_DELAY); +} + +static void +i2c_setscl(struct cambria_gpio_softc *sc, int val) +{ + + mtx_lock(&Giant); + GPIO_CONF_CLR(sc, IXP425_GPIO_GPOUTR, GPIO_I2C_SCL_BIT); + if (val) + GPIO_CONF_SET(sc, IXP425_GPIO_GPOER, GPIO_I2C_SCL_BIT); + else + GPIO_CONF_CLR(sc, IXP425_GPIO_GPOER, GPIO_I2C_SCL_BIT); + mtx_unlock(&Giant); + DELAY(I2C_DELAY); +} + +static void +i2c_sendstart(struct cambria_gpio_softc *sc) +{ + i2c_setsda(sc, 1); + i2c_setscl(sc, 1); + i2c_setsda(sc, 0); + i2c_setscl(sc, 0); +} + +static void +i2c_sendstop(struct cambria_gpio_softc *sc) +{ + i2c_setscl(sc, 1); + i2c_setsda(sc, 1); + i2c_setscl(sc, 0); + i2c_setsda(sc, 0); +} + +static void +i2c_sendbyte(struct cambria_gpio_softc *sc, u_char data) +{ + int i; + + for (i=7; i>=0; i--) { + i2c_setsda(sc, data & (1<=0; i--) + { + i2c_setscl(sc, 1); + if (i2c_getsda(sc)) + data |= (1<sc_dev; + int error; + + error = iicbus_request_bus(device_get_parent(dev), dev, + IIC_DONTWAIT); + if (error) + return (error); + + i2c_sendstart(sc); + i2c_sendbyte(sc, PLD_ADDR | LSB); + *val = (i2c_readbyte(sc) & (1 << pin)) != 0; + i2c_sendstop(sc); + + iicbus_release_bus(device_get_parent(dev), dev); + + return (0); +} + +static int +cambria_gpio_write(struct cambria_gpio_softc *sc) +{ + device_t dev = sc->sc_dev; + int error; + + error = iicbus_request_bus(device_get_parent(dev), dev, + IIC_DONTWAIT); + if (error) + return (error); + + i2c_sendstart(sc); + i2c_sendbyte(sc, PLD_ADDR & ~LSB); + i2c_sendbyte(sc, sc->sc_latch); + i2c_sendstop(sc); + + iicbus_release_bus(device_get_parent(dev), dev); + + return (0); +} + +static int +cambria_gpio_pin_max(device_t dev, int *maxpin) +{ + + *maxpin = GPIO_PINS - 1; + return (0); +} + +static int +cambria_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + + if (pin >= GPIO_PINS) + return (EINVAL); + + *caps = sc->sc_pins[pin].gp_caps; + return (0); +} + +static int +cambria_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + + if (pin >= GPIO_PINS) + return (EINVAL); + + *flags = sc->sc_pins[pin].gp_flags; + return (0); +} + +static int +cambria_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + + if (pin >= GPIO_PINS) + return (EINVAL); + + memcpy(name, sc->sc_pins[pin].gp_name, GPIOMAXNAME); + return (0); +} + +static int +cambria_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + int error; + + if (pin >= GPIO_PINS) + return (EINVAL); + + /* Filter out unwanted flags */ + if ((flags &= sc->sc_pins[pin].gp_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); + + GPIO_LOCK(sc); + sc->sc_pins[pin].gp_flags = flags; + + sc->sc_latch |= (1 << pin); + error = cambria_gpio_write(sc); + GPIO_UNLOCK(sc); + + return (error); +} + +static int +cambria_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + int error; + + if (pin >= GPIO_PINS || sc->sc_pins[pin].gp_flags != GPIO_PIN_OUTPUT) + return (EINVAL); + + GPIO_LOCK(sc); + if (value) + sc->sc_latch |= (1 << pin); + else + sc->sc_latch &= ~(1 << pin); + error = cambria_gpio_write(sc); + GPIO_UNLOCK(sc); + + return (error); +} + +static int +cambria_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + int error = 0; + + if (pin >= GPIO_PINS) + return (EINVAL); + + GPIO_LOCK(sc); + if (sc->sc_pins[pin].gp_flags == GPIO_PIN_OUTPUT) + *val = (sc->sc_latch & (1 << pin)) ? 1 : 0; + else + error = cambria_gpio_read(sc, pin, val); + GPIO_UNLOCK(sc); + + return (error); +} + +static int +cambria_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + int error; + + if (pin >= GPIO_PINS || sc->sc_pins[pin].gp_flags != GPIO_PIN_OUTPUT) + return (EINVAL); + + GPIO_LOCK(sc); + sc->sc_latch ^= (1 << pin); + error = cambria_gpio_write(sc); + GPIO_UNLOCK(sc); + + return (error); +} + +static int +cambria_gpio_probe(device_t dev) +{ + + device_set_desc(dev, "Gateworks Cambria GPIO driver"); + return (0); +} + +static int +cambria_gpio_attach(device_t dev) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + int pin; + + sc->sc_dev = dev; + sc->sc_iot = ixp425_softc->sc_iot; + sc->sc_gpio_ioh = ixp425_softc->sc_gpio_ioh; + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + + for (pin = 0; pin < GPIO_PINS; pin++) { + struct cambria_gpio_pin *p = &cambria_gpio_pins[pin]; + + strncpy(sc->sc_pins[pin].gp_name, p->name, GPIOMAXNAME); + sc->sc_pins[pin].gp_pin = pin; + sc->sc_pins[pin].gp_caps = GPIO_PIN_INPUT|GPIO_PIN_OUTPUT; + sc->sc_pins[pin].gp_flags = 0; + cambria_gpio_pin_setflags(dev, pin, p->flags); + } + + device_add_child(dev, "gpioc", device_get_unit(dev)); + device_add_child(dev, "gpiobus", device_get_unit(dev)); + return (bus_generic_attach(dev)); +} + +static int +cambria_gpio_detach(device_t dev) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + + KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized")); + + bus_generic_detach(dev); + + mtx_destroy(&sc->sc_mtx); + + return(0); +} + +static device_method_t cambria_gpio_methods[] = { + DEVMETHOD(device_probe, cambria_gpio_probe), + DEVMETHOD(device_attach, cambria_gpio_attach), + DEVMETHOD(device_detach, cambria_gpio_detach), + + /* GPIO protocol */ + DEVMETHOD(gpio_pin_max, cambria_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, cambria_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, cambria_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, cambria_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, cambria_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, cambria_gpio_pin_get), + DEVMETHOD(gpio_pin_set, cambria_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, cambria_gpio_pin_toggle), + {0, 0}, +}; + +static driver_t cambria_gpio_driver = { + "gpio_cambria", + cambria_gpio_methods, + sizeof(struct cambria_gpio_softc), +}; +static devclass_t cambria_gpio_devclass; +extern devclass_t gpiobus_devclass, gpioc_devclass; +extern driver_t gpiobus_driver, gpioc_driver; + +DRIVER_MODULE(gpio_cambria, iicbus, cambria_gpio_driver, cambria_gpio_devclass, 0, 0); +DRIVER_MODULE(gpiobus, gpio_cambria, gpiobus_driver, gpiobus_devclass, 0, 0); +DRIVER_MODULE(gpioc, gpio_cambria, gpioc_driver, gpioc_devclass, 0, 0); +MODULE_VERSION(gpio_cambria, 1); +MODULE_DEPEND(gpio_cambria, iicbus, 1, 1, 1); diff --git a/sys/arm/xscale/ixp425/files.avila b/sys/arm/xscale/ixp425/files.avila index 38b93296256b..5008e0910a12 100644 --- a/sys/arm/xscale/ixp425/files.avila +++ b/sys/arm/xscale/ixp425/files.avila @@ -6,4 +6,5 @@ arm/xscale/ixp425/avila_gpio.c optional avila_gpio arm/xscale/ixp425/cambria_exp_space.c standard arm/xscale/ixp425/cambria_fled.c optional cambria_fled arm/xscale/ixp425/cambria_led.c optional cambria_led +arm/xscale/ixp425/cambria_gpio.c optional cambria_gpio arm/xscale/ixp425/ixdp425_pci.c optional pci diff --git a/sys/dev/gpio/gpiobus.c b/sys/dev/gpio/gpiobus.c index 3a044fd75f4c..d9736949596d 100644 --- a/sys/dev/gpio/gpiobus.c +++ b/sys/dev/gpio/gpiobus.c @@ -481,7 +481,7 @@ static device_method_t gpiobus_methods[] = { { 0, 0 } }; -static driver_t gpiobus_driver = { +driver_t gpiobus_driver = { "gpiobus", gpiobus_methods, sizeof(struct gpiobus_softc) diff --git a/sys/dev/gpio/gpioc.c b/sys/dev/gpio/gpioc.c index e92326e196a2..6a4c0c91938d 100644 --- a/sys/dev/gpio/gpioc.c +++ b/sys/dev/gpio/gpioc.c @@ -188,7 +188,7 @@ static device_method_t gpioc_methods[] = { { 0, 0 } }; -static driver_t gpioc_driver = { +driver_t gpioc_driver = { "gpioc", gpioc_methods, sizeof(struct gpioc_softc)