octeon_gpio.c revision 228925
1228925Sgonzo/*- 2228925Sgonzo * Copyright (c) 2011, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 3228925Sgonzo * All rights reserved. 4228925Sgonzo * 5228925Sgonzo * Redistribution and use in source and binary forms, with or without 6228925Sgonzo * modification, are permitted provided that the following conditions 7228925Sgonzo * are met: 8228925Sgonzo * 1. Redistributions of source code must retain the above copyright 9228925Sgonzo * notice unmodified, this list of conditions, and the following 10228925Sgonzo * disclaimer. 11228925Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 12228925Sgonzo * notice, this list of conditions and the following disclaimer in the 13228925Sgonzo * documentation and/or other materials provided with the distribution. 14228925Sgonzo * 15228925Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16228925Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17228925Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18228925Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19228925Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20228925Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21228925Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22228925Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23228925Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24228925Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25228925Sgonzo * SUCH DAMAGE. 26228925Sgonzo */ 27228925Sgonzo 28228925Sgonzo/* 29228925Sgonzo * GPIO driver for Cavium Octeon 30228925Sgonzo */ 31228925Sgonzo 32228925Sgonzo#include <sys/cdefs.h> 33228925Sgonzo__FBSDID("$FreeBSD: head/sys/mips/cavium/octeon_gpio.c 228925 2011-12-28 05:57:03Z gonzo $"); 34228925Sgonzo 35228925Sgonzo#include <sys/param.h> 36228925Sgonzo#include <sys/systm.h> 37228925Sgonzo#include <sys/bus.h> 38228925Sgonzo 39228925Sgonzo#include <sys/kernel.h> 40228925Sgonzo#include <sys/module.h> 41228925Sgonzo#include <sys/rman.h> 42228925Sgonzo#include <sys/lock.h> 43228925Sgonzo#include <sys/mutex.h> 44228925Sgonzo#include <sys/gpio.h> 45228925Sgonzo 46228925Sgonzo#include <machine/bus.h> 47228925Sgonzo#include <machine/resource.h> 48228925Sgonzo 49228925Sgonzo#include <contrib/octeon-sdk/cvmx.h> 50228925Sgonzo#include <contrib/octeon-sdk/cvmx-gpio.h> 51228925Sgonzo#include <contrib/octeon-sdk/cvmx-interrupt.h> 52228925Sgonzo 53228925Sgonzo#include <mips/cavium/octeon_gpiovar.h> 54228925Sgonzo 55228925Sgonzo#include "gpio_if.h" 56228925Sgonzo 57228925Sgonzo#define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) 58228925Sgonzo 59228925Sgonzostruct octeon_gpio_pin { 60228925Sgonzo const char *name; 61228925Sgonzo int pin; 62228925Sgonzo int flags; 63228925Sgonzo}; 64228925Sgonzo 65228925Sgonzo/* 66228925Sgonzo * on CAP100 GPIO 7 is "Factory defaults" button 67228925Sgonzo * 68228925Sgonzo */ 69228925Sgonzostatic struct octeon_gpio_pin octeon_gpio_pins[] = { 70228925Sgonzo { "F/D", 7, GPIO_PIN_INPUT}, 71228925Sgonzo { NULL, 0, 0}, 72228925Sgonzo}; 73228925Sgonzo 74228925Sgonzo/* 75228925Sgonzo * Helpers 76228925Sgonzo */ 77228925Sgonzostatic void octeon_gpio_pin_configure(struct octeon_gpio_softc *sc, 78228925Sgonzo struct gpio_pin *pin, uint32_t flags); 79228925Sgonzo 80228925Sgonzo/* 81228925Sgonzo * Driver stuff 82228925Sgonzo */ 83228925Sgonzostatic void octeon_gpio_identify(driver_t *, device_t); 84228925Sgonzostatic int octeon_gpio_probe(device_t dev); 85228925Sgonzostatic int octeon_gpio_attach(device_t dev); 86228925Sgonzostatic int octeon_gpio_detach(device_t dev); 87228925Sgonzostatic int octeon_gpio_filter(void *arg); 88228925Sgonzostatic void octeon_gpio_intr(void *arg); 89228925Sgonzo 90228925Sgonzo/* 91228925Sgonzo * GPIO interface 92228925Sgonzo */ 93228925Sgonzostatic int octeon_gpio_pin_max(device_t dev, int *maxpin); 94228925Sgonzostatic int octeon_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); 95228925Sgonzostatic int octeon_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t 96228925Sgonzo *flags); 97228925Sgonzostatic int octeon_gpio_pin_getname(device_t dev, uint32_t pin, char *name); 98228925Sgonzostatic int octeon_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags); 99228925Sgonzostatic int octeon_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); 100228925Sgonzostatic int octeon_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val); 101228925Sgonzostatic int octeon_gpio_pin_toggle(device_t dev, uint32_t pin); 102228925Sgonzo 103228925Sgonzostatic void 104228925Sgonzoocteon_gpio_pin_configure(struct octeon_gpio_softc *sc, struct gpio_pin *pin, 105228925Sgonzo unsigned int flags) 106228925Sgonzo{ 107228925Sgonzo uint32_t mask; 108228925Sgonzo cvmx_gpio_bit_cfgx_t gpio_cfgx; 109228925Sgonzo 110228925Sgonzo mask = 1 << pin->gp_pin; 111228925Sgonzo GPIO_LOCK(sc); 112228925Sgonzo 113228925Sgonzo /* 114228925Sgonzo * Manage input/output 115228925Sgonzo */ 116228925Sgonzo if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { 117228925Sgonzo gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(pin->gp_pin)); 118228925Sgonzo pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); 119228925Sgonzo if (flags & GPIO_PIN_OUTPUT) { 120228925Sgonzo pin->gp_flags |= GPIO_PIN_OUTPUT; 121228925Sgonzo gpio_cfgx.s.tx_oe = 1; 122228925Sgonzo } 123228925Sgonzo else { 124228925Sgonzo pin->gp_flags |= GPIO_PIN_INPUT; 125228925Sgonzo gpio_cfgx.s.tx_oe = 0; 126228925Sgonzo } 127228925Sgonzo if (flags & GPIO_PIN_INVIN) 128228925Sgonzo gpio_cfgx.s.rx_xor = 1; 129228925Sgonzo else 130228925Sgonzo gpio_cfgx.s.rx_xor = 0; 131228925Sgonzo cvmx_write_csr(CVMX_GPIO_BIT_CFGX(pin->gp_pin), gpio_cfgx.u64); 132228925Sgonzo } 133228925Sgonzo 134228925Sgonzo GPIO_UNLOCK(sc); 135228925Sgonzo} 136228925Sgonzo 137228925Sgonzostatic int 138228925Sgonzoocteon_gpio_pin_max(device_t dev, int *maxpin) 139228925Sgonzo{ 140228925Sgonzo 141228925Sgonzo *maxpin = OCTEON_GPIO_PINS - 1; 142228925Sgonzo return (0); 143228925Sgonzo} 144228925Sgonzo 145228925Sgonzostatic int 146228925Sgonzoocteon_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 147228925Sgonzo{ 148228925Sgonzo struct octeon_gpio_softc *sc = device_get_softc(dev); 149228925Sgonzo int i; 150228925Sgonzo 151228925Sgonzo for (i = 0; i < sc->gpio_npins; i++) { 152228925Sgonzo if (sc->gpio_pins[i].gp_pin == pin) 153228925Sgonzo break; 154228925Sgonzo } 155228925Sgonzo 156228925Sgonzo if (i >= sc->gpio_npins) 157228925Sgonzo return (EINVAL); 158228925Sgonzo 159228925Sgonzo GPIO_LOCK(sc); 160228925Sgonzo *caps = sc->gpio_pins[i].gp_caps; 161228925Sgonzo GPIO_UNLOCK(sc); 162228925Sgonzo 163228925Sgonzo return (0); 164228925Sgonzo} 165228925Sgonzo 166228925Sgonzostatic int 167228925Sgonzoocteon_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) 168228925Sgonzo{ 169228925Sgonzo struct octeon_gpio_softc *sc = device_get_softc(dev); 170228925Sgonzo int i; 171228925Sgonzo 172228925Sgonzo for (i = 0; i < sc->gpio_npins; i++) { 173228925Sgonzo if (sc->gpio_pins[i].gp_pin == pin) 174228925Sgonzo break; 175228925Sgonzo } 176228925Sgonzo 177228925Sgonzo if (i >= sc->gpio_npins) 178228925Sgonzo return (EINVAL); 179228925Sgonzo 180228925Sgonzo GPIO_LOCK(sc); 181228925Sgonzo *flags = sc->gpio_pins[i].gp_flags; 182228925Sgonzo GPIO_UNLOCK(sc); 183228925Sgonzo 184228925Sgonzo return (0); 185228925Sgonzo} 186228925Sgonzo 187228925Sgonzostatic int 188228925Sgonzoocteon_gpio_pin_getname(device_t dev, uint32_t pin, char *name) 189228925Sgonzo{ 190228925Sgonzo struct octeon_gpio_softc *sc = device_get_softc(dev); 191228925Sgonzo int i; 192228925Sgonzo 193228925Sgonzo for (i = 0; i < sc->gpio_npins; i++) { 194228925Sgonzo if (sc->gpio_pins[i].gp_pin == pin) 195228925Sgonzo break; 196228925Sgonzo } 197228925Sgonzo 198228925Sgonzo if (i >= sc->gpio_npins) 199228925Sgonzo return (EINVAL); 200228925Sgonzo 201228925Sgonzo GPIO_LOCK(sc); 202228925Sgonzo memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); 203228925Sgonzo GPIO_UNLOCK(sc); 204228925Sgonzo 205228925Sgonzo return (0); 206228925Sgonzo} 207228925Sgonzo 208228925Sgonzostatic int 209228925Sgonzoocteon_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 210228925Sgonzo{ 211228925Sgonzo int i; 212228925Sgonzo struct octeon_gpio_softc *sc = device_get_softc(dev); 213228925Sgonzo 214228925Sgonzo for (i = 0; i < sc->gpio_npins; i++) { 215228925Sgonzo if (sc->gpio_pins[i].gp_pin == pin) 216228925Sgonzo break; 217228925Sgonzo } 218228925Sgonzo 219228925Sgonzo if (i >= sc->gpio_npins) 220228925Sgonzo return (EINVAL); 221228925Sgonzo 222228925Sgonzo /* Filter out unwanted flags */ 223228925Sgonzo if ((flags &= sc->gpio_pins[i].gp_caps) != flags) 224228925Sgonzo return (EINVAL); 225228925Sgonzo 226228925Sgonzo /* Can't mix input/output together */ 227228925Sgonzo if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == 228228925Sgonzo (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) 229228925Sgonzo return (EINVAL); 230228925Sgonzo 231228925Sgonzo octeon_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); 232228925Sgonzo return (0); 233228925Sgonzo} 234228925Sgonzo 235228925Sgonzostatic int 236228925Sgonzoocteon_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) 237228925Sgonzo{ 238228925Sgonzo struct octeon_gpio_softc *sc = device_get_softc(dev); 239228925Sgonzo int i; 240228925Sgonzo 241228925Sgonzo for (i = 0; i < sc->gpio_npins; i++) { 242228925Sgonzo if (sc->gpio_pins[i].gp_pin == pin) 243228925Sgonzo break; 244228925Sgonzo } 245228925Sgonzo 246228925Sgonzo if (i >= sc->gpio_npins) 247228925Sgonzo return (EINVAL); 248228925Sgonzo 249228925Sgonzo GPIO_LOCK(sc); 250228925Sgonzo if (value) 251228925Sgonzo cvmx_gpio_set(1 << pin); 252228925Sgonzo else 253228925Sgonzo cvmx_gpio_clear(1 << pin); 254228925Sgonzo GPIO_UNLOCK(sc); 255228925Sgonzo 256228925Sgonzo return (0); 257228925Sgonzo} 258228925Sgonzo 259228925Sgonzostatic int 260228925Sgonzoocteon_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) 261228925Sgonzo{ 262228925Sgonzo struct octeon_gpio_softc *sc = device_get_softc(dev); 263228925Sgonzo int i; 264228925Sgonzo uint64_t state; 265228925Sgonzo 266228925Sgonzo for (i = 0; i < sc->gpio_npins; i++) { 267228925Sgonzo if (sc->gpio_pins[i].gp_pin == pin) 268228925Sgonzo break; 269228925Sgonzo } 270228925Sgonzo 271228925Sgonzo if (i >= sc->gpio_npins) 272228925Sgonzo return (EINVAL); 273228925Sgonzo 274228925Sgonzo GPIO_LOCK(sc); 275228925Sgonzo state = cvmx_gpio_read(); 276228925Sgonzo *val = (state & (1 << pin)) ? 1 : 0; 277228925Sgonzo GPIO_UNLOCK(sc); 278228925Sgonzo 279228925Sgonzo return (0); 280228925Sgonzo} 281228925Sgonzo 282228925Sgonzostatic int 283228925Sgonzoocteon_gpio_pin_toggle(device_t dev, uint32_t pin) 284228925Sgonzo{ 285228925Sgonzo int i; 286228925Sgonzo uint64_t state; 287228925Sgonzo struct octeon_gpio_softc *sc = device_get_softc(dev); 288228925Sgonzo 289228925Sgonzo for (i = 0; i < sc->gpio_npins; i++) { 290228925Sgonzo if (sc->gpio_pins[i].gp_pin == pin) 291228925Sgonzo break; 292228925Sgonzo } 293228925Sgonzo 294228925Sgonzo if (i >= sc->gpio_npins) 295228925Sgonzo return (EINVAL); 296228925Sgonzo 297228925Sgonzo GPIO_LOCK(sc); 298228925Sgonzo /* 299228925Sgonzo * XXX: Need to check if read returns actual state of output 300228925Sgonzo * pins or we need to keep this information by ourself 301228925Sgonzo */ 302228925Sgonzo state = cvmx_gpio_read(); 303228925Sgonzo if (state & (1 << pin)) 304228925Sgonzo cvmx_gpio_clear(1 << pin); 305228925Sgonzo else 306228925Sgonzo cvmx_gpio_set(1 << pin); 307228925Sgonzo GPIO_UNLOCK(sc); 308228925Sgonzo 309228925Sgonzo return (0); 310228925Sgonzo} 311228925Sgonzo 312228925Sgonzostatic int 313228925Sgonzoocteon_gpio_filter(void *arg) 314228925Sgonzo{ 315228925Sgonzo cvmx_gpio_bit_cfgx_t gpio_cfgx; 316228925Sgonzo void **cookie = arg; 317228925Sgonzo struct octeon_gpio_softc *sc = *cookie; 318228925Sgonzo long int irq = (cookie - sc->gpio_intr_cookies); 319228925Sgonzo 320228925Sgonzo if ((irq < 0) || (irq >= OCTEON_GPIO_IRQS)) 321228925Sgonzo return (FILTER_STRAY); 322228925Sgonzo 323228925Sgonzo gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(irq)); 324228925Sgonzo /* Clear rising edge detector */ 325228925Sgonzo if (gpio_cfgx.s.int_type == OCTEON_GPIO_IRQ_EDGE) 326228925Sgonzo cvmx_gpio_interrupt_clear(1 << irq); 327228925Sgonzo /* disable interrupt */ 328228925Sgonzo gpio_cfgx.s.int_en = 0; 329228925Sgonzo cvmx_write_csr(CVMX_GPIO_BIT_CFGX(irq), gpio_cfgx.u64); 330228925Sgonzo 331228925Sgonzo return (FILTER_SCHEDULE_THREAD); 332228925Sgonzo} 333228925Sgonzo 334228925Sgonzostatic void 335228925Sgonzoocteon_gpio_intr(void *arg) 336228925Sgonzo{ 337228925Sgonzo cvmx_gpio_bit_cfgx_t gpio_cfgx; 338228925Sgonzo void **cookie = arg; 339228925Sgonzo struct octeon_gpio_softc *sc = *cookie; 340228925Sgonzo long int irq = (cookie - sc->gpio_intr_cookies); 341228925Sgonzo 342228925Sgonzo if ((irq < 0) || (irq >= OCTEON_GPIO_IRQS)) { 343228925Sgonzo printf("%s: invalid GPIO IRQ: %ld\n", 344228925Sgonzo __func__, irq); 345228925Sgonzo return; 346228925Sgonzo } 347228925Sgonzo 348228925Sgonzo GPIO_LOCK(sc); 349228925Sgonzo gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(irq)); 350228925Sgonzo /* disable interrupt */ 351228925Sgonzo gpio_cfgx.s.int_en = 1; 352228925Sgonzo cvmx_write_csr(CVMX_GPIO_BIT_CFGX(irq), gpio_cfgx.u64); 353228925Sgonzo 354228925Sgonzo /* TODO: notify bus here or something */ 355228925Sgonzo printf("GPIO IRQ for pin %ld\n", irq); 356228925Sgonzo GPIO_UNLOCK(sc); 357228925Sgonzo} 358228925Sgonzo 359228925Sgonzostatic void 360228925Sgonzoocteon_gpio_identify(driver_t *drv, device_t parent) 361228925Sgonzo{ 362228925Sgonzo 363228925Sgonzo BUS_ADD_CHILD(parent, 0, "gpio", 0); 364228925Sgonzo} 365228925Sgonzo 366228925Sgonzostatic int 367228925Sgonzoocteon_gpio_probe(device_t dev) 368228925Sgonzo{ 369228925Sgonzo 370228925Sgonzo device_set_desc(dev, "Cavium Octeon GPIO driver"); 371228925Sgonzo return (0); 372228925Sgonzo} 373228925Sgonzo 374228925Sgonzostatic int 375228925Sgonzoocteon_gpio_attach(device_t dev) 376228925Sgonzo{ 377228925Sgonzo struct octeon_gpio_softc *sc = device_get_softc(dev); 378228925Sgonzo struct octeon_gpio_pin *pinp; 379228925Sgonzo cvmx_gpio_bit_cfgx_t gpio_cfgx; 380228925Sgonzo 381228925Sgonzo int i; 382228925Sgonzo 383228925Sgonzo KASSERT((device_get_unit(dev) == 0), 384228925Sgonzo ("octeon_gpio: Only one gpio module supported")); 385228925Sgonzo 386228925Sgonzo mtx_init(&sc->gpio_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 387228925Sgonzo MTX_DEF); 388228925Sgonzo 389228925Sgonzo for ( i = 0; i < OCTEON_GPIO_IRQS; i++) { 390228925Sgonzo if ((sc->gpio_irq_res[i] = bus_alloc_resource(dev, 391228925Sgonzo SYS_RES_IRQ, &sc->gpio_irq_rid[i], 392228925Sgonzo CVMX_IRQ_GPIO0 + i, CVMX_IRQ_GPIO0 + i, 1, 393228925Sgonzo RF_SHAREABLE | RF_ACTIVE)) == NULL) { 394228925Sgonzo device_printf(dev, "unable to allocate IRQ resource\n"); 395228925Sgonzo return (ENXIO); 396228925Sgonzo } 397228925Sgonzo 398228925Sgonzo sc->gpio_intr_cookies[i] = sc; 399228925Sgonzo if ((bus_setup_intr(dev, sc->gpio_irq_res[i], INTR_TYPE_MISC, 400228925Sgonzo octeon_gpio_filter, octeon_gpio_intr, 401228925Sgonzo &(sc->gpio_intr_cookies[i]), &sc->gpio_ih[i]))) { 402228925Sgonzo device_printf(dev, 403228925Sgonzo "WARNING: unable to register interrupt handler\n"); 404228925Sgonzo return (ENXIO); 405228925Sgonzo } 406228925Sgonzo } 407228925Sgonzo 408228925Sgonzo sc->dev = dev; 409228925Sgonzo /* Configure all pins as input */ 410228925Sgonzo /* disable interrupts for all pins */ 411228925Sgonzo pinp = octeon_gpio_pins; 412228925Sgonzo i = 0; 413228925Sgonzo while (pinp->name) { 414228925Sgonzo strncpy(sc->gpio_pins[i].gp_name, pinp->name, GPIOMAXNAME); 415228925Sgonzo sc->gpio_pins[i].gp_pin = pinp->pin; 416228925Sgonzo sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; 417228925Sgonzo sc->gpio_pins[i].gp_flags = 0; 418228925Sgonzo octeon_gpio_pin_configure(sc, &sc->gpio_pins[i], pinp->flags); 419228925Sgonzo pinp++; 420228925Sgonzo i++; 421228925Sgonzo } 422228925Sgonzo 423228925Sgonzo sc->gpio_npins = i; 424228925Sgonzo 425228925Sgonzo#if 0 426228925Sgonzo /* 427228925Sgonzo * Sample: how to enable edge-triggered interrupt 428228925Sgonzo * for GPIO pin 429228925Sgonzo */ 430228925Sgonzo gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(7)); 431228925Sgonzo gpio_cfgx.s.int_en = 1; 432228925Sgonzo gpio_cfgx.s.int_type = OCTEON_GPIO_IRQ_EDGE; 433228925Sgonzo cvmx_write_csr(CVMX_GPIO_BIT_CFGX(7), gpio_cfgx.u64); 434228925Sgonzo#endif 435228925Sgonzo 436228925Sgonzo if (bootverbose) { 437228925Sgonzo for (i = 0; i < 16; i++) { 438228925Sgonzo gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(i)); 439228925Sgonzo device_printf(dev, "[pin%d] output=%d, invinput=%d, intr=%d, intr_type=%s\n", 440228925Sgonzo i, gpio_cfgx.s.tx_oe, gpio_cfgx.s.rx_xor, 441228925Sgonzo gpio_cfgx.s.int_en, gpio_cfgx.s.int_type ? "rising edge" : "level"); 442228925Sgonzo } 443228925Sgonzo } 444228925Sgonzo 445228925Sgonzo device_add_child(dev, "gpioc", device_get_unit(dev)); 446228925Sgonzo device_add_child(dev, "gpiobus", device_get_unit(dev)); 447228925Sgonzo return (bus_generic_attach(dev)); 448228925Sgonzo} 449228925Sgonzo 450228925Sgonzostatic int 451228925Sgonzoocteon_gpio_detach(device_t dev) 452228925Sgonzo{ 453228925Sgonzo struct octeon_gpio_softc *sc = device_get_softc(dev); 454228925Sgonzo int i; 455228925Sgonzo 456228925Sgonzo KASSERT(mtx_initialized(&sc->gpio_mtx), ("gpio mutex not initialized")); 457228925Sgonzo 458228925Sgonzo for ( i = 0; i < OCTEON_GPIO_IRQS; i++) { 459228925Sgonzo bus_release_resource(dev, SYS_RES_IRQ, 460228925Sgonzo sc->gpio_irq_rid[i], sc->gpio_irq_res[i]); 461228925Sgonzo } 462228925Sgonzo bus_generic_detach(dev); 463228925Sgonzo 464228925Sgonzo mtx_destroy(&sc->gpio_mtx); 465228925Sgonzo 466228925Sgonzo return(0); 467228925Sgonzo} 468228925Sgonzo 469228925Sgonzostatic device_method_t octeon_gpio_methods[] = { 470228925Sgonzo DEVMETHOD(device_identify, octeon_gpio_identify), 471228925Sgonzo DEVMETHOD(device_probe, octeon_gpio_probe), 472228925Sgonzo DEVMETHOD(device_attach, octeon_gpio_attach), 473228925Sgonzo DEVMETHOD(device_detach, octeon_gpio_detach), 474228925Sgonzo 475228925Sgonzo /* GPIO protocol */ 476228925Sgonzo DEVMETHOD(gpio_pin_max, octeon_gpio_pin_max), 477228925Sgonzo DEVMETHOD(gpio_pin_getname, octeon_gpio_pin_getname), 478228925Sgonzo DEVMETHOD(gpio_pin_getflags, octeon_gpio_pin_getflags), 479228925Sgonzo DEVMETHOD(gpio_pin_getcaps, octeon_gpio_pin_getcaps), 480228925Sgonzo DEVMETHOD(gpio_pin_setflags, octeon_gpio_pin_setflags), 481228925Sgonzo DEVMETHOD(gpio_pin_get, octeon_gpio_pin_get), 482228925Sgonzo DEVMETHOD(gpio_pin_set, octeon_gpio_pin_set), 483228925Sgonzo DEVMETHOD(gpio_pin_toggle, octeon_gpio_pin_toggle), 484228925Sgonzo {0, 0}, 485228925Sgonzo}; 486228925Sgonzo 487228925Sgonzostatic driver_t octeon_gpio_driver = { 488228925Sgonzo "gpio", 489228925Sgonzo octeon_gpio_methods, 490228925Sgonzo sizeof(struct octeon_gpio_softc), 491228925Sgonzo}; 492228925Sgonzostatic devclass_t octeon_gpio_devclass; 493228925Sgonzo 494228925SgonzoDRIVER_MODULE(octeon_gpio, ciu, octeon_gpio_driver, octeon_gpio_devclass, 0, 0); 495