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>
This commit is contained in:
Myles Watson
2014-12-04 17:05:05 -08:00
committed by ChromeOS Commit Bot
parent 741a2d4589
commit 215399af25

View File

@@ -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);