summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMyles Watson <mylesgw@chromium.org>2014-12-04 17:05:05 -0800
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2014-12-31 00:01:31 +0000
commit215399af25bfd489997ed86e7d17e8253cbfe517 (patch)
tree4a994342ccc5f770b4815b4e53721311438bbbaf
parent741a2d45897d3d82462b065c790cf7eae6823d4e (diff)
downloadchrome-ec-215399af25bfd489997ed86e7d17e8253cbfe517.tar.gz
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 <mylesgw@chromium.org> Change-Id: I129a6586dca4d5eb141c86fd92fbfbb70080bc2a Reviewed-on: https://chromium-review.googlesource.com/234392 Reviewed-by: Randall Spangler <rspangler@chromium.org> Tested-by: Myles Watson <mylesgw@chromium.org> Commit-Queue: Myles Watson <mylesgw@chromium.org>
-rw-r--r--chip/nrf51/gpio.c141
1 files 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);