* Matt Dainty <[email protected]> [2013-01-14 11:13:59]: > 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.
Here's an updated patch for the driver, the only change is in the *_match() function that is now simplified and much more reliable thanks to the committed Soekris comBIOS patch to bios(4). I've still left the GENERIC config unchanged as there's no other gpio(4) drivers enabled, I'm guessing as general policy. I'll send a separate patch with the various man page changes. Matt --- /dev/null Wed Mar 13 10:27:40 2013 +++ sys/dev/isa/soekris.c Tue Feb 19 08:31:10 2013 @@ -0,0 +1,239 @@ +/* $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 */ + +extern char *hw_vendor, *hw_prod; + +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; + + if (strcmp(hw_vendor, "Soekris Engineering") || + strcmp(hw_prod, "net6501")) + return (0); + + 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
