1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

cpu/msp430: implement gpio_ll_irq

This commit is contained in:
Marian Buschsieweke 2024-05-20 09:38:29 +02:00
parent 1a0da9a7af
commit 355f2335d5
No known key found for this signature in database
GPG Key ID: 77AA882EC78084E6
2 changed files with 146 additions and 0 deletions

View File

@ -24,4 +24,5 @@ FEATURES_PROVIDED += periph_pm
FEATURES_PROVIDED += periph_timer_query_freqs
FEATURES_PROVIDED += periph_gpio_ll
FEATURES_PROVIDED += periph_gpio_ll_irq
FEATURES_PROVIDED += periph_gpio_ll_switch_dir

View File

@ -0,0 +1,145 @@
/*
* Copyright (C) 2024 Marian Buschsieweke
*
* 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_msp430
* @ingroup drivers_periph_gpio_ll_irq
* @{
*
* @file
* @brief IRQ implementation of the GPIO Low-Level API for MSP430
*
* @author Marian Buschsieweke <marian.buschsieweke@posteo.net>
*
* @}
*/
#include <errno.h>
#include <stdbool.h>
#include "compiler_hints.h"
#include "container.h"
#include "periph/gpio_ll.h"
#include "periph/gpio_ll_irq.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#define ISR_PORTS_NUMOF 2U
#define PINS_PER_PORT 8U
#define ISR_NUMOF (ISR_PORTS_NUMOF * PINS_PER_PORT)
struct isr_ctx {
gpio_ll_cb_t cb;
void *arg;
};
static struct isr_ctx _isr_ctx[ISR_NUMOF];
static unsigned _idx(uword_t port_num, uint8_t pin)
{
if (port_num == 2) {
return pin + 8;
}
return pin;
}
int gpio_ll_irq(gpio_port_t _port, uint8_t pin, gpio_irq_trig_t trig,
gpio_ll_cb_t cb, void *arg)
{
uword_t port_num = gpio_port_num(_port);
if (unlikely((port_num > 2) || (port_num == 0))) {
DEBUG_PUTS("[gpio_ll_irq] GPIO port without IRQ support used");
return -ENOTSUP;
}
switch (trig) {
case GPIO_TRIGGER_EDGE_RISING:
case GPIO_TRIGGER_EDGE_FALLING:
break;
default:
DEBUG_PUTS("[gpio_ll_irq] unsupported trigger used");
return -ENOTSUP;
}
msp430_port_p1_p2_t *port = container_of((msp430_port_t *)_port, msp430_port_p1_p2_t, base);
unsigned idx = _idx(port_num, pin);
uword_t mask = 1U << pin;
/* Disable IRQs to avoid race when updating CTX. Relying on compiler to
* use BIC.B instruction to implement this, which is inherently atomic. */
port->IE &= ~mask;
_isr_ctx[idx].cb = cb;
_isr_ctx[idx].arg = arg;
/* Configuring interrupt edge select. Relying on atomic BIS.B and BIC.B
* instructions used by the compiler */
port->IES &= ~mask;
if (trig == GPIO_TRIGGER_EDGE_FALLING) {
port->IES |= mask;
}
/* Clear pending IRQs, relying on atomic BIC.B instruction used by the
* compiler. */
port->IFG &= ~mask;
/* Finally, enabling the IRQ, relying on atomic BIS.B instruction used by
* the compiler */
port->IE |= mask;
return 0;
}
/* It appears that disabling the interrupt in IE does not only mask IRQs,
* but disables the IRQ edge detector hardware completely. Hence, IRQs
* that came in while masked will not trigger after unmasking, as the
* API would expect. For this reason, we don't provide gpio_ll_irq_unmask() but
* only gpio_ll_irq_unmask_and_clear().
*/
void gpio_ll_irq_mask(gpio_port_t _port, uint8_t pin)
{
msp430_port_p1_p2_t *port = container_of((msp430_port_t *)_port, msp430_port_p1_p2_t, base);
port->IE &= ~(1U << pin);
}
void gpio_ll_irq_unmask_and_clear(gpio_port_t _port, uint8_t pin)
{
msp430_port_p1_p2_t *port = container_of((msp430_port_t *)_port, msp430_port_p1_p2_t, base);
uword_t mask = 1U << pin;
/* We clear IFG anyway despite bits in IFG not getting set without IE, a
* call may rely on this function clearing IRQs while already unmasked. */
port->IFG &= ~mask;
port->IE |= mask;
}
static void _isr_handler(msp430_port_p1_p2_t *port, int ctx)
{
for (unsigned i = 0; i < PINS_PER_PORT; i++) {
unsigned mask = 1U << i;
if ((port->IE & mask) && (port->IFG & mask)) {
port->IFG &= ~mask;
_isr_ctx[i + ctx].cb(_isr_ctx[i + ctx].arg);
}
}
}
ISR(PORT1_VECTOR, isr_port1)
{
__enter_isr();
_isr_handler(&PORT_1, 0);
__exit_isr();
}
ISR(PORT2_VECTOR, isr_port2)
{
__enter_isr();
_isr_handler(&PORT_2, 8);
__exit_isr();
}