From bf5c1110acd49ae0151ef12838f1b834f5412639 Mon Sep 17 00:00:00 2001 From: Thomas Stilwell Date: Tue, 9 Jul 2019 09:06:10 -0700 Subject: [PATCH 01/13] cpu/kinetis/gpio: fix irq contexts not being freed --- cpu/kinetis/periph/gpio.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/cpu/kinetis/periph/gpio.c b/cpu/kinetis/periph/gpio.c index 2988efd0bcec..30a2497b75a4 100644 --- a/cpu/kinetis/periph/gpio.c +++ b/cpu/kinetis/periph/gpio.c @@ -94,7 +94,7 @@ /** * @brief Define the number of simultaneously configurable interrupt channels * - * We have configured 4-bits per pin, so we can go up to 16 simultaneous active + * We have configured 4-bits per pin, so we can go up to 15 simultaneous active * extern interrupt sources. */ #define CTX_NUMOF (8U) @@ -148,10 +148,13 @@ static inline int pin_num(gpio_t pin) /** * @brief Get context for a specific pin + * + * @return index of context for specified pin + * @return -1 if pin has no context */ static inline int get_ctx(int port, int pin) { - return (isr_map[(port * 4) + (pin >> 3)] >> ((pin & 0x7) * 4)) & 0xf; + return ((isr_map[(port * 4) + (pin >> 3)] >> ((pin & 0x7) * 4)) & 0xf) - 1; } /** @@ -177,13 +180,15 @@ static void write_map(int port, int pin, int ctx) } /** - * @brief Clear the context for the given pin + * @brief Free the context for the given pin */ -static void ctx_clear(int port, int pin) +static void ctx_free(int port, int pin) { int ctx = get_ctx(port, pin); - - write_map(port, pin, ctx); + /* clear the context pointer for this pin */ + write_map(port, pin, 0); + /* mark the context entry as free */ + isr_ctx[ctx].cb = NULL; } #endif /* MODULE_PERIPH_GPIO_IRQ */ @@ -226,20 +231,17 @@ void gpio_init_port(gpio_t pin, uint32_t pcr) clk_en(pin); #ifdef KINETIS_HAVE_PCR -#ifdef MODULE_PERIPH_GPIO_IRQ /* if the given interrupt was previously configured as interrupt source, we * need to free its interrupt context. We to this only after we * re-configured the pin in case an event is happening just in between... */ - uint32_t isr_state = port(pin)->PCR[pin_num(pin)]; -#endif /* MODULE_PERIPH_GPIO_IRQ */ /* set new PCR value */ port(pin)->PCR[pin_num(pin)] = pcr; #ifdef MODULE_PERIPH_GPIO_IRQ - /* and clear the interrupt context if needed */ - if (isr_state & PORT_PCR_IRQC_MASK) { - ctx_clear(port_num(pin), pin_num(pin)); + /* and free the interrupt context if needed */ + if (get_ctx(port_num(pin), pin_num(pin)) >= 0) { + ctx_free(port_num(pin), pin_num(pin)); } #endif /* MODULE_PERIPH_GPIO_IRQ */ #else @@ -300,7 +302,8 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, isr_ctx[ctx_num].cb = cb; isr_ctx[ctx_num].arg = arg; isr_ctx[ctx_num].state = flank; - write_map(port_num(pin), pin_num(pin), ctx_num); + /* context map requires ctx_num + 1, as 0 is the no-context value */ + write_map(port_num(pin), pin_num(pin), ctx_num + 1); /* clear interrupt flags */ port(pin)->ISFR &= ~(1 << pin_num(pin)); From 53565b431070ed46b21fd742aaea6bf5d526441f Mon Sep 17 00:00:00 2001 From: Thomas Stilwell Date: Sat, 27 Jun 2020 02:40:25 -0500 Subject: [PATCH 02/13] cpu/kinetis/gpio: gpio_init_port(): don't unset interrupts during init --- cpu/kinetis/include/periph_cpu.h | 4 ++++ cpu/kinetis/periph/gpio.c | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/cpu/kinetis/include/periph_cpu.h b/cpu/kinetis/include/periph_cpu.h index 12f461456ec2..4437280149ca 100644 --- a/cpu/kinetis/include/periph_cpu.h +++ b/cpu/kinetis/include/periph_cpu.h @@ -787,6 +787,10 @@ typedef struct { /** * @brief CPU internal function for initializing PORTs * + * Sets the pin's PORT.PCR to the specified value, excluding PCR.IRQC + * which is not allowed to be set with this function. If pcr has GPIO_AF_ANALOG + * then PCR.IRQC will be cleared, as interrupts do not work in analog mode. + * * @param[in] pin pin to initialize * @param[in] pcr value for the PORT's PCR register */ diff --git a/cpu/kinetis/periph/gpio.c b/cpu/kinetis/periph/gpio.c index 30a2497b75a4..25d13414f84c 100644 --- a/cpu/kinetis/periph/gpio.c +++ b/cpu/kinetis/periph/gpio.c @@ -230,17 +230,20 @@ void gpio_init_port(gpio_t pin, uint32_t pcr) /* enable PORT clock in case it was not active before */ clk_en(pin); -#ifdef KINETIS_HAVE_PCR - /* if the given interrupt was previously configured as interrupt source, we - * need to free its interrupt context. We to this only after we - * re-configured the pin in case an event is happening just in between... */ + /* We don't support setting IRQC this way. It's managed in this file only */ + assert(!(pcr & PORT_PCR_IRQC_MASK)); - /* set new PCR value */ - port(pin)->PCR[pin_num(pin)] = pcr; +#ifdef KINETIS_HAVE_PCR + /* set new PCR value, keeping the existing IRQC value */ + uint32_t old_pcr = port(pin)->PCR[pin_num(pin)]; + port(pin)->PCR[pin_num(pin)] = pcr | (old_pcr & PORT_PCR_IRQC_MASK); #ifdef MODULE_PERIPH_GPIO_IRQ - /* and free the interrupt context if needed */ - if (get_ctx(port_num(pin), pin_num(pin)) >= 0) { + /* Pin interrupts can only be used in digital muxing modes, so disable + * them if we're configuring for analog. Also gpio_init() triggers this. */ + if ((pcr & PORT_PCR_MUX_MASK) == GPIO_AF_ANALOG + && get_ctx(port_num(pin), pin_num(pin)) >= 0) { + gpio_irq_disable(pin); ctx_free(port_num(pin), pin_num(pin)); } #endif /* MODULE_PERIPH_GPIO_IRQ */ From e8906a88606e7bf2ae4792742e11203f68229c60 Mon Sep 17 00:00:00 2001 From: Thomas Stilwell Date: Tue, 9 Jul 2019 14:00:15 -0700 Subject: [PATCH 03/13] cpu/kinetis/gpio: fix wrong calculation for ISR_MAP_SIZE It's calculated in bytes but is used to declare an array of uint32_t. sizeof(isr_map) went from 448 to 112 (in bss) --- cpu/kinetis/periph/gpio.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cpu/kinetis/periph/gpio.c b/cpu/kinetis/periph/gpio.c index 25d13414f84c..6cd2742241a2 100644 --- a/cpu/kinetis/periph/gpio.c +++ b/cpu/kinetis/periph/gpio.c @@ -86,10 +86,9 @@ #ifdef MODULE_PERIPH_GPIO_IRQ /** - * @brief Calculate the needed memory (in byte) needed to save 4 bits per MCU - * pin + * @brief Calculate the needed memory (in bytes) to store 4 bits per MCU pin */ -#define ISR_MAP_SIZE (GPIO_PORTS_NUMOF * PINS_PER_PORT * 4 / 8) +#define ISR_MAP_SIZE (GPIO_PORTS_NUMOF * PINS_PER_PORT * 4 / 8 / sizeof(uint32_t)) /** * @brief Define the number of simultaneously configurable interrupt channels From 8393bf26d70d66faebf7ac20ee69c30f19c136d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Sat, 3 Jun 2017 04:22:34 +0200 Subject: [PATCH 04/13] cpu/kinetis: implement LLWU (Low-Leakage Wakeup Unit) --- cpu/kinetis/Makefile.dep | 1 + cpu/kinetis/Makefile.features | 1 + cpu/kinetis/cpu.c | 14 +++ cpu/kinetis/include/cpu_conf_kinetis.h | 139 +++++++++++++++++++++++++ cpu/kinetis/include/llwu.h | 79 ++++++++++++++ cpu/kinetis/periph/llwu.c | 107 +++++++++++++++++++ 6 files changed, 341 insertions(+) create mode 100644 cpu/kinetis/include/llwu.h create mode 100644 cpu/kinetis/periph/llwu.c diff --git a/cpu/kinetis/Makefile.dep b/cpu/kinetis/Makefile.dep index 45e3557e3810..dc867833686a 100644 --- a/cpu/kinetis/Makefile.dep +++ b/cpu/kinetis/Makefile.dep @@ -17,6 +17,7 @@ else ifneq (,$(filter periph_mcg,$(FEATURES_USED))) USEMODULE += periph_mcg endif +USEMODULE += periph_llwu USEMODULE += periph_wdog USEMODULE += pm_layered diff --git a/cpu/kinetis/Makefile.features b/cpu/kinetis/Makefile.features index 0cc03f5e152a..42532a11da3a 100644 --- a/cpu/kinetis/Makefile.features +++ b/cpu/kinetis/Makefile.features @@ -10,6 +10,7 @@ ifneq (,$(filter-out $(_KINETIS_CPU_MODELS_WITHOUT_HWRNG),$(CPU_MODEL))) FEATURES_PROVIDED += periph_hwrng endif +FEATURES_PROVIDED += periph_llwu FEATURES_PROVIDED += periph_gpio FEATURES_PROVIDED += periph_gpio_irq diff --git a/cpu/kinetis/cpu.c b/cpu/kinetis/cpu.c index 2e2a2a3cfaea..418f2ffdc4d6 100644 --- a/cpu/kinetis/cpu.c +++ b/cpu/kinetis/cpu.c @@ -23,6 +23,11 @@ #ifdef MODULE_PERIPH_MCG #include "mcg.h" #endif +#if defined(MODULE_PERIPH_LLWU) +#include "llwu.h" +#elif defined(MODULE_PM_LAYERED) +#include "pm_layered.h" +#endif /** * @brief Initialize the CPU, set IRQ priorities @@ -46,6 +51,15 @@ void cpu_init(void) /* initialize stdio prior to periph_init() to allow use of DEBUG() there */ stdio_init(); +#if defined(MODULE_PERIPH_LLWU) + /* initialize the LLWU module for sleep/wakeup management */ + llwu_init(); +#elif defined(MODULE_PM_LAYERED) + /* Block LLS mode since we are not using the LLWU module, which is required + * to be able to wake up from LLS */ + pm_block(KINETIS_PM_LLS); +#endif + /* trigger static peripheral initialization */ periph_init(); } diff --git a/cpu/kinetis/include/cpu_conf_kinetis.h b/cpu/kinetis/include/cpu_conf_kinetis.h index 421b9e740833..04c7758f92db 100644 --- a/cpu/kinetis/include/cpu_conf_kinetis.h +++ b/cpu/kinetis/include/cpu_conf_kinetis.h @@ -61,6 +61,145 @@ extern "C" #define PIN_INTERRUPT_EDGE 0b1011 /** @} */ +/** + * @brief Mapping internal module interrupts to LLWU wake up sources + * + * @note Modules not listed here CAN NOT be used to wake the CPU from LLS or + * VLLSx power modes. + * + * The numbers are hardware specific, but have the same values across all the + * supported Kinetis CPUs + */ +typedef enum llwu_wakeup_module { + LLWU_WAKEUP_MODULE_LPTMR0 = 0, + LLWU_WAKEUP_MODULE_CMP0 = 1, + LLWU_WAKEUP_MODULE_RADIO = 2, /* KWx1Z devices */ + LLWU_WAKEUP_MODULE_CMP1 = 2, /* others */ + LLWU_WAKEUP_MODULE_DCDC = 3, /* KWx1Z devices */ + LLWU_WAKEUP_MODULE_CMP2 = 3, /* others */ + LLWU_WAKEUP_MODULE_TSI = 4, + LLWU_WAKEUP_MODULE_RTC_ALARM = 5, + LLWU_WAKEUP_MODULE_RESERVED = 6, + LLWU_WAKEUP_MODULE_RTC_SECONDS = 7, + LLWU_WAKEUP_MODULE_NUMOF +} llwu_wakeup_module_t; + +/** + * @brief Mapping LLWU wakeup pin sources to PORT interrupt numbers + */ +typedef struct { + PORT_Type *port; /**< PORT register */ + uint32_t isfr_mask; /**< ISFR bitmask */ +} llwu_wakeup_pin_to_port_t; + +/** + * @brief Mapping physical pins to LLWU wakeup pin source numbers + * + * @note Pins not listed here CAN NOT be used to wake the CPU from LLS or + * VLLSx power modes. + * + * The numbers are hardware specific, but have the same values across all the + * supported Kinetis CPUs. + */ +#if defined(KINETIS_SERIES_W) && defined(KINETIS_CORE_Z) && (KINETIS_SUBFAMILY == 1) +/* KW41Z has different LLWU pins */ +typedef enum llwu_wakeup_pin { + LLWU_WAKEUP_PIN_PTC16 = 0, + LLWU_WAKEUP_PIN_PTC17 = 1, + LLWU_WAKEUP_PIN_PTC18 = 2, + LLWU_WAKEUP_PIN_PTC19 = 3, + LLWU_WAKEUP_PIN_PTA16 = 4, + LLWU_WAKEUP_PIN_PTA17 = 5, + LLWU_WAKEUP_PIN_PTA18 = 6, + LLWU_WAKEUP_PIN_PTA19 = 7, + LLWU_WAKEUP_PIN_PTB0 = 8, + LLWU_WAKEUP_PIN_PTC0 = 9, + LLWU_WAKEUP_PIN_PTC2 = 10, + LLWU_WAKEUP_PIN_PTC3 = 11, + LLWU_WAKEUP_PIN_PTC4 = 12, + LLWU_WAKEUP_PIN_PTC5 = 13, + LLWU_WAKEUP_PIN_PTC6 = 14, + LLWU_WAKEUP_PIN_PTC7 = 15, + LLWU_WAKEUP_PIN_NUMOF, + LLWU_WAKEUP_PIN_UNDEF +} llwu_wakeup_pin_t; + +/** + * @brief Mapping LLWU wakeup pin number to PORT module interrupt flags + */ +static const llwu_wakeup_pin_to_port_t llwu_wakeup_pin_to_port[LLWU_WAKEUP_PIN_NUMOF] = { + [LLWU_WAKEUP_PIN_PTC16] = { .port = PORTC, .isfr_mask = (1u << 16), }, + [LLWU_WAKEUP_PIN_PTC17] = { .port = PORTC, .isfr_mask = (1u << 17), }, + [LLWU_WAKEUP_PIN_PTC18] = { .port = PORTC, .isfr_mask = (1u << 18), }, + [LLWU_WAKEUP_PIN_PTC19] = { .port = PORTC, .isfr_mask = (1u << 19), }, + [LLWU_WAKEUP_PIN_PTA16] = { .port = PORTA, .isfr_mask = (1u << 16), }, + [LLWU_WAKEUP_PIN_PTA17] = { .port = PORTA, .isfr_mask = (1u << 17), }, + [LLWU_WAKEUP_PIN_PTA18] = { .port = PORTA, .isfr_mask = (1u << 18), }, + [LLWU_WAKEUP_PIN_PTA19] = { .port = PORTA, .isfr_mask = (1u << 19), }, + [LLWU_WAKEUP_PIN_PTB0 ] = { .port = PORTB, .isfr_mask = (1u << 0), }, + [LLWU_WAKEUP_PIN_PTC0 ] = { .port = PORTC, .isfr_mask = (1u << 0), }, + [LLWU_WAKEUP_PIN_PTC2 ] = { .port = PORTC, .isfr_mask = (1u << 2), }, + [LLWU_WAKEUP_PIN_PTC3 ] = { .port = PORTC, .isfr_mask = (1u << 3), }, + [LLWU_WAKEUP_PIN_PTC4 ] = { .port = PORTC, .isfr_mask = (1u << 4), }, + [LLWU_WAKEUP_PIN_PTC5 ] = { .port = PORTC, .isfr_mask = (1u << 5), }, + [LLWU_WAKEUP_PIN_PTC6 ] = { .port = PORTC, .isfr_mask = (1u << 6), }, + [LLWU_WAKEUP_PIN_PTC7 ] = { .port = PORTC, .isfr_mask = (1u << 7), }, +}; +#else +typedef enum llwu_wakeup_pin { + LLWU_WAKEUP_PIN_PTE1 = 0, + LLWU_WAKEUP_PIN_PTE2 = 1, + LLWU_WAKEUP_PIN_PTE4 = 2, + LLWU_WAKEUP_PIN_PTA4 = 3, + LLWU_WAKEUP_PIN_PTA13 = 4, + LLWU_WAKEUP_PIN_PTB0 = 5, + LLWU_WAKEUP_PIN_PTC1 = 6, + LLWU_WAKEUP_PIN_PTC3 = 7, + LLWU_WAKEUP_PIN_PTC4 = 8, + LLWU_WAKEUP_PIN_PTC5 = 9, + LLWU_WAKEUP_PIN_PTC6 = 10, + LLWU_WAKEUP_PIN_PTC11 = 11, + LLWU_WAKEUP_PIN_PTD0 = 12, + LLWU_WAKEUP_PIN_PTD2 = 13, + LLWU_WAKEUP_PIN_PTD4 = 14, + LLWU_WAKEUP_PIN_PTD6 = 15, + LLWU_WAKEUP_PIN_NUMOF, + LLWU_WAKEUP_PIN_UNDEF +} llwu_wakeup_pin_t; + +/** + * @brief Mapping LLWU wakeup pin number to PORT module interrupt flags + */ +static const llwu_wakeup_pin_to_port_t llwu_wakeup_pin_to_port[LLWU_WAKEUP_PIN_NUMOF] = { + [LLWU_WAKEUP_PIN_PTE1 ] = { .port = PORTE, .isfr_mask = (1u << 1), }, + [LLWU_WAKEUP_PIN_PTE2 ] = { .port = PORTE, .isfr_mask = (1u << 2), }, + [LLWU_WAKEUP_PIN_PTE4 ] = { .port = PORTE, .isfr_mask = (1u << 4), }, + [LLWU_WAKEUP_PIN_PTA4 ] = { .port = PORTA, .isfr_mask = (1u << 4), }, + [LLWU_WAKEUP_PIN_PTA13] = { .port = PORTA, .isfr_mask = (1u << 13), }, + [LLWU_WAKEUP_PIN_PTB0 ] = { .port = PORTB, .isfr_mask = (1u << 0), }, + [LLWU_WAKEUP_PIN_PTC1 ] = { .port = PORTC, .isfr_mask = (1u << 1), }, + [LLWU_WAKEUP_PIN_PTC3 ] = { .port = PORTC, .isfr_mask = (1u << 3), }, + [LLWU_WAKEUP_PIN_PTC4 ] = { .port = PORTC, .isfr_mask = (1u << 4), }, + [LLWU_WAKEUP_PIN_PTC5 ] = { .port = PORTC, .isfr_mask = (1u << 5), }, + [LLWU_WAKEUP_PIN_PTC6 ] = { .port = PORTC, .isfr_mask = (1u << 6), }, + [LLWU_WAKEUP_PIN_PTC11] = { .port = PORTC, .isfr_mask = (1u << 11), }, + [LLWU_WAKEUP_PIN_PTD0 ] = { .port = PORTD, .isfr_mask = (1u << 0), }, + [LLWU_WAKEUP_PIN_PTD2 ] = { .port = PORTD, .isfr_mask = (1u << 2), }, + [LLWU_WAKEUP_PIN_PTD4 ] = { .port = PORTD, .isfr_mask = (1u << 4), }, + [LLWU_WAKEUP_PIN_PTD6 ] = { .port = PORTD, .isfr_mask = (1u << 6), }, +}; +#endif + +/** + * @brief LLWU wakeup pin edge settings + */ +typedef enum llwu_wakeup_edge { + LLWU_WAKEUP_EDGE_NONE = 0, + LLWU_WAKEUP_EDGE_RISING = 1, + LLWU_WAKEUP_EDGE_FALLING = 2, + LLWU_WAKEUP_EDGE_BOTH = 3, +} llwu_wakeup_edge_t; + /** * @name Compatibility definitions between vendor headers * @{ diff --git a/cpu/kinetis/include/llwu.h b/cpu/kinetis/include/llwu.h new file mode 100644 index 000000000000..8ca36124935f --- /dev/null +++ b/cpu/kinetis/include/llwu.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2017 SKF AB + * + * 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. + */ + +/** + * @defgroup cpu_kinetis_llwu Kinetis LLWU + * @ingroup cpu_kinetis + * @brief Kinetis low leakage wakeup unit (LLWU) driver + + * @{ + * + * @file + * @brief Interface definition for the Kinetis LLWU driver. + * + * @author Joakim Nohlgård + */ + +#ifndef LLWU_H +#define LLWU_H + +#include "cpu.h" +#include "bit.h" +#include "periph_conf.h" +#include "periph/gpio.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @brief Initialize the Low-Leakage Wake Up (LLWU) hardware module + */ +void llwu_init(void); + +/** + * @brief Enable a wakeup module in the LLWU + */ +inline static void llwu_wakeup_module_enable(llwu_wakeup_module_t mod) +{ + assert(mod < LLWU_WAKEUP_MODULE_NUMOF); + bit_set8(&LLWU->ME, mod); +} + +/** + * @brief Disable a wakeup module in the LLWU + */ +inline static void llwu_wakeup_module_disable(llwu_wakeup_module_t mod) +{ + assert(mod < LLWU_WAKEUP_MODULE_NUMOF); + bit_clear8(&LLWU->ME, mod); +} + +/** + * @brief Set the mode for a wakeup pin in the LLWU + * + * If @p cb is NULL when the pin edge is detected, the CPU will wake up for a + * few cycles which can allow other hardware modules to detect other interrupts. + * This may be particularily useful when using the wakeup pin for communication + * functions such as UART RX etc. + * + * @param[in] pin The pin to modify + * @param[in] edge Edge detection setting (rising, falling, both, none) + * @param[in] cb Callback function to execute when woken with this pin + * @param[in] arg Argument that will be passed to the callback + */ +void llwu_wakeup_pin_set(llwu_wakeup_pin_t pin, llwu_wakeup_edge_t edge, gpio_cb_t cb, void *arg); + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif /* LLWU_H */ diff --git a/cpu/kinetis/periph/llwu.c b/cpu/kinetis/periph/llwu.c new file mode 100644 index 000000000000..eec2826402e9 --- /dev/null +++ b/cpu/kinetis/periph/llwu.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2017 SKF AB + * + * 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_kinetis + * @{ + * + * @file + * @brief Low-leakage wakeup unit (LLWU) driver + * + * @author Joakim Nohlgård + * + * @} + */ + +#include "cpu.h" +#include "bit.h" +#include "llwu.h" +#include "bitarithm.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +typedef struct { + gpio_cb_t cb; + void *arg; +} llwu_pin_config_t; + +static llwu_pin_config_t llwu_pin_config[LLWU_WAKEUP_PIN_NUMOF]; + +void llwu_init(void) +{ + /* Setup Low Leakage Wake-up Unit (LLWU) */ +#ifdef SIM_SCGC4_LLWU_SHIFT + /* Only the first generation Kinetis K CPUs have a clock gate for the LLWU, + * for all others the LLWU clock is always on */ + bit_set32(&SIM->SCGC4, SIM_SCGC4_LLWU_SHIFT); /* Enable LLWU clock gate */ +#endif + + /* LLWU needs to have a priority which is equal to or more urgent than the + * PORT module to avoid races between the LLWU pin detect interrupt and the + * PORT pin detect interrupt */ + NVIC_SetPriority(LLWU_IRQn, 0); +} + +void llwu_wakeup_pin_set(llwu_wakeup_pin_t pin, llwu_wakeup_edge_t edge, gpio_cb_t cb, void *arg) +{ + assert(pin < LLWU_WAKEUP_PIN_NUMOF); + llwu_pin_config[pin].cb = cb; + llwu_pin_config[pin].arg = arg; + + /* The fields are two bits per pin, and the setting registers are 8 bits + * wide, hence 4 pins per register. */ + unsigned field_offset = (pin & 0x03) << 1; + unsigned reg_offset = pin >> 2; + (&LLWU->PE1)[reg_offset] = ((&LLWU->PE1)[reg_offset] & + (~(0b11 << field_offset))) | + (edge << field_offset); +} + +void isr_llwu(void) +{ + DEBUG("LLWU IRQ\n"); + + for (unsigned reg = 0; reg < ((LLWU_WAKEUP_PIN_NUMOF + 7) / 8); ++reg) { + uint8_t status = *(&LLWU->F1 + reg); + /* Clear pin interrupt flags */ + *(&LLWU->F1 + reg) = status; + DEBUG("llwu: F%u = %02x\n", reg + 1, (unsigned) status); + + while (status) { + unsigned pin = bitarithm_lsb(status); + status &= ~(1 << pin); + pin += reg * 8; + DEBUG("llwu: wakeup pin %u\n", pin); + gpio_cb_t cb = llwu_pin_config[pin].cb; + if (cb) { + cb(llwu_pin_config[pin].arg); + } + /* Clear PORT interrupt flag to avoid spurious duplicates. */ + /* In essence, this behavior is similar to a software debounce. Even + * very quick contact bounces after the LLWU has begun to wake the + * CPU may cause the PORT module to pick up the same trigger event, + * which may lead to duplicate software events when using the same + * callback for gpio_init_int and llwu_wakeup_pin_set. The bounces + * would normally be ignored because of the processing delay in the + * interrupt handling before the interrupt flag is cleared, but + * since there are two interrupt flags, one in the LLWU module and + * one in the PORT module, we can get two events. */ + DEBUG("PORT ISFR: %08" PRIx32 "\n", llwu_wakeup_pin_to_port[pin].port->ISFR); + llwu_wakeup_pin_to_port[pin].port->ISFR = llwu_wakeup_pin_to_port[pin].isfr_mask; + } + } + /* Read only register F3, the flag will need to be cleared in the peripheral + * instead of writing a 1 to the MWUFx bit. */ + DEBUG("llwu: F3 = %02x\n", LLWU->F3); + /* Mask the LLWU IRQ until the module interrupt handlers have had a chance + * to run and clear the F3 flags */ + NVIC_DisableIRQ(LLWU_IRQn); + + cortexm_isr_end(); +} From d71ebb86c82c612f7f92cc465255305da3aed451 Mon Sep 17 00:00:00 2001 From: Thomas Stilwell Date: Tue, 9 Jul 2019 04:40:03 -0700 Subject: [PATCH 05/13] cpu/kinetis/gpio: use LLWU to wake if available --- cpu/kinetis/periph/gpio.c | 93 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/cpu/kinetis/periph/gpio.c b/cpu/kinetis/periph/gpio.c index 6cd2742241a2..7c69089af6b9 100644 --- a/cpu/kinetis/periph/gpio.c +++ b/cpu/kinetis/periph/gpio.c @@ -21,17 +21,27 @@ * @author Johann Fischer * @author Jonas Remmert * @author Joakim Nohlgård + * @author Thomas Stilwell * * @} */ #include #include + +#include "log.h" #include "cpu.h" -#include "bitarithm.h" #include "bit.h" +#include "bitarithm.h" #include "periph/gpio.h" +#if MODULE_PERIPH_LLWU +#include "llwu.h" +#endif + +#define ENABLE_DEBUG 0 +#include "debug.h" + /* Single-port MCU*/ #if !defined(PORTA_BASE) && defined(PORT_BASE) # define PORTA_BASE PORT_BASE @@ -287,6 +297,20 @@ void gpio_write(gpio_t pin, int value) } #ifdef MODULE_PERIPH_GPIO_IRQ +#ifdef MODULE_PERIPH_LLWU +static llwu_wakeup_pin_t llwu_pin_from_gpio(gpio_t pin) +{ + for (unsigned i = 0; i < LLWU_WAKEUP_PIN_NUMOF; ++i) { + if (llwu_wakeup_pin_to_port[i].port == port(pin) + && llwu_wakeup_pin_to_port[i].isfr_mask == (1u << pin_num(pin)) + ) { + return i; + } + } + return LLWU_WAKEUP_PIN_UNDEF; +} +#endif /* MODULE_PERIPH_LLWU */ + int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, gpio_cb_t cb, void *arg) { @@ -294,6 +318,16 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, return -1; } +#ifdef MODULE_PERIPH_LLWU + if (llwu_pin_from_gpio(pin) == LLWU_WAKEUP_PIN_UNDEF) + { +#else + { +#endif + LOG_INFO("[gpio] will block power mode %u for pin interrupt while enabled\n", + KINETIS_PM_LLS); + } + /* try go grab a free spot in the context array */ int ctx_num = get_free_ctx(); if (ctx_num < 0) { @@ -314,21 +348,76 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, NVIC_EnableIRQ(port_irqs[port_num(pin)]); /* finally, enable the interrupt for the selected pin */ - port(pin)->PCR[pin_num(pin)] |= flank; + gpio_irq_enable(pin); + return 0; } void gpio_irq_enable(gpio_t pin) { + /* mustn't proceed if interrupts are enabled already */ + if (port(pin)->PCR[pin_num(pin)] & PORT_PCR_IRQC_MASK) { + return; + } + int ctx = get_ctx(port_num(pin), pin_num(pin)); port(pin)->PCR[pin_num(pin)] |= isr_ctx[ctx].state; + +#ifdef MODULE_PERIPH_LLWU + /* Check if the pin can be used as LLWU source. Configure LLWU if so, + * but also leave the pin configured as a GPIO IRQ source because LLWU is + * not active in any non-LL mode. If not, block PM_LLS as we can't otherwise + * provide the requested pin interrupt. + */ + llwu_wakeup_pin_t llwu_pin = llwu_pin_from_gpio(pin); + if (llwu_pin != LLWU_WAKEUP_PIN_UNDEF) { + llwu_wakeup_edge_t edge = (isr_ctx[ctx].state >> PORT_PCR_IRQC_SHIFT) + - (GPIO_RISING >> PORT_PCR_IRQC_SHIFT) + + LLWU_WAKEUP_EDGE_RISING; + llwu_wakeup_pin_set(llwu_pin, edge, isr_ctx[ctx].cb, isr_ctx[ctx].arg); + } + + else + { +#else /* MODULE_PERIPH_LLWU */ + { +#endif + DEBUG("[gpio] blocking power mode %u for pin interrupt\n", + KINETIS_PM_LLS); + PM_BLOCK(KINETIS_PM_LLS); + } } void gpio_irq_disable(gpio_t pin) { int ctx = get_ctx(port_num(pin), pin_num(pin)); isr_ctx[ctx].state = port(pin)->PCR[pin_num(pin)] & PORT_PCR_IRQC_MASK; + + /* mustn't proceed if interrupts are disabled already */ + if (!isr_ctx[ctx].state) { + return; + } + port(pin)->PCR[pin_num(pin)] &= ~(PORT_PCR_IRQC_MASK); + +#ifdef MODULE_PERIPH_LLWU + /* Disable LLWU for this pin if we had enabled it */ + llwu_wakeup_pin_t llwu_pin = llwu_pin_from_gpio(pin); + if (llwu_pin != LLWU_WAKEUP_PIN_UNDEF) + { + llwu_wakeup_pin_set(llwu_pin, LLWU_WAKEUP_EDGE_NONE, + NULL, NULL); + } + + else + { +#else /* MODULE_PERIPH_LLWU */ + { +#endif + DEBUG("[gpio] unblocking power mode %u for pin interrupt\n", + KINETIS_PM_LLS); + PM_UNBLOCK(KINETIS_PM_LLS); + } } static inline void irq_handler(PORT_Type *port, int port_num) From 6a674ba0dc40e74705ebc179cda60985021c4859 Mon Sep 17 00:00:00 2001 From: Thomas Stilwell Date: Fri, 27 Mar 2020 21:08:16 -0500 Subject: [PATCH 06/13] cpu/kinetis/uart: implement poweron/poweroff --- cpu/kinetis/include/periph_cpu.h | 5 + cpu/kinetis/periph/uart.c | 289 ++++++++++++++++++++++++++----- 2 files changed, 249 insertions(+), 45 deletions(-) diff --git a/cpu/kinetis/include/periph_cpu.h b/cpu/kinetis/include/periph_cpu.h index 4437280149ca..ba0ef1142ae9 100644 --- a/cpu/kinetis/include/periph_cpu.h +++ b/cpu/kinetis/include/periph_cpu.h @@ -525,6 +525,11 @@ typedef struct { uart_type_t type; /**< Hardware module type (KINETIS_UART or KINETIS_LPUART)*/ } uart_conf_t; +/** + * @brief Override the default uart_isr_ctx_t in uart.c + */ +#define HAVE_UART_ISR_CTX_T + #if !defined(KINETIS_HAVE_PLL) #if defined(MCG_C6_PLLS_MASK) || DOXYGEN /** diff --git a/cpu/kinetis/periph/uart.c b/cpu/kinetis/periph/uart.c index f1c5edf378cd..723d3d2aab3d 100644 --- a/cpu/kinetis/periph/uart.c +++ b/cpu/kinetis/periph/uart.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Eistec AB + * Copyright (C) 2017-2018 Eistec AB * Copyright (C) 2014 PHYTEC Messtechnik GmbH * Copyright (C) 2014 Freie Universität Berlin * @@ -77,6 +77,25 @@ #define LPUART_1_SRC 0 #endif +#ifndef LPUART_IDLECFG +/* See IDLECFG in the reference manual. Longer idle configurations give more + * robust LPUART RX from low power modes, but will also keep the CPU awake for + * longer periods. */ +#define LPUART_IDLECFG (0b001) +#endif + +/* This power mode is blocked while the LP/UART clock needs to stay running */ +#ifndef UART_CLOCK_PM_BLOCKER +#define UART_CLOCK_PM_BLOCKER KINETIS_PM_STOP +#endif + +typedef struct { + uart_rx_cb_t rx_cb; /**< data received interrupt callback */ + void *arg; /**< argument to both callback routines */ + unsigned active; /**< set to 1 while the receiver is active, to avoid mismatched PM_BLOCK/PM_UNBLOCK */ + unsigned enabled; /**< set to 1 while the receiver is enabled, to avoid mismatched PM_BLOCK/PM_UNBLOCK */ +} uart_isr_ctx_t; + /** * @brief Runtime configuration space, holds pointers to callback functions for RX */ @@ -86,9 +105,13 @@ static inline void uart_init_pins(uart_t uart); #if KINETIS_HAVE_UART static inline void uart_init_uart(uart_t uart, uint32_t baudrate); +static inline void uart_poweron_uart(uart_t uart); +static inline void uart_poweroff_uart(uart_t uart); #endif #if KINETIS_HAVE_LPUART static inline void uart_init_lpuart(uart_t uart, uint32_t baudrate); +static inline void uart_poweron_lpuart(uart_t uart); +static inline void uart_poweroff_lpuart(uart_t uart); #endif /* Only use the dispatch function for uart_write if both UART and LPUART are @@ -110,15 +133,24 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) { assert(uart < UART_NUMOF); + /* disable interrupts from UART module */ + NVIC_DisableIRQ(uart_config[uart].irqn); + + /* Turn on module clock gate */ + bit_set32(uart_config[uart].scgc_addr, uart_config[uart].scgc_bit); + + /* Power off before messing with settings, this will ensure a consistent + * state if we are re-initializing an already initialized module */ + uart_poweroff(uart); + /* remember callback addresses */ config[uart].rx_cb = rx_cb; config[uart].arg = arg; + config[uart].active = 0; + config[uart].enabled = 0; uart_init_pins(uart); - /* Turn on module clock gate */ - bit_set32(uart_config[uart].scgc_addr, uart_config[uart].scgc_bit); - switch (uart_config[uart].type) { #if KINETIS_HAVE_UART case KINETIS_UART: @@ -133,19 +165,78 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) default: return UART_NODEV; } + + /* Turn on module */ + uart_poweron(uart); + + /* enable interrupts from UART module */ + NVIC_EnableIRQ(uart_config[uart].irqn); + return UART_OK; } void uart_poweron(uart_t uart) { - (void)uart; - /* not implemented (yet) */ + assert(uart < UART_NUMOF); + unsigned state = irq_disable(); + + if (config[uart].rx_cb && !config[uart].enabled) { + config[uart].enabled = 1; + DEBUG("uart: Blocking power mode %u\n", UART_CLOCK_PM_BLOCKER); + PM_BLOCK(UART_CLOCK_PM_BLOCKER); + } + + switch (uart_config[uart].type) { +#if KINETIS_HAVE_UART + case KINETIS_UART: + uart_poweron_uart(uart); + break; +#endif +#if KINETIS_HAVE_LPUART + case KINETIS_LPUART: + uart_poweron_lpuart(uart); + break; +#endif + default: + break; + } + + irq_restore(state); } void uart_poweroff(uart_t uart) { - (void)uart; - /* not implemented (yet) */ + assert(uart < UART_NUMOF); + unsigned state = irq_disable(); + if (config[uart].rx_cb && config[uart].enabled) { + config[uart].enabled = 0; + DEBUG("uart: Unblocking power mode %u\n", UART_CLOCK_PM_BLOCKER); + PM_UNBLOCK(UART_CLOCK_PM_BLOCKER); + + if (config[uart].active) { + /* We were in the middle of a RX sequence, need to release that + * clock blocker as well */ + PM_UNBLOCK(UART_CLOCK_PM_BLOCKER); + config[uart].active = 0; + } + } + + switch (uart_config[uart].type) { +#if KINETIS_HAVE_UART + case KINETIS_UART: + uart_poweroff_uart(uart); + break; +#endif +#if KINETIS_HAVE_LPUART + case KINETIS_LPUART: + uart_poweroff_lpuart(uart); + break; +#endif + default: + break; + } + + irq_restore(state); } #if KINETIS_HAVE_UART && KINETIS_HAVE_LPUART @@ -189,7 +280,7 @@ static inline void uart_init_uart(uart_t uart, uint32_t baudrate) clk = uart_config[uart].freq; /* disable transmitter and receiver */ - dev->C2 &= ~(UART_C2_TE_MASK | UART_C2_RE_MASK); + dev->C2 = 0; /* Select mode */ dev->C1 = uart_config[uart].mode; @@ -213,8 +304,8 @@ static inline void uart_init_uart(uart_t uart, uint32_t baudrate) * TXFIFOSIZE == 0 means size = 1 (i.e. only one byte, no hardware FIFO) */ if ((dev->PFIFO & UART_PFIFO_TXFIFOSIZE_MASK) != 0) { uint8_t txfifo_size = - (2 << ((dev->PFIFO & UART_PFIFO_TXFIFOSIZE_MASK) >> - UART_PFIFO_TXFIFOSIZE_SHIFT)); + (2 << ((dev->PFIFO & UART_PFIFO_TXFIFOSIZE_MASK) >> + UART_PFIFO_TXFIFOSIZE_SHIFT)); dev->TWFIFO = UART_TWFIFO_TXWATER(txfifo_size - 1); } else { @@ -228,15 +319,19 @@ static inline void uart_init_uart(uart_t uart, uint32_t baudrate) dev->CFIFO = UART_CFIFO_RXFLUSH_MASK | UART_CFIFO_TXFLUSH_MASK; #endif /* KINETIS_UART_ADVANCED */ - /* enable transmitter and receiver + RX interrupt */ - dev->C2 |= UART_C2_TE_MASK | UART_C2_RE_MASK | UART_C2_RIE_MASK; - - /* enable receive interrupt */ - NVIC_EnableIRQ(uart_config[uart].irqn); + if (config[uart].rx_cb) { + /* enable RX active edge interrupt */ + bit_set8(&dev->BDH, UART_BDH_RXEDGIE_SHIFT); + /* enable receiver + RX interrupt + IDLE interrupt */ + dev->C2 = UART_C2_RIE_MASK | UART_C2_ILIE_MASK; + /* enable interrupts on failure flags */ + dev->C3 |= UART_C3_ORIE_MASK | UART_C3_PEIE_MASK | UART_C3_FEIE_MASK | UART_C3_NEIE_MASK; + } + /* clear interrupt flags */ + uint8_t s = dev->S2; + dev->S2 = s; } -#endif /* KINETIS_HAVE_UART */ -#if KINETIS_HAVE_UART KINETIS_UART_WRITE_INLINE void uart_write_uart(uart_t uart, const uint8_t *data, size_t len) { UART_Type *dev = uart_config[uart].dev; @@ -245,6 +340,30 @@ KINETIS_UART_WRITE_INLINE void uart_write_uart(uart_t uart, const uint8_t *data, while (!(dev->S1 & UART_S1_TDRE_MASK)) {} dev->D = data[i]; } + /* Wait for transmission complete */ + while ((dev->S1 & UART_S1_TC_MASK) == 0) {} +} + +static inline void uart_poweron_uart(uart_t uart) +{ + UART_Type *dev = uart_config[uart].dev; + + /* Enable transmitter */ + bit_set8(&dev->C2, UART_C2_TE_SHIFT); + if (config[uart].rx_cb) { + /* Enable receiver */ + bit_set8(&dev->C2, UART_C2_RE_SHIFT); + } +} + +static inline void uart_poweroff_uart(uart_t uart) +{ + UART_Type *dev = uart_config[uart].dev; + + /* Disable receiver */ + bit_clear8(&dev->C2, UART_C2_RE_SHIFT); + /* Disable transmitter */ + bit_clear8(&dev->C2, UART_C2_TE_SHIFT); } #if defined(UART_0_ISR) || defined(UART_1_ISR) || defined(UART_2_ISR) || \ @@ -253,31 +372,64 @@ static inline void irq_handler_uart(uart_t uart) { UART_Type *dev = uart_config[uart].dev; - /* - * On Cortex-M0, it happens that S1 is read with LDR - * instruction instead of LDRB. This will read the data register - * at the same time and arrived byte will be lost. Maybe it's a GCC bug. - * - * Observed with: arm-none-eabi-gcc (4.8.3-8+..) - * It does not happen with: arm-none-eabi-gcc (4.8.3-9+11) - */ - - if (dev->S1 & UART_S1_RDRF_MASK) { - /* RDRF flag will be cleared when dev-D was read */ - uint8_t data = dev->D; - - if (config[uart].rx_cb != NULL) { + uint8_t s1 = dev->S1; + uint8_t s2 = dev->S2; + /* Clear IRQ flags */ + dev->S2 = s2; + /* The IRQ flags in S1 are cleared by reading the D register */ + uint8_t data = dev->D; + (void) data; + if (dev->SFIFO & UART_SFIFO_RXUF_MASK) { + /* RX FIFO underrun occurred, flush the RX FIFO to get the internal + * pointer back in sync */ + dev->CFIFO |= UART_CFIFO_RXFLUSH_MASK; + /* Clear SFIFO flags */ + dev->SFIFO = dev->SFIFO; + DEBUG("UART RXUF\n"); + } + DEBUG("U: %c\n", data); + if (s2 & UART_S2_RXEDGIF_MASK) { + if (!config[uart].active) { + config[uart].active = 1; + /* Keep UART clock on until we are finished with RX */ + DEBUG("UART ACTIVE\n"); + PM_BLOCK(UART_CLOCK_PM_BLOCKER); + } + } + if (s1 & UART_S1_OR_MASK) { + /* UART overrun, some data has been lost */ + DEBUG("UART OR\n"); + } + if (s1 & UART_S1_RDRF_MASK) { + if (s1 & (UART_S1_FE_MASK | UART_S1_PF_MASK | UART_S1_NF_MASK)) { + if (s1 & UART_S1_FE_MASK) { + DEBUG("UART framing error %02x\n", (unsigned) s1); + } + if (s1 & UART_S1_PF_MASK) { + DEBUG("UART parity error %02x\n", (unsigned) s1); + } + if (s1 & UART_S1_NF_MASK) { + DEBUG("UART noise %02x\n", (unsigned) s1); + } + /* FE is set when a logic 0 is accepted as the stop bit. */ + /* PF is set when PE is set, S2[LBKDE] is disabled, and the parity + * of the received data does not match its parity bit. */ + /* NF is set when the UART detects noise on the receiver input. */ + } + /* Only run callback if no error occurred */ + else if (config[uart].rx_cb != NULL) { config[uart].rx_cb(config[uart].arg, data); } } - -#if (KINETIS_UART_ADVANCED == 0) - /* clear overrun flag */ - if (dev->S1 & UART_S1_OR_MASK) { - dev->S1 = UART_S1_OR_MASK; + if (s1 & UART_S1_IDLE_MASK) { + if (config[uart].active) { + config[uart].active = 0; + /* UART clock can stop now that RX is complete */ + PM_UNBLOCK(UART_CLOCK_PM_BLOCKER); + DEBUG("UART IDLE\n"); + } } -#endif - + DEBUG("UART: s1 %x C1 %x C2 %x S1 %x S2 %x D %x SF %x\n", s1, dev->C1, dev->C2, dev->S1, dev->S2, data, dev->SFIFO); cortexm_isr_end(); } #endif @@ -358,14 +510,21 @@ static inline void uart_init_lpuart(uart_t uart, uint32_t baudrate) } uint32_t sbr = clk / (best_osr * baudrate); - /* set baud rate */ + /* set baud rate, enable RX active edge interrupt */ dev->BAUD = LPUART_BAUD_OSR(best_osr - 1) | LPUART_BAUD_SBR(sbr); - /* enable transmitter and receiver + RX interrupt */ - dev->CTRL |= LPUART_CTRL_TE_MASK | LPUART_CTRL_RE_MASK | LPUART_CTRL_RIE_MASK; - - /* enable receive interrupt */ - NVIC_EnableIRQ(uart_config[uart].irqn); + if (config[uart].rx_cb) { + /* enable RX active edge interrupt */ + bit_set32(&dev->BAUD, LPUART_BAUD_RXEDGIE_SHIFT); + /* enable RX interrupt + error interrupts */ + dev->CTRL |= LPUART_CTRL_RIE_MASK | + LPUART_CTRL_ILIE_MASK | LPUART_CTRL_IDLECFG(LPUART_IDLECFG) | + LPUART_CTRL_ORIE_MASK | LPUART_CTRL_PEIE_MASK | + LPUART_CTRL_FEIE_MASK | LPUART_CTRL_NEIE_MASK; + } + /* clear interrupt flags */ + uint32_t s = dev->STAT; + dev->STAT = s; } KINETIS_UART_WRITE_INLINE void uart_write_lpuart(uart_t uart, const uint8_t *data, size_t len) @@ -376,6 +535,30 @@ KINETIS_UART_WRITE_INLINE void uart_write_lpuart(uart_t uart, const uint8_t *dat while ((dev->STAT & LPUART_STAT_TDRE_MASK) == 0) {} dev->DATA = data[i]; } + /* Wait for transmission complete */ + while ((dev->STAT & LPUART_STAT_TC_MASK) == 0) {} +} + +static inline void uart_poweron_lpuart(uart_t uart) +{ + LPUART_Type *dev = uart_config[uart].dev; + + /* Enable transmitter */ + bit_set32(&dev->CTRL, LPUART_CTRL_TE_SHIFT); + if (config[uart].rx_cb) { + /* Enable receiver */ + bit_set32(&dev->CTRL, LPUART_CTRL_RE_SHIFT); + } +} + +static inline void uart_poweroff_lpuart(uart_t uart) +{ + LPUART_Type *dev = uart_config[uart].dev; + + /* Disable receiver */ + bit_clear32(&dev->CTRL, LPUART_CTRL_RE_SHIFT); + /* Disable transmitter */ + bit_clear32(&dev->CTRL, LPUART_CTRL_TE_SHIFT); } #if defined(LPUART_0_ISR) || defined(LPUART_1_ISR) || defined(LPUART_2_ISR) || \ @@ -387,6 +570,14 @@ static inline void irq_handler_lpuart(uart_t uart) /* Clear all IRQ flags */ dev->STAT = stat; + if (stat & LPUART_STAT_RXEDGIF_MASK) { + if (!config[uart].active) { + config[uart].active = 1; + /* Keep UART clock on until we are finished with RX */ + DEBUG("LPUART ACTIVE\n"); + PM_BLOCK(UART_CLOCK_PM_BLOCKER); + } + } if (stat & LPUART_STAT_RDRF_MASK) { /* RDRF flag will be cleared when LPUART_DATA is read */ uint8_t data = dev->DATA; @@ -413,6 +604,14 @@ static inline void irq_handler_lpuart(uart_t uart) * receive the data */ DEBUG("LPUART overrun %08" PRIx32 "\n", stat); } + if (stat & LPUART_STAT_IDLE_MASK) { + if (config[uart].active) { + config[uart].active = 0; + /* UART clock can stop now that RX is complete */ + PM_UNBLOCK(UART_CLOCK_PM_BLOCKER); + DEBUG("LPUART IDLE\n"); + } + } cortexm_isr_end(); } From 4407253d90ada06ca589e97ed77be93ca07155a7 Mon Sep 17 00:00:00 2001 From: Thomas Stilwell Date: Fri, 27 Mar 2020 21:09:39 -0500 Subject: [PATCH 07/13] cpu/kinetis/uart: use LLWU and GPIO IRQ to wake from low power modes --- .../common/kw41z/include/periph_conf_common.h | 6 +- cpu/kinetis/Makefile.dep | 3 + cpu/kinetis/periph/uart.c | 201 ++++++++++++++---- 3 files changed, 170 insertions(+), 40 deletions(-) diff --git a/boards/common/kw41z/include/periph_conf_common.h b/boards/common/kw41z/include/periph_conf_common.h index 6eafe086951a..55804920efc2 100644 --- a/boards/common/kw41z/include/periph_conf_common.h +++ b/boards/common/kw41z/include/periph_conf_common.h @@ -118,8 +118,10 @@ static const uart_conf_t uart_config[] = { }; #define UART_NUMOF ARRAY_SIZE(uart_config) #define LPUART_0_ISR isr_lpuart0 -/* Use MCGIRCLK (internal reference 4 MHz clock) */ -#define LPUART_0_SRC 3 +/* Use MCGIRCLK (4 MHz internal reference - not available <= KINETIS_PM_LLS) */ +#define LPUART_0_SRC 3 +#define UART_CLOCK_PM_BLOCKER KINETIS_PM_LLS +#define UART_MAX_UNCLOCKED_BAUDRATE 19200ul /** @} */ /** diff --git a/cpu/kinetis/Makefile.dep b/cpu/kinetis/Makefile.dep index dc867833686a..00c795de6482 100644 --- a/cpu/kinetis/Makefile.dep +++ b/cpu/kinetis/Makefile.dep @@ -22,3 +22,6 @@ USEMODULE += periph_wdog USEMODULE += pm_layered include $(RIOTCPU)/cortexm_common/Makefile.dep +ifneq (,$(filter periph_uart,$(USEMODULE))) + FEATURES_OPTIONAL += periph_gpio_irq +endif diff --git a/cpu/kinetis/periph/uart.c b/cpu/kinetis/periph/uart.c index 723d3d2aab3d..9e4cbb282a81 100644 --- a/cpu/kinetis/periph/uart.c +++ b/cpu/kinetis/periph/uart.c @@ -20,14 +20,17 @@ * @author Hauke Petersen * @author Johann Fischer * @author Joakim Nohlgård + * @author Thomas Stilwell * * @} */ +#include "log.h" #include "cpu.h" #include "bit.h" #include "periph_conf.h" #include "periph/uart.h" +#include "periph/gpio.h" #define ENABLE_DEBUG (0) #include "debug.h" @@ -84,11 +87,31 @@ #define LPUART_IDLECFG (0b001) #endif -/* This power mode is blocked while the LP/UART clock needs to stay running */ +/* This power mode is blocked while the LP/UART clock needs to stay running. + * This happens if an RX callback is configured and the UART cannot wake on a + * start bit without a clock running. This also happens if an RX callback + * is configured and the baudrate is higher than + * (UART_MAX_UNCLOCKED_BAUDRATE * 1.2). It happens also while a frame is being + * received. + * This default value is safe for all configurations and should be lowered + * by the board configuration to the highest power mode which disables the + * configured UART clock */ #ifndef UART_CLOCK_PM_BLOCKER #define UART_CLOCK_PM_BLOCKER KINETIS_PM_STOP #endif +/* In power modes <= UART_CLOCK_PM_BLOCKER an LP/UART clock is not running. + * Waking for UART RX can occur from LLWU or other sources but it takes time + * to start a UART sampling clock after waking on a start bit. Therefore the + * maximum usable baudrate depends on the UART clock source and the wakeup + * source, but 9600 should be a safe default with most configurations. Using a + * baudrate faster than (UART_MAX_UNCLOCKED_BAUDRATE * 1.2) will block + * UART_CLOCK_PM_BLOCKER while an RX callback is configured. + * This should be increased by the board configuration in most cases. */ +#ifndef UART_MAX_UNCLOCKED_BAUDRATE +#define UART_MAX_UNCLOCKED_BAUDRATE 9600ul +#endif + typedef struct { uart_rx_cb_t rx_cb; /**< data received interrupt callback */ void *arg; /**< argument to both callback routines */ @@ -129,6 +152,64 @@ KINETIS_UART_WRITE_INLINE void uart_write_lpuart(uart_t uart, const uint8_t *dat #endif #endif +#ifdef MODULE_PERIPH_GPIO_IRQ +/** + * @brief pin interrupt callback for UART RX pin + * + * This function is called only when we're waiting for a start bit to cause + * a pin interrupt so we can start the UART clock. + */ +static void uart_pin_int_cb(void *arg) +{ + uart_t uart = (uart_t)arg; + if (!config[uart].active) { + config[uart].active = 1; + + /* Keep UART clock on until we are finished with RX */ + PM_BLOCK(UART_CLOCK_PM_BLOCKER); + + /* We don't need the pin interrupt until the bus goes idle again */ + gpio_irq_disable(uart_config[uart].pin_rx); + + DEBUG("UART pin IRQ\n"); + } +} +#endif + +#ifdef MODULE_PERIPH_GPIO_IRQ +static uint32_t _get_baudrate(uart_t uart) +{ + uint32_t baudrate = 0; + + switch (uart_config[uart].type) { +#if KINETIS_HAVE_UART + case KINETIS_UART: { + UART_Type *dev = uart_config[uart].dev; + uint16_t sbr = 0; + sbr += (dev->BDL & UART_BDL_SBR_MASK) >> UART_BDL_SBR_SHIFT; + sbr += (dev->BDH & UART_BDH_SBR_MASK) >> UART_BDH_SBR_SHIFT << 8; + baudrate = uart_config[uart].freq / sbr / 16; + break; + } +#endif +#if KINETIS_HAVE_LPUART + case KINETIS_LPUART: { + LPUART_Type *dev = uart_config[uart].dev; + uint8_t osr = (dev->BAUD & LPUART_BAUD_OSR_MASK) + >> LPUART_BAUD_OSR_SHIFT; + uint16_t sbr = (dev->BAUD & LPUART_BAUD_SBR_MASK) + >> LPUART_BAUD_SBR_SHIFT; + baudrate = uart_config[uart].freq / ((osr + 1) * sbr); + break; + } +#endif + default: + break; + } + return baudrate; +} +#endif /* MODULE_PERIPH_GPIO_IRQ */ + int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) { assert(uart < UART_NUMOF); @@ -180,12 +261,6 @@ void uart_poweron(uart_t uart) assert(uart < UART_NUMOF); unsigned state = irq_disable(); - if (config[uart].rx_cb && !config[uart].enabled) { - config[uart].enabled = 1; - DEBUG("uart: Blocking power mode %u\n", UART_CLOCK_PM_BLOCKER); - PM_BLOCK(UART_CLOCK_PM_BLOCKER); - } - switch (uart_config[uart].type) { #if KINETIS_HAVE_UART case KINETIS_UART: @@ -201,6 +276,44 @@ void uart_poweron(uart_t uart) break; } + if (!config[uart].enabled) { + config[uart].enabled = 1; + + if (config[uart].rx_cb) { + + /* As long as the baudrate isn't too fast, we can avoid needing + * to keep the UART clocked continuously by detecting the falling + * edge of a start bit using a pin interrupt. */ +#ifdef MODULE_PERIPH_GPIO_IRQ + if (_get_baudrate(uart) < UART_MAX_UNCLOCKED_BAUDRATE * 1.2) { + + /* This will block power modes appropriate for maintaining a + * working interrupt as a wakeup source. */ + gpio_init_int(uart_config[uart].pin_rx, GPIO_IN, GPIO_FALLING, + uart_pin_int_cb, (void*)uart); + + /* gpio_init_int() has reconfigured pin_rx to GPIO mode */ + uart_init_pins(uart); + } + else + { + /* We can't wake fast enough from a pin interrupt to start + * the UART clock in time to catch the incoming frame, so we + * need to keep the UART clock running */ + LOG_INFO("[uart] Blocking power mode %u because baudrate > %" + PRIu32 "\n", + UART_CLOCK_PM_BLOCKER, UART_MAX_UNCLOCKED_BAUDRATE); + PM_BLOCK(UART_CLOCK_PM_BLOCKER); + } +#else + LOG_INFO("[uart] Blocking power mode %u because module " + "periph_gpio_irq is needed to sleep with RX enabled\n", + UART_CLOCK_PM_BLOCKER); + PM_BLOCK(UART_CLOCK_PM_BLOCKER); +#endif /* MODULE_PERIPH_GPIO_IRQ */ + } + } + irq_restore(state); } @@ -208,19 +321,36 @@ void uart_poweroff(uart_t uart) { assert(uart < UART_NUMOF); unsigned state = irq_disable(); - if (config[uart].rx_cb && config[uart].enabled) { - config[uart].enabled = 0; - DEBUG("uart: Unblocking power mode %u\n", UART_CLOCK_PM_BLOCKER); - PM_UNBLOCK(UART_CLOCK_PM_BLOCKER); + if (config[uart].rx_cb) { + if (config[uart].enabled) { + config[uart].enabled = 0; + + /* We may have been using a pin interrupt to wake the UART clock. */ +#ifdef MODULE_PERIPH_GPIO_IRQ + if (_get_baudrate(uart) < UART_MAX_UNCLOCKED_BAUDRATE * 1.2) { + /* IRQ was already disabled if active is set */ + if (!config[uart].active) { + gpio_irq_disable(uart_config[uart].pin_rx); + } + } + else + { +#else + { +#endif + LOG_INFO("[uart] Unblocking power mode %u\n", + UART_CLOCK_PM_BLOCKER); + PM_UNBLOCK(UART_CLOCK_PM_BLOCKER); + } - if (config[uart].active) { - /* We were in the middle of a RX sequence, need to release that - * clock blocker as well */ - PM_UNBLOCK(UART_CLOCK_PM_BLOCKER); - config[uart].active = 0; + if (config[uart].active) { + config[uart].active = 0; + /* We were in the middle of a RX sequence, need to release that + * clock blocker as well */ + PM_UNBLOCK(UART_CLOCK_PM_BLOCKER); + } } } - switch (uart_config[uart].type) { #if KINETIS_HAVE_UART case KINETIS_UART: @@ -235,7 +365,6 @@ void uart_poweroff(uart_t uart) default: break; } - irq_restore(state); } @@ -320,8 +449,6 @@ static inline void uart_init_uart(uart_t uart, uint32_t baudrate) #endif /* KINETIS_UART_ADVANCED */ if (config[uart].rx_cb) { - /* enable RX active edge interrupt */ - bit_set8(&dev->BDH, UART_BDH_RXEDGIE_SHIFT); /* enable receiver + RX interrupt + IDLE interrupt */ dev->C2 = UART_C2_RIE_MASK | UART_C2_ILIE_MASK; /* enable interrupts on failure flags */ @@ -334,6 +461,10 @@ static inline void uart_init_uart(uart_t uart, uint32_t baudrate) KINETIS_UART_WRITE_INLINE void uart_write_uart(uart_t uart, const uint8_t *data, size_t len) { + if (!config[uart].enabled) { + return; + } + UART_Type *dev = uart_config[uart].dev; for (size_t i = 0; i < len; i++) { @@ -388,14 +519,6 @@ static inline void irq_handler_uart(uart_t uart) DEBUG("UART RXUF\n"); } DEBUG("U: %c\n", data); - if (s2 & UART_S2_RXEDGIF_MASK) { - if (!config[uart].active) { - config[uart].active = 1; - /* Keep UART clock on until we are finished with RX */ - DEBUG("UART ACTIVE\n"); - PM_BLOCK(UART_CLOCK_PM_BLOCKER); - } - } if (s1 & UART_S1_OR_MASK) { /* UART overrun, some data has been lost */ DEBUG("UART OR\n"); @@ -426,6 +549,10 @@ static inline void irq_handler_uart(uart_t uart) config[uart].active = 0; /* UART clock can stop now that RX is complete */ PM_UNBLOCK(UART_CLOCK_PM_BLOCKER); +#ifdef MODULE_PERIPH_GPIO_IRQ + /* Need an interrupt source to wake for the next start bit */ + gpio_irq_enable(uart_config[uart].pin_rx); +#endif DEBUG("UART IDLE\n"); } } @@ -514,8 +641,6 @@ static inline void uart_init_lpuart(uart_t uart, uint32_t baudrate) dev->BAUD = LPUART_BAUD_OSR(best_osr - 1) | LPUART_BAUD_SBR(sbr); if (config[uart].rx_cb) { - /* enable RX active edge interrupt */ - bit_set32(&dev->BAUD, LPUART_BAUD_RXEDGIE_SHIFT); /* enable RX interrupt + error interrupts */ dev->CTRL |= LPUART_CTRL_RIE_MASK | LPUART_CTRL_ILIE_MASK | LPUART_CTRL_IDLECFG(LPUART_IDLECFG) | @@ -529,6 +654,10 @@ static inline void uart_init_lpuart(uart_t uart, uint32_t baudrate) KINETIS_UART_WRITE_INLINE void uart_write_lpuart(uart_t uart, const uint8_t *data, size_t len) { + if (!config[uart].enabled) { + return; + } + LPUART_Type *dev = uart_config[uart].dev; for (size_t i = 0; i < len; i++) { @@ -570,14 +699,6 @@ static inline void irq_handler_lpuart(uart_t uart) /* Clear all IRQ flags */ dev->STAT = stat; - if (stat & LPUART_STAT_RXEDGIF_MASK) { - if (!config[uart].active) { - config[uart].active = 1; - /* Keep UART clock on until we are finished with RX */ - DEBUG("LPUART ACTIVE\n"); - PM_BLOCK(UART_CLOCK_PM_BLOCKER); - } - } if (stat & LPUART_STAT_RDRF_MASK) { /* RDRF flag will be cleared when LPUART_DATA is read */ uint8_t data = dev->DATA; @@ -609,6 +730,10 @@ static inline void irq_handler_lpuart(uart_t uart) config[uart].active = 0; /* UART clock can stop now that RX is complete */ PM_UNBLOCK(UART_CLOCK_PM_BLOCKER); +#ifdef MODULE_PERIPH_GPIO_IRQ + /* Need an interrupt source to wake for the next start bit */ + gpio_irq_enable(uart_config[uart].pin_rx); +#endif DEBUG("LPUART IDLE\n"); } } From eab4d2c8727736c8d6434b97e148067b3d0a37df Mon Sep 17 00:00:00 2001 From: Thomas Stilwell Date: Fri, 27 Mar 2020 20:22:20 -0500 Subject: [PATCH 08/13] cpu/kinetis/timer: add PM blockers --- cpu/kinetis/periph/timer.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/cpu/kinetis/periph/timer.c b/cpu/kinetis/periph/timer.c index 05e7dfa70030..0c39a415ff57 100644 --- a/cpu/kinetis/periph/timer.c +++ b/cpu/kinetis/periph/timer.c @@ -203,6 +203,11 @@ static inline int pit_set(uint8_t dev, uint32_t timeout) const uint8_t ch = pit_config[dev].count_ch; /* Disable IRQs to minimize the number of lost ticks */ unsigned int mask = irq_disable(); + if (!(PIT->CHANNEL[ch].TCTRL & PIT_TCTRL_TIE_MASK)) { + /* The PIT is halted in low power modes, block them when a timer target + * has been set */ + PM_BLOCK(KINETIS_PM_STOP); + } /* Subtract if there was anything left on the counter */ pit[dev].count -= PIT->CHANNEL[ch].CVAL; /* Set new timeout */ @@ -224,6 +229,11 @@ static inline int pit_set_absolute(uint8_t dev, uint32_t target) uint8_t ch = pit_config[dev].count_ch; /* Disable IRQs to minimize the number of lost ticks */ unsigned int mask = irq_disable(); + if (!(PIT->CHANNEL[ch].TCTRL & PIT_TCTRL_TIE_MASK)) { + /* The PIT is halted in low power modes, block them when a timer target + * has been set */ + PM_BLOCK(KINETIS_PM_STOP); + } uint32_t now = pit[dev].count - PIT->CHANNEL[ch].CVAL; uint32_t offset = target - now; @@ -247,6 +257,11 @@ static inline int pit_clear(uint8_t dev) /* Disable IRQs to minimize the number of lost ticks */ unsigned int mask = irq_disable(); + if (PIT->CHANNEL[ch].TCTRL & PIT_TCTRL_TIE_MASK) { + /* Allow low power modes again */ + PM_UNBLOCK(KINETIS_PM_STOP); + } + /* Subtract if there was anything left on the counter */ pit[dev].count -= PIT->CHANNEL[ch].CVAL; /* No need to add PIT_MAX_VALUE + 1 to the counter because of modulo 2**32 */ @@ -274,6 +289,11 @@ static inline void pit_start(uint8_t dev) { uint8_t ch = pit_config[dev].prescaler_ch; + if (PIT->CHANNEL[ch].TCTRL & PIT_TCTRL_TEN_MASK) { + /* Already running */ + return; + } + PIT->CHANNEL[ch].TCTRL = PIT_TCTRL_TEN_MASK; } @@ -281,6 +301,11 @@ static inline void pit_stop(uint8_t dev) { uint8_t ch = pit_config[dev].prescaler_ch; + if (!(PIT->CHANNEL[ch].TCTRL & PIT_TCTRL_TEN_MASK)) { + /* Already stopped */ + return; + } + PIT->CHANNEL[ch].TCTRL = 0; } @@ -301,6 +326,8 @@ static inline void pit_irq_handler(tim_t dev) PIT->CHANNEL[ch].LDVAL = PIT_MAX_VALUE; PIT->CHANNEL[ch].TFLG = PIT_TFLG_TIF_MASK; PIT->CHANNEL[ch].TCTRL = PIT_TCTRL_CHN_MASK | PIT_TCTRL_TEN_MASK; + /* Allow low power modes again */ + PM_UNBLOCK(KINETIS_PM_STOP); if (pit_ctx->isr_ctx.cb != NULL) { pit_ctx->isr_ctx.cb(pit_ctx->isr_ctx.arg, 0); From 6b4554459c9d222ee74f9aa8edef4a6da07d9bf4 Mon Sep 17 00:00:00 2001 From: Thomas Stilwell Date: Fri, 27 Mar 2020 20:23:18 -0500 Subject: [PATCH 09/13] cpu/kinetis/timer: support LLWU Kinetis boards using LPTMR must define LPTMR.llwu --- .../common/kw41z/include/periph_conf_common.h | 19 ++++++++++--------- boards/frdm-k22f/include/periph_conf.h | 17 +++++++++-------- boards/frdm-k64f/include/periph_conf.h | 17 +++++++++-------- boards/mulle/include/periph_conf.h | 19 ++++++++++--------- cpu/kinetis/include/periph_cpu.h | 2 ++ cpu/kinetis/periph/timer.c | 7 +++++++ 6 files changed, 47 insertions(+), 34 deletions(-) diff --git a/boards/common/kw41z/include/periph_conf_common.h b/boards/common/kw41z/include/periph_conf_common.h index 55804920efc2..5079315e3d3f 100644 --- a/boards/common/kw41z/include/periph_conf_common.h +++ b/boards/common/kw41z/include/periph_conf_common.h @@ -83,15 +83,16 @@ static const clock_config_t clock_config = { .count_ch = 1, \ }, \ } -#define LPTMR_NUMOF (1U) -#define LPTMR_CONFIG { \ - { \ - .dev = LPTMR0, \ - .irqn = LPTMR0_IRQn, \ - .src = 2, \ - .base_freq = 32768u, \ - } \ - } +#define LPTMR_NUMOF (1U) +#define LPTMR_CONFIG { \ + { \ + .dev = LPTMR0, \ + .irqn = LPTMR0_IRQn, \ + .base_freq = 32768u, \ + .src = 2, \ + .llwu = LLWU_WAKEUP_MODULE_LPTMR0, \ + }, \ +} #define TIMER_NUMOF ((PIT_NUMOF) + (LPTMR_NUMOF)) #define PIT_BASECLOCK (CLOCK_BUSCLOCK) #define LPTMR_ISR_0 isr_lptmr0 diff --git a/boards/frdm-k22f/include/periph_conf.h b/boards/frdm-k22f/include/periph_conf.h index 8963e6a5356a..f864ae66b353 100644 --- a/boards/frdm-k22f/include/periph_conf.h +++ b/boards/frdm-k22f/include/periph_conf.h @@ -79,14 +79,15 @@ static const clock_config_t clock_config = { .count_ch = 3, \ }, \ } -#define LPTMR_NUMOF (1U) -#define LPTMR_CONFIG { \ - { \ - .dev = LPTMR0, \ - .irqn = LPTMR0_IRQn, \ - .src = 2, \ - .base_freq = 32768u, \ - }, \ +#define LPTMR_NUMOF (1U) +#define LPTMR_CONFIG { \ + { \ + .dev = LPTMR0, \ + .irqn = LPTMR0_IRQn, \ + .base_freq = 32768u, \ + .src = 2, \ + .llwu = LLWU_WAKEUP_MODULE_LPTMR0, \ + }, \ } #define TIMER_NUMOF ((PIT_NUMOF) + (LPTMR_NUMOF)) diff --git a/boards/frdm-k64f/include/periph_conf.h b/boards/frdm-k64f/include/periph_conf.h index 2f14b750321d..1038ead9d57f 100644 --- a/boards/frdm-k64f/include/periph_conf.h +++ b/boards/frdm-k64f/include/periph_conf.h @@ -80,14 +80,15 @@ static const clock_config_t clock_config = { .count_ch = 3, \ }, \ } -#define LPTMR_NUMOF (1U) -#define LPTMR_CONFIG { \ - { \ - .dev = LPTMR0, \ - .irqn = LPTMR0_IRQn, \ - .src = 2, \ - .base_freq = 32768u, \ - }, \ +#define LPTMR_NUMOF (1U) +#define LPTMR_CONFIG { \ + { \ + .dev = LPTMR0, \ + .irqn = LPTMR0_IRQn, \ + .base_freq = 32768u, \ + .src = 2, \ + .llwu = LLWU_WAKEUP_MODULE_LPTMR0, \ + }, \ } #define TIMER_NUMOF ((PIT_NUMOF) + (LPTMR_NUMOF)) diff --git a/boards/mulle/include/periph_conf.h b/boards/mulle/include/periph_conf.h index 5ff6e0240536..516e58e72d21 100644 --- a/boards/mulle/include/periph_conf.h +++ b/boards/mulle/include/periph_conf.h @@ -102,15 +102,16 @@ static const clock_config_t clock_config = { .count_ch = 3, \ }, \ } -#define LPTMR_NUMOF (1U) -#define LPTMR_CONFIG { \ - { \ - .dev = LPTMR0, \ - .irqn = LPTMR0_IRQn, \ - .src = 2, \ - .base_freq = 32768u, \ - } \ - } +#define LPTMR_NUMOF (1U) +#define LPTMR_CONFIG { \ + { \ + .dev = LPTMR0, \ + .irqn = LPTMR0_IRQn, \ + .base_freq = 32768u, \ + .src = 2, \ + .llwu = LLWU_WAKEUP_MODULE_LPTMR0, \ + }, \ +} #define TIMER_NUMOF ((PIT_NUMOF) + (LPTMR_NUMOF)) #define PIT_BASECLOCK (CLOCK_BUSCLOCK) diff --git a/cpu/kinetis/include/periph_cpu.h b/cpu/kinetis/include/periph_cpu.h index ba0ef1142ae9..871e2a68e1af 100644 --- a/cpu/kinetis/include/periph_cpu.h +++ b/cpu/kinetis/include/periph_cpu.h @@ -388,6 +388,8 @@ typedef struct { LPTMR_Type *dev; /** Input clock frequency */ uint32_t base_freq; + /** LLWU wakeup module number for this timer */ + llwu_wakeup_module_t llwu; /** Clock source setting */ uint8_t src; /** IRQn interrupt number */ diff --git a/cpu/kinetis/periph/timer.c b/cpu/kinetis/periph/timer.c index 0c39a415ff57..78fb8b44f5a9 100644 --- a/cpu/kinetis/periph/timer.c +++ b/cpu/kinetis/periph/timer.c @@ -30,8 +30,12 @@ #include "bit.h" #include "board.h" #include "periph_conf.h" +#if MODULE_PERIPH_LLWU +#include "llwu.h" +#endif #include "periph/timer.h" + #ifdef PIT_LTMR64H_LTH_MASK /* The KW41Z PIT module provides only one IRQ for all PIT channels combined. */ /* TODO: find a better way to distinguish which Kinetis CPUs have separate PIT @@ -409,6 +413,9 @@ static inline int lptmr_init(uint8_t dev, uint32_t freq, timer_cb_t cb, void *ar /* Enable IRQs on the counting channel */ NVIC_ClearPendingIRQ(lptmr_config[dev].irqn); NVIC_EnableIRQ(lptmr_config[dev].irqn); +#if MODULE_PERIPH_LLWU + llwu_wakeup_module_enable(lptmr_config[dev].llwu); +#endif _lptmr_set_cb_config(dev, cb, arg); From 17a8c0496e2a62a36c0c1d47c11c51ffb60cbe55 Mon Sep 17 00:00:00 2001 From: Thomas Stilwell Date: Fri, 27 Mar 2020 20:23:12 -0500 Subject: [PATCH 10/13] cpu/kinetis/rtt: support LLWU --- cpu/kinetis/periph/rtt.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cpu/kinetis/periph/rtt.c b/cpu/kinetis/periph/rtt.c index ee22cf63f504..02732d2fd6e6 100644 --- a/cpu/kinetis/periph/rtt.c +++ b/cpu/kinetis/periph/rtt.c @@ -31,6 +31,9 @@ #include "cpu.h" #include "bit.h" #include "periph/rtt.h" +#ifdef MODULE_PERIPH_LLWU +#include "llwu.h" +#endif #include "periph_conf.h" #define ENABLE_DEBUG (0) @@ -65,7 +68,7 @@ void rtt_init(void) /* Time Invalid Flag is set, clear TIF by writing TSR */ /* Stop counter to make TSR writable */ - bit_clear32(&RTC->SR, RTC_SR_TCE_SHIFT); + rtt_poweroff(); RTC->TSR = 0; } @@ -76,6 +79,10 @@ void rtt_init(void) /* Enable RTC interrupts */ NVIC_EnableIRQ(RTC_IRQn); +#ifdef MODULE_PERIPH_LLWU + /* Enable RTC wake from LLS */ + llwu_wakeup_module_enable(LLWU_WAKEUP_MODULE_RTC_ALARM); +#endif rtt_poweron(); } From a6c6336ca6a61d8d0f2bd60344075cc26cd5190e Mon Sep 17 00:00:00 2001 From: Thomas Stilwell Date: Fri, 27 Mar 2020 20:20:36 -0500 Subject: [PATCH 11/13] cpu/kinstis/i2c: add PM blockers --- cpu/kinetis/periph/i2c.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cpu/kinetis/periph/i2c.c b/cpu/kinetis/periph/i2c.c index e103899e3127..fafb420ffd01 100644 --- a/cpu/kinetis/periph/i2c.c +++ b/cpu/kinetis/periph/i2c.c @@ -385,9 +385,12 @@ int i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, uint8_t fla * (reason: needed to empty D register) */ dummy = i2c->D; (void)dummy; - /* Wait until the ISR signals back */ TRACE("i2c: read C1=%02x S=%02x\n", (unsigned)i2c->C1, (unsigned)i2c->S); + /* Keep module clock enabled by blocking low power modes */ + PM_BLOCK(KINETIS_PM_STOP); + /* Wait until the ISR signals back */ thread_flags_t tflg = thread_flags_wait_any(THREAD_FLAG_KINETIS_I2C | THREAD_FLAG_TIMEOUT); + PM_UNBLOCK(KINETIS_PM_STOP); TRACE("i2c: rx done, %u left, ret: %d\n", i2c_state[dev].rx.bytes_left, i2c_state[dev].retval); if (!(tflg & THREAD_FLAG_KINETIS_I2C)) { @@ -435,8 +438,11 @@ int i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_t len, uint /* Initiate transfer by writing the first byte, the remaining bytes will * be fed by the ISR */ i2c->D = *((const uint8_t *)data); + /* Keep module clock enabled by blocking low power modes */ + PM_BLOCK(KINETIS_PM_STOP); /* Wait until the ISR signals back */ thread_flags_t tflg = thread_flags_wait_any(THREAD_FLAG_KINETIS_I2C | THREAD_FLAG_TIMEOUT); + PM_UNBLOCK(KINETIS_PM_STOP); TRACE("i2c: rx done, %u left, ret: %d\n", i2c_state[dev].rx.bytes_left, i2c_state[dev].retval); if (!(tflg & THREAD_FLAG_KINETIS_I2C)) { From 1e2ce307d71a771fc8c43064601d3ae2da79c0ea Mon Sep 17 00:00:00 2001 From: Thomas Stilwell Date: Fri, 27 Mar 2020 20:31:34 -0500 Subject: [PATCH 12/13] cpu/kinetis/pm: enable low power modes by default --- cpu/kinetis/include/periph_cpu.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cpu/kinetis/include/periph_cpu.h b/cpu/kinetis/include/periph_cpu.h index 871e2a68e1af..85f8db926585 100644 --- a/cpu/kinetis/include/periph_cpu.h +++ b/cpu/kinetis/include/periph_cpu.h @@ -140,6 +140,14 @@ enum { KINETIS_PM_STOP = 2, KINETIS_PM_WAIT = 3, }; + +/** + * @brief Override the default initial PM blocker to unblock all modes + */ +#ifndef PM_BLOCKER_INITIAL +#define PM_BLOCKER_INITIAL 0x00000000 +#endif + #if MODULE_PM_LAYERED #include "pm_layered.h" /** From e74eee2c8dc8c2ac77fd8d0c4f2d9cf586c985c1 Mon Sep 17 00:00:00 2001 From: Thomas Stilwell Date: Sat, 27 Jun 2020 03:49:55 -0500 Subject: [PATCH 13/13] boards/openlabs-kw41z-mini: remove obsolete llwu in UART config This was needed in earlier pm_layered PRs but now it will break compiling when LLWU is merged. --- boards/openlabs-kw41z-mini/include/periph_conf.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/boards/openlabs-kw41z-mini/include/periph_conf.h b/boards/openlabs-kw41z-mini/include/periph_conf.h index 3278c172bf87..7f70455bea42 100644 --- a/boards/openlabs-kw41z-mini/include/periph_conf.h +++ b/boards/openlabs-kw41z-mini/include/periph_conf.h @@ -142,9 +142,6 @@ static const uart_conf_t uart_config[] = { .scgc_bit = SIM_SCGC5_LPUART0_SHIFT, .mode = UART_MODE_8N1, .type = KINETIS_LPUART, -#ifdef MODULE_PERIPH_LLWU /* TODO remove ifdef after #11789 is merged */ - .llwu_rx = LLWU_WAKEUP_PIN_PTC6, -#endif }, }; #define UART_NUMOF ARRAY_SIZE(uart_config)