avila_gpio.c revision 214946
1/*- 2 * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 3 * Copyright (c) 2009, Luiz Otavio O Souza. 4 * Copyright (c) 2010, Andrew Thompson <thompsa@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice unmodified, this list of conditions, and the following 12 * disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30/* 31 * GPIO driver for Gateworks Avilia 32 */ 33 34#include <sys/cdefs.h> 35__FBSDID("$FreeBSD: head/sys/arm/xscale/ixp425/avila_gpio.c 214946 2010-11-07 20:33:39Z thompsa $"); 36 37#include <sys/param.h> 38#include <sys/systm.h> 39#include <sys/bus.h> 40 41#include <sys/kernel.h> 42#include <sys/module.h> 43#include <sys/rman.h> 44#include <sys/lock.h> 45#include <sys/mutex.h> 46#include <sys/gpio.h> 47 48#include <machine/bus.h> 49#include <machine/resource.h> 50#include <arm/xscale/ixp425/ixp425reg.h> 51#include <arm/xscale/ixp425/ixp425var.h> 52 53#include "gpio_if.h" 54 55#define GPIO_SET_BITS(sc, reg, bits) \ 56 GPIO_CONF_WRITE_4(sc, reg, GPIO_CONF_READ_4(sc, (reg)) | (bits)) 57 58#define GPIO_CLEAR_BITS(sc, reg, bits) \ 59 GPIO_CONF_WRITE_4(sc, reg, GPIO_CONF_READ_4(sc, (reg)) & ~(bits)) 60 61#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 62#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 63#define GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) 64 65struct avila_gpio_softc { 66 device_t sc_dev; 67 struct mtx sc_mtx; 68 bus_space_tag_t sc_iot; 69 bus_space_handle_t sc_gpio_ioh; 70 uint32_t sc_valid; 71 struct gpio_pin sc_pins[IXP4XX_GPIO_PINS]; 72}; 73 74struct avila_gpio_pin { 75 const char *name; 76 int pin; 77 int caps; 78}; 79 80#define GPIO_PIN_IO (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT) 81static struct avila_gpio_pin avila_gpio_pins[] = { 82 { "GPIO0", 0, GPIO_PIN_IO }, 83 { "GPIO1", 1, GPIO_PIN_IO }, 84 { "GPIO2", 2, GPIO_PIN_IO }, 85 { "GPIO3", 3, GPIO_PIN_IO }, 86 { "GPIO4", 4, GPIO_PIN_IO }, 87 /* 88 * The following pins are connected to system devices and should not 89 * really be frobbed. 90 */ 91#if 0 92 { "SER_ENA", 5, GPIO_PIN_IO }, 93 { "I2C_SCL", 6, GPIO_PIN_IO }, 94 { "I2C_SDA", 7, GPIO_PIN_IO }, 95 { "PCI_INTD", 8, GPIO_PIN_IO }, 96 { "PCI_INTC", 9, GPIO_PIN_IO }, 97 { "PCI_INTB", 10, GPIO_PIN_IO }, 98 { "PCI_INTA", 11, GPIO_PIN_IO }, 99 { "ATA_INT", 12, GPIO_PIN_IO }, 100 { "PCI_RST", 13, GPIO_PIN_IO }, 101 { "PCI_CLK", 14, GPIO_PIN_OUTPUT }, 102 { "EX_CLK", 15, GPIO_PIN_OUTPUT }, 103#endif 104}; 105#undef GPIO_PIN_IO 106 107/* 108 * Helpers 109 */ 110static void avila_gpio_pin_configure(struct avila_gpio_softc *sc, 111 struct gpio_pin *pin, uint32_t flags); 112static int avila_gpio_pin_flags(struct avila_gpio_softc *sc, uint32_t pin); 113 114/* 115 * Driver stuff 116 */ 117static int avila_gpio_probe(device_t dev); 118static int avila_gpio_attach(device_t dev); 119static int avila_gpio_detach(device_t dev); 120 121/* 122 * GPIO interface 123 */ 124static int avila_gpio_pin_max(device_t dev, int *maxpin); 125static int avila_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); 126static int avila_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t 127 *flags); 128static int avila_gpio_pin_getname(device_t dev, uint32_t pin, char *name); 129static int avila_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags); 130static int avila_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); 131static int avila_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val); 132static int avila_gpio_pin_toggle(device_t dev, uint32_t pin); 133 134static int 135avila_gpio_pin_flags(struct avila_gpio_softc *sc, uint32_t pin) 136{ 137 uint32_t v; 138 139 v = GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & (1 << pin); 140 141 return (v ? GPIO_PIN_INPUT : GPIO_PIN_OUTPUT); 142} 143 144static void 145avila_gpio_pin_configure(struct avila_gpio_softc *sc, struct gpio_pin *pin, 146 unsigned int flags) 147{ 148 uint32_t mask; 149 150 mask = 1 << pin->gp_pin; 151 GPIO_LOCK(sc); 152 153 /* 154 * Manage input/output 155 */ 156 if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { 157 pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); 158 if (flags & GPIO_PIN_OUTPUT) { 159 pin->gp_flags |= GPIO_PIN_OUTPUT; 160 GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOER, mask); 161 } 162 else { 163 pin->gp_flags |= GPIO_PIN_INPUT; 164 GPIO_SET_BITS(sc, IXP425_GPIO_GPOER, mask); 165 } 166 } 167 168 GPIO_UNLOCK(sc); 169} 170 171static int 172avila_gpio_pin_max(device_t dev, int *maxpin) 173{ 174 175 *maxpin = IXP4XX_GPIO_PINS - 1; 176 return (0); 177} 178 179static int 180avila_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 181{ 182 struct avila_gpio_softc *sc = device_get_softc(dev); 183 184 if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin))) 185 return (EINVAL); 186 187 GPIO_LOCK(sc); 188 *caps = sc->sc_pins[pin].gp_caps; 189 GPIO_UNLOCK(sc); 190 191 return (0); 192} 193 194static int 195avila_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) 196{ 197 struct avila_gpio_softc *sc = device_get_softc(dev); 198 199 if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin))) 200 return (EINVAL); 201 202 GPIO_LOCK(sc); 203 /* refresh since we do not own all the pins */ 204 sc->sc_pins[pin].gp_flags = avila_gpio_pin_flags(sc, pin); 205 *flags = sc->sc_pins[pin].gp_flags; 206 GPIO_UNLOCK(sc); 207 208 return (0); 209} 210 211static int 212avila_gpio_pin_getname(device_t dev, uint32_t pin, char *name) 213{ 214 struct avila_gpio_softc *sc = device_get_softc(dev); 215 216 if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin))) 217 return (EINVAL); 218 219 GPIO_LOCK(sc); 220 memcpy(name, sc->sc_pins[pin].gp_name, GPIOMAXNAME); 221 GPIO_UNLOCK(sc); 222 223 return (0); 224} 225 226static int 227avila_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 228{ 229 struct avila_gpio_softc *sc = device_get_softc(dev); 230 uint32_t mask = 1 << pin; 231 232 if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask)) 233 return (EINVAL); 234 235 /* Filter out unwanted flags */ 236 if ((flags &= sc->sc_pins[pin].gp_caps) != flags) 237 return (EINVAL); 238 239 /* Can't mix input/output together */ 240 if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == 241 (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) 242 return (EINVAL); 243 244 avila_gpio_pin_configure(sc, &sc->sc_pins[pin], flags); 245 return (0); 246} 247 248static int 249avila_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) 250{ 251 struct avila_gpio_softc *sc = device_get_softc(dev); 252 uint32_t mask = 1 << pin; 253 254 if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask)) 255 return (EINVAL); 256 257 GPIO_LOCK(sc); 258 if (value) 259 GPIO_SET_BITS(sc, IXP425_GPIO_GPOUTR, mask); 260 else 261 GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOUTR, mask); 262 GPIO_UNLOCK(sc); 263 264 return (0); 265} 266 267static int 268avila_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) 269{ 270 struct avila_gpio_softc *sc = device_get_softc(dev); 271 272 if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin))) 273 return (EINVAL); 274 275 GPIO_LOCK(sc); 276 *val = (GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & (1 << pin)) ? 1 : 0; 277 GPIO_UNLOCK(sc); 278 279 return (0); 280} 281 282static int 283avila_gpio_pin_toggle(device_t dev, uint32_t pin) 284{ 285 struct avila_gpio_softc *sc = device_get_softc(dev); 286 uint32_t mask = 1 << pin; 287 int res; 288 289 if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask)) 290 return (EINVAL); 291 292 GPIO_LOCK(sc); 293 res = (GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & mask) ? 1 : 0; 294 if (res) 295 GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOUTR, mask); 296 else 297 GPIO_SET_BITS(sc, IXP425_GPIO_GPOUTR, mask); 298 GPIO_UNLOCK(sc); 299 300 return (0); 301} 302 303static int 304avila_gpio_probe(device_t dev) 305{ 306 307 device_set_desc(dev, "Gateworks Avila GPIO driver"); 308 return (0); 309} 310 311static int 312avila_gpio_attach(device_t dev) 313{ 314#define N(a) (sizeof(a) / sizeof(a[0])) 315 struct avila_gpio_softc *sc = device_get_softc(dev); 316 struct ixp425_softc *sa = device_get_softc(device_get_parent(dev)); 317 int i; 318 319 sc->sc_dev = dev; 320 sc->sc_iot = sa->sc_iot; 321 sc->sc_gpio_ioh = sa->sc_gpio_ioh; 322 323 mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 324 MTX_DEF); 325 326 for (i = 0; i < N(avila_gpio_pins); i++) { 327 struct avila_gpio_pin *p = &avila_gpio_pins[i]; 328 329 strncpy(sc->sc_pins[p->pin].gp_name, p->name, GPIOMAXNAME); 330 sc->sc_pins[p->pin].gp_pin = p->pin; 331 sc->sc_pins[p->pin].gp_caps = p->caps; 332 sc->sc_pins[p->pin].gp_flags = avila_gpio_pin_flags(sc, p->pin); 333 sc->sc_valid |= 1 << p->pin; 334 } 335 336 device_add_child(dev, "gpioc", device_get_unit(dev)); 337 device_add_child(dev, "gpiobus", device_get_unit(dev)); 338 return (bus_generic_attach(dev)); 339#undef N 340} 341 342static int 343avila_gpio_detach(device_t dev) 344{ 345 struct avila_gpio_softc *sc = device_get_softc(dev); 346 347 KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized")); 348 349 bus_generic_detach(dev); 350 351 mtx_destroy(&sc->sc_mtx); 352 353 return(0); 354} 355 356static device_method_t gpio_avila_methods[] = { 357 DEVMETHOD(device_probe, avila_gpio_probe), 358 DEVMETHOD(device_attach, avila_gpio_attach), 359 DEVMETHOD(device_detach, avila_gpio_detach), 360 361 /* GPIO protocol */ 362 DEVMETHOD(gpio_pin_max, avila_gpio_pin_max), 363 DEVMETHOD(gpio_pin_getname, avila_gpio_pin_getname), 364 DEVMETHOD(gpio_pin_getflags, avila_gpio_pin_getflags), 365 DEVMETHOD(gpio_pin_getcaps, avila_gpio_pin_getcaps), 366 DEVMETHOD(gpio_pin_setflags, avila_gpio_pin_setflags), 367 DEVMETHOD(gpio_pin_get, avila_gpio_pin_get), 368 DEVMETHOD(gpio_pin_set, avila_gpio_pin_set), 369 DEVMETHOD(gpio_pin_toggle, avila_gpio_pin_toggle), 370 {0, 0}, 371}; 372 373static driver_t gpio_avila_driver = { 374 "gpio_avila", 375 gpio_avila_methods, 376 sizeof(struct avila_gpio_softc), 377}; 378static devclass_t gpio_avila_devclass; 379 380DRIVER_MODULE(gpio_avila, ixp, gpio_avila_driver, gpio_avila_devclass, 0, 0); 381