1 /* $NetBSD: bcm2835_gpio.c,v 1.2 2014/05/19 08:33:41 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2014 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Frank Kardel. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: bcm2835_gpio.c,v 1.2 2014/05/19 08:33:41 skrll Exp $"); 34 35 /* 36 * Driver for BCM2835 GPIO 37 * 38 * see: http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf 39 */ 40 41 #include "gpio.h" 42 43 #include <sys/param.h> 44 #include <sys/device.h> 45 #include <sys/systm.h> 46 #include <sys/mutex.h> 47 #include <sys/bus.h> 48 #include <sys/intr.h> 49 #include <sys/kernel.h> 50 #include <sys/gpio.h> 51 #include <dev/gpio/gpiovar.h> 52 53 #include <sys/bitops.h> 54 55 #include <arm/broadcom/bcm_amba.h> 56 #include <arm/broadcom/bcm2835reg.h> 57 #include <arm/broadcom/bcm2835_gpioreg.h> 58 #include <arm/broadcom/bcm2835_gpio_subr.h> 59 60 /* #define BCM2835_GPIO_DEBUG */ 61 #ifdef BCM2835_GPIO_DEBUG 62 int bcm2835gpiodebug = 3; 63 #define DPRINTF(l, x) do { if (l <= bcm2835gpiodebug) { printf x; } } while (0) 64 #else 65 #define DPRINTF(l, x) 66 #endif 67 68 struct bcmgpio_softc { 69 device_t sc_dev; 70 bus_space_tag_t sc_iot; 71 bus_space_handle_t sc_ioh; 72 struct gpio_chipset_tag sc_gpio_gc; 73 gpio_pin_t sc_gpio_pins[32]; 74 }; 75 76 static int bcmgpio_match(device_t, cfdata_t, void *); 77 static void bcmgpio_attach(device_t, device_t, void *); 78 79 #if NGPIO > 0 80 static int bcm2835gpio_gpio_pin_read(void *, int); 81 static void bcm2835gpio_gpio_pin_write(void *, int, int); 82 static void bcm2835gpio_gpio_pin_ctl(void *, int, int); 83 #endif 84 85 CFATTACH_DECL_NEW(bcmgpio, sizeof(struct bcmgpio_softc), 86 bcmgpio_match, bcmgpio_attach, NULL, NULL); 87 88 static int 89 bcmgpio_match(device_t parent, cfdata_t cf, void *aux) 90 { 91 struct amba_attach_args * const aaa = aux; 92 93 if (strcmp(aaa->aaa_name, "bcmgpio") != 0) 94 return 0; 95 96 return 1; 97 } 98 99 static void 100 bcmgpio_attach(device_t parent, device_t self, void *aux) 101 { 102 struct bcmgpio_softc * const sc = device_private(self); 103 #if NGPIO > 0 104 int pin, minpin, maxpin; 105 u_int func; 106 struct gpiobus_attach_args gba; 107 #endif 108 109 sc->sc_dev = self; 110 111 #if NGPIO > 0 112 if (device_unit(sc->sc_dev) > 1) { 113 aprint_naive(" NO GPIO\n"); 114 aprint_normal(": NO GPIO\n"); 115 return; 116 } else if (device_unit(sc->sc_dev) == 1) { 117 maxpin = 53; 118 minpin = 32; 119 } else { 120 maxpin = 31; 121 minpin = 0; 122 } 123 124 aprint_naive("\n"); 125 aprint_normal(": GPIO [%d...%d]\n", minpin, maxpin); 126 127 /* already mapped - nothing to gain from struct amba_attach_args */ 128 sc->sc_iot = &bcm2835_bs_tag; 129 sc->sc_ioh = BCM2835_IOPHYSTOVIRT(BCM2835_GPIO_BASE); 130 131 for (pin = minpin; pin <= maxpin; pin++) { 132 int epin = pin - minpin; 133 134 sc->sc_gpio_pins[epin].pin_num = epin; 135 /* 136 * find out pins still available for GPIO 137 */ 138 func = bcm2835gpio_function_read(pin); 139 140 if (func == BCM2835_GPIO_IN || 141 func == BCM2835_GPIO_OUT) { 142 sc->sc_gpio_pins[epin].pin_caps = GPIO_PIN_INPUT | 143 GPIO_PIN_OUTPUT | 144 GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE | 145 GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN; 146 /* read initial state */ 147 sc->sc_gpio_pins[epin].pin_state = 148 bcm2835gpio_gpio_pin_read(sc, epin); 149 DPRINTF(1, ("%s: attach pin %d\n", device_xname(sc->sc_dev), pin)); 150 } else { 151 sc->sc_gpio_pins[epin].pin_caps = 0; 152 sc->sc_gpio_pins[epin].pin_state = 0; 153 DPRINTF(1, ("%s: skip pin %d - func = 0x%x\n", device_xname(sc->sc_dev), pin, func)); 154 } 155 } 156 157 /* create controller tag */ 158 sc->sc_gpio_gc.gp_cookie = sc; 159 sc->sc_gpio_gc.gp_pin_read = bcm2835gpio_gpio_pin_read; 160 sc->sc_gpio_gc.gp_pin_write = bcm2835gpio_gpio_pin_write; 161 sc->sc_gpio_gc.gp_pin_ctl = bcm2835gpio_gpio_pin_ctl; 162 163 gba.gba_gc = &sc->sc_gpio_gc; 164 gba.gba_pins = sc->sc_gpio_pins; 165 gba.gba_npins = maxpin - minpin + 1; 166 167 config_found_ia(self, "gpiobus", &gba, gpiobus_print); 168 #else 169 aprint_normal_dev(sc->sc_dev, "no GPIO configured in kernel"); 170 #endif 171 } 172 173 #if NGPIO > 0 174 /* GPIO support functions */ 175 static int 176 bcm2835gpio_gpio_pin_read(void *arg, int pin) 177 { 178 struct bcmgpio_softc *sc = arg; 179 int epin = pin + device_unit(sc->sc_dev) * 32; 180 uint32_t val; 181 int res; 182 183 if (device_unit(sc->sc_dev) > 1) { 184 return 0; 185 } 186 187 val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 188 BCM2835_GPIO_GPLEV(epin / BCM2835_GPIO_GPLEV_PINS_PER_REGISTER)); 189 190 res = val & (1 << (epin % BCM2835_GPIO_GPLEV_PINS_PER_REGISTER)) ? 191 GPIO_PIN_HIGH : GPIO_PIN_LOW; 192 193 DPRINTF(2, ("%s: gpio_read pin %d->%d\n", device_xname(sc->sc_dev), epin, (res == GPIO_PIN_HIGH))); 194 195 return res; 196 } 197 198 static void 199 bcm2835gpio_gpio_pin_write(void *arg, int pin, int value) 200 { 201 struct bcmgpio_softc *sc = arg; 202 int epin = pin + device_unit(sc->sc_dev) * 32; 203 bus_size_t reg; 204 205 if (device_unit(sc->sc_dev) > 1) { 206 return; 207 } 208 209 if (value == GPIO_PIN_HIGH) { 210 reg = BCM2835_GPIO_GPSET(epin / BCM2835_GPIO_GPSET_PINS_PER_REGISTER); 211 } else { 212 reg = BCM2835_GPIO_GPCLR(epin / BCM2835_GPIO_GPCLR_PINS_PER_REGISTER); 213 } 214 215 bus_space_write_4(sc->sc_iot, sc->sc_ioh, 216 reg, 1 << (epin % BCM2835_GPIO_GPSET_PINS_PER_REGISTER)); 217 DPRINTF(2, ("%s: gpio_write pin %d<-%d\n", device_xname(sc->sc_dev), epin, (value == GPIO_PIN_HIGH))); 218 } 219 220 static void 221 bcm2835gpio_gpio_pin_ctl(void *arg, int pin, int flags) 222 { 223 struct bcmgpio_softc *sc = arg; 224 uint32_t cmd; 225 int epin = pin + device_unit(sc->sc_dev) * 32; 226 227 if (device_unit(sc->sc_dev) > 1) { 228 return; 229 } 230 231 DPRINTF(2, ("%s: gpio_ctl pin %d flags 0x%x\n", device_xname(sc->sc_dev), epin, flags)); 232 233 if (flags & (GPIO_PIN_OUTPUT|GPIO_PIN_INPUT)) { 234 if ((flags & GPIO_PIN_INPUT) || !(flags & GPIO_PIN_OUTPUT)) { 235 /* for safety INPUT will overide output */ 236 bcm2835gpio_function_select(epin, BCM2835_GPIO_IN); 237 } else { 238 bcm2835gpio_function_select(epin, BCM2835_GPIO_OUT); 239 } 240 } 241 242 if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) { 243 cmd = (flags & GPIO_PIN_PULLUP) ? 244 BCM2835_GPIO_GPPUD_PULLUP : BCM2835_GPIO_GPPUD_PULLDOWN; 245 } else { 246 cmd = BCM2835_GPIO_GPPUD_PULLOFF; 247 } 248 249 /* set up control signal */ 250 bus_space_write_4(sc->sc_iot, sc->sc_ioh, 251 BCM2835_GPIO_GPPUD, cmd); 252 delay(1); /* wait 150 cycles */ 253 /* set clock signal */ 254 bus_space_write_4(sc->sc_iot, sc->sc_ioh, 255 BCM2835_GPIO_GPPUDCLK(device_unit(sc->sc_dev)), 256 1 << (epin % BCM2835_GPIO_GPPUD_PINS_PER_REGISTER)); 257 delay(1); /* wait 150 cycles */ 258 /* reset control signal and clock */ 259 bus_space_write_4(sc->sc_iot, sc->sc_ioh, 260 BCM2835_GPIO_GPPUD, BCM2835_GPIO_GPPUD_PULLOFF); 261 bus_space_write_4(sc->sc_iot, sc->sc_ioh, 262 BCM2835_GPIO_GPPUDCLK(device_unit(sc->sc_dev)), 263 0); 264 } 265 #endif /* NGPIO > 0 */ 266