Attached is a patch that adds soekris(4) which provides access to the
GPIO and LEDs as implemented by the onboard Xilinx FPGA on the Soekris
net6501. The driver provides two GPIO buses; one for the 16 real GPIO
pins exposed on the board, and another which has the LEDs coerced into
the GPIO framework as output-only pins.

I kept them separate to prevent confusion and make the code slightly
simpler, if it's preferred to have just one GPIO bus let me know.

It's enabled in the kernel config with:

soekris0 at isa? port 0x680
gpio*    at soekris?

The driver cannot be enabled by default as there's no reliable way to
detect the hardware, it's just a handful of I/O ports at a known
address, (GPIO isn't enabled by default in GENERIC so a kernel compile
is required regardless).

This is what is shown at boot:

soekris0 at isa0 port 0x680/32
gpio0 at soekris0: 16 pins
gpio1 at soekris0: 2 pins

I then have the following in /etc/rc.securelevel:

gpioctl -q gpio1 0 set out error_led
gpioctl -q gpio1 1 set out ready_led

If this driver is acceptable I can send a further patch with man pages.
Tested on a Soekris net6501 running amd64, LEDs work and I've put
simple LED and pushbutton circuits on the GPIO pins.

Comments?

Matt

--- /dev/null   Tue Jan  8 21:22:57 2013
+++ sys/dev/isa/soekris.c       Tue Jan  8 21:17:02 2013
@@ -0,0 +1,235 @@
+/*     $OpenBSD$ */
+
+/*
+ * Copyright (c) 2013 Matt Dainty <[email protected]>
+ *
+ * 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.
+ */
+
+/*
+ * Soekris net6501 GPIO and LEDs as implemented by the onboard Xilinx FPGA 
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/gpio.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/isa/isavar.h>
+
+#include <dev/gpio/gpiovar.h>
+
+#define        SOEKRIS_BASE            0x680   /* Base address of FPGA I/O */
+#define        SOEKRIS_IOSIZE          32      /* I/O region size */
+
+#define        SOEKRIS_NPINS           16      /* Number of Pins */
+#define        SOEKRIS_GPIO_INPUT      0x000   /* Current state of pins */
+#define        SOEKRIS_GPIO_OUTPUT     0x004   /* Set state of output pins */
+#define        SOEKRIS_GPIO_RESET      0x008   /* Reset output pins */
+#define        SOEKRIS_GPIO_SET        0x00c   /* Set output pins */
+#define        SOEKRIS_GPIO_DIR        0x010   /* Direction, set for output */
+
+#define        SOEKRIS_NLEDS           2       /* Number of LEDs */
+#define        SOEKRIS_LED_ERROR       0x01c   /* Offset to error LED */
+#define        SOEKRIS_LED_READY       0x01d   /* Offset to ready LED */
+
+const u_int soekris_led_offset[SOEKRIS_NLEDS] = {
+       SOEKRIS_LED_ERROR, SOEKRIS_LED_READY
+};
+
+struct soekris_softc {
+       struct device            sc_dev;
+
+       bus_space_tag_t          sc_iot;
+       bus_space_handle_t       sc_ioh;
+
+       struct gpio_chipset_tag  sc_gpio_gc;
+       gpio_pin_t               sc_gpio_pins[SOEKRIS_NPINS];
+
+       /* Fake GPIO device for the LEDs */
+       struct gpio_chipset_tag  sc_led_gc;
+       gpio_pin_t               sc_led_pins[SOEKRIS_NLEDS];
+};
+
+int     soekris_match(struct device *, void *, void *);
+void    soekris_attach(struct device *, struct device *, void *);
+int     soekris_gpio_read(void *, int);
+void    soekris_gpio_write(void *, int, int);
+void    soekris_gpio_ctl(void *, int, int);
+int     soekris_led_read(void *, int);
+void    soekris_led_write(void *, int, int);
+void    soekris_led_ctl(void *, int, int);
+
+struct cfattach soekris_ca = {
+       sizeof(struct soekris_softc), soekris_match, soekris_attach
+};
+
+struct cfdriver soekris_cd = {
+       NULL, "soekris", DV_DULL
+};
+
+int
+soekris_match(struct device *parent, void *match, void *aux)
+{
+       struct isa_attach_args *ia = aux;
+       bus_space_handle_t ioh;
+
+       /* Need some sort of heuristic to match the Soekris net6501 */
+
+       if (ia->ia_iobase != SOEKRIS_BASE || bus_space_map(ia->ia_iot,
+           ia->ia_iobase, SOEKRIS_IOSIZE, 0, &ioh) != 0)
+               return (0);
+
+       bus_space_unmap(ia->ia_iot, ioh, SOEKRIS_IOSIZE);
+       ia->ia_iosize = SOEKRIS_IOSIZE;
+       ia->ipa_nio = 1;
+       ia->ipa_nmem = 0;
+       ia->ipa_nirq = 0;
+       ia->ipa_ndrq = 0;
+
+       return (1);
+}
+
+void
+soekris_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct soekris_softc *sc = (void *)self;
+       struct isa_attach_args *ia = aux;
+       struct gpiobus_attach_args gba1, gba2;
+       u_int data;
+       int i;
+
+       if (bus_space_map(ia->ia_iot, ia->ia_iobase, ia->ia_iosize, 0,
+           &sc->sc_ioh) != 0) {
+               printf(": can't map i/o space\n");
+               return;
+       }
+
+       printf("\n");
+
+       sc->sc_iot = ia->ia_iot;
+
+       data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SOEKRIS_GPIO_DIR);
+
+       for (i = 0; i < SOEKRIS_NPINS; i++) {
+               sc->sc_gpio_pins[i].pin_num = i;
+               sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT |
+                   GPIO_PIN_OUTPUT;
+               sc->sc_gpio_pins[i].pin_flags = (data & (1 << i)) ?
+                   GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
+               sc->sc_gpio_pins[i].pin_state = soekris_gpio_read(sc, i);
+       }
+
+       sc->sc_gpio_gc.gp_cookie = sc;
+       sc->sc_gpio_gc.gp_pin_read = soekris_gpio_read;
+       sc->sc_gpio_gc.gp_pin_write = soekris_gpio_write;
+       sc->sc_gpio_gc.gp_pin_ctl = soekris_gpio_ctl;
+
+       gba1.gba_name = "gpio";
+       gba1.gba_gc = &sc->sc_gpio_gc;
+       gba1.gba_pins = sc->sc_gpio_pins;
+       gba1.gba_npins = SOEKRIS_NPINS;
+
+       (void)config_found(&sc->sc_dev, &gba1, gpiobus_print);
+
+       for (i = 0; i < SOEKRIS_NLEDS; i++) {
+               sc->sc_led_pins[i].pin_num = i;
+               sc->sc_led_pins[i].pin_caps = GPIO_PIN_OUTPUT;
+               sc->sc_led_pins[i].pin_flags = GPIO_PIN_OUTPUT;
+               sc->sc_led_pins[i].pin_state = soekris_led_read(sc, i);
+       }
+
+       sc->sc_led_gc.gp_cookie = sc;
+       sc->sc_led_gc.gp_pin_read = soekris_led_read;
+       sc->sc_led_gc.gp_pin_write = soekris_led_write;
+       sc->sc_led_gc.gp_pin_ctl = soekris_led_ctl;
+
+       gba2.gba_name = "gpio";
+       gba2.gba_gc = &sc->sc_led_gc;
+       gba2.gba_pins = sc->sc_led_pins;
+       gba2.gba_npins = SOEKRIS_NLEDS;
+
+       (void)config_found(&sc->sc_dev, &gba2, gpiobus_print);
+}
+
+int
+soekris_gpio_read(void *arg, int pin)
+{
+       struct soekris_softc *sc = arg;
+       u_int16_t data;
+
+       data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SOEKRIS_GPIO_INPUT);
+
+       return (data & (1 << pin)) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
+}
+
+void
+soekris_gpio_write(void *arg, int pin, int value)
+{
+       struct soekris_softc *sc = arg;
+       u_int16_t data;
+
+       data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SOEKRIS_GPIO_INPUT);
+
+       if (value == GPIO_PIN_LOW)
+               data &= ~(1 << pin);
+       else if (value == GPIO_PIN_HIGH)
+               data |= (1 << pin);
+
+       bus_space_write_2(sc->sc_iot, sc->sc_ioh, SOEKRIS_GPIO_OUTPUT, data);
+}
+
+void
+soekris_gpio_ctl(void *arg, int pin, int flags)
+{
+       struct soekris_softc *sc = arg;
+       u_int16_t data;
+
+       data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SOEKRIS_GPIO_DIR);
+
+       if (flags & GPIO_PIN_INPUT)
+               data &= ~(1 << pin);
+       if (flags & GPIO_PIN_OUTPUT)
+               data |= (1 << pin);
+
+       bus_space_write_2(sc->sc_iot, sc->sc_ioh, SOEKRIS_GPIO_DIR, data);
+}
+
+int
+soekris_led_read(void *arg, int pin)
+{
+       struct soekris_softc *sc = arg;
+       u_int8_t value;
+
+       value = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
+           soekris_led_offset[pin]);
+
+       return (value & 0x1) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
+}
+
+void
+soekris_led_write(void *arg, int pin, int value)
+{
+       struct soekris_softc *sc = arg;
+
+       bus_space_write_1(sc->sc_iot, sc->sc_ioh, soekris_led_offset[pin],
+           value);
+}
+
+void
+soekris_led_ctl(void *arg, int pin, int flags)
+{
+}
--- sys/arch/amd64/conf/files.amd64.orig        Tue Jan  8 15:13:27 2013
+++ sys/arch/amd64/conf/files.amd64     Tue Jan  8 15:15:45 2013
@@ -195,6 +195,11 @@
 attach fd at fdc
 file   dev/isa/fd.c                            fd needs-flag
 
+# Soekris GPIO & LEDs driver
+device soekris: gpiobus
+attach soekris at isa
+file   dev/isa/soekris.c                       soekris
+
 pseudo-device  pctr
 file   arch/amd64/amd64/pctr.c         pctr needs-flag
 
--- sys/arch/i386/conf/files.i386.orig  Tue Jan  8 15:13:43 2013
+++ sys/arch/i386/conf/files.i386       Tue Jan  8 15:17:10 2013
@@ -227,6 +227,11 @@
 attach ahc at isa with ahc_isa
 file   arch/i386/isa/ahc_isa.c         ahc_isa
 
+# Soekris GPIO & LEDs driver
+device soekris: gpiobus
+attach soekris at isa
+file   dev/isa/soekris.c               soekris
+
 # Pentium performance counters
 pseudo-device  pctr
 file   arch/i386/i386/pctr.c           pctr needs-flag

Reply via email to