summaryrefslogtreecommitdiffstats
path: root/sys/arm/lpc/lpc_gpio.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm/lpc/lpc_gpio.c')
-rw-r--r--sys/arm/lpc/lpc_gpio.c547
1 files changed, 547 insertions, 0 deletions
diff --git a/sys/arm/lpc/lpc_gpio.c b/sys/arm/lpc/lpc_gpio.c
new file mode 100644
index 0000000..4c06302
--- /dev/null
+++ b/sys/arm/lpc/lpc_gpio.c
@@ -0,0 +1,547 @@
+/*-
+ * Copyright (c) 2011 Jakub Wojciech Klama <jceel@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, 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 on LPC32x0 consist of 4 ports:
+ * - Port0 with 8 input/output pins
+ * - Port1 with 24 input/output pins
+ * - Port2 with 13 input/output pins
+ * - Port3 with:
+ * - 26 input pins (GPI_00..GPI_09 + GPI_15..GPI_23 + GPI_25 + GPI_27..GPI_28)
+ * - 24 output pins (GPO_00..GPO_23)
+ * - 6 input/ouput pins (GPIO_00..GPIO_05)
+ *
+ * Pins are mapped to logical pin number as follows:
+ * [0..9] -> GPI_00..GPI_09 (port 3)
+ * [10..18] -> GPI_15..GPI_23 (port 3)
+ * [19] -> GPI_25 (port 3)
+ * [20..21] -> GPI_27..GPI_28 (port 3)
+ * [22..45] -> GPO_00..GPO_23 (port 3)
+ * [46..51] -> GPIO_00..GPIO_05 (port 3)
+ * [52..64] -> P2.0..P2.12 (port 2)
+ * [65..88] -> P1.0..P1.23 (port 1)
+ * [89..96] -> P0.0..P0.7 (port 0)
+ *
+ */
+
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/time.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <sys/gpio.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+#include <machine/resource.h>
+#include <machine/frame.h>
+#include <machine/intr.h>
+#include <machine/fdt.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/lpc/lpcreg.h>
+#include <arm/lpc/lpcvar.h>
+
+#include "gpio_if.h"
+
+struct lpc_gpio_softc
+{
+ device_t lg_dev;
+ struct resource * lg_res;
+ bus_space_tag_t lg_bst;
+ bus_space_handle_t lg_bsh;
+};
+
+struct lpc_gpio_pinmap
+{
+ int lp_start_idx;
+ int lp_pin_count;
+ int lp_port;
+ int lp_start_bit;
+ int lp_flags;
+};
+
+static const struct lpc_gpio_pinmap lpc_gpio_pins[] = {
+ { 0, 10, 3, 0, GPIO_PIN_INPUT },
+ { 10, 9, 3, 15, GPIO_PIN_INPUT },
+ { 19, 1, 3, 25, GPIO_PIN_INPUT },
+ { 20, 2, 3, 27, GPIO_PIN_INPUT },
+ { 22, 24, 3, 0, GPIO_PIN_OUTPUT },
+ /*
+ * -1 below is to mark special case for Port3 GPIO pins, as they
+ * have other bits in Port 3 registers as inputs and as outputs
+ */
+ { 46, 6, 3, -1, GPIO_PIN_INPUT | GPIO_PIN_OUTPUT },
+ { 52, 13, 2, 0, GPIO_PIN_INPUT | GPIO_PIN_OUTPUT },
+ { 65, 24, 1, 0, GPIO_PIN_INPUT | GPIO_PIN_OUTPUT },
+ { 89, 8, 0, 0, GPIO_PIN_INPUT | GPIO_PIN_OUTPUT },
+ { -1, -1, -1, -1, -1 },
+};
+
+#define LPC_GPIO_NPINS \
+ (LPC_GPIO_P0_COUNT + LPC_GPIO_P1_COUNT + \
+ LPC_GPIO_P2_COUNT + LPC_GPIO_P3_COUNT)
+
+#define LPC_GPIO_PIN_IDX(_map, _idx) \
+ (_idx - _map->lp_start_idx)
+
+#define LPC_GPIO_PIN_BIT(_map, _idx) \
+ (_map->lp_start_bit + LPC_GPIO_PIN_IDX(_map, _idx))
+
+static int lpc_gpio_probe(device_t);
+static int lpc_gpio_attach(device_t);
+static int lpc_gpio_detach(device_t);
+
+static int lpc_gpio_pin_max(device_t, int *);
+static int lpc_gpio_pin_getcaps(device_t, uint32_t, uint32_t *);
+static int lpc_gpio_pin_getflags(device_t, uint32_t, uint32_t *);
+static int lpc_gpio_pin_setflags(device_t, uint32_t, uint32_t);
+static int lpc_gpio_pin_getname(device_t, uint32_t, char *);
+static int lpc_gpio_pin_get(device_t, uint32_t, uint32_t *);
+static int lpc_gpio_pin_set(device_t, uint32_t, uint32_t);
+static int lpc_gpio_pin_toggle(device_t, uint32_t);
+
+static const struct lpc_gpio_pinmap *lpc_gpio_get_pinmap(int);
+
+static struct lpc_gpio_softc *lpc_gpio_sc = NULL;
+
+#define lpc_gpio_read_4(_sc, _reg) \
+ bus_space_read_4(_sc->lg_bst, _sc->lg_bsh, _reg)
+#define lpc_gpio_write_4(_sc, _reg, _val) \
+ bus_space_write_4(_sc->lg_bst, _sc->lg_bsh, _reg, _val)
+#define lpc_gpio_get_4(_sc, _test, _reg1, _reg2) \
+ lpc_gpio_read_4(_sc, ((_test) ? _reg1 : _reg2))
+#define lpc_gpio_set_4(_sc, _test, _reg1, _reg2, _val) \
+ lpc_gpio_write_4(_sc, ((_test) ? _reg1 : _reg2), _val)
+
+static int
+lpc_gpio_probe(device_t dev)
+{
+ if (!ofw_bus_is_compatible(dev, "lpc,gpio"))
+ return (ENXIO);
+
+ device_set_desc(dev, "LPC32x0 GPIO");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+lpc_gpio_attach(device_t dev)
+{
+ struct lpc_gpio_softc *sc = device_get_softc(dev);
+ int rid;
+
+ sc->lg_dev = dev;
+
+ rid = 0;
+ sc->lg_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->lg_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ return (ENXIO);
+ }
+
+ sc->lg_bst = rman_get_bustag(sc->lg_res);
+ sc->lg_bsh = rman_get_bushandle(sc->lg_res);
+
+ lpc_gpio_sc = sc;
+
+ 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
+lpc_gpio_detach(device_t dev)
+{
+ return (EBUSY);
+}
+
+static int
+lpc_gpio_pin_max(device_t dev, int *npins)
+{
+ *npins = LPC_GPIO_NPINS - 1;
+ return (0);
+}
+
+static int
+lpc_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ const struct lpc_gpio_pinmap *map;
+
+ if (pin > LPC_GPIO_NPINS)
+ return (ENODEV);
+
+ map = lpc_gpio_get_pinmap(pin);
+
+ *caps = map->lp_flags;
+ return (0);
+}
+
+static int
+lpc_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct lpc_gpio_softc *sc = device_get_softc(dev);
+ const struct lpc_gpio_pinmap *map;
+ uint32_t state;
+ int dir;
+
+ if (pin > LPC_GPIO_NPINS)
+ return (ENODEV);
+
+ map = lpc_gpio_get_pinmap(pin);
+
+ /* Check whether it's bidirectional pin */
+ if ((map->lp_flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) !=
+ (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
+ *flags = map->lp_flags;
+ return (0);
+ }
+
+ switch (map->lp_port) {
+ case 0:
+ state = lpc_gpio_read_4(sc, LPC_GPIO_P0_DIR_STATE);
+ dir = (state & (1 << LPC_GPIO_PIN_BIT(map, pin)));
+ break;
+ case 1:
+ state = lpc_gpio_read_4(sc, LPC_GPIO_P1_DIR_STATE);
+ dir = (state & (1 << LPC_GPIO_PIN_BIT(map, pin)));
+ break;
+ case 2:
+ state = lpc_gpio_read_4(sc, LPC_GPIO_P2_DIR_STATE);
+ dir = (state & (1 << LPC_GPIO_PIN_BIT(map, pin)));
+ break;
+ case 3:
+ state = lpc_gpio_read_4(sc, LPC_GPIO_P2_DIR_STATE);
+ dir = (state & (1 << (25 + LPC_GPIO_PIN_IDX(map, pin))));
+ break;
+ default:
+ panic("unknown GPIO port");
+ }
+
+ *flags = dir ? GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
+
+ return (0);
+}
+
+static int
+lpc_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct lpc_gpio_softc *sc = device_get_softc(dev);
+ const struct lpc_gpio_pinmap *map;
+ uint32_t dir, state;
+
+ if (pin > LPC_GPIO_NPINS)
+ return (ENODEV);
+
+ map = lpc_gpio_get_pinmap(pin);
+
+ /* Check whether it's bidirectional pin */
+ if ((map->lp_flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) !=
+ (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
+ return (ENOTSUP);
+
+ if (flags & GPIO_PIN_INPUT)
+ dir = 0;
+
+ if (flags & GPIO_PIN_OUTPUT)
+ dir = 1;
+
+ switch (map->lp_port) {
+ case 0:
+ state = (1 << LPC_GPIO_PIN_IDX(map, pin));
+ lpc_gpio_set_4(sc, dir, LPC_GPIO_P0_DIR_SET,
+ LPC_GPIO_P0_DIR_CLR, state);
+ break;
+ case 1:
+ state = (1 << LPC_GPIO_PIN_IDX(map, pin));
+ lpc_gpio_set_4(sc, dir, LPC_GPIO_P1_DIR_SET,
+ LPC_GPIO_P0_DIR_CLR, state);
+ break;
+ case 2:
+ state = (1 << LPC_GPIO_PIN_IDX(map, pin));
+ lpc_gpio_set_4(sc, dir, LPC_GPIO_P2_DIR_SET,
+ LPC_GPIO_P0_DIR_CLR, state);
+ break;
+ case 3:
+ state = (1 << (25 + (pin - map->lp_start_idx)));
+ lpc_gpio_set_4(sc, dir, LPC_GPIO_P2_DIR_SET,
+ LPC_GPIO_P0_DIR_CLR, state);
+ break;
+ }
+
+ return (0);
+}
+
+static int
+lpc_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ const struct lpc_gpio_pinmap *map;
+ int idx;
+
+ map = lpc_gpio_get_pinmap(pin);
+ idx = LPC_GPIO_PIN_IDX(map, pin);
+
+ switch (map->lp_port) {
+ case 0:
+ case 1:
+ case 2:
+ snprintf(name, GPIOMAXNAME - 1, "P%d.%d", map->lp_port,
+ map->lp_start_bit + LPC_GPIO_PIN_IDX(map, pin));
+ break;
+ case 3:
+ if (map->lp_start_bit == -1) {
+ snprintf(name, GPIOMAXNAME - 1, "GPIO_%02d", idx);
+ break;
+ }
+
+ snprintf(name, GPIOMAXNAME - 1, "GP%c_%02d",
+ (map->lp_flags & GPIO_PIN_INPUT) ? 'I' : 'O',
+ map->lp_start_bit + idx);
+ break;
+ }
+
+ return (0);
+}
+
+static int
+lpc_gpio_pin_get(device_t dev, uint32_t pin, uint32_t *value)
+{
+ struct lpc_gpio_softc *sc = device_get_softc(dev);
+ const struct lpc_gpio_pinmap *map;
+ uint32_t state, flags;
+ int dir;
+
+ map = lpc_gpio_get_pinmap(pin);
+
+ if (lpc_gpio_pin_getflags(dev, pin, &flags))
+ return (ENXIO);
+
+ if (flags & GPIO_PIN_OUTPUT)
+ dir = 1;
+
+ if (flags & GPIO_PIN_INPUT)
+ dir = 0;
+
+ switch (map->lp_port) {
+ case 0:
+ state = lpc_gpio_get_4(sc, dir, LPC_GPIO_P0_OUTP_STATE,
+ LPC_GPIO_P0_INP_STATE);
+ *value = !!(state & (1 << LPC_GPIO_PIN_BIT(map, pin)));
+ case 1:
+ state = lpc_gpio_get_4(sc, dir, LPC_GPIO_P1_OUTP_STATE,
+ LPC_GPIO_P1_INP_STATE);
+ *value = !!(state & (1 << LPC_GPIO_PIN_BIT(map, pin)));
+ case 2:
+ state = lpc_gpio_read_4(sc, LPC_GPIO_P2_INP_STATE);
+ *value = !!(state & (1 << LPC_GPIO_PIN_BIT(map, pin)));
+ case 3:
+ state = lpc_gpio_get_4(sc, dir, LPC_GPIO_P3_OUTP_STATE,
+ LPC_GPIO_P3_INP_STATE);
+ if (map->lp_start_bit == -1) {
+ if (dir)
+ *value = !!(state & (1 << (25 +
+ LPC_GPIO_PIN_IDX(map, pin))));
+ else
+ *value = !!(state & (1 << (10 +
+ LPC_GPIO_PIN_IDX(map, pin))));
+ }
+
+ *value = !!(state & (1 << LPC_GPIO_PIN_BIT(map, pin)));
+ }
+
+ return (0);
+}
+
+static int
+lpc_gpio_pin_set(device_t dev, uint32_t pin, uint32_t value)
+{
+ struct lpc_gpio_softc *sc = device_get_softc(dev);
+ const struct lpc_gpio_pinmap *map;
+ uint32_t state, flags;
+
+ map = lpc_gpio_get_pinmap(pin);
+
+ if (lpc_gpio_pin_getflags(dev, pin, &flags))
+ return (ENXIO);
+
+ if ((flags & GPIO_PIN_OUTPUT) == 0)
+ return (EINVAL);
+
+ state = (1 << LPC_GPIO_PIN_BIT(map, pin));
+
+ switch (map->lp_port) {
+ case 0:
+ lpc_gpio_set_4(sc, value, LPC_GPIO_P0_OUTP_SET,
+ LPC_GPIO_P0_OUTP_CLR, state);
+ break;
+ case 1:
+ lpc_gpio_set_4(sc, value, LPC_GPIO_P1_OUTP_SET,
+ LPC_GPIO_P1_OUTP_CLR, state);
+ break;
+ case 2:
+ lpc_gpio_set_4(sc, value, LPC_GPIO_P2_OUTP_SET,
+ LPC_GPIO_P2_OUTP_CLR, state);
+ break;
+ case 3:
+ if (map->lp_start_bit == -1)
+ state = (1 << (25 + LPC_GPIO_PIN_IDX(map, pin)));
+
+ lpc_gpio_set_4(sc, value, LPC_GPIO_P3_OUTP_SET,
+ LPC_GPIO_P3_OUTP_CLR, state);
+ break;
+ }
+
+ return (0);
+}
+
+static int
+lpc_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ const struct lpc_gpio_pinmap *map;
+ uint32_t flags;
+
+ map = lpc_gpio_get_pinmap(pin);
+
+ if (lpc_gpio_pin_getflags(dev, pin, &flags))
+ return (ENXIO);
+
+ if ((flags & GPIO_PIN_OUTPUT) == 0)
+ return (EINVAL);
+
+ panic("not implemented yet");
+
+ return (0);
+
+}
+
+static const struct lpc_gpio_pinmap *
+lpc_gpio_get_pinmap(int pin)
+{
+ const struct lpc_gpio_pinmap *map;
+
+ for (map = &lpc_gpio_pins[0]; map->lp_start_idx != -1; map++) {
+ if (pin >= map->lp_start_idx &&
+ pin < map->lp_start_idx + map->lp_pin_count)
+ return map;
+ }
+
+ panic("pin number %d out of range", pin);
+}
+
+int
+lpc_gpio_set_flags(device_t dev, int pin, int flags)
+{
+ if (lpc_gpio_sc == NULL)
+ return (ENXIO);
+
+ return lpc_gpio_pin_setflags(lpc_gpio_sc->lg_dev, pin, flags);
+}
+
+int
+lpc_gpio_set_state(device_t dev, int pin, int state)
+{
+ if (lpc_gpio_sc == NULL)
+ return (ENXIO);
+
+ return lpc_gpio_pin_set(lpc_gpio_sc->lg_dev, pin, state);
+}
+
+int
+lpc_gpio_get_state(device_t dev, int pin, int *state)
+{
+ if (lpc_gpio_sc == NULL)
+ return (ENXIO);
+
+ return lpc_gpio_pin_get(lpc_gpio_sc->lg_dev, pin, state);
+}
+
+void
+platform_gpio_init()
+{
+ /* Preset SPI devices CS pins to one */
+ bus_space_write_4(fdtbus_bs_tag,
+ LPC_GPIO_BASE, LPC_GPIO_P3_OUTP_SET,
+ 1 << (SSD1289_CS_PIN - LPC_GPIO_GPO_00(0)) |
+ 1 << (SSD1289_DC_PIN - LPC_GPIO_GPO_00(0)) |
+ 1 << (ADS7846_CS_PIN - LPC_GPIO_GPO_00(0)));
+}
+
+static device_method_t lpc_gpio_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, lpc_gpio_probe),
+ DEVMETHOD(device_attach, lpc_gpio_attach),
+ DEVMETHOD(device_detach, lpc_gpio_detach),
+
+ /* GPIO interface */
+ DEVMETHOD(gpio_pin_max, lpc_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getcaps, lpc_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_getflags, lpc_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_setflags, lpc_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_getname, lpc_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_set, lpc_gpio_pin_set),
+ DEVMETHOD(gpio_pin_get, lpc_gpio_pin_get),
+ DEVMETHOD(gpio_pin_toggle, lpc_gpio_pin_toggle),
+
+ { 0, 0 }
+};
+
+static devclass_t lpc_gpio_devclass;
+
+static driver_t lpc_gpio_driver = {
+ "lpcgpio",
+ lpc_gpio_methods,
+ sizeof(struct lpc_gpio_softc),
+};
+
+extern devclass_t gpiobus_devclass, gpioc_devclass;
+extern driver_t gpiobus_driver, gpioc_driver;
+
+DRIVER_MODULE(lpcgpio, simplebus, lpc_gpio_driver, lpc_gpio_devclass, 0, 0);
+DRIVER_MODULE(gpiobus, lpcgpio, gpiobus_driver, gpiobus_devclass, 0, 0);
+DRIVER_MODULE(gpioc, lpcgpio, gpioc_driver, gpioc_devclass, 0, 0);
+MODULE_VERSION(lpcgpio, 1);
OpenPOWER on IntegriCloud