1 /* from $OpenBSD$ */ 2 3 /* 4 * ported to NetBSD by Frank Kardel 5 * (http://permalink.gmane.org/gmane.os.openbsd.tech/31317) 6 * 7 * Copyright (c) 2013 Matt Dainty <matt <at> bodgit-n-scarper.com> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN 18 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 /* 23 * Soekris net6501 GPIO and LEDs as implemented by the onboard Xilinx FPGA 24 */ 25 26 #include <sys/cdefs.h> 27 __KERNEL_RCSID(0, "$NetBSD: soekrisgpio.c,v 1.4 2021/04/24 23:36:55 thorpej Exp $"); 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/types.h> 32 #include <sys/device.h> 33 34 #include <sys/bus.h> 35 #include <machine/autoconf.h> 36 37 #include <dev/isa/isavar.h> 38 39 #include <sys/gpio.h> 40 #include <dev/gpio/gpiovar.h> 41 42 #include "gpio.h" 43 44 #define SOEKRIS_BASE 0x680 /* Base address of FPGA I/O */ 45 #define SOEKRIS_IOSIZE 32 /* I/O region size */ 46 47 #define SOEKRIS_NPINS 16 /* Number of Pins */ 48 #define SOEKRIS_GPIO_INPUT 0x000 /* Current state of pins */ 49 #define SOEKRIS_GPIO_OUTPUT 0x004 /* Set state of output pins */ 50 #define SOEKRIS_GPIO_RESET 0x008 /* Reset output pins */ 51 #define SOEKRIS_GPIO_SET 0x00c /* Set output pins */ 52 #define SOEKRIS_GPIO_DIR 0x010 /* Direction, set for output */ 53 54 #define SOEKRIS_NLEDS 2 /* Number of LEDs */ 55 #define SOEKRIS_LED_ERROR 0x01c /* Offset to error LED */ 56 #define SOEKRIS_LED_READY 0x01d /* Offset to ready LED */ 57 58 const u_int soekris_led_offset[SOEKRIS_NLEDS] = { 59 SOEKRIS_LED_ERROR, SOEKRIS_LED_READY 60 }; 61 62 struct soekris_softc { 63 device_t sc_dev; 64 65 bus_space_tag_t sc_iot; 66 bus_space_handle_t sc_ioh; 67 68 struct gpio_chipset_tag sc_gpio_gc; 69 gpio_pin_t sc_gpio_pins[SOEKRIS_NPINS]; 70 71 /* Fake GPIO device for the LEDs */ 72 struct gpio_chipset_tag sc_led_gc; 73 gpio_pin_t sc_led_pins[SOEKRIS_NLEDS]; 74 }; 75 76 static int soekris_match(device_t, cfdata_t , void *); 77 static void soekris_attach(device_t, device_t, void *); 78 static int soekris_detach(device_t, int); 79 static int soekris_gpio_read(void *, int); 80 static void soekris_gpio_write(void *, int, int); 81 static void soekris_gpio_ctl(void *, int, int); 82 static int soekris_led_read(void *, int); 83 static void soekris_led_write(void *, int, int); 84 static void soekris_led_ctl(void *, int, int); 85 86 CFATTACH_DECL_NEW(soekrisgpio, 87 sizeof(struct soekris_softc), 88 soekris_match, 89 soekris_attach, 90 soekris_detach, 91 NULL); 92 93 static int 94 soekris_match(device_t parent, cfdata_t match, void *aux) 95 { 96 struct isa_attach_args *ia = aux; 97 bus_space_handle_t ioh; 98 int iobase; 99 100 if (ia->ia_nio < 1) 101 return (0); 102 103 /* Disallow wildcarded i/o address. */ 104 if (ia->ia_io[0].ir_addr == ISA_UNKNOWN_PORT) 105 return (0); 106 107 iobase = ia->ia_io[0].ir_addr; 108 109 /* Need some sort of heuristic to match the Soekris net6501 */ 110 111 if (iobase != SOEKRIS_BASE || bus_space_map(ia->ia_iot, 112 iobase, SOEKRIS_IOSIZE, 0, &ioh) != 0) 113 return (0); 114 115 bus_space_unmap(ia->ia_iot, ioh, SOEKRIS_IOSIZE); 116 117 ia->ia_io[0].ir_size = SOEKRIS_IOSIZE; 118 119 return (1); 120 } 121 122 static void 123 soekris_attach(device_t parent, device_t self, void *aux) 124 { 125 struct soekris_softc *sc = device_private(self); 126 struct isa_attach_args *ia = aux; 127 struct gpiobus_attach_args gba1, gba2; 128 u_int data; 129 int i; 130 131 if (bus_space_map(ia->ia_iot, ia->ia_io[0].ir_addr, 132 ia->ia_io[0].ir_size, 0, &sc->sc_ioh) != 0) { 133 aprint_normal(": can't map i/o space\n"); 134 return; 135 } 136 137 aprint_normal("\n"); 138 139 sc->sc_dev = self; 140 sc->sc_iot = ia->ia_iot; 141 142 data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SOEKRIS_GPIO_DIR); 143 144 for (i = 0; i < SOEKRIS_NPINS; i++) { 145 sc->sc_gpio_pins[i].pin_num = i; 146 sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT | 147 GPIO_PIN_OUTPUT; 148 sc->sc_gpio_pins[i].pin_flags = (data & (1 << i)) ? 149 GPIO_PIN_OUTPUT : GPIO_PIN_INPUT; 150 sc->sc_gpio_pins[i].pin_state = soekris_gpio_read(sc, i); 151 } 152 153 sc->sc_gpio_gc.gp_cookie = sc; 154 sc->sc_gpio_gc.gp_pin_read = soekris_gpio_read; 155 sc->sc_gpio_gc.gp_pin_write = soekris_gpio_write; 156 sc->sc_gpio_gc.gp_pin_ctl = soekris_gpio_ctl; 157 158 gba1.gba_gc = &sc->sc_gpio_gc; 159 gba1.gba_pins = sc->sc_gpio_pins; 160 gba1.gba_npins = SOEKRIS_NPINS; 161 162 for (i = 0; i < SOEKRIS_NLEDS; i++) { 163 sc->sc_led_pins[i].pin_num = i; 164 sc->sc_led_pins[i].pin_caps = GPIO_PIN_OUTPUT; 165 sc->sc_led_pins[i].pin_flags = GPIO_PIN_OUTPUT; 166 sc->sc_led_pins[i].pin_state = soekris_led_read(sc, i); 167 } 168 169 sc->sc_led_gc.gp_cookie = sc; 170 sc->sc_led_gc.gp_pin_read = soekris_led_read; 171 sc->sc_led_gc.gp_pin_write = soekris_led_write; 172 sc->sc_led_gc.gp_pin_ctl = soekris_led_ctl; 173 174 gba2.gba_gc = &sc->sc_led_gc; 175 gba2.gba_pins = sc->sc_led_pins; 176 gba2.gba_npins = SOEKRIS_NLEDS; 177 178 #if NGPIO > 0 179 (void)config_found(sc->sc_dev, &gba1, gpiobus_print, CFARG_EOL); 180 (void)config_found(sc->sc_dev, &gba2, gpiobus_print, CFARG_EOL); 181 #endif 182 } 183 184 static int 185 soekris_detach(device_t self, int flags) 186 { 187 struct soekris_softc *sc = device_private(self); 188 189 bus_space_unmap(sc->sc_iot, sc->sc_ioh, SOEKRIS_IOSIZE); 190 return 0; 191 } 192 193 static int 194 soekris_gpio_read(void *arg, int pin) 195 { 196 struct soekris_softc *sc = arg; 197 u_int16_t data; 198 199 data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SOEKRIS_GPIO_INPUT); 200 201 return (data & (1 << pin)) ? GPIO_PIN_HIGH : GPIO_PIN_LOW; 202 } 203 204 static void 205 soekris_gpio_write(void *arg, int pin, int value) 206 { 207 struct soekris_softc *sc = arg; 208 u_int16_t data; 209 210 data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SOEKRIS_GPIO_INPUT); 211 212 if (value == GPIO_PIN_LOW) 213 data &= ~(1 << pin); 214 else if (value == GPIO_PIN_HIGH) 215 data |= (1 << pin); 216 217 bus_space_write_2(sc->sc_iot, sc->sc_ioh, SOEKRIS_GPIO_OUTPUT, data); 218 } 219 220 static void 221 soekris_gpio_ctl(void *arg, int pin, int flags) 222 { 223 struct soekris_softc *sc = arg; 224 u_int16_t data; 225 226 data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SOEKRIS_GPIO_DIR); 227 228 if (flags & GPIO_PIN_INPUT) 229 data &= ~(1 << pin); 230 if (flags & GPIO_PIN_OUTPUT) 231 data |= (1 << pin); 232 233 bus_space_write_2(sc->sc_iot, sc->sc_ioh, SOEKRIS_GPIO_DIR, data); 234 } 235 236 static int 237 soekris_led_read(void *arg, int pin) 238 { 239 struct soekris_softc *sc = arg; 240 u_int8_t value; 241 242 value = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 243 soekris_led_offset[pin]); 244 245 return (value & 0x1) ? GPIO_PIN_HIGH : GPIO_PIN_LOW; 246 } 247 248 static void 249 soekris_led_write(void *arg, int pin, int value) 250 { 251 struct soekris_softc *sc = arg; 252 253 bus_space_write_1(sc->sc_iot, sc->sc_ioh, soekris_led_offset[pin], 254 value); 255 } 256 257 static void 258 soekris_led_ctl(void *arg, int pin, int flags) 259 { 260 } 261