mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
cpu/sam0_common: implement periph_gpio_ll_irq
Co-authored-by: benpicco <benpicco@googlemail.com>
This commit is contained in:
parent
855756524f
commit
1484d305bf
@ -16,6 +16,10 @@ config CPU_COMMON_SAM0
|
||||
select HAS_PERIPH_GPIO
|
||||
select HAS_PERIPH_GPIO_IRQ
|
||||
select HAS_PERIPH_GPIO_LL
|
||||
select HAS_PERIPH_GPIO_LL_IRQ
|
||||
select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH
|
||||
select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW
|
||||
select HAS_PERIPH_GPIO_LL_IRQ_UNMASK
|
||||
select HAS_PERIPH_I2C_RECONFIGURE
|
||||
select HAS_PERIPH_RTT_SET_COUNTER
|
||||
select HAS_PERIPH_RTT_OVERFLOW
|
||||
|
@ -14,6 +14,10 @@ FEATURES_PROVIDED += periph_flashpage_pagewise
|
||||
FEATURES_PROVIDED += periph_flashpage_rwee
|
||||
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
|
||||
FEATURES_PROVIDED += periph_gpio_ll
|
||||
FEATURES_PROVIDED += periph_gpio_ll_irq
|
||||
FEATURES_PROVIDED += periph_gpio_ll_irq_level_triggered_high
|
||||
FEATURES_PROVIDED += periph_gpio_ll_irq_level_triggered_low
|
||||
FEATURES_PROVIDED += periph_gpio_ll_irq_unmask
|
||||
FEATURES_PROVIDED += periph_i2c_reconfigure
|
||||
FEATURES_PROVIDED += periph_rtt_overflow
|
||||
FEATURES_PROVIDED += periph_rtt_set_counter
|
||||
@ -36,6 +40,9 @@ FEATURES_PROVIDED += periph_wdt periph_wdt_cb periph_wdt_warning_period
|
||||
FEATURES_CONFLICT += periph_rtc:periph_rtt
|
||||
FEATURES_CONFLICT_MSG += "The RTC and RTT map to the same hardware peripheral."
|
||||
|
||||
FEATURES_CONFLICT += periph_gpio_irq:periph_gpio_ll_irq
|
||||
FEATURES_CONFLICT_MSG += "GPIO IRQs can only be managed with one API."
|
||||
|
||||
include $(RIOTCPU)/cortexm_common/Makefile.features
|
||||
|
||||
# Add sam0 configurations after including cortexm_common so sam0 takes precendence
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Freie Universität Berlin
|
||||
* 2017 OTA keys S.A.
|
||||
* 2023 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
@ -15,11 +16,7 @@
|
||||
* @file
|
||||
* @brief CPU specific part of the Peripheral GPIO Low-Level API
|
||||
*
|
||||
* @author Troels Hoffmeyer <troels.d.hoffmeyer@gmail.com>
|
||||
* @author Thomas Eichinger <thomas.eichinger@fu-berlin.de>
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
* @author Juergen Fitschen <me@jue.yt>
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@posteo.net>
|
||||
*/
|
||||
|
||||
#ifndef GPIO_LL_ARCH_H
|
||||
|
@ -255,6 +255,7 @@ typedef enum {
|
||||
GPIO_MUX_L = 0xb, /**< select peripheral function L */
|
||||
GPIO_MUX_M = 0xc, /**< select peripheral function M */
|
||||
GPIO_MUX_N = 0xd, /**< select peripheral function N */
|
||||
GPIO_MUX_DISABLED = 0xff, /**< Disable */
|
||||
} gpio_mux_t;
|
||||
#endif
|
||||
|
||||
|
@ -53,6 +53,29 @@ static inline void print_str(const char *str)
|
||||
}
|
||||
#endif
|
||||
|
||||
void gpio_ll_mux(gpio_port_t port, uint8_t pin, gpio_mux_t mux)
|
||||
{
|
||||
assume(pin < 32);
|
||||
assume(gpio_port_unpack_addr(port) == NULL);
|
||||
PortGroup *iobus = (PortGroup *)port;
|
||||
PortGroup *apb = sam0_gpio_iobus2ap(iobus);
|
||||
|
||||
unsigned irq_state = irq_disable();
|
||||
if (mux == GPIO_MUX_DISABLED) {
|
||||
apb->PINCFG[pin].bit.PMUXEN = 0;
|
||||
}
|
||||
else {
|
||||
unsigned pmux_reg = pin >> 1;
|
||||
unsigned pmux_pos = (pin & 0x01) << 2;
|
||||
apb->PINCFG[pin].bit.PMUXEN = 1;
|
||||
unsigned pmux = apb->PMUX[pmux_reg].reg;
|
||||
pmux &= ~(PORT_PMUX_PMUXE_Msk << pmux_pos);
|
||||
pmux |= (unsigned)mux << pmux_pos;
|
||||
apb->PMUX[pmux_reg].reg = pmux;
|
||||
}
|
||||
irq_restore(irq_state);
|
||||
}
|
||||
|
||||
int gpio_ll_init(gpio_port_t port, uint8_t pin, gpio_conf_t conf)
|
||||
{
|
||||
assume(pin < 32);
|
||||
|
312
cpu/sam0_common/periph/gpio_ll_irq.c
Normal file
312
cpu/sam0_common/periph/gpio_ll_irq.c
Normal file
@ -0,0 +1,312 @@
|
||||
/*
|
||||
* Copyright (C) 2015 HAW Hamburg
|
||||
* 2016 INRIA
|
||||
* 2023 Gerson Fernando Budke
|
||||
* 2023 Hugues Larrive
|
||||
* 2023 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_sam0_common
|
||||
* @ingroup drivers_periph_gpio_ll_irq
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief IRQ implementation of the GPIO Low-Level API for SAM0
|
||||
*
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@posteo.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "bitarithm.h"
|
||||
#include "cpu.h"
|
||||
#include "irq.h"
|
||||
#include "periph/gpio_ll_irq.h"
|
||||
#include "periph_conf.h"
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
/**
|
||||
* @brief Number of external interrupt lines
|
||||
*/
|
||||
#ifdef CPU_COMMON_SAML1X
|
||||
#define IRQS_NUMOF (8U)
|
||||
#else
|
||||
#define IRQS_NUMOF (16U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The GCLK used for clocking EXTI
|
||||
*/
|
||||
#ifndef CONFIG_SAM0_GCLK_GPIO
|
||||
#define CONFIG_SAM0_GCLK_GPIO (SAM0_GCLK_MAIN)
|
||||
#endif
|
||||
|
||||
/* Consistify naming */
|
||||
#ifndef EIC_SEC
|
||||
#define EIC_SEC EIC
|
||||
#endif
|
||||
|
||||
struct isr_ctx {
|
||||
gpio_ll_cb_t cb;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
static struct isr_ctx isr_ctx[IRQS_NUMOF];
|
||||
|
||||
extern void gpio_ll_mux(gpio_port_t port, uint8_t pin, gpio_mux_t mux);
|
||||
|
||||
static int get_exti_num(unsigned port_num, uint8_t pin)
|
||||
{
|
||||
if (port_num >= ARRAY_SIZE(exti_config)) {
|
||||
return -1;
|
||||
}
|
||||
return exti_config[port_num][pin];
|
||||
}
|
||||
|
||||
static IRQn_Type exti2irqn(unsigned exti_num)
|
||||
{
|
||||
(void)exti_num;
|
||||
assume(exti_num < IRQS_NUMOF);
|
||||
#if defined(CPU_COMMON_SAMD5X)
|
||||
return EIC_0_IRQn + exti_num;
|
||||
#elif defined(CPU_COMMON_SAML1X)
|
||||
if (exti_num <= 3) {
|
||||
return EIC_0_IRQn + exti_num;
|
||||
}
|
||||
return EIC_OTHER_IRQn;
|
||||
#else
|
||||
return EIC_IRQn;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void enable_trigger(unsigned exti_num, gpio_irq_trig_t trig)
|
||||
{
|
||||
unsigned config_reg = exti_num >> 3;
|
||||
unsigned config_pos = (exti_num & 0x7) << 2;
|
||||
|
||||
/* configure trigger with IRQs disabled */
|
||||
unsigned irq_state = irq_disable();
|
||||
uint32_t conf = EIC_SEC->CONFIG[config_reg].reg;
|
||||
conf &= ~(EIC_CONFIG_SENSE0_Msk << config_pos);
|
||||
conf |= ((uint32_t)trig) << config_pos;
|
||||
EIC_SEC->CONFIG[config_reg].reg = conf;
|
||||
irq_restore(irq_state);
|
||||
|
||||
NVIC_EnableIRQ(exti2irqn(exti_num));
|
||||
}
|
||||
|
||||
static void disable_trigger(unsigned exti_num)
|
||||
{
|
||||
unsigned config_reg = exti_num >> 3;
|
||||
unsigned config_pos = (exti_num & 0x7) << 2;
|
||||
|
||||
/* configure trigger with IRQs disabled */
|
||||
unsigned irq_state = irq_disable();
|
||||
uint32_t conf = EIC_SEC->CONFIG[config_reg].reg;
|
||||
conf &= ~(EIC_CONFIG_SENSE0_Msk << config_pos);
|
||||
EIC_SEC->CONFIG[config_reg].reg = conf;
|
||||
irq_restore(irq_state);
|
||||
}
|
||||
|
||||
static void eic_sync(void)
|
||||
{
|
||||
#ifdef EIC_STATUS_SYNCBUSY
|
||||
while (EIC_SEC->STATUS.bit.SYNCBUSY) { }
|
||||
#endif
|
||||
#ifdef EIC_SYNCBUSY_ENABLE
|
||||
while (EIC_SEC->SYNCBUSY.bit.ENABLE) { }
|
||||
#endif
|
||||
}
|
||||
|
||||
static void eic_enable_clock(void)
|
||||
{
|
||||
/* Enable EIC clock */
|
||||
#ifdef PM_APBAMASK_EIC
|
||||
PM->APBAMASK.reg |= PM_APBAMASK_EIC;
|
||||
GCLK->CLKCTRL.reg = EIC_GCLK_ID
|
||||
| GCLK_CLKCTRL_CLKEN
|
||||
| GCLK_CLKCTRL_GEN(CONFIG_SAM0_GCLK_GPIO);
|
||||
while (GCLK->STATUS.bit.SYNCBUSY) {}
|
||||
#endif
|
||||
#ifdef MCLK_APBAMASK_EIC
|
||||
MCLK->APBAMASK.reg |= MCLK_APBAMASK_EIC;
|
||||
GCLK->PCHCTRL[EIC_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(CONFIG_SAM0_GCLK_GPIO);
|
||||
/* disable the EIC module*/
|
||||
EIC_SEC->CTRLA.reg = 0;
|
||||
eic_sync();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void eic_enable(void)
|
||||
{
|
||||
#ifdef EIC_CTRL_ENABLE
|
||||
EIC_SEC->CTRL.reg = EIC_CTRL_ENABLE;
|
||||
#endif
|
||||
#ifdef EIC_CTRLA_ENABLE
|
||||
EIC_SEC->CTRLA.reg = EIC_CTRLA_ENABLE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void gpio_ll_irq_mask(gpio_port_t port, uint8_t pin)
|
||||
{
|
||||
unsigned port_num = GPIO_PORT_NUM(port);
|
||||
int exti_num = get_exti_num(port_num, pin);
|
||||
assume((unsigned)exti_num < IRQS_NUMOF);
|
||||
|
||||
EIC_SEC->INTENCLR.reg = 1U << exti_num;
|
||||
}
|
||||
|
||||
void gpio_ll_irq_unmask(gpio_port_t port, uint8_t pin)
|
||||
{
|
||||
unsigned port_num = GPIO_PORT_NUM(port);
|
||||
int exti_num = get_exti_num(port_num, pin);
|
||||
assume((unsigned)exti_num < IRQS_NUMOF);
|
||||
|
||||
EIC_SEC->INTENSET.reg = 1U << exti_num;
|
||||
}
|
||||
|
||||
void gpio_ll_irq_unmask_and_clear(gpio_port_t port, uint8_t pin)
|
||||
{
|
||||
unsigned port_num = GPIO_PORT_NUM(port);
|
||||
int exti_num = get_exti_num(port_num, pin);
|
||||
assume(exti_num >= 0);
|
||||
|
||||
uint32_t mask = 1U << exti_num;
|
||||
EIC_SEC->INTFLAG.reg = mask;
|
||||
EIC_SEC->INTENSET.reg = mask;
|
||||
}
|
||||
|
||||
int gpio_ll_irq(gpio_port_t port, uint8_t pin, gpio_irq_trig_t trig,
|
||||
gpio_ll_cb_t cb, void *arg)
|
||||
{
|
||||
unsigned port_num = GPIO_PORT_NUM(port);
|
||||
int exti_num = get_exti_num(port_num, pin);
|
||||
|
||||
assume(cb);
|
||||
|
||||
if (exti_num < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (isr_ctx[exti_num].cb) {
|
||||
DEBUG("[gpio_ll_irq] IRQ already configured for EXTI %d (P%c%u)\n",
|
||||
exti_num, 'A' + (char)port_num, (unsigned)pin);
|
||||
}
|
||||
|
||||
isr_ctx[exti_num].cb = cb;
|
||||
isr_ctx[exti_num].arg = arg;
|
||||
|
||||
gpio_ll_mux(port, pin, GPIO_MUX_A);
|
||||
|
||||
eic_enable_clock();
|
||||
|
||||
enable_trigger(exti_num, trig);
|
||||
|
||||
/* clear any spurious IRQ */
|
||||
EIC_SEC->INTFLAG.reg = 1U << exti_num;
|
||||
|
||||
/* enable IRQ */
|
||||
EIC_SEC->INTENSET.reg = 1U << exti_num;
|
||||
|
||||
#ifdef EIC_WAKEUP_WAKEUPEN0
|
||||
unsigned irq_state = irq_disable();
|
||||
EIC_SEC->WAKEUP.reg |= 1U << exti_num;
|
||||
irq_restore(irq_state);
|
||||
#endif
|
||||
|
||||
eic_enable();
|
||||
|
||||
eic_sync();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gpio_ll_irq_off(gpio_port_t port, uint8_t pin)
|
||||
{
|
||||
unsigned port_num = GPIO_PORT_NUM(port);
|
||||
int exti_num = get_exti_num(port_num, pin);
|
||||
|
||||
assume((unsigned)exti_num < IRQS_NUMOF);
|
||||
|
||||
/* First, disable IRQs */
|
||||
EIC_SEC->INTENCLR.reg = 1U << exti_num;
|
||||
|
||||
gpio_ll_mux(port, pin, GPIO_MUX_DISABLED);
|
||||
|
||||
/* Disabling the trigger may conserve power */
|
||||
disable_trigger(exti_num);
|
||||
|
||||
#ifdef EIC_WAKEUP_WAKEUPEN0
|
||||
unsigned irq_state = irq_disable();
|
||||
EIC_SEC->WAKEUP.reg &= ~(1U << exti_num);
|
||||
irq_restore(irq_state);
|
||||
#endif
|
||||
|
||||
/* Finally, clear the callback */
|
||||
isr_ctx[exti_num].cb = NULL;
|
||||
}
|
||||
|
||||
MAYBE_UNUSED
|
||||
static void isr_eic_unknown_num(void)
|
||||
{
|
||||
/* read & clear interrupt flags */
|
||||
uint32_t state = EIC_SEC->INTFLAG.reg & EIC_SEC->INTENSET.reg;
|
||||
state &= EIC_INTFLAG_EXTINT_Msk;
|
||||
EIC_SEC->INTFLAG.reg = state;
|
||||
|
||||
/* execute interrupt callbacks */
|
||||
uint8_t num = 0;
|
||||
while (state) {
|
||||
state = bitarithm_test_and_clear(state, &num);
|
||||
isr_ctx[num].cb(isr_ctx[num].arg);
|
||||
}
|
||||
|
||||
cortexm_isr_end();
|
||||
}
|
||||
|
||||
MAYBE_UNUSED
|
||||
static void isr_eic_known_num(unsigned num)
|
||||
{
|
||||
EIC_SEC->INTFLAG.reg = 1U << num;
|
||||
isr_ctx[num].cb(isr_ctx[num].arg);
|
||||
cortexm_isr_end();
|
||||
}
|
||||
|
||||
#if !defined(CPU_COMMON_SAML1X) && !defined(CPU_COMMON_SAMD5X)
|
||||
void isr_eic(void) { isr_eic_unknown_num(); }
|
||||
#endif
|
||||
|
||||
#if defined(CPU_COMMON_SAML1X)
|
||||
void isr_eic_other(void) { isr_eic_unknown_num(); }
|
||||
#endif
|
||||
|
||||
#if defined(CPU_COMMON_SAML1X) || defined(CPU_COMMON_SAMD5X)
|
||||
void isr_eic0(void) { isr_eic_known_num(0); }
|
||||
void isr_eic1(void) { isr_eic_known_num(1); }
|
||||
void isr_eic2(void) { isr_eic_known_num(2); }
|
||||
void isr_eic3(void) { isr_eic_known_num(3); }
|
||||
#endif
|
||||
#if defined(CPU_COMMON_SAMD5X)
|
||||
void isr_eic4(void) { isr_eic_known_num(4); }
|
||||
void isr_eic5(void) { isr_eic_known_num(5); }
|
||||
void isr_eic6(void) { isr_eic_known_num(6); }
|
||||
void isr_eic7(void) { isr_eic_known_num(7); }
|
||||
void isr_eic8(void) { isr_eic_known_num(8); }
|
||||
void isr_eic9(void) { isr_eic_known_num(9); }
|
||||
void isr_eic10(void) { isr_eic_known_num(10); }
|
||||
void isr_eic11(void) { isr_eic_known_num(11); }
|
||||
void isr_eic12(void) { isr_eic_known_num(12); }
|
||||
void isr_eic13(void) { isr_eic_known_num(13); }
|
||||
void isr_eic14(void) { isr_eic_known_num(14); }
|
||||
void isr_eic15(void) { isr_eic_known_num(15); }
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user