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$"); 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 61214946Sthompsastruct avila_gpio_softc { 62214946Sthompsa device_t sc_dev; 63214946Sthompsa bus_space_tag_t sc_iot; 64214946Sthompsa bus_space_handle_t sc_gpio_ioh; 65214946Sthompsa uint32_t sc_valid; 66214946Sthompsa struct gpio_pin sc_pins[IXP4XX_GPIO_PINS]; 67214946Sthompsa}; 68214946Sthompsa 69214946Sthompsastruct avila_gpio_pin { 70214946Sthompsa const char *name; 71214946Sthompsa int pin; 72214946Sthompsa int caps; 73214946Sthompsa}; 74214946Sthompsa 75214946Sthompsa#define GPIO_PIN_IO (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT) 76214946Sthompsastatic struct avila_gpio_pin avila_gpio_pins[] = { 77214946Sthompsa { "GPIO0", 0, GPIO_PIN_IO }, 78214946Sthompsa { "GPIO1", 1, GPIO_PIN_IO }, 79214946Sthompsa { "GPIO2", 2, GPIO_PIN_IO }, 80214946Sthompsa { "GPIO3", 3, GPIO_PIN_IO }, 81214946Sthompsa { "GPIO4", 4, GPIO_PIN_IO }, 82214946Sthompsa /* 83214946Sthompsa * The following pins are connected to system devices and should not 84214946Sthompsa * really be frobbed. 85214946Sthompsa */ 86214946Sthompsa#if 0 87214946Sthompsa { "SER_ENA", 5, GPIO_PIN_IO }, 88214946Sthompsa { "I2C_SCL", 6, GPIO_PIN_IO }, 89214946Sthompsa { "I2C_SDA", 7, GPIO_PIN_IO }, 90214946Sthompsa { "PCI_INTD", 8, GPIO_PIN_IO }, 91214946Sthompsa { "PCI_INTC", 9, GPIO_PIN_IO }, 92214946Sthompsa { "PCI_INTB", 10, GPIO_PIN_IO }, 93214946Sthompsa { "PCI_INTA", 11, GPIO_PIN_IO }, 94214946Sthompsa { "ATA_INT", 12, GPIO_PIN_IO }, 95214946Sthompsa { "PCI_RST", 13, GPIO_PIN_IO }, 96214946Sthompsa { "PCI_CLK", 14, GPIO_PIN_OUTPUT }, 97214946Sthompsa { "EX_CLK", 15, GPIO_PIN_OUTPUT }, 98214946Sthompsa#endif 99214946Sthompsa}; 100214946Sthompsa#undef GPIO_PIN_IO 101214946Sthompsa 102214946Sthompsa/* 103214946Sthompsa * Helpers 104214946Sthompsa */ 105214946Sthompsastatic void avila_gpio_pin_configure(struct avila_gpio_softc *sc, 106214946Sthompsa struct gpio_pin *pin, uint32_t flags); 107214946Sthompsastatic int avila_gpio_pin_flags(struct avila_gpio_softc *sc, uint32_t pin); 108214946Sthompsa 109214946Sthompsa/* 110214946Sthompsa * Driver stuff 111214946Sthompsa */ 112214946Sthompsastatic int avila_gpio_probe(device_t dev); 113214946Sthompsastatic int avila_gpio_attach(device_t dev); 114214946Sthompsastatic int avila_gpio_detach(device_t dev); 115214946Sthompsa 116214946Sthompsa/* 117214946Sthompsa * GPIO interface 118214946Sthompsa */ 119214946Sthompsastatic int avila_gpio_pin_max(device_t dev, int *maxpin); 120214946Sthompsastatic int avila_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); 121214946Sthompsastatic int avila_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t 122214946Sthompsa *flags); 123214946Sthompsastatic int avila_gpio_pin_getname(device_t dev, uint32_t pin, char *name); 124214946Sthompsastatic int avila_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags); 125214946Sthompsastatic int avila_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); 126214946Sthompsastatic int avila_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val); 127214946Sthompsastatic int avila_gpio_pin_toggle(device_t dev, uint32_t pin); 128214946Sthompsa 129214946Sthompsastatic int 130214946Sthompsaavila_gpio_pin_flags(struct avila_gpio_softc *sc, uint32_t pin) 131214946Sthompsa{ 132214946Sthompsa uint32_t v; 133214946Sthompsa 134214946Sthompsa v = GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & (1 << pin); 135214946Sthompsa 136214946Sthompsa return (v ? GPIO_PIN_INPUT : GPIO_PIN_OUTPUT); 137214946Sthompsa} 138214946Sthompsa 139214946Sthompsastatic void 140214946Sthompsaavila_gpio_pin_configure(struct avila_gpio_softc *sc, struct gpio_pin *pin, 141214946Sthompsa unsigned int flags) 142214946Sthompsa{ 143214946Sthompsa uint32_t mask; 144214946Sthompsa 145214946Sthompsa mask = 1 << pin->gp_pin; 146214946Sthompsa 147214946Sthompsa /* 148214946Sthompsa * Manage input/output 149214946Sthompsa */ 150214946Sthompsa if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { 151216681Simp IXP4XX_GPIO_LOCK(); 152214946Sthompsa pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); 153214946Sthompsa if (flags & GPIO_PIN_OUTPUT) { 154214946Sthompsa pin->gp_flags |= GPIO_PIN_OUTPUT; 155214946Sthompsa GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOER, mask); 156214946Sthompsa } 157214946Sthompsa else { 158214946Sthompsa pin->gp_flags |= GPIO_PIN_INPUT; 159214946Sthompsa GPIO_SET_BITS(sc, IXP425_GPIO_GPOER, mask); 160214946Sthompsa } 161216681Simp IXP4XX_GPIO_UNLOCK(); 162214946Sthompsa } 163214946Sthompsa} 164214946Sthompsa 165214946Sthompsastatic int 166214946Sthompsaavila_gpio_pin_max(device_t dev, int *maxpin) 167214946Sthompsa{ 168214946Sthompsa 169214946Sthompsa *maxpin = IXP4XX_GPIO_PINS - 1; 170214946Sthompsa return (0); 171214946Sthompsa} 172214946Sthompsa 173214946Sthompsastatic int 174214946Sthompsaavila_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 175214946Sthompsa{ 176214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 177214946Sthompsa 178214946Sthompsa if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin))) 179214946Sthompsa return (EINVAL); 180214946Sthompsa 181214946Sthompsa *caps = sc->sc_pins[pin].gp_caps; 182214946Sthompsa return (0); 183214946Sthompsa} 184214946Sthompsa 185214946Sthompsastatic int 186214946Sthompsaavila_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) 187214946Sthompsa{ 188214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 189214946Sthompsa 190214946Sthompsa if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin))) 191214946Sthompsa return (EINVAL); 192214946Sthompsa 193216681Simp IXP4XX_GPIO_LOCK(); 194214946Sthompsa /* refresh since we do not own all the pins */ 195214946Sthompsa sc->sc_pins[pin].gp_flags = avila_gpio_pin_flags(sc, pin); 196214946Sthompsa *flags = sc->sc_pins[pin].gp_flags; 197216681Simp IXP4XX_GPIO_UNLOCK(); 198214946Sthompsa 199214946Sthompsa return (0); 200214946Sthompsa} 201214946Sthompsa 202214946Sthompsastatic int 203214946Sthompsaavila_gpio_pin_getname(device_t dev, uint32_t pin, char *name) 204214946Sthompsa{ 205214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 206214946Sthompsa 207214946Sthompsa if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin))) 208214946Sthompsa return (EINVAL); 209214946Sthompsa 210214946Sthompsa memcpy(name, sc->sc_pins[pin].gp_name, GPIOMAXNAME); 211214946Sthompsa return (0); 212214946Sthompsa} 213214946Sthompsa 214214946Sthompsastatic int 215214946Sthompsaavila_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 216214946Sthompsa{ 217214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 218214946Sthompsa uint32_t mask = 1 << pin; 219214946Sthompsa 220214946Sthompsa if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask)) 221214946Sthompsa return (EINVAL); 222214946Sthompsa 223249550Sdim /* Check for unwanted flags. */ 224249550Sdim if ((flags & sc->sc_pins[pin].gp_caps) != flags) 225214946Sthompsa return (EINVAL); 226214946Sthompsa 227214946Sthompsa /* Can't mix input/output together */ 228214946Sthompsa if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == 229214946Sthompsa (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) 230214946Sthompsa return (EINVAL); 231214946Sthompsa 232214946Sthompsa avila_gpio_pin_configure(sc, &sc->sc_pins[pin], flags); 233214946Sthompsa return (0); 234214946Sthompsa} 235214946Sthompsa 236214946Sthompsastatic int 237214946Sthompsaavila_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) 238214946Sthompsa{ 239214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 240214946Sthompsa uint32_t mask = 1 << pin; 241214946Sthompsa 242214946Sthompsa if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask)) 243214946Sthompsa return (EINVAL); 244214946Sthompsa 245216681Simp IXP4XX_GPIO_LOCK(); 246214946Sthompsa if (value) 247214946Sthompsa GPIO_SET_BITS(sc, IXP425_GPIO_GPOUTR, mask); 248214946Sthompsa else 249214946Sthompsa GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOUTR, mask); 250216681Simp IXP4XX_GPIO_UNLOCK(); 251214946Sthompsa 252214946Sthompsa return (0); 253214946Sthompsa} 254214946Sthompsa 255214946Sthompsastatic int 256214946Sthompsaavila_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) 257214946Sthompsa{ 258214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 259214946Sthompsa 260214946Sthompsa if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & (1 << pin))) 261214946Sthompsa return (EINVAL); 262214946Sthompsa 263216681Simp IXP4XX_GPIO_LOCK(); 264214946Sthompsa *val = (GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & (1 << pin)) ? 1 : 0; 265216681Simp IXP4XX_GPIO_UNLOCK(); 266214946Sthompsa 267214946Sthompsa return (0); 268214946Sthompsa} 269214946Sthompsa 270214946Sthompsastatic int 271214946Sthompsaavila_gpio_pin_toggle(device_t dev, uint32_t pin) 272214946Sthompsa{ 273214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 274214946Sthompsa uint32_t mask = 1 << pin; 275214946Sthompsa int res; 276214946Sthompsa 277214946Sthompsa if (pin >= IXP4XX_GPIO_PINS || !(sc->sc_valid & mask)) 278214946Sthompsa return (EINVAL); 279214946Sthompsa 280216681Simp IXP4XX_GPIO_LOCK(); 281215319Sthompsa res = GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR) & mask; 282214946Sthompsa if (res) 283214946Sthompsa GPIO_CLEAR_BITS(sc, IXP425_GPIO_GPOUTR, mask); 284214946Sthompsa else 285214946Sthompsa GPIO_SET_BITS(sc, IXP425_GPIO_GPOUTR, mask); 286216681Simp IXP4XX_GPIO_UNLOCK(); 287214946Sthompsa 288214946Sthompsa return (0); 289214946Sthompsa} 290214946Sthompsa 291214946Sthompsastatic int 292214946Sthompsaavila_gpio_probe(device_t dev) 293214946Sthompsa{ 294214946Sthompsa 295214946Sthompsa device_set_desc(dev, "Gateworks Avila GPIO driver"); 296214946Sthompsa return (0); 297214946Sthompsa} 298214946Sthompsa 299214946Sthompsastatic int 300214946Sthompsaavila_gpio_attach(device_t dev) 301214946Sthompsa{ 302214946Sthompsa#define N(a) (sizeof(a) / sizeof(a[0])) 303214946Sthompsa struct avila_gpio_softc *sc = device_get_softc(dev); 304214946Sthompsa struct ixp425_softc *sa = device_get_softc(device_get_parent(dev)); 305214946Sthompsa int i; 306214946Sthompsa 307214946Sthompsa sc->sc_dev = dev; 308214946Sthompsa sc->sc_iot = sa->sc_iot; 309214946Sthompsa sc->sc_gpio_ioh = sa->sc_gpio_ioh; 310214946Sthompsa 311214946Sthompsa for (i = 0; i < N(avila_gpio_pins); i++) { 312214946Sthompsa struct avila_gpio_pin *p = &avila_gpio_pins[i]; 313214946Sthompsa 314214946Sthompsa strncpy(sc->sc_pins[p->pin].gp_name, p->name, GPIOMAXNAME); 315214946Sthompsa sc->sc_pins[p->pin].gp_pin = p->pin; 316214946Sthompsa sc->sc_pins[p->pin].gp_caps = p->caps; 317214946Sthompsa sc->sc_pins[p->pin].gp_flags = avila_gpio_pin_flags(sc, p->pin); 318214946Sthompsa sc->sc_valid |= 1 << p->pin; 319214946Sthompsa } 320214946Sthompsa 321214946Sthompsa device_add_child(dev, "gpioc", device_get_unit(dev)); 322214946Sthompsa device_add_child(dev, "gpiobus", device_get_unit(dev)); 323214946Sthompsa return (bus_generic_attach(dev)); 324214946Sthompsa#undef N 325214946Sthompsa} 326214946Sthompsa 327214946Sthompsastatic int 328214946Sthompsaavila_gpio_detach(device_t dev) 329214946Sthompsa{ 330214946Sthompsa 331214946Sthompsa bus_generic_detach(dev); 332214946Sthompsa 333214946Sthompsa return(0); 334214946Sthompsa} 335214946Sthompsa 336214946Sthompsastatic device_method_t gpio_avila_methods[] = { 337214946Sthompsa DEVMETHOD(device_probe, avila_gpio_probe), 338214946Sthompsa DEVMETHOD(device_attach, avila_gpio_attach), 339214946Sthompsa DEVMETHOD(device_detach, avila_gpio_detach), 340214946Sthompsa 341214946Sthompsa /* GPIO protocol */ 342214946Sthompsa DEVMETHOD(gpio_pin_max, avila_gpio_pin_max), 343214946Sthompsa DEVMETHOD(gpio_pin_getname, avila_gpio_pin_getname), 344214946Sthompsa DEVMETHOD(gpio_pin_getflags, avila_gpio_pin_getflags), 345214946Sthompsa DEVMETHOD(gpio_pin_getcaps, avila_gpio_pin_getcaps), 346214946Sthompsa DEVMETHOD(gpio_pin_setflags, avila_gpio_pin_setflags), 347214946Sthompsa DEVMETHOD(gpio_pin_get, avila_gpio_pin_get), 348214946Sthompsa DEVMETHOD(gpio_pin_set, avila_gpio_pin_set), 349214946Sthompsa DEVMETHOD(gpio_pin_toggle, avila_gpio_pin_toggle), 350214946Sthompsa {0, 0}, 351214946Sthompsa}; 352214946Sthompsa 353214946Sthompsastatic driver_t gpio_avila_driver = { 354214946Sthompsa "gpio_avila", 355214946Sthompsa gpio_avila_methods, 356214946Sthompsa sizeof(struct avila_gpio_softc), 357214946Sthompsa}; 358214946Sthompsastatic devclass_t gpio_avila_devclass; 359229883Sthompsaextern devclass_t gpiobus_devclass, gpioc_devclass; 360229883Sthompsaextern driver_t gpiobus_driver, gpioc_driver; 361214946Sthompsa 362214946SthompsaDRIVER_MODULE(gpio_avila, ixp, gpio_avila_driver, gpio_avila_devclass, 0, 0); 363229883SthompsaDRIVER_MODULE(gpiobus, gpio_avila, gpiobus_driver, gpiobus_devclass, 0, 0); 364229883SthompsaDRIVER_MODULE(gpioc, gpio_avila, gpioc_driver, gpioc_devclass, 0, 0); 365229883SthompsaMODULE_VERSION(gpio_avila, 1); 366