From 41d9078ed44cb90f1ea5a676e45eb43e2384ac8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Wed, 8 Apr 2015 23:27:37 +0200 Subject: [PATCH 1/4] cc2538: gpio: Factor out duplicated ISR code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes the code easier to maintain, and this reduces the binary image size. Signed-off-by: Benoît Thébaudeau --- cpu/cc2538/dev/gpio.c | 73 +++++++++++++------------------------------ 1 file changed, 21 insertions(+), 52 deletions(-) diff --git a/cpu/cc2538/dev/gpio.c b/cpu/cc2538/dev/gpio.c index 581e52839..0d6bb3610 100644 --- a/cpu/cc2538/dev/gpio.c +++ b/cpu/cc2538/dev/gpio.c @@ -79,69 +79,38 @@ notify(uint8_t mask, uint8_t port) } } /*---------------------------------------------------------------------------*/ -/** \brief Interrupt service routine for Port A */ -void -gpio_port_a_isr() +/** \brief Interrupt service routine for Port \a port + * \param port Number between 0 and 3. Port A: 0, Port B: 1, etc. + */ +static void +gpio_port_isr(uint8_t port) { + uint32_t base; + lpm_exit(); ENERGEST_ON(ENERGEST_TYPE_IRQ); - notify(REG(GPIO_A_BASE + GPIO_MIS), GPIO_A_NUM); + base = GPIO_PORT_TO_BASE(port); - GPIO_CLEAR_INTERRUPT(GPIO_A_BASE, 0xFF); - GPIO_CLEAR_POWER_UP_INTERRUPT(GPIO_A_NUM, 0xFF); + notify(REG(base + GPIO_MIS), port); + + GPIO_CLEAR_INTERRUPT(base, 0xFF); + GPIO_CLEAR_POWER_UP_INTERRUPT(port, 0xFF); ENERGEST_OFF(ENERGEST_TYPE_IRQ); } /*---------------------------------------------------------------------------*/ -/** \brief Interrupt service routine for Port B */ -void -gpio_port_b_isr() -{ - lpm_exit(); - - ENERGEST_ON(ENERGEST_TYPE_IRQ); - - notify(REG(GPIO_B_BASE + GPIO_MIS), GPIO_B_NUM); - - GPIO_CLEAR_INTERRUPT(GPIO_B_BASE, 0xFF); - GPIO_CLEAR_POWER_UP_INTERRUPT(GPIO_B_NUM, 0xFF); - - ENERGEST_OFF(ENERGEST_TYPE_IRQ); -} -/*---------------------------------------------------------------------------*/ -/** \brief Interrupt service routine for Port C */ -void -gpio_port_c_isr() -{ - lpm_exit(); - - ENERGEST_ON(ENERGEST_TYPE_IRQ); - - notify(REG(GPIO_C_BASE + GPIO_MIS), GPIO_C_NUM); - - GPIO_CLEAR_INTERRUPT(GPIO_C_BASE, 0xFF); - GPIO_CLEAR_POWER_UP_INTERRUPT(GPIO_C_NUM, 0xFF); - - ENERGEST_OFF(ENERGEST_TYPE_IRQ); -} -/*---------------------------------------------------------------------------*/ -/** \brief Interrupt service routine for Port D */ -void -gpio_port_d_isr() -{ - lpm_exit(); - - ENERGEST_ON(ENERGEST_TYPE_IRQ); - - notify(REG(GPIO_D_BASE + GPIO_MIS), GPIO_D_NUM); - - GPIO_CLEAR_INTERRUPT(GPIO_D_BASE, 0xFF); - GPIO_CLEAR_POWER_UP_INTERRUPT(GPIO_D_NUM, 0xFF); - - ENERGEST_OFF(ENERGEST_TYPE_IRQ); +#define GPIO_PORT_ISR(lowercase_port, uppercase_port) \ +void \ +gpio_port_##lowercase_port##_isr(void) \ +{ \ + gpio_port_isr(GPIO_##uppercase_port##_NUM); \ } +GPIO_PORT_ISR(a, A) +GPIO_PORT_ISR(b, B) +GPIO_PORT_ISR(c, C) +GPIO_PORT_ISR(d, D) /*---------------------------------------------------------------------------*/ void gpio_init() From 1e67ab394182e44ecfdff4c889300d39b653f91c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Wed, 8 Apr 2015 22:49:50 +0200 Subject: [PATCH 2/4] cc2538: gpio: Add macros to get interrupt status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce new useful GPIO macros to: - get the raw interrupt status of a port, - get the masked interrupt status of a port, - get the power-up interrupt status of a port. These macros are cleaner and less error-prone than raw register access code copied all over the place. Signed-off-by: Benoît Thébaudeau --- cpu/cc2538/dev/gpio.c | 2 +- cpu/cc2538/dev/gpio.h | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/cpu/cc2538/dev/gpio.c b/cpu/cc2538/dev/gpio.c index 0d6bb3610..6d3f1fc94 100644 --- a/cpu/cc2538/dev/gpio.c +++ b/cpu/cc2538/dev/gpio.c @@ -93,7 +93,7 @@ gpio_port_isr(uint8_t port) base = GPIO_PORT_TO_BASE(port); - notify(REG(base + GPIO_MIS), port); + notify(GPIO_GET_MASKED_INT_STATUS(base), port); GPIO_CLEAR_INTERRUPT(base, 0xFF); GPIO_CLEAR_POWER_UP_INTERRUPT(port, 0xFF); diff --git a/cpu/cc2538/dev/gpio.h b/cpu/cc2538/dev/gpio.h index 33980501b..e97ba2825 100644 --- a/cpu/cc2538/dev/gpio.h +++ b/cpu/cc2538/dev/gpio.h @@ -210,6 +210,31 @@ typedef void (* gpio_callback_t)(uint8_t port, uint8_t pin); #define GPIO_DISABLE_INTERRUPT(PORT_BASE, PIN_MASK) \ do { REG((PORT_BASE) + GPIO_IE) &= ~(PIN_MASK); } while(0) +/** \brief Get raw interrupt status of port with PORT_BASE. + * \param PORT_BASE GPIO Port register offset + * \return Bit-mask reflecting the raw interrupt status of all the port pins + * + * The bits set in the returned bit-mask reflect the status of the interrupts + * trigger conditions detected (raw, before interrupt masking), indicating that + * all the requirements are met, before they are finally allowed to trigger by + * the interrupt mask. The bits cleared indicate that corresponding input pins + * have not initiated an interrupt. + */ +#define GPIO_GET_RAW_INT_STATUS(PORT_BASE) \ + REG((PORT_BASE) + GPIO_RIS) + +/** \brief Get masked interrupt status of port with PORT_BASE. + * \param PORT_BASE GPIO Port register offset + * \return Bit-mask reflecting the masked interrupt status of all the port pins + * + * The bits set in the returned bit-mask reflect the status of input lines + * triggering an interrupt. The bits cleared indicate that either no interrupt + * has been generated, or the interrupt is masked. This is the state of the + * interrupt after interrupt masking. + */ +#define GPIO_GET_MASKED_INT_STATUS(PORT_BASE) \ + REG((PORT_BASE) + GPIO_MIS) + /** \brief Clear interrupt triggering for pins with PIN_MASK of port with * PORT_BASE. * \param PORT_BASE GPIO Port register offset @@ -270,6 +295,14 @@ typedef void (* gpio_callback_t)(uint8_t port, uint8_t pin); do { REG(GPIO_PORT_TO_BASE(PORT) + GPIO_PI_IEN) &= \ ~((PIN_MASK) << ((PORT) << 3)); } while(0) +/** \brief Get power-up interrupt status of port PORT. + * \param PORT GPIO Port (not port base address) + * \return Bit-mask reflecting the power-up interrupt status of all the port + * pins + */ +#define GPIO_GET_POWER_UP_INT_STATUS(PORT) \ + ((REG(GPIO_PORT_TO_BASE(PORT) + GPIO_IRQ_DETECT_ACK) >> ((PORT) << 3)) & 0xFF) + /** \brief Clear power-up interrupt triggering for pins with PIN_MASK of port * PORT. * \param PORT GPIO Port (not port base address) From 1a5632cba0861a74936006a4b464e85da4581548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Thu, 9 Apr 2015 00:44:51 +0200 Subject: [PATCH 3/4] cc2538: gpio: Fix missed power-up interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Power-up interrupts do not always update the regular interrupt status. Because of that, in order not to miss power-up interrupts, the ISR must handle both the regular and the power-up interrupt statuses. Signed-off-by: Benoît Thébaudeau --- cpu/cc2538/dev/gpio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpu/cc2538/dev/gpio.c b/cpu/cc2538/dev/gpio.c index 6d3f1fc94..cce0c6b22 100644 --- a/cpu/cc2538/dev/gpio.c +++ b/cpu/cc2538/dev/gpio.c @@ -93,7 +93,8 @@ gpio_port_isr(uint8_t port) base = GPIO_PORT_TO_BASE(port); - notify(GPIO_GET_MASKED_INT_STATUS(base), port); + notify(GPIO_GET_MASKED_INT_STATUS(base) | GPIO_GET_POWER_UP_INT_STATUS(port), + port); GPIO_CLEAR_INTERRUPT(base, 0xFF); GPIO_CLEAR_POWER_UP_INTERRUPT(port, 0xFF); From 3d9d52de873817ad7dc5f8e921d7218960e65cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Thu, 9 Apr 2015 00:55:04 +0200 Subject: [PATCH 4/4] cc2538: gpio: Fix missed interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only the interrupt flags that have been handled must be cleared. Otherwise, if a new interrupt occurs after the interrupt statuses are read and before they are cleared, then it is discarded without having been handled. This issue was particularly likely with two interrupt trigger conditions occurring on different pins of the same port in a short period of time. Signed-off-by: Benoît Thébaudeau --- cpu/cc2538/dev/gpio.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cpu/cc2538/dev/gpio.c b/cpu/cc2538/dev/gpio.c index cce0c6b22..eb561c757 100644 --- a/cpu/cc2538/dev/gpio.c +++ b/cpu/cc2538/dev/gpio.c @@ -86,18 +86,20 @@ static void gpio_port_isr(uint8_t port) { uint32_t base; + uint8_t int_status, power_up_int_status; lpm_exit(); ENERGEST_ON(ENERGEST_TYPE_IRQ); base = GPIO_PORT_TO_BASE(port); + int_status = GPIO_GET_MASKED_INT_STATUS(base); + power_up_int_status = GPIO_GET_POWER_UP_INT_STATUS(port); - notify(GPIO_GET_MASKED_INT_STATUS(base) | GPIO_GET_POWER_UP_INT_STATUS(port), - port); + notify(int_status | power_up_int_status, port); - GPIO_CLEAR_INTERRUPT(base, 0xFF); - GPIO_CLEAR_POWER_UP_INTERRUPT(port, 0xFF); + GPIO_CLEAR_INTERRUPT(base, int_status); + GPIO_CLEAR_POWER_UP_INTERRUPT(port, power_up_int_status); ENERGEST_OFF(ENERGEST_TYPE_IRQ); }