summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/arm/conf/CAMBRIA4
-rw-r--r--sys/arm/conf/CAMBRIA.hints4
-rw-r--r--sys/arm/xscale/ixp425/cambria_gpio.c471
-rw-r--r--sys/arm/xscale/ixp425/files.avila1
-rw-r--r--sys/dev/gpio/gpiobus.c2
-rw-r--r--sys/dev/gpio/gpioc.c2
6 files changed, 482 insertions, 2 deletions
diff --git a/sys/arm/conf/CAMBRIA b/sys/arm/conf/CAMBRIA
index 174a7c7..ed16ed8 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 6a1f889..f727c36 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 0000000..89b07d8
--- /dev/null
+++ b/sys/arm/xscale/ixp425/cambria_gpio.c
@@ -0,0 +1,471 @@
+/*-
+ * Copyright (c) 2010, Andrew Thompson <thompsa@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/gpio.h>
+
+#include <arm/xscale/ixp425/ixp425reg.h>
+#include <arm/xscale/ixp425/ixp425var.h>
+#include <arm/xscale/ixp425/ixdp425reg.h>
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+
+#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<<i));
+ i2c_setscl(sc, 1);
+ i2c_setscl(sc, 0);
+ }
+ i2c_setscl(sc, 1);
+ i2c_getsda(sc);
+ i2c_setscl(sc, 0);
+}
+
+static u_char
+i2c_readbyte(struct cambria_gpio_softc *sc)
+{
+ int i;
+ unsigned char data=0;
+
+ for (i=7; i>=0; i--)
+ {
+ i2c_setscl(sc, 1);
+ if (i2c_getsda(sc))
+ data |= (1<<i);
+ i2c_setscl(sc, 0);
+ }
+ return data;
+}
+
+static int
+cambria_gpio_read(struct cambria_gpio_softc *sc, uint32_t pin, unsigned int *val)
+{
+ 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);
+ *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 38b9329..5008e09 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 3a044fd..d973694 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 e92326e..6a4c0c9 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)
OpenPOWER on IntegriCloud