drivers: gpio: sam0: Add interrupt support

This adds interrupt support to the SAM0 GPIO driver.  This is heavily
inspired by @nzmichaelh work in #5715.  The primary difference
from that implementation is that here the External Interrupt
Controller (EIC) is separated out into an interrupt controller driver
that is less tightly coupled to the GPIO API.  Instead it implements
more of a conversion from the EIC's own odd multiplexing to a more
traditional port and pin mask IRQ-like callback.  Unfortunately,
through the EIC on the SAMD2x are relatively well behaved
in terms of pin to EIC line mappings, other chips that share the
peripheral interface are not.  So the EIC driver implements a
per-line lookup to the pin and port pair using definitions extracted
from the ASF headers.

The EIC driver still makes some assumptions about how it will be used:
mostly it assumes exactly one callback per port.  This should be fine
as the only intended user is the GPIO driver itself.

This has been tested with some simple programs and with
tests/drivers/gpio/gpio_basic_api on a SAMD21 breakout and an
adafruit_trinket_m0 board.

Signed-off-by: Derek Hageman <hageman@inthat.cloud>
This commit is contained in:
Derek Hageman 2019-03-01 19:29:07 -07:00 committed by Anas Nashif
parent 1892c67b26
commit 4462069d74
10 changed files with 1384 additions and 10 deletions

View file

@ -8,13 +8,37 @@
#include <device.h>
#include <gpio.h>
#include <soc.h>
#include <interrupt_controller/sam0_eic.h>
#include "gpio_utils.h"
struct gpio_sam0_config {
PortGroup *regs;
#ifdef CONFIG_SAM0_EIC
u8_t id;
#endif
};
struct gpio_sam0_data {
#ifdef CONFIG_SAM0_EIC
sys_slist_t callbacks;
#endif
};
#define DEV_CFG(dev) \
((const struct gpio_sam0_config *const)(dev)->config->config_info)
#define DEV_DATA(dev) \
((struct gpio_sam0_data *const)(dev)->driver_data)
#ifdef CONFIG_SAM0_EIC
static void gpio_sam0_isr(u32_t pins, void *arg)
{
struct device *const dev = (struct device *) arg;
struct gpio_sam0_data *const data = DEV_DATA(dev);
gpio_fire_callbacks(&data->callbacks, dev, pins);
}
#endif
static int gpio_sam0_config(struct device *dev, int access_op, u32_t pin,
int flags)
@ -61,13 +85,59 @@ static int gpio_sam0_config(struct device *dev, int access_op, u32_t pin,
return -ENOTSUP;
}
/* Write the now-built pin configuration */
regs->PINCFG[pin] = pincfg;
#ifdef CONFIG_SAM0_EIC
if ((flags & GPIO_INT) != 0) {
/*
* The EIC requires setting the pin to function A, so do
* that as part of the interrupt enable, so that the API
* is consistent.
*/
pincfg.bit.PMUXEN = 1;
if (pin & 1) {
regs->PMUX[pin / 2].bit.PMUXO = PORT_PMUX_PMUXE_A_Val;
} else {
regs->PMUX[pin / 2].bit.PMUXE = PORT_PMUX_PMUXE_A_Val;
}
enum sam0_eic_trigger trigger;
if ((flags & GPIO_INT_DOUBLE_EDGE) != 0) {
trigger = SAM0_EIC_BOTH;
} else if (flags & GPIO_INT_EDGE) {
if (flags & GPIO_INT_ACTIVE_HIGH) {
trigger = SAM0_EIC_RISING;
} else {
trigger = SAM0_EIC_FALLING;
}
} else {
if (flags & GPIO_INT_ACTIVE_HIGH) {
trigger = SAM0_EIC_HIGH;
} else {
trigger = SAM0_EIC_LOW;
}
}
int retval = sam0_eic_acquire(config->id, pin, trigger,
(flags & GPIO_INT_DEBOUNCE) != 0,
gpio_sam0_isr, dev);
if (retval != 0) {
return retval;
}
} else {
sam0_eic_release(config->id, pin);
/*
* Pinmux disabled by the config set, so this
* correctly inverts the EIC enable part above.
*/
}
#else
if ((flags & GPIO_INT) != 0) {
/* TODO(mlhx): implement. */
return -ENOTSUP;
}
#endif
/* Write the now-built pin configuration */
regs->PINCFG[pin] = pincfg;
if ((flags & GPIO_POL_MASK) != GPIO_POL_NORMAL) {
return -ENOTSUP;
@ -113,10 +183,57 @@ static int gpio_sam0_read(struct device *dev, int access_op, u32_t pin,
return 0;
}
#ifdef CONFIG_SAM0_EIC
int gpio_sam0_manage_callback(struct device *dev,
struct gpio_callback *callback, bool set)
{
struct gpio_sam0_data *const data = DEV_DATA(dev);
return gpio_manage_callback(&data->callbacks, callback, set);
}
int gpio_sam0_enable_callback(struct device *dev, int access_op, u32_t pin)
{
const struct gpio_sam0_config *config = DEV_CFG(dev);
if (access_op != GPIO_ACCESS_BY_PIN) {
return -ENOTSUP;
}
return sam0_eic_enable_interrupt(config->id, pin);
}
int gpio_sam0_disable_callback(struct device *dev, int access_op, u32_t pin)
{
const struct gpio_sam0_config *config = DEV_CFG(dev);
if (access_op != GPIO_ACCESS_BY_PIN) {
return -ENOTSUP;
}
return sam0_eic_disable_interrupt(config->id, pin);
}
u32_t gpio_sam0_get_pending_int(struct device *dev)
{
const struct gpio_sam0_config *config = DEV_CFG(dev);
return sam0_eic_interrupt_pending(config->id);
}
#endif
static const struct gpio_driver_api gpio_sam0_api = {
.config = gpio_sam0_config,
.write = gpio_sam0_write,
.read = gpio_sam0_read,
#ifdef CONFIG_SAM0_EIC
.manage_callback = gpio_sam0_manage_callback,
.enable_callback = gpio_sam0_enable_callback,
.disable_callback = gpio_sam0_disable_callback,
.get_pending_int = gpio_sam0_get_pending_int,
#endif
};
static int gpio_sam0_init(struct device *dev) { return 0; }
@ -126,11 +243,17 @@ static int gpio_sam0_init(struct device *dev) { return 0; }
static const struct gpio_sam0_config gpio_sam0_config_0 = {
.regs = (PortGroup *)DT_ATMEL_SAM0_GPIO_PORT_A_BASE_ADDRESS,
#ifdef CONFIG_SAM0_EIC
.id = 0,
#endif
};
static struct gpio_sam0_data gpio_sam0_data_0;
DEVICE_AND_API_INIT(gpio_sam0_0, DT_ATMEL_SAM0_GPIO_PORT_A_LABEL,
gpio_sam0_init, NULL, &gpio_sam0_config_0, POST_KERNEL,
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &gpio_sam0_api);
gpio_sam0_init, &gpio_sam0_data_0, &gpio_sam0_config_0,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&gpio_sam0_api);
#endif
/* Port B */
@ -138,11 +261,17 @@ DEVICE_AND_API_INIT(gpio_sam0_0, DT_ATMEL_SAM0_GPIO_PORT_A_LABEL,
static const struct gpio_sam0_config gpio_sam0_config_1 = {
.regs = (PortGroup *)DT_ATMEL_SAM0_GPIO_PORT_B_BASE_ADDRESS,
#ifdef CONFIG_SAM0_EIC
.id = 1,
#endif
};
static struct gpio_sam0_data gpio_sam0_data_1;
DEVICE_AND_API_INIT(gpio_sam0_1, DT_ATMEL_SAM0_GPIO_PORT_B_LABEL,
gpio_sam0_init, NULL, &gpio_sam0_config_1, POST_KERNEL,
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &gpio_sam0_api);
gpio_sam0_init, &gpio_sam0_data_1, &gpio_sam0_config_1,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&gpio_sam0_api);
#endif
/* Port C */
@ -150,9 +279,15 @@ DEVICE_AND_API_INIT(gpio_sam0_1, DT_ATMEL_SAM0_GPIO_PORT_B_LABEL,
static const struct gpio_sam0_config gpio_sam0_config_2 = {
.regs = (PortGroup *)DT_ATMEL_SAM0_GPIO_PORT_C_BASE_ADDRESS,
#ifdef CONFIG_SAM0_EIC
.id = 2,
#endif
};
static struct gpio_sam0_data gpio_sam0_data_2;
DEVICE_AND_API_INIT(gpio_sam0_2, DT_ATMEL_SAM0_GPIO_PORT_C_LABEL,
gpio_sam0_init, NULL, &gpio_sam0_config_2, POST_KERNEL,
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &gpio_sam0_api);
gpio_sam0_init, &gpio_sam0_data_2, &gpio_sam0_config_2,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&gpio_sam0_api);
#endif

View file

@ -12,3 +12,4 @@ zephyr_sources_ifdef(CONFIG_SOC_FAMILY_STM32 exti_stm32.c)
zephyr_sources_ifdef(CONFIG_CAVS_ICTL cavs_ictl.c)
zephyr_sources_ifdef(CONFIG_DW_ICTL dw_ictl.c)
zephyr_sources_ifdef(CONFIG_RV32M1_INTMUX rv32m1_intmux.c)
zephyr_sources_ifdef(CONFIG_SAM0_EIC sam0_eic.c)

View file

@ -164,4 +164,6 @@ source "drivers/interrupt_controller/Kconfig.s1000"
source "drivers/interrupt_controller/Kconfig.rv32m1"
source "drivers/interrupt_controller/Kconfig.sam0"
endmenu

View file

@ -0,0 +1,17 @@
# Kconfig - SAM0 EIC configuration
#
# Copyright (c) 2019 Derek Hageman <hageman@inthat.cloud>
#
# SPDX-License-Identifier: Apache-2.0
#
if SOC_FAMILY_SAM0
config SAM0_EIC
bool "External Interrupt Controller (EIC) Driver for SAM0 series devices"
default y
help
Enable EIC driver for SAM0 series of devices. This is required for
GPIO interrupt support.
endif # SOC_FAMILY_SAM0

View file

@ -0,0 +1,394 @@
/*
* Copyright (c) 2019 Derek Hageman <hageman@inthat.cloud>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <device.h>
#include <soc.h>
#include "sam0_eic.h"
#include "sam0_eic_priv.h"
struct sam0_eic_line_assignment {
u8_t pin : 5;
u8_t port : 2;
u8_t enabled : 1;
};
struct sam0_eic_port_data {
sam0_eic_callback_t cb;
void *data;
};
struct sam0_eic_data {
struct sam0_eic_port_data ports[PORT_GROUPS];
struct sam0_eic_line_assignment lines[EIC_EXTINT_NUM];
};
#define DEV_DATA(dev) \
((struct sam0_eic_data *const)(dev)->driver_data)
DEVICE_DECLARE(sam0_eic);
static void wait_synchronization(void)
{
while (EIC->STATUS.bit.SYNCBUSY) {
}
}
static void sam0_eic_isr(void *arg)
{
struct device *dev = (struct device *)arg;
struct sam0_eic_data *const dev_data = DEV_DATA(dev);
u16_t bits = EIC->INTFLAG.reg;
u32_t line_index;
/* Acknowledge all interrupts */
EIC->INTFLAG.reg = bits;
/* No clz on M0, so just do a quick test */
#if __CORTEX_M >= 3
line_index = __CLZ(__RBIT(bits));
bits >>= line_index;
#else
if (bits & 0xFF) {
line_index = 0;
} else {
line_index = 8;
bits >>= 8;
}
#endif
/*
* Map the EIC lines to the port pin masks based on which port is
* selected in the line data.
*/
for (; bits; bits >>= 1, line_index++) {
if (!(bits & 1)) {
continue;
}
/*
* These could be aggregated together into one call, but
* usually on a single one will be set, so just call them
* one by one.
*/
struct sam0_eic_line_assignment *line_assignment =
&dev_data->lines[line_index];
struct sam0_eic_port_data *port_data =
&dev_data->ports[line_assignment->port];
port_data->cb(BIT(line_assignment->pin), port_data->data);
}
}
int sam0_eic_acquire(int port, int pin, enum sam0_eic_trigger trigger,
bool filter, sam0_eic_callback_t cb, void *data)
{
struct device *dev = DEVICE_GET(sam0_eic);
struct sam0_eic_data *dev_data = dev->driver_data;
struct sam0_eic_port_data *port_data;
struct sam0_eic_line_assignment *line_assignment;
u32_t mask;
int line_index;
int config_index;
int config_shift;
int key;
u32_t config;
line_index = sam0_eic_map_to_line(port, pin);
if (line_index < 0) {
return line_index;
}
mask = BIT(line_index);
config_index = line_index / 8;
config_shift = (line_index % 8) * 4;
/* Lock everything so it's safe to reconfigure */
key = irq_lock();
/* Disable the EIC for reconfiguration */
EIC->CTRL.bit.ENABLE = 0;
line_assignment = &dev_data->lines[line_index];
/* Check that the required line is available */
if (line_assignment->enabled) {
if (line_assignment->port != port ||
line_assignment->pin != pin) {
goto err_in_use;
}
}
/* Set the EIC configuration data */
port_data = &dev_data->ports[port];
port_data->cb = cb;
port_data->data = data;
line_assignment->pin = pin;
line_assignment->port = port;
line_assignment->enabled = 1;
config = EIC->CONFIG[config_index].reg;
config &= ~(0xF << config_shift);
switch (trigger) {
case SAM0_EIC_RISING:
config |= EIC_CONFIG_SENSE0_RISE << config_shift;
break;
case SAM0_EIC_FALLING:
config |= EIC_CONFIG_SENSE0_FALL << config_shift;
break;
case SAM0_EIC_BOTH:
config |= EIC_CONFIG_SENSE0_BOTH << config_shift;
break;
case SAM0_EIC_HIGH:
config |= EIC_CONFIG_SENSE0_HIGH << config_shift;
break;
case SAM0_EIC_LOW:
config |= EIC_CONFIG_SENSE0_LOW << config_shift;
break;
}
if (filter) {
config |= EIC_CONFIG_FILTEN0 << config_shift;
}
/* Apply the config to the EIC itself */
EIC->CONFIG[config_index].reg = config;
EIC->CTRL.bit.ENABLE = 1;
wait_synchronization();
/*
* Errata: The EIC generates a spurious interrupt for the newly
* enabled pin after being enabled, so clear it before re-enabling
* the IRQ.
*/
EIC->INTFLAG.reg = mask;
irq_unlock(key);
return 0;
err_in_use:
EIC->CTRL.bit.ENABLE = 1;
wait_synchronization();
irq_unlock(key);
return -EBUSY;
}
static bool sam0_eic_check_ownership(int port, int pin, int line_index)
{
struct device *dev = DEVICE_GET(sam0_eic);
struct sam0_eic_data *dev_data = dev->driver_data;
struct sam0_eic_line_assignment *line_assignment =
&dev_data->lines[line_index];
if (!line_assignment->enabled) {
return false;
}
if (line_assignment->port != port ||
line_assignment->pin != pin) {
return false;
}
return true;
}
int sam0_eic_release(int port, int pin)
{
struct device *dev = DEVICE_GET(sam0_eic);
struct sam0_eic_data *dev_data = dev->driver_data;
u32_t mask;
int line_index;
int config_index;
int config_shift;
int key;
line_index = sam0_eic_map_to_line(port, pin);
if (line_index < 0) {
return line_index;
}
mask = BIT(line_index);
config_index = line_index / 8;
config_shift = (line_index % 8) * 4;
/* Lock everything so it's safe to reconfigure */
key = irq_lock();
/* Disable the EIC */
EIC->CTRL.bit.ENABLE = 0;
wait_synchronization();
/*
* Check to make sure the requesting actually owns the line and do
* nothing if it does not.
*/
if (!sam0_eic_check_ownership(port, pin, line_index)) {
goto done;
}
dev_data->lines[line_index].enabled = 0;
/* Clear the EIC config, including the trigger condition */
EIC->CONFIG[config_index].reg &= ~(0xF << config_shift);
/* Clear any pending interrupt for it */
EIC->INTENCLR.reg = mask;
EIC->INTFLAG.reg = mask;
done:
EIC->CTRL.bit.ENABLE = 1;
wait_synchronization();
irq_unlock(key);
return 0;
}
int sam0_eic_enable_interrupt(int port, int pin)
{
u32_t mask;
int line_index;
line_index = sam0_eic_map_to_line(port, pin);
if (line_index < 0) {
return line_index;
}
if (!sam0_eic_check_ownership(port, pin, line_index)) {
return -EBUSY;
}
mask = BIT(line_index);
EIC->INTFLAG.reg = mask;
EIC->INTENSET.reg = mask;
return 0;
}
int sam0_eic_disable_interrupt(int port, int pin)
{
u32_t mask;
int line_index;
line_index = sam0_eic_map_to_line(port, pin);
if (line_index < 0) {
return line_index;
}
if (!sam0_eic_check_ownership(port, pin, line_index)) {
return -EBUSY;
}
mask = BIT(line_index);
EIC->INTENCLR.reg = mask;
EIC->INTFLAG.reg = mask;
return 0;
}
u32_t sam0_eic_interrupt_pending(int port)
{
struct device *dev = DEVICE_GET(sam0_eic);
struct sam0_eic_data *dev_data = dev->driver_data;
struct sam0_eic_line_assignment *line_assignment;
u32_t set = EIC->INTFLAG.reg;
u32_t mask = 0;
for (int line_index = 0; line_index < EIC_EXTINT_NUM; line_index++) {
line_assignment = &dev_data->lines[line_index];
if (!line_assignment->enabled) {
continue;
}
if (line_assignment->port != port) {
continue;
}
if (!(set & BIT(line_index))) {
continue;
}
mask |= BIT(line_assignment->pin);
}
return mask;
}
#define SAM0_EIC_IRQ_CONNECT(n) \
do { \
IRQ_CONNECT(DT_ATMEL_SAM0_EIC_0_IRQ_ ## n, \
DT_ATMEL_SAM0_EIC_0_IRQ_ ## n ## _PRIORITY, \
sam0_eic_isr, DEVICE_GET(sam0_eic), 0); \
irq_enable(DT_ATMEL_SAM0_EIC_0_IRQ_ ## n); \
} while (0)
static int sam0_eic_init(struct device *dev)
{
ARG_UNUSED(dev);
/* Enable the EIC clock in PM */
PM->APBAMASK.bit.EIC_ = 1;
/* Enable the GCLK */
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_EIC | GCLK_CLKCTRL_GEN_GCLK0 |
GCLK_CLKCTRL_CLKEN;
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_0
SAM0_EIC_IRQ_CONNECT(0);
#endif
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_1
SAM0_EIC_IRQ_CONNECT(1);
#endif
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_2
SAM0_EIC_IRQ_CONNECT(2);
#endif
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_3
SAM0_EIC_IRQ_CONNECT(3);
#endif
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_4
SAM0_EIC_IRQ_CONNECT(4);
#endif
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_5
SAM0_EIC_IRQ_CONNECT(5);
#endif
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_6
SAM0_EIC_IRQ_CONNECT(6);
#endif
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_7
SAM0_EIC_IRQ_CONNECT(7);
#endif
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_8
SAM0_EIC_IRQ_CONNECT(8);
#endif
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_9
SAM0_EIC_IRQ_CONNECT(9);
#endif
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_10
SAM0_EIC_IRQ_CONNECT(10);
#endif
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_11
SAM0_EIC_IRQ_CONNECT(11);
#endif
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_12
SAM0_EIC_IRQ_CONNECT(12);
#endif
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_13
SAM0_EIC_IRQ_CONNECT(13);
#endif
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_14
SAM0_EIC_IRQ_CONNECT(14);
#endif
#ifdef DT_ATMEL_SAM0_EIC_0_IRQ_15
SAM0_EIC_IRQ_CONNECT(15);
#endif
EIC->CTRL.bit.ENABLE = 1;
wait_synchronization();
return 0;
}
static struct sam0_eic_data eic_data;
DEVICE_INIT(sam0_eic, DT_ATMEL_SAM0_EIC_0_LABEL, sam0_eic_init,
&eic_data, NULL,
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2019 Derek Hageman <hageman@inthat.cloud>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_SAM0_EIC_H_
#define ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_SAM0_EIC_H_
#include <zephyr/types.h>
/* callback for EIC interrupt */
typedef void (*sam0_eic_callback_t)(u32_t pins, void *data);
/**
* @brief EIC trigger condition
*/
enum sam0_eic_trigger {
/* Rising edge */
SAM0_EIC_RISING,
/* Falling edge */
SAM0_EIC_FALLING,
/* Both edges */
SAM0_EIC_BOTH,
/* High level detection */
SAM0_EIC_HIGH,
/* Low level detection */
SAM0_EIC_LOW,
};
/**
* @brief Acquire an EIC interrupt for specific port and pin combination
*
* This acquires the EIC interrupt for a specific port and pin combination,
* or returns an error if the required line is not available. Only a single
* callback per port is supported and supplying a different one will
* change it for all lines on that port.
*
* @param port port index (A=0, etc)
* @param pin pin in the port
* @param trigger trigger condition
* @param cb interrupt callback
* @param data parameter to the interrupt callback
*/
int sam0_eic_acquire(int port, int pin, enum sam0_eic_trigger trigger,
bool filter, sam0_eic_callback_t cb, void *data);
/**
* @brief Release the EIC interrupt for a specific port and pin combination
*
* Release the EIC configuration for a specific port and pin combination.
* No effect if that combination does not currently hold the associated
* EIC line.
*
* @param port port index (A=0, etc)
* @param pin pin in the port
*/
int sam0_eic_release(int port, int pin);
/**
* @brief Enable the EIC interrupt for a specific port and pin combination
*
* @param port port index (A=0, etc)
* @param pin pin in the port
*/
int sam0_eic_enable_interrupt(int port, int pin);
/**
* @brief Disable the EIC interrupt for a specific port and pin combination
*
* @param port port index (A=0, etc)
* @param pin pin in the port
*/
int sam0_eic_disable_interrupt(int port, int pin);
/**
* @brief Test if there is an EIC interrupt pending for a port
*
* @param port port index (A=0, etc)
*/
u32_t sam0_eic_interrupt_pending(int por);
#endif /* ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_SAM0_EIC_H_ */

View file

@ -0,0 +1,700 @@
/*
* Copyright (c) 2019 Derek Hageman <hageman@inthat.cloud>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_SAM0_EIC_PRIV_H_
#define ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_SAM0_EIC_PRIV_H_
#include <zephyr/types.h>
#include <soc.h>
/*
* Used in the ASF headers, but not always defined by them (looks like they
* sometimes define __L instead).
*/
#ifndef _L
#define _L(i) i ## L
#endif
/*
* Unfortunately the ASF headers define the EIC mappings somewhat painfully:
* the macros have both the port letter and are only defined if that pin
* has an EIC channel. So we can't just use a macro expansion here, because
* some of them might be undefined for a port and we can't test for another
* macro definition inside a macro.
*/
static const u8_t sam0_eic_channels[PORT_GROUPS][32] = {
#if PORT_GROUPS >= 1
{
#ifdef PIN_PA00A_EIC_EXTINT_NUM
PIN_PA00A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA01A_EIC_EXTINT_NUM
PIN_PA01A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA02A_EIC_EXTINT_NUM
PIN_PA02A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA03A_EIC_EXTINT_NUM
PIN_PA03A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA04A_EIC_EXTINT_NUM
PIN_PA04A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA05A_EIC_EXTINT_NUM
PIN_PA05A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA06A_EIC_EXTINT_NUM
PIN_PA06A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA07A_EIC_EXTINT_NUM
PIN_PA07A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA08A_EIC_EXTINT_NUM
PIN_PA08A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA09A_EIC_EXTINT_NUM
PIN_PA09A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA10A_EIC_EXTINT_NUM
PIN_PA10A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA11A_EIC_EXTINT_NUM
PIN_PA11A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA12A_EIC_EXTINT_NUM
PIN_PA12A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA13A_EIC_EXTINT_NUM
PIN_PA13A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA14A_EIC_EXTINT_NUM
PIN_PA14A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA15A_EIC_EXTINT_NUM
PIN_PA15A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA16A_EIC_EXTINT_NUM
PIN_PA16A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA17A_EIC_EXTINT_NUM
PIN_PA17A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA18A_EIC_EXTINT_NUM
PIN_PA18A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA19A_EIC_EXTINT_NUM
PIN_PA19A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA20A_EIC_EXTINT_NUM
PIN_PA20A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA21A_EIC_EXTINT_NUM
PIN_PA21A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA22A_EIC_EXTINT_NUM
PIN_PA22A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA23A_EIC_EXTINT_NUM
PIN_PA23A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA24A_EIC_EXTINT_NUM
PIN_PA24A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA25A_EIC_EXTINT_NUM
PIN_PA25A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA26A_EIC_EXTINT_NUM
PIN_PA26A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA27A_EIC_EXTINT_NUM
PIN_PA27A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA28A_EIC_EXTINT_NUM
PIN_PA28A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA29A_EIC_EXTINT_NUM
PIN_PA29A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA30A_EIC_EXTINT_NUM
PIN_PA30A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PA31A_EIC_EXTINT_NUM
PIN_PA31A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
},
#endif
#if PORT_GROUPS >= 2
{
#ifdef PIN_PB00A_EIC_EXTINT_NUM
PIN_PB00A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB01A_EIC_EXTINT_NUM
PIN_PB01A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB02A_EIC_EXTINT_NUM
PIN_PB02A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB03A_EIC_EXTINT_NUM
PIN_PB03A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB04A_EIC_EXTINT_NUM
PIN_PB04A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB05A_EIC_EXTINT_NUM
PIN_PB05A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB06A_EIC_EXTINT_NUM
PIN_PB06A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB07A_EIC_EXTINT_NUM
PIN_PB07A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB08A_EIC_EXTINT_NUM
PIN_PB08A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB09A_EIC_EXTINT_NUM
PIN_PB09A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB10A_EIC_EXTINT_NUM
PIN_PB10A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB11A_EIC_EXTINT_NUM
PIN_PB11A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB12A_EIC_EXTINT_NUM
PIN_PB12A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB13A_EIC_EXTINT_NUM
PIN_PB13A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB14A_EIC_EXTINT_NUM
PIN_PB14A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB15A_EIC_EXTINT_NUM
PIN_PB15A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB16A_EIC_EXTINT_NUM
PIN_PB16A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB17A_EIC_EXTINT_NUM
PIN_PB17A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB18A_EIC_EXTINT_NUM
PIN_PB18A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB19A_EIC_EXTINT_NUM
PIN_PB19A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB20A_EIC_EXTINT_NUM
PIN_PB20A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB21A_EIC_EXTINT_NUM
PIN_PB21A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB22A_EIC_EXTINT_NUM
PIN_PB22A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB23A_EIC_EXTINT_NUM
PIN_PB23A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB24A_EIC_EXTINT_NUM
PIN_PB24A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB25A_EIC_EXTINT_NUM
PIN_PB25A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB26A_EIC_EXTINT_NUM
PIN_PB26A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB27A_EIC_EXTINT_NUM
PIN_PB27A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB28A_EIC_EXTINT_NUM
PIN_PB28A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB29A_EIC_EXTINT_NUM
PIN_PB29A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB30A_EIC_EXTINT_NUM
PIN_PB30A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PB31A_EIC_EXTINT_NUM
PIN_PB31A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
},
#endif
#if PORT_GROUPS >= 3
{
#ifdef PIN_PC00A_EIC_EXTINT_NUM
PIN_PC00A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC01A_EIC_EXTINT_NUM
PIN_PC01A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC02A_EIC_EXTINT_NUM
PIN_PC02A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC03A_EIC_EXTINT_NUM
PIN_PC03A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC04A_EIC_EXTINT_NUM
PIN_PC04A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC05A_EIC_EXTINT_NUM
PIN_PC05A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC06A_EIC_EXTINT_NUM
PIN_PC06A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC07A_EIC_EXTINT_NUM
PIN_PC07A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC08A_EIC_EXTINT_NUM
PIN_PC08A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC09A_EIC_EXTINT_NUM
PIN_PC09A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC10A_EIC_EXTINT_NUM
PIN_PC10A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC11A_EIC_EXTINT_NUM
PIN_PC11A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC12A_EIC_EXTINT_NUM
PIN_PC12A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC13A_EIC_EXTINT_NUM
PIN_PC13A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC14A_EIC_EXTINT_NUM
PIN_PC14A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC15A_EIC_EXTINT_NUM
PIN_PC15A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC16A_EIC_EXTINT_NUM
PIN_PC16A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC17A_EIC_EXTINT_NUM
PIN_PC17A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC18A_EIC_EXTINT_NUM
PIN_PC18A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC19A_EIC_EXTINT_NUM
PIN_PC19A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC20A_EIC_EXTINT_NUM
PIN_PC20A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC21A_EIC_EXTINT_NUM
PIN_PC21A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC22A_EIC_EXTINT_NUM
PIN_PC22A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC23A_EIC_EXTINT_NUM
PIN_PC23A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC24A_EIC_EXTINT_NUM
PIN_PC24A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC25A_EIC_EXTINT_NUM
PIN_PC25A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC26A_EIC_EXTINT_NUM
PIN_PC26A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC27A_EIC_EXTINT_NUM
PIN_PC27A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC28A_EIC_EXTINT_NUM
PIN_PC28A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC29A_EIC_EXTINT_NUM
PIN_PC29A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC30A_EIC_EXTINT_NUM
PIN_PC30A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PC31A_EIC_EXTINT_NUM
PIN_PC31A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
},
#endif
#if PORT_GROUPS >= 4
{
#ifdef PIN_PD00A_EIC_EXTINT_NUM
PIN_PD00A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD01A_EIC_EXTINT_NUM
PIN_PD01A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD02A_EIC_EXTINT_NUM
PIN_PD02A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD03A_EIC_EXTINT_NUM
PIN_PD03A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD04A_EIC_EXTINT_NUM
PIN_PD04A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD05A_EIC_EXTINT_NUM
PIN_PD05A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD06A_EIC_EXTINT_NUM
PIN_PD06A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD07A_EIC_EXTINT_NUM
PIN_PD07A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD08A_EIC_EXTINT_NUM
PIN_PD08A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD09A_EIC_EXTINT_NUM
PIN_PD09A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD10A_EIC_EXTINT_NUM
PIN_PD10A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD11A_EIC_EXTINT_NUM
PIN_PD11A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD12A_EIC_EXTINT_NUM
PIN_PD12A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD13A_EIC_EXTINT_NUM
PIN_PD13A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD14A_EIC_EXTINT_NUM
PIN_PD14A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD15A_EIC_EXTINT_NUM
PIN_PD15A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD16A_EIC_EXTINT_NUM
PIN_PD16A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD17A_EIC_EXTINT_NUM
PIN_PD17A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD18A_EIC_EXTINT_NUM
PIN_PD18A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD19A_EIC_EXTINT_NUM
PIN_PD19A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD20A_EIC_EXTINT_NUM
PIN_PD20A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD21A_EIC_EXTINT_NUM
PIN_PD21A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD22A_EIC_EXTINT_NUM
PIN_PD22A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD23A_EIC_EXTINT_NUM
PIN_PD23A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD24A_EIC_EXTINT_NUM
PIN_PD24A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD25A_EIC_EXTINT_NUM
PIN_PD25A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD26A_EIC_EXTINT_NUM
PIN_PD26A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD27A_EIC_EXTINT_NUM
PIN_PD27A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD28A_EIC_EXTINT_NUM
PIN_PD28A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD29A_EIC_EXTINT_NUM
PIN_PD29A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD30A_EIC_EXTINT_NUM
PIN_PD30A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
#ifdef PIN_PD31A_EIC_EXTINT_NUM
PIN_PD31A_EIC_EXTINT_NUM,
#else
0xFF,
#endif
},
#endif
};
static inline int sam0_eic_map_to_line(int port, int pin)
{
u8_t ch = sam0_eic_channels[port][pin];
if (ch == 0xFF) {
return -ENOTSUP;
}
return ch;
}
#endif /* ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_SAM0_EIC_PRIV_H_ */

View file

@ -67,6 +67,13 @@
};
};
eic: eic@40001800 {
compatible = "atmel,sam0-eic";
reg = <0x40001800 0x1C>;
interrupts = <4 0>;
label = "EIC";
};
pinmux_a: pinmux@41004400 {
compatible = "atmel,sam0-pinmux";
reg = <0x41004400 0x80>;

View file

@ -0,0 +1,33 @@
---
title: Atmel SAM0 External Interrupt Controller
version: 0.1
description: >
This binding describes the Atmel SAM0 series External Interrupt Controller
properties:
compatible:
category: required
type: string
description: compatible strings
constraint: "atmel,sam0-eic"
generation: define
reg:
type: array
description: mmio register space
generation: define
category: required
interrupts:
type: array
category: required
description: required interrupts
generation: define
label:
type: string
category: required
description: Human readable string describing the device (used by Zephyr for API name)
generation: define
...

View file

@ -3,4 +3,5 @@
# Copyright (c) 2017 Google LLC.
# SPDX-License-Identifier: Apache-2.0
zephyr_include_directories(${ZEPHYR_BASE}/drivers)
add_subdirectory(common)