From ba413bf9c271368de7f0768d6624aadd17aaa58e Mon Sep 17 00:00:00 2001 From: bouyer Date: Wed, 11 May 2016 18:33:40 +0000 Subject: [PATCH] As proposed in http://mail-index.netbsd.org/tech-kern/2016/04/28/msg020504.html add gpio interrupt support to the gpio framework, and an implementation for the allwinner gpio backend (tested on A20 only). gpio(4) has new public functions: - gpio_intr() called by backends when an interrupt condition for a gpio pin is present - gpio_find_device() and gpio_get_name(), support functions for gpio(4) users, wich respectively returns a void * cookie for a gpio device given its name, and returns the name given the cookie. - gpio_pin_ctl_intr(), which is used to configure interrupts on a gpio pin and registers a callback. - gpio_pin_irqen(), which is used to mask/unmask interrupts on a pin. Nothing in the NetBSD tree uses this yet, but I have a i2c driver (at https://github.com/mbouyer/marine_chartplotter/tree/master/software/NetBSD/driver) which uses it. --- sys/arch/arm/allwinner/awin_gpio.c | 201 ++++++++++++++++++++++++++--- sys/arch/arm/allwinner/awin_io.c | 5 +- sys/arch/arm/allwinner/awin_reg.h | 3 +- sys/dev/gpio/gpio.c | 79 +++++++++++- sys/dev/gpio/gpiovar.h | 15 ++- 5 files changed, 279 insertions(+), 24 deletions(-) diff --git a/sys/arch/arm/allwinner/awin_gpio.c b/sys/arch/arm/allwinner/awin_gpio.c index d2953c5d6689..1137d661ffbd 100644 --- a/sys/arch/arm/allwinner/awin_gpio.c +++ b/sys/arch/arm/allwinner/awin_gpio.c @@ -35,7 +35,7 @@ #include -__KERNEL_RCSID(1, "$NetBSD: awin_gpio.c,v 1.20 2015/10/02 16:04:40 bouyer Exp $"); +__KERNEL_RCSID(1, "$NetBSD: awin_gpio.c,v 1.21 2016/05/11 18:33:40 bouyer Exp $"); #include #include @@ -57,16 +57,7 @@ static void awin_gpio_attach(device_t, device_t, void *); static int awin_gpio_pin_read(void *, int); static void awin_gpio_pin_write(void *, int, int); static void awin_gpio_pin_ctl(void *, int, int); - -#if 0 -static const int ist_maps[] = { - [IST_LEVEL_LOW] = AWIN_PIO_EINT_LOW_LEVEL, - [IST_LEVEL_HIGH] = AWIN_PIO_EINT_HIGH_LEVEL, - [IST_EDGE_FALLING] = AWIN_PIO_EINT_POSITIVE_EDGE, - [IST_EDGE_RISING] = AWIN_PIO_EINT_NEGATIVE_EDGE, - [IST_EDGE_BOTH] = AWIN_PIO_EINT_DOUBLE_EDGE, -}; -#endif +static void awin_gpio_pin_irqen(void *, int, bool); struct awin_gpio_pin_cfg { uint32_t cfg[4]; @@ -77,16 +68,19 @@ struct awin_gpio_pin_cfg { static struct awin_gpio_pin_group { bus_addr_t grp_offset; uint32_t grp_pin_mask; + uint32_t grp_pin_intr_mask; uint32_t grp_pin_inuse_mask; bus_space_handle_t grp_bsh; struct awin_gpio_pin_cfg grp_cfg; struct gpio_chipset_tag grp_gc_tag; const int grp_index; const char grp_nc_name[6]; + device_t grp_gpio_dev; } pin_groups[] = { [0] = { .grp_offset = AWIN_PIO_OFFSET + 0 * AWIN_PIO_GRP_SIZE, .grp_pin_mask = __BIT(AWIN_PIO_PA_PINS) - 1, + .grp_pin_intr_mask = 0, .grp_gc_tag = { .gp_cookie = &pin_groups[0], .gp_pin_read = awin_gpio_pin_read, @@ -99,6 +93,7 @@ static struct awin_gpio_pin_group { [1] = { .grp_offset = AWIN_PIO_OFFSET + 1 * AWIN_PIO_GRP_SIZE, .grp_pin_mask = __BIT(AWIN_PIO_PB_PINS) - 1, + .grp_pin_intr_mask = 0, .grp_gc_tag = { .gp_cookie = &pin_groups[1], .gp_pin_read = awin_gpio_pin_read, @@ -111,6 +106,7 @@ static struct awin_gpio_pin_group { [2] = { .grp_offset = AWIN_PIO_OFFSET + 2 * AWIN_PIO_GRP_SIZE, .grp_pin_mask = __BIT(AWIN_PIO_PC_PINS) - 1, + .grp_pin_intr_mask = 0, .grp_gc_tag = { .gp_cookie = &pin_groups[2], .gp_pin_read = awin_gpio_pin_read, @@ -123,6 +119,7 @@ static struct awin_gpio_pin_group { [3] = { .grp_offset = AWIN_PIO_OFFSET + 3 * AWIN_PIO_GRP_SIZE, .grp_pin_mask = __BIT(AWIN_PIO_PD_PINS) - 1, + .grp_pin_intr_mask = 0, .grp_gc_tag = { .gp_cookie = &pin_groups[3], .gp_pin_read = awin_gpio_pin_read, @@ -135,6 +132,7 @@ static struct awin_gpio_pin_group { [4] = { .grp_offset = AWIN_PIO_OFFSET + 4 * AWIN_PIO_GRP_SIZE, .grp_pin_mask = __BIT(AWIN_PIO_PE_PINS) - 1, + .grp_pin_intr_mask = 0, .grp_gc_tag = { .gp_cookie = &pin_groups[4], .gp_pin_read = awin_gpio_pin_read, @@ -147,6 +145,7 @@ static struct awin_gpio_pin_group { [5] = { .grp_offset = AWIN_PIO_OFFSET + 5 * AWIN_PIO_GRP_SIZE, .grp_pin_mask = __BIT(AWIN_PIO_PF_PINS) - 1, + .grp_pin_intr_mask = 0, .grp_gc_tag = { .gp_cookie = &pin_groups[5], .gp_pin_read = awin_gpio_pin_read, @@ -159,6 +158,7 @@ static struct awin_gpio_pin_group { [6] = { .grp_offset = AWIN_PIO_OFFSET + 6 * AWIN_PIO_GRP_SIZE, .grp_pin_mask = __BIT(AWIN_PIO_PG_PINS) - 1, + .grp_pin_intr_mask = 0, .grp_gc_tag = { .gp_cookie = &pin_groups[6], .gp_pin_read = awin_gpio_pin_read, @@ -171,11 +171,13 @@ static struct awin_gpio_pin_group { [7] = { .grp_offset = AWIN_PIO_OFFSET + 7 * AWIN_PIO_GRP_SIZE, .grp_pin_mask = __BIT(AWIN_PIO_PH_PINS) - 1, + .grp_pin_intr_mask = AWIN_PIO_PH_EINT_PINS, .grp_gc_tag = { .gp_cookie = &pin_groups[7], .gp_pin_read = awin_gpio_pin_read, .gp_pin_write = awin_gpio_pin_write, .gp_pin_ctl = awin_gpio_pin_ctl, + .gp_pin_irqen = awin_gpio_pin_irqen, }, .grp_index = 7, .grp_nc_name = "nc-ph", @@ -183,6 +185,7 @@ static struct awin_gpio_pin_group { [8] = { .grp_offset = AWIN_PIO_OFFSET + 8 * AWIN_PIO_GRP_SIZE, .grp_pin_mask = __BIT(AWIN_PIO_PI_PINS) - 1, + .grp_pin_intr_mask = 0, .grp_gc_tag = { .gp_cookie = &pin_groups[8], .gp_pin_read = awin_gpio_pin_read, @@ -201,6 +204,7 @@ static struct awin_gpio_pin_group { .gp_pin_ctl = awin_gpio_pin_ctl, }, .grp_pin_mask = 0, + .grp_pin_intr_mask = 0, .grp_index = 9, .grp_nc_name = "nc-pj", }, @@ -213,6 +217,7 @@ static struct awin_gpio_pin_group { .gp_pin_ctl = awin_gpio_pin_ctl, }, .grp_pin_mask = 0, + .grp_pin_intr_mask = 0, .grp_index = 10, .grp_nc_name = "nc-pk", }, @@ -225,6 +230,7 @@ static struct awin_gpio_pin_group { .gp_pin_ctl = awin_gpio_pin_ctl, }, .grp_pin_mask = 0, + .grp_pin_intr_mask = 0, .grp_index = 11, .grp_nc_name = "nc-pl", }, @@ -237,6 +243,7 @@ static struct awin_gpio_pin_group { .gp_pin_ctl = awin_gpio_pin_ctl, }, .grp_pin_mask = 0, + .grp_pin_intr_mask = 0, .grp_index = 12, .grp_nc_name = "nc-pm", }, @@ -249,6 +256,7 @@ static struct awin_gpio_pin_group { .gp_pin_ctl = awin_gpio_pin_ctl, }, .grp_pin_mask = 0, + .grp_pin_intr_mask = 0, .grp_nc_name = "nc-pn", }, }; @@ -258,13 +266,22 @@ static struct awin_gpio_softc { device_t sc_dev; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; + bus_space_handle_t sc_eint_bsh; + void *sc_ih; + kmutex_t sc_intr_lock; + uint32_t sc_ecfg[4]; + uint32_t sc_eintr_configured; } awin_gpio_sc = { .sc_bst = &armv7_generic_bs_tag, }; +#define INT_OFFSET(x) ((x) - AWIN_PIO_INT_CFG0_REG) + CFATTACH_DECL_NEW(awin_gpio, sizeof(struct awin_gpio_softc), awin_gpio_match, awin_gpio_attach, NULL, NULL); +static int awin_gpio_intr(void *); + static int awin_gpio_match(device_t parent, cfdata_t cf, void *aux) { @@ -345,13 +362,20 @@ awin_gpio_config_pins(device_t self) pin->pin_num = num + (i << 5); pin->pin_caps = pincaps; pin->pin_flags = pincaps; + if ((grp->grp_pin_intr_mask & (1 << num)) != 0){ + pin->pin_caps |= + GPIO_PIN_EVENTS | + GPIO_PIN_LEVEL | + GPIO_PIN_FALLING; + } pin->pin_state = (data & 1) != 0; pin++; } } gba.gba_npins = pin - gba.gba_pins; - config_found_ia(self, "gpiobus", &gba, awin_gpio_cfprint); + grp->grp_gpio_dev = + config_found_ia(self, "gpiobus", &gba, awin_gpio_cfprint); } } #endif /* NGPIO > 0 */ @@ -372,6 +396,15 @@ awin_gpio_attach(device_t parent, device_t self, void *aux) aprint_naive("\n"); aprint_normal("\n"); + sc->sc_ih = intr_establish(loc->loc_intr, + IPL_VM, IST_LEVEL | IST_MPSAFE, + awin_gpio_intr, &awin_gpio_sc); + if (sc->sc_ih == NULL) { + aprint_error_dev(self, "failed to establish interrupt %d\n", + loc->loc_intr); + return; + } + aprint_normal_dev(self, "interrupting on irq %d\n", loc->loc_intr); for (u_int i = 0; i < __arraycount(pin_groups); i++) { struct awin_gpio_pin_group * const grp = &pin_groups[i]; @@ -393,6 +426,32 @@ awin_gpio_attach(device_t parent, device_t self, void *aux) #endif } +static int +awin_gpio_intr(void *a) +{ + struct awin_gpio_softc *sc = a; + uint32_t enabled; + uint32_t pending; + struct awin_gpio_pin_group * const grp = &pin_groups[7]; /* XXX */ + + mutex_enter(&sc->sc_intr_lock); + enabled = bus_space_read_4(sc->sc_bst, sc->sc_eint_bsh, + INT_OFFSET(AWIN_PIO_INT_CTL_REG)); + pending = bus_space_read_4(sc->sc_bst, sc->sc_eint_bsh, + INT_OFFSET(AWIN_PIO_INT_STA_REG)); + /* keep only enabled interrupts */ + pending &= enabled; + /* mask and ack pending interrupts */ + enabled &= ~pending; + bus_space_write_4(sc->sc_bst, sc->sc_eint_bsh, + INT_OFFSET(AWIN_PIO_INT_CTL_REG), enabled); + bus_space_write_4(sc->sc_bst, sc->sc_eint_bsh, + INT_OFFSET(AWIN_PIO_INT_STA_REG), pending); + mutex_exit(&sc->sc_intr_lock); + gpio_intr(grp->grp_gpio_dev, pending); + return 1; +} + static u_int awin_gpio_get_pin_func(const struct awin_gpio_pin_cfg *cfg, u_int pin) { @@ -457,6 +516,47 @@ awin_gpio_update_cfg_regs(bus_space_tag_t bst, struct awin_gpio_pin_group *grp, } } +static void +awin_gpio_set_pin_eint(struct awin_gpio_softc *sc, int pin, uint32_t m) +{ + int g = (pin >> 3); + int s = ((pin & 7) * 4); + KASSERT(g < 4); + sc->sc_ecfg[g] &= ~(0xf << s); + sc->sc_ecfg[g] |= ((m & 0xf) << s); +} + +static void +awin_gpio_update_eint_regs(struct awin_gpio_softc *sc) +{ + int i; + + KASSERT(mutex_owned(&sc->sc_intr_lock)); + for (i = 0; i < 4; i++) { + bus_space_write_4(sc->sc_bst, sc->sc_eint_bsh, i * 4, + sc->sc_ecfg[i]); + } +} + +static void +awin_gpio_clear_eint(struct awin_gpio_softc *sc, int pin) +{ + bus_space_write_4(sc->sc_bst, sc->sc_eint_bsh, + INT_OFFSET(AWIN_PIO_INT_STA_REG), (1U <sc_intr_lock)); + enabled = bus_space_read_4(sc->sc_bst, sc->sc_eint_bsh, + INT_OFFSET(AWIN_PIO_INT_CTL_REG)); + enabled &= sc->sc_eintr_configured; + bus_space_write_4(sc->sc_bst, sc->sc_eint_bsh, + INT_OFFSET(AWIN_PIO_INT_CTL_REG), enabled); +} + void awin_gpio_init(void) { @@ -465,7 +565,6 @@ awin_gpio_init(void) #ifdef VERBOSE_INIT_ARM printf(" free"); #endif - if (awin_chip_id() == AWIN_CHIP_ID_A31) { pin_groups[0].grp_pin_mask = __BIT(AWIN_A31_PIO_PA_PINS) - 1; pin_groups[1].grp_pin_mask = __BIT(AWIN_A31_PIO_PB_PINS) - 1; @@ -577,6 +676,15 @@ awin_gpio_init(void) popcount32(grp->grp_pin_mask & ~grp->grp_pin_inuse_mask)); #endif } + + mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_VM); + bus_space_subregion(sc->sc_bst, awin_core_bsh, + AWIN_PIO_OFFSET + AWIN_PIO_INT_CFG0_REG, + AWIN_PIO_INT_SIZE, &sc->sc_eint_bsh); + for (u_int i = 0; i < 3; i++) { + sc->sc_ecfg[i] = 0; + } + sc->sc_eintr_configured = 0; } bool @@ -764,6 +872,7 @@ awin_gpio_pin_ctl(void *cookie, int pin, int flags) { struct awin_gpio_pin_group * const grp = cookie; struct awin_gpio_pin_cfg ncfg = grp->grp_cfg; + struct awin_gpio_softc *sc = &awin_gpio_sc; u_int pull_value = AWIN_PIO_PULL_NONE; if (flags & GPIO_PIN_PULLUP) { @@ -773,16 +882,74 @@ awin_gpio_pin_ctl(void *cookie, int pin, int flags) } awin_gpio_set_pin_pull(&ncfg, pin, pull_value); - if (flags & GPIO_PIN_INPUT) { - awin_gpio_set_pin_func(&ncfg, pin, AWIN_PIO_FUNC_INPUT); - } else if (flags & GPIO_PIN_OUTPUT) { - awin_gpio_set_pin_func(&ncfg, pin, AWIN_PIO_FUNC_OUTPUT); + mutex_enter(&sc->sc_intr_lock); + if (flags & GPIO_PIN_EVENTS) { + KASSERT(awin_chip_id() == AWIN_CHIP_ID_A20); + KASSERT(grp->grp_index == 7); + KASSERT(pin <= 21); + KASSERT((grp->grp_pin_intr_mask & (1 << pin)) != 0); + awin_gpio_set_pin_func(&ncfg, pin, AWIN_PIO_PH_EINT_FUNC); + if (flags & GPIO_PIN_LEVEL) { + if (flags & GPIO_PIN_FALLING) { + awin_gpio_set_pin_eint(sc, pin, + AWIN_PIO_EINT_LOW_LEVEL); + } else { + awin_gpio_set_pin_eint(sc, pin, + AWIN_PIO_EINT_HIGH_LEVEL); + } + } else { + if (flags & GPIO_PIN_FALLING) { + awin_gpio_set_pin_eint(sc, pin, + AWIN_PIO_EINT_NEGATIVE_EDGE); + } else { + awin_gpio_set_pin_eint(sc, pin, + AWIN_PIO_EINT_POSITIVE_EDGE); + } + } + awin_gpio_update_eint_regs(sc); + sc->sc_eintr_configured |= 1 << pin; + awin_gpio_clear_eint(sc, pin); + } else { + if ((grp->grp_pin_intr_mask & (1 << pin)) != 0) { + sc->sc_eintr_configured &= ~(1 << pin); + awin_gpio_update_eint_mask(sc); + awin_gpio_clear_eint(sc, pin); + } + if (flags & GPIO_PIN_INPUT) { + awin_gpio_set_pin_func(&ncfg, pin, + AWIN_PIO_FUNC_INPUT); + } else if (flags & GPIO_PIN_OUTPUT) { + awin_gpio_set_pin_func(&ncfg, pin, + AWIN_PIO_FUNC_OUTPUT); + } } /* * Now update any config register that changed. */ awin_gpio_update_cfg_regs(&armv7_generic_bs_tag, grp, &ncfg); + mutex_exit(&sc->sc_intr_lock); +} + +static void +awin_gpio_pin_irqen(void *cookie, int pin, bool enable) +{ + struct awin_gpio_pin_group * const grp = cookie; + uint32_t enabled; + struct awin_gpio_softc *sc = &awin_gpio_sc; + + KASSERT(grp->grp_index == 7); + mutex_enter(&sc->sc_intr_lock); + enabled = bus_space_read_4(sc->sc_bst, sc->sc_eint_bsh, + INT_OFFSET(AWIN_PIO_INT_CTL_REG)); + if (enable) + enabled |= (1 << pin); + else + enabled &= ~(1 << pin); + enabled &= sc->sc_eintr_configured; + bus_space_write_4(sc->sc_bst, sc->sc_eint_bsh, + INT_OFFSET(AWIN_PIO_INT_CTL_REG), enabled); + mutex_exit(&sc->sc_intr_lock); } bool diff --git a/sys/arch/arm/allwinner/awin_io.c b/sys/arch/arm/allwinner/awin_io.c index d9fc1a92c764..9bb542685031 100644 --- a/sys/arch/arm/allwinner/awin_io.c +++ b/sys/arch/arm/allwinner/awin_io.c @@ -31,7 +31,7 @@ #include -__KERNEL_RCSID(1, "$NetBSD: awin_io.c,v 1.45 2016/04/25 20:15:46 bouyer Exp $"); +__KERNEL_RCSID(1, "$NetBSD: awin_io.c,v 1.46 2016/05/11 18:33:40 bouyer Exp $"); #include #include @@ -97,7 +97,8 @@ awinio_print(void *aux, const char *pnp) static const struct awin_locators awin_locators[] = { { "awinicu", OFFANDSIZE(INTC), NOPORT, NOINTR, A10|REQ }, - { "awingpio", OFFANDSIZE(PIO), NOPORT, NOINTR, A10|A20|A31|REQ }, + { "awingpio", OFFANDSIZE(PIO), NOPORT, AWIN_IRQ_PIO, A10|A20|REQ }, + { "awingpio", OFFANDSIZE(PIO), NOPORT, NOINTR, A31|REQ }, { "awingpio", OFFANDSIZE(A80_PIO), NOPORT, NOINTR, A80|REQ }, { "awindma", OFFANDSIZE(DMA), NOPORT, AWIN_IRQ_DMA, A10|A20 }, { "awindma", OFFANDSIZE(DMA), NOPORT, AWIN_A31_IRQ_DMA, A31 }, diff --git a/sys/arch/arm/allwinner/awin_reg.h b/sys/arch/arm/allwinner/awin_reg.h index e8be6cff87fd..206609f70730 100644 --- a/sys/arch/arm/allwinner/awin_reg.h +++ b/sys/arch/arm/allwinner/awin_reg.h @@ -1,4 +1,4 @@ -/* $NetBSD: awin_reg.h,v 1.88 2016/04/25 20:15:46 bouyer Exp $ */ +/* $NetBSD: awin_reg.h,v 1.89 2016/05/11 18:33:40 bouyer Exp $ */ /*- * Copyright (c) 2013 The NetBSD Foundation, Inc. @@ -1293,6 +1293,7 @@ struct awin_mmc_idma_descriptor { #define AWIN_PIO_INT_DEB_REG 0x0218 #define AWIN_PIO_SDR_PAD_DEV_REG 0x0220 #define AWIN_PIO_SDR_PAD_PUL_REG 0x0224 +#define AWIN_PIO_INT_SIZE 0x0028 #define AWIN_PIO_CFG_PINMASK(pin) (7 << (4*((pin) & 7))) #define AWIN_PIO_DRV_MASK(pin) ((x) << (2*((pin) & 15))) diff --git a/sys/dev/gpio/gpio.c b/sys/dev/gpio/gpio.c index cee01419ba36..c2f2c3ad20d1 100644 --- a/sys/dev/gpio/gpio.c +++ b/sys/dev/gpio/gpio.c @@ -1,4 +1,4 @@ -/* $NetBSD: gpio.c,v 1.57 2014/07/25 08:10:36 dholland Exp $ */ +/* $NetBSD: gpio.c,v 1.58 2016/05/11 18:33:40 bouyer Exp $ */ /* $OpenBSD: gpio.c,v 1.6 2006/01/14 12:33:49 grange Exp $ */ /* @@ -19,7 +19,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.57 2014/07/25 08:10:36 dholland Exp $"); +__KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.58 2016/05/11 18:33:40 bouyer Exp $"); /* * General Purpose Input/Output framework. @@ -298,6 +298,45 @@ gpiobus_print(void *aux, const char *pnp) return UNCONF; } +/* called from backends when a interrupt even occurs */ +void +gpio_intr(device_t self, uint32_t evts) +{ + struct gpio_softc *sc = device_private(self); + void (*callback)(void *); + void *callback_arg; + + for (int i = 0; i < sc->sc_npins; i++) { + if (evts & (1 << i)) { + mutex_enter(&sc->sc_mtx); + callback = sc->sc_pins[i].pin_callback; + callback_arg = sc->sc_pins[i].pin_callback_arg; + DPRINTFN(2, ("gpio pin %d event callback %p\n", i, callback)); + if (callback != NULL) { + callback(callback_arg); + } + mutex_exit(&sc->sc_mtx); + } + } +} + +void * +gpio_find_device(const char *name) +{ + device_t gpio_dev; + gpio_dev = device_find_by_xname(name); + if (gpio_dev == NULL) + return NULL; + return device_private(gpio_dev); +} + +const char * +gpio_get_name(void *gpio) +{ + struct gpio_softc *sc = gpio; + return device_xname(sc->sc_dev); +} + /* return 1 if all pins can be mapped, 0 if not */ int gpio_pin_can_map(void *gpio, int offset, uint32_t mask) @@ -379,8 +418,42 @@ void gpio_pin_ctl(void *gpio, struct gpio_pinmap *map, int pin, int flags) { struct gpio_softc *sc = gpio; + struct gpio_pin *pinp = &sc->sc_pins[map->pm_map[pin]]; - return gpiobus_pin_ctl(sc->sc_gc, map->pm_map[pin], flags); + KASSERT((flags & GPIO_PIN_EVENTS) == 0); + mutex_enter(&sc->sc_mtx); + gpiobus_pin_ctl(sc->sc_gc, map->pm_map[pin], flags); + pinp->pin_callback = NULL; + pinp->pin_callback_arg = NULL; + mutex_exit(&sc->sc_mtx); +} + +int +gpio_pin_ctl_intr(void *gpio, struct gpio_pinmap *map, int pin, int flags, + int ipl, void (*callback)(void *), void *arg) +{ + struct gpio_softc *sc = gpio; + struct gpio_pin *pinp = &sc->sc_pins[map->pm_map[pin]]; + KASSERT((flags & GPIO_PIN_EVENTS) != 0); + if (ipl != IPL_VM) + return EINVAL; + mutex_enter(&sc->sc_mtx); + if (pinp->pin_callback != NULL) { + mutex_exit(&sc->sc_mtx); + return EEXIST; + } + pinp->pin_callback = callback; + pinp->pin_callback_arg = arg; + gpiobus_pin_ctl(sc->sc_gc, map->pm_map[pin], flags); + mutex_exit(&sc->sc_mtx); + return 0; +} + +void +gpio_pin_irqen(void *gpio, struct gpio_pinmap *map, int pin, bool en) +{ + struct gpio_softc *sc = gpio; + gpiobus_pin_irqen(sc->sc_gc, map->pm_map[pin], en); } int diff --git a/sys/dev/gpio/gpiovar.h b/sys/dev/gpio/gpiovar.h index d9608af79e9f..5a97ee4a70b6 100644 --- a/sys/dev/gpio/gpiovar.h +++ b/sys/dev/gpio/gpiovar.h @@ -1,4 +1,4 @@ -/* $NetBSD: gpiovar.h,v 1.15 2011/11/13 13:20:02 mbalmer Exp $ */ +/* $NetBSD: gpiovar.h,v 1.16 2016/05/11 18:33:40 bouyer Exp $ */ /* $OpenBSD: gpiovar.h,v 1.3 2006/01/14 12:33:49 grange Exp $ */ /* @@ -31,6 +31,7 @@ typedef struct gpio_chipset_tag { int (*gp_pin_read)(void *, int); void (*gp_pin_write)(void *, int, int); void (*gp_pin_ctl)(void *, int, int); + void (*gp_pin_irqen)(void *, int, bool); } *gpio_chipset_tag_t; /* GPIO pin description */ @@ -41,6 +42,8 @@ typedef struct gpio_pin { int pin_state; /* current state */ int pin_mapped; /* is mapped */ gpio_chipset_tag_t pin_gc; /* reference the controller */ + void (*pin_callback)(void *); /* irq callback */ + void * pin_callback_arg; /* callback arg */ } gpio_pin_t; /* Attach GPIO framework to the controller */ @@ -63,6 +66,8 @@ int gpiobus_print(void *, const char *); ((gc)->gp_pin_write((gc)->gp_cookie, (pin), (value))) #define gpiobus_pin_ctl(gc, pin, flags) \ ((gc)->gp_pin_ctl((gc)->gp_cookie, (pin), (flags))) +#define gpiobus_pin_irqen(gc, pin, en) \ + ((gc)->gp_pin_irqen((gc)->gp_cookie, (pin), (en))) /* Attach devices connected to the GPIO pins */ struct gpio_attach_args { @@ -90,16 +95,24 @@ struct gpio_name { LIST_ENTRY(gpio_name) gp_next; }; +void * gpio_find_device(const char *); +const char * gpio_get_name(void *gpio); int gpio_pin_can_map(void *, int, uint32_t); int gpio_pin_map(void *, int, uint32_t, struct gpio_pinmap *); void gpio_pin_unmap(void *, struct gpio_pinmap *); int gpio_pin_read(void *, struct gpio_pinmap *, int); void gpio_pin_write(void *, struct gpio_pinmap *, int, int); void gpio_pin_ctl(void *, struct gpio_pinmap *, int, int); +int gpio_pin_ctl_intr(void *, struct gpio_pinmap *, int, int, + int, void (*)(void *), void *); +void gpio_pin_irqen(void *, struct gpio_pinmap *, int, bool); int gpio_pin_caps(void *, struct gpio_pinmap *, int); +int gpio_pin_wait(void *, int); int gpio_npins(uint32_t); int gpio_lock(void *); void gpio_unlock(void *); +void gpio_intr(device_t, u_int32_t); + #endif /* !_DEV_GPIO_GPIOVAR_H_ */