avila_gpio.c revision 214946
1214946Sthompsa/*- 2214946Sthompsa * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 3214946Sthompsa * Copyright (c) 2009, Luiz Otavio O Souza. 4214946Sthompsa * Copyright (c) 2010, Andrew Thompson <thompsa@FreeBSD.org> 5214946Sthompsa * All rights reserved. 6214946Sthompsa * 7214946Sthompsa * Redistribution and use in source and binary forms, with or without 8214946Sthompsa * modification, are permitted provided that the following conditions 9214946Sthompsa * are met: 10214946Sthompsa * 1. Redistributions of source code must retain the above copyright 11214946Sthompsa * notice unmodified, this list of conditions, and the following 12214946Sthompsa * disclaimer. 13214946Sthompsa * 2. Redistributions in binary form must reproduce the above copyright 14214946Sthompsa * notice, this list of conditions and the following disclaimer in the 15214946Sthompsa * documentation and/or other materials provided with the distribution. 16214946Sthompsa * 17214946Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18214946Sthompsa * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19214946Sthompsa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20214946Sthompsa * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21214946Sthompsa * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22214946Sthompsa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23214946Sthompsa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24214946Sthompsa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25214946Sthompsa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26214946Sthompsa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27214946Sthompsa * SUCH DAMAGE. 28214946Sthompsa */ 29214946Sthompsa 30214946Sthompsa/* 31214946Sthompsa * GPIO driver for Gateworks Avilia 32214946Sthompsa */ 33214946Sthompsa 34214946Sthompsa#include <sys/cdefs.h> 35214946Sthompsa__FBSDID("$FreeBSD: head/sys/arm/xscale/ixp425/avila_gpio.c 214946 2010-11-07 20:33:39Z thompsa $"); 36214946Sthompsa 37214946Sthompsa#include <sys/param.h> 38214946Sthompsa#include <sys/systm.h> 39214946Sthompsa#include <sys/bus.h> 40214946Sthompsa 41214946Sthompsa#include <sys/kernel.h> 42214946Sthompsa#include <sys/module.h> 43214946Sthompsa#include <sys/rman.h> 44214946Sthompsa#include <sys/lock.h> 45214946Sthompsa#include <sys/mutex.h> 46214946Sthompsa#include <sys/gpio.h> 47214946Sthompsa 48214946Sthompsa#include <machine/bus.h> 49214946Sthompsa#include <machine/resource.h> 50214946Sthompsa#include <arm/xscale/ixp425/ixp425reg.h> 51214946Sthompsa#include <arm/xscale/ixp425/ixp425var.h> 52214946Sthompsa 53214946Sthompsa#include "gpio_if.h" 54214946Sthompsa 55214946Sthompsa#define GPIO_SET_BITS(sc, reg, bits) \ 56214946Sthompsa GPIO_CONF_WRITE_4(sc, reg, GPIO_CONF_READ_4(sc, (reg)) | (bits)) 57214946Sthompsa 58214946Sthompsa#define GPIO_CLEAR_BITS(sc, reg, bits) \ 59214946Sthompsa GPIO_CONF_WRITE_4(sc, reg, GPIO_CONF_READ_4(sc, (reg)) & ~(bits)) 60214946Sthompsa 61214946Sthompsa#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 62214946Sthompsa#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 63214946Sthompsa#define GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) 64214946Sthompsa 65214946Sthompsastruct avila_gpio_softc { 66214946Sthompsa device_t sc_dev; 67214946Sthompsa struct mtx sc_mtx; 68214946Sthompsa bus_space_tag_t sc_iot; 69214946Sthompsa bus_space_handle_t sc_gpio_ioh; 70214946Sthompsa uint32_t sc_valid; 71214946Sthompsa struct gpio_pin sc_pins[IXP4XX_GPIO_PINS]; 72214946Sthompsa}; 73214946Sthompsa 74214946Sthompsastruct avila_gpio_pin { 75214946Sthompsa const char *name; 76214946Sthompsa int pin; 77214946Sthompsa int caps; 78214946Sthompsa}; 79214946Sthompsa 80214946Sthompsa#define GPIO_PIN_IO (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT) 81214946Sthompsastatic struct avila_gpio_pin avila_gpio_pins[] = { 82214946Sthompsa { "GPIO0", 0, GPIO_PIN_IO }, 83214946Sthompsa { "GPIO1", 1, GPIO_PIN_IO }, 84214946Sthompsa { "GPIO2", 2, GPIO_PIN_IO }, 85214946Sthompsa { "GPIO3", 3, GPIO_PIN_IO }, 86214946Sthompsa { "GPIO4", 4, GPIO_PIN_IO }, 87214946Sthompsa /* 88214946Sthompsa * The following pins are connected to system devices and should not 89214946Sthompsa * really be frobbed. 90214946Sthompsa */ 91214946Sthompsa#if 0 92214946Sthompsa { "SER_ENA", 5, GPIO_PIN_IO }, 93214946Sthompsa { "I2C_SCL", 6, GPIO_PIN_IO }, 94214946Sthompsa { "I2C_SDA", 7, GPIO_PIN_IO }, 95214946Sthompsa { "PCI_INTD", 8, GPIO_PIN_IO }, 96214946Sthompsa { "PCI_INTC", 9, GPIO_PIN_IO }, 97214946Sthompsa { "PCI_INTB", 10, GPIO_PIN_IO }, 98214946Sthompsa { "PCI_INTA", 11, GPIO_PIN_IO }, 99214946Sthompsa { "ATA_INT", 12, GPIO_PIN_IO }, 100214946Sthompsa { "PCI_RST", 13, GPIO_PIN_IO }, 101214946Sthompsa { "PCI_CLK", 14, GPIO_PIN_OUTPUT }, 102214946Sthompsa { "EX_CLK", 15, GPIO_PIN_OUTPUT }, 103214946Sthompsa#endif 104214946Sthompsa}; 105214946Sthompsa#undef GPIO_PIN_IO 106214946Sthompsa 107214946Sthompsa/* 108214946Sthompsa * Helpers 109214946Sthompsa */ 110214946Sthompsastatic void avila_gpio_pin_configure(struct avila_gpio_softc *sc, 111214946Sthompsa struct gpio_pin *pin, uint32_t flags); 112214946Sthompsastatic int avila_gpio_pin_flags(struct avila_gpio_softc *sc, uint32_t pin); 113214946Sthompsa 114214946Sthompsa/* 115214946Sthompsa * Driver stuff 116214946Sthompsa */ 117214946Sthompsastatic int avila_gpio_probe(device_t dev); 118214946Sthompsastatic int avila_gpio_attach(device_t dev); 119214946Sthompsastatic int avila_gpio_detach(device_t dev); 120214946Sthompsa 121214946Sthompsa/* 122214946Sthompsa * GPIO interface 123214946Sthompsa */ 124214946Sthompsastatic int avila_gpio_pin_max(device_t dev, int *maxpin); 125214946Sthompsastatic int avila_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); 126214946Sthompsastatic int avila_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t 127214946Sthompsa *flags); 128214946Sthompsastatic int avila_gpio_pin_getname(device_t dev, uint32_t pin, char *name); 129214946Sthompsastatic int avila_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags); 130214946Sthompsastatic int avila_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); 131214946Sthompsastatic int avila_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val); 132214946Sthompsastatic int avila_gpio_pin_toggle(device_t dev, uint32_t pin); 133214946Sthompsa 134214946Sthompsastatic int 135214946Sthompsaavila_gpio_pin_flags(struct avila_gpio_softc *sc, uint32_t pin) 136214946Sthompsa{ 137214946Sthompsa uint32_t v; 138214946Sthompsa 139214946Sthompsa v = GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & (1 << pin); 140214946Sthompsa 141214946Sthompsa return (v ? GPIO_PIN_INPUT : GPIO_PIN_OUTPUT); 142214946Sthompsa} 143214946Sthompsa 144214946Sthompsastatic void 145214946Sthompsaavila_gpio_pin_configure(struct avila_gpio_softc *sc, struct gpio_pin *pin, 146214946Sthompsa unsigned int flags) 147214946Sthompsa{ 148214946Sthompsa uint32_t mask; 149214946Sthompsa 150214946Sthompsa mask = 1 << pin->gp_pin; 151214946Sthompsa GPIO_LOCK(sc); 152214946Sthompsa 153214946Sthompsa /* 154214946Sthompsa * Manage input/output 155214946Sthompsa */ 156214946Sthompsa if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { 157214946Sthompsa pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); 158214946Sthompsa if (flags & GPIO_PIN_OUTPUT) { 159214946Sthompsa pin->gp_flags |= GPIO_PIN_OUTPUT; 160214946Sthompsa GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOER, mask); 161214946Sthompsa } 162214946Sthompsa else { 163214946Sthompsa pin->gp_flags |= GPIO_PIN_INPUT; 164214946Sthompsa GPIO_SET_BITS(sc, IXP425_GPIO_GPOER, mask); 165214946Sthompsa } 166214946Sthompsa } 167214946Sthompsa 168214946Sthompsa GPIO_UNLOCK(sc); 169214946Sthompsa} 170214946Sthompsa 171214946Sthompsastatic int 172214946Sthompsaavila_gpio_pin_max(device_t dev, int *maxpin) 173214946Sthompsa{ 174214946Sthompsa 175214946Sthompsa *maxpin = IXP4XX_GPIO_PINS - 1; 176214946Sthompsa return (0); 177214946Sthompsa} 178214946Sthompsa 179214946Sthompsastatic int 180214946Sthompsaavila_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 181214946Sthompsa{ 182214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 183214946Sthompsa 184214946Sthompsa if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin))) 185214946Sthompsa return (EINVAL); 186214946Sthompsa 187214946Sthompsa GPIO_LOCK(sc); 188214946Sthompsa *caps = sc->sc_pins[pin].gp_caps; 189214946Sthompsa GPIO_UNLOCK(sc); 190214946Sthompsa 191214946Sthompsa return (0); 192214946Sthompsa} 193214946Sthompsa 194214946Sthompsastatic int 195214946Sthompsaavila_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) 196214946Sthompsa{ 197214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 198214946Sthompsa 199214946Sthompsa if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin))) 200214946Sthompsa return (EINVAL); 201214946Sthompsa 202214946Sthompsa GPIO_LOCK(sc); 203214946Sthompsa /* refresh since we do not own all the pins */ 204214946Sthompsa sc->sc_pins[pin].gp_flags = avila_gpio_pin_flags(sc, pin); 205214946Sthompsa *flags = sc->sc_pins[pin].gp_flags; 206214946Sthompsa GPIO_UNLOCK(sc); 207214946Sthompsa 208214946Sthompsa return (0); 209214946Sthompsa} 210214946Sthompsa 211214946Sthompsastatic int 212214946Sthompsaavila_gpio_pin_getname(device_t dev, uint32_t pin, char *name) 213214946Sthompsa{ 214214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 215214946Sthompsa 216214946Sthompsa if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin))) 217214946Sthompsa return (EINVAL); 218214946Sthompsa 219214946Sthompsa GPIO_LOCK(sc); 220214946Sthompsa memcpy(name, sc->sc_pins[pin].gp_name, GPIOMAXNAME); 221214946Sthompsa GPIO_UNLOCK(sc); 222214946Sthompsa 223214946Sthompsa return (0); 224214946Sthompsa} 225214946Sthompsa 226214946Sthompsastatic int 227214946Sthompsaavila_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 228214946Sthompsa{ 229214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 230214946Sthompsa uint32_t mask = 1 << pin; 231214946Sthompsa 232214946Sthompsa if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask)) 233214946Sthompsa return (EINVAL); 234214946Sthompsa 235214946Sthompsa /* Filter out unwanted flags */ 236214946Sthompsa if ((flags &= sc->sc_pins[pin].gp_caps) != flags) 237214946Sthompsa return (EINVAL); 238214946Sthompsa 239214946Sthompsa /* Can't mix input/output together */ 240214946Sthompsa if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == 241214946Sthompsa (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) 242214946Sthompsa return (EINVAL); 243214946Sthompsa 244214946Sthompsa avila_gpio_pin_configure(sc, &sc->sc_pins[pin], flags); 245214946Sthompsa return (0); 246214946Sthompsa} 247214946Sthompsa 248214946Sthompsastatic int 249214946Sthompsaavila_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) 250214946Sthompsa{ 251214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 252214946Sthompsa uint32_t mask = 1 << pin; 253214946Sthompsa 254214946Sthompsa if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask)) 255214946Sthompsa return (EINVAL); 256214946Sthompsa 257214946Sthompsa GPIO_LOCK(sc); 258214946Sthompsa if (value) 259214946Sthompsa GPIO_SET_BITS(sc, IXP425_GPIO_GPOUTR, mask); 260214946Sthompsa else 261214946Sthompsa GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOUTR, mask); 262214946Sthompsa GPIO_UNLOCK(sc); 263214946Sthompsa 264214946Sthompsa return (0); 265214946Sthompsa} 266214946Sthompsa 267214946Sthompsastatic int 268214946Sthompsaavila_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) 269214946Sthompsa{ 270214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 271214946Sthompsa 272214946Sthompsa if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin))) 273214946Sthompsa return (EINVAL); 274214946Sthompsa 275214946Sthompsa GPIO_LOCK(sc); 276214946Sthompsa *val = (GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & (1 << pin)) ? 1 : 0; 277214946Sthompsa GPIO_UNLOCK(sc); 278214946Sthompsa 279214946Sthompsa return (0); 280214946Sthompsa} 281214946Sthompsa 282214946Sthompsastatic int 283214946Sthompsaavila_gpio_pin_toggle(device_t dev, uint32_t pin) 284214946Sthompsa{ 285214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 286214946Sthompsa uint32_t mask = 1 << pin; 287214946Sthompsa int res; 288214946Sthompsa 289214946Sthompsa if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask)) 290214946Sthompsa return (EINVAL); 291214946Sthompsa 292214946Sthompsa GPIO_LOCK(sc); 293214946Sthompsa res = (GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & mask) ? 1 : 0; 294214946Sthompsa if (res) 295214946Sthompsa GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOUTR, mask); 296214946Sthompsa else 297214946Sthompsa GPIO_SET_BITS(sc, IXP425_GPIO_GPOUTR, mask); 298214946Sthompsa GPIO_UNLOCK(sc); 299214946Sthompsa 300214946Sthompsa return (0); 301214946Sthompsa} 302214946Sthompsa 303214946Sthompsastatic int 304214946Sthompsaavila_gpio_probe(device_t dev) 305214946Sthompsa{ 306214946Sthompsa 307214946Sthompsa device_set_desc(dev, "Gateworks Avila GPIO driver"); 308214946Sthompsa return (0); 309214946Sthompsa} 310214946Sthompsa 311214946Sthompsastatic int 312214946Sthompsaavila_gpio_attach(device_t dev) 313214946Sthompsa{ 314214946Sthompsa#define N(a) (sizeof(a) / sizeof(a[0])) 315214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 316214946Sthompsa struct ixp425_softc *sa = device_get_softc(device_get_parent(dev)); 317214946Sthompsa int i; 318214946Sthompsa 319214946Sthompsa sc->sc_dev = dev; 320214946Sthompsa sc->sc_iot = sa->sc_iot; 321214946Sthompsa sc->sc_gpio_ioh = sa->sc_gpio_ioh; 322214946Sthompsa 323214946Sthompsa mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 324214946Sthompsa MTX_DEF); 325214946Sthompsa 326214946Sthompsa for (i = 0; i < N(avila_gpio_pins); i++) { 327214946Sthompsa struct avila_gpio_pin *p = &avila_gpio_pins[i]; 328214946Sthompsa 329214946Sthompsa strncpy(sc->sc_pins[p->pin].gp_name, p->name, GPIOMAXNAME); 330214946Sthompsa sc->sc_pins[p->pin].gp_pin = p->pin; 331214946Sthompsa sc->sc_pins[p->pin].gp_caps = p->caps; 332214946Sthompsa sc->sc_pins[p->pin].gp_flags = avila_gpio_pin_flags(sc, p->pin); 333214946Sthompsa sc->sc_valid |= 1 << p->pin; 334214946Sthompsa } 335214946Sthompsa 336214946Sthompsa device_add_child(dev, "gpioc", device_get_unit(dev)); 337214946Sthompsa device_add_child(dev, "gpiobus", device_get_unit(dev)); 338214946Sthompsa return (bus_generic_attach(dev)); 339214946Sthompsa#undef N 340214946Sthompsa} 341214946Sthompsa 342214946Sthompsastatic int 343214946Sthompsaavila_gpio_detach(device_t dev) 344214946Sthompsa{ 345214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 346214946Sthompsa 347214946Sthompsa KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized")); 348214946Sthompsa 349214946Sthompsa bus_generic_detach(dev); 350214946Sthompsa 351214946Sthompsa mtx_destroy(&sc->sc_mtx); 352214946Sthompsa 353214946Sthompsa return(0); 354214946Sthompsa} 355214946Sthompsa 356214946Sthompsastatic device_method_t gpio_avila_methods[] = { 357214946Sthompsa DEVMETHOD(device_probe, avila_gpio_probe), 358214946Sthompsa DEVMETHOD(device_attach, avila_gpio_attach), 359214946Sthompsa DEVMETHOD(device_detach, avila_gpio_detach), 360214946Sthompsa 361214946Sthompsa /* GPIO protocol */ 362214946Sthompsa DEVMETHOD(gpio_pin_max, avila_gpio_pin_max), 363214946Sthompsa DEVMETHOD(gpio_pin_getname, avila_gpio_pin_getname), 364214946Sthompsa DEVMETHOD(gpio_pin_getflags, avila_gpio_pin_getflags), 365214946Sthompsa DEVMETHOD(gpio_pin_getcaps, avila_gpio_pin_getcaps), 366214946Sthompsa DEVMETHOD(gpio_pin_setflags, avila_gpio_pin_setflags), 367214946Sthompsa DEVMETHOD(gpio_pin_get, avila_gpio_pin_get), 368214946Sthompsa DEVMETHOD(gpio_pin_set, avila_gpio_pin_set), 369214946Sthompsa DEVMETHOD(gpio_pin_toggle, avila_gpio_pin_toggle), 370214946Sthompsa {0, 0}, 371214946Sthompsa}; 372214946Sthompsa 373214946Sthompsastatic driver_t gpio_avila_driver = { 374214946Sthompsa "gpio_avila", 375214946Sthompsa gpio_avila_methods, 376214946Sthompsa sizeof(struct avila_gpio_softc), 377214946Sthompsa}; 378214946Sthompsastatic devclass_t gpio_avila_devclass; 379214946Sthompsa 380214946SthompsaDRIVER_MODULE(gpio_avila, ixp, gpio_avila_driver, gpio_avila_devclass, 0, 0); 381