From 215399af25bfd489997ed86e7d17e8253cbfe517 Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Thu, 4 Dec 2014 17:05:05 -0800 Subject: nrf51: Add support for the interrupts in GPIOTE to gpio.c Add structures to keep track of which interrupts are associated with which pin. There are IN[] events, and one PORT event. The IN[] events have an array. The PORT event can be used for multiple pins at once. BUG=chrome-os-partner:34477 BRANCH=none TEST=Configured pins as IN[] events and PORT events, and saw console output. Signed-off-by: Myles Watson Change-Id: I129a6586dca4d5eb141c86fd92fbfbb70080bc2a Reviewed-on: https://chromium-review.googlesource.com/234392 Reviewed-by: Randall Spangler Tested-by: Myles Watson Commit-Queue: Myles Watson --- chip/nrf51/gpio.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 2 deletions(-) diff --git a/chip/nrf51/gpio.c b/chip/nrf51/gpio.c index 3ff09d1703..40f5f03593 100644 --- a/chip/nrf51/gpio.c +++ b/chip/nrf51/gpio.c @@ -10,6 +10,13 @@ #include "task.h" #include "util.h" +/* + * For each interrupt (INT0-INT3, PORT), record which GPIO entry uses it. + */ + +static const struct gpio_info *gpio_ints[NRF51_GPIOTE_IN_COUNT]; +static const struct gpio_info *gpio_int_port; + volatile uint32_t * const nrf51_alt_funcs[] = { /* UART */ &NRF51_UART_PSELRTS, @@ -74,6 +81,20 @@ void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags) NRF51_GPIO0_OUTCLR = mask; } + /* Interrupt levels */ + if (flags & GPIO_INT_SHARED) { + /* + * There are no shared edge-triggered interrupts; + * they're either high or low. + */ + ASSERT((flags & (GPIO_INT_F_RISING | GPIO_INT_F_FALLING)) == 0); + ASSERT((flags & GPIO_INT_LEVEL) != GPIO_INT_LEVEL); + if (flags & GPIO_INT_F_LOW) + val |= NRF51_PIN_CNF_SENSE_LOW; + else if (flags & GPIO_INT_F_HIGH) + val |= NRF51_PIN_CNF_SENSE_HIGH; + } + NRF51_PIN_CNF(bit) = val; } @@ -110,6 +131,11 @@ void gpio_pre_init(void) is_warm = 1; } + /* Initialize Interrupt configuration */ + for (i = 0; i < NRF51_GPIOTE_IN_COUNT; i++) + gpio_ints[i] = NULL; + gpio_int_port = NULL; + /* Set all GPIOs to defaults */ for (i = 0; i < GPIO_COUNT; i++, g++) { int flags = g->flags; @@ -155,13 +181,124 @@ void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func) /* - * TODO: implement GPIO interrupt. + * Enable the interrupt associated with the "signal" + * The architecture has one general (PORT) + * and NRF51_GPIOTE_IN_COUNT single-pin (IN0, IN1, ...) interrupts. + * */ int gpio_enable_interrupt(enum gpio_signal signal) { - return EC_ERROR_INVAL; + int pin; + const struct gpio_info *g = gpio_list + signal; + + /* Fail if not implemented or no interrupt handler */ + if (!g->mask || !g->irq_handler) + return EC_ERROR_INVAL; + + /* If it's not shared, use INT0-INT3, otherwise use PORT. */ + if (!(g->flags & GPIO_INT_SHARED)) { + int int_num, free_slot = -1; + uint32_t event_config = 0; + + for (int_num = 0; int_num < NRF51_GPIOTE_IN_COUNT; int_num++) { + if (gpio_ints[int_num] == g) + return EC_SUCCESS; /* This is already set up. */ + + if (gpio_ints[int_num] == NULL && free_slot == -1) + free_slot = int_num; + } + + ASSERT(free_slot != -1); + + gpio_ints[free_slot] = g; + pin = 31 - __builtin_clz(g->mask); + event_config = (pin << NRF51_GPIOTE_PSEL_POS) | + NRF51_GPIOTE_MODE_EVENT; + + ASSERT(g->flags & (GPIO_INT_F_RISING | GPIO_INT_F_FALLING)); + + /* RISING | FALLING = TOGGLE */ + if (g->flags & GPIO_INT_F_RISING) + event_config |= NRF51_GPIOTE_POLARITY_LOTOHI; + if (g->flags & GPIO_INT_F_FALLING) + event_config |= NRF51_GPIOTE_POLARITY_HITOLO; + + NRF51_GPIOTE_CONFIG(free_slot) = event_config; + + /* Enable the IN[] interrupt. */ + NRF51_GPIOTE_INTENSET = 1 << free_slot; + + } else { + /* The first handler for the shared interrupt wins. */ + if (gpio_int_port == NULL) { + gpio_int_port = g; + + /* Enable the PORT interrupt. */ + NRF51_GPIOTE_INTENSET = 1 << NRF51_GPIOTE_PORT_BIT; + } + } + + return EC_SUCCESS; } + +/* + * Disable the interrupt associated with the "signal" + * The architecture has one general (PORT) + * and NRF51_GPIOTE_IN_COUNT single-pin (IN0, IN1, ...) interrupts. + */ +int gpio_disable_interrupt(enum gpio_signal signal) +{ + const struct gpio_info *g = gpio_list + signal; + int i; + + /* Fail if not implemented or no interrupt handler */ + if (!g->mask || !g->irq_handler) + return EC_ERROR_INVAL; + + /* If it's not shared, use INT0-INT3, otherwise use PORT. */ + if (!(g->flags && GPIO_INT_SHARED)) { + for (i = 0; i < NRF51_GPIOTE_IN_COUNT; i++) { + /* Remove matching handler. */ + if (gpio_ints[i] == g) { + /* Disable the interrupt */ + NRF51_GPIOTE_INTENCLR = + 1 << NRF51_GPIOTE_IN_BIT(i); + /* Zero the handler */ + gpio_ints[i] = NULL; + } + } + } else { + /* Disable the interrupt */ + NRF51_GPIOTE_INTENCLR = 1 << NRF51_GPIOTE_PORT_BIT; + /* Zero the shared handler */ + gpio_int_port = NULL; + } + + return EC_SUCCESS; +} + +/* + * Clear interrupt and run handler. + */ void gpio_interrupt(void) { + const struct gpio_info *g; + int i; + + for (i = 0; i < NRF51_GPIOTE_IN_COUNT; i++) { + if (NRF51_GPIOTE_IN(i)) { + NRF51_GPIOTE_IN(i) = 0; + g = gpio_ints[i]; + if (g && g->irq_handler) + g->irq_handler(g - gpio_list); + } + } + + if (NRF51_GPIOTE_PORT) { + NRF51_GPIOTE_PORT = 0; + g = gpio_int_port; + if (g && g->irq_handler) + g->irq_handler(g - gpio_list); + } } DECLARE_IRQ(NRF51_PERID_GPIOTE, gpio_interrupt, 1); -- cgit v1.2.1