From a42df69ea82c83bdacc21e0a92b0bb6c7f4397fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Thu, 13 Sep 2018 11:15:30 +0200 Subject: [PATCH 01/34] frdm-k64f: Add LPTMR configuration --- boards/frdm-k64f/include/periph_conf.h | 29 ++++++++++++++++---------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/boards/frdm-k64f/include/periph_conf.h b/boards/frdm-k64f/include/periph_conf.h index f27cd18198b6..54770d93bf33 100644 --- a/boards/frdm-k64f/include/periph_conf.h +++ b/boards/frdm-k64f/include/periph_conf.h @@ -70,18 +70,25 @@ static const clock_config_t clock_config = { * @{ */ #define PIT_NUMOF (2U) -#define PIT_CONFIG { \ - { \ - .prescaler_ch = 0, \ - .count_ch = 1, \ - }, \ - { \ - .prescaler_ch = 2, \ - .count_ch = 3, \ - }, \ +#define PIT_CONFIG { \ + { \ + .prescaler_ch = 0, \ + .count_ch = 1, \ + }, \ + { \ + .prescaler_ch = 2, \ + .count_ch = 3, \ + }, \ } -#define LPTMR_NUMOF (0U) -#define LPTMR_CONFIG {} +#define LPTMR_NUMOF (1U) +#define LPTMR_CONFIG { \ + { \ + .dev = LPTMR0, \ + .irqn = LPTMR0_IRQn, \ + .src = 2, \ + .base_freq = 32768u, \ + }, \ +} #define TIMER_NUMOF ((PIT_NUMOF) + (LPTMR_NUMOF)) #define PIT_BASECLOCK (CLOCK_BUSCLOCK) From 5a92a2d1bcee038362f4f128bd5ca12e1280f23a 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 02/34] kinetis: Add low power support --- cpu/kinetis/Makefile.features | 1 + cpu/kinetis/Makefile.include | 6 +- cpu/kinetis/cpu.c | 15 +++ cpu/kinetis/include/cpu_conf_kinetis.h | 87 +++++++++++++ cpu/kinetis/include/llwu.h | 79 ++++++++++++ cpu/kinetis/include/periph_cpu.h | 29 ++++- cpu/kinetis/periph/llwu.c | 101 +++++++++++++++ cpu/kinetis/periph/pm.c | 59 +++++++-- cpu/kinetis/periph/timer.c | 28 +++++ cpu/kinetis/periph/uart.c | 162 ++++++++++++++++++++----- 10 files changed, 524 insertions(+), 43 deletions(-) create mode 100644 cpu/kinetis/include/llwu.h create mode 100644 cpu/kinetis/periph/llwu.c diff --git a/cpu/kinetis/Makefile.features b/cpu/kinetis/Makefile.features index 8ebcde8bed25..8b214c30578d 100644 --- a/cpu/kinetis/Makefile.features +++ b/cpu/kinetis/Makefile.features @@ -2,6 +2,7 @@ FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_hwrng FEATURES_PROVIDED += periph_gpio FEATURES_PROVIDED += periph_gpio_irq +FEATURES_PROVIDED += periph_llwu FEATURES_PROVIDED += periph_mcg include $(RIOTCPU)/cortexm_common/Makefile.features diff --git a/cpu/kinetis/Makefile.include b/cpu/kinetis/Makefile.include index 59a0b948fd61..37e11cc3bf8b 100644 --- a/cpu/kinetis/Makefile.include +++ b/cpu/kinetis/Makefile.include @@ -36,10 +36,14 @@ export UNDEF += $(BINDIR)/cpu/fcfield.o # include common periph drivers USEMODULE += periph_common -# select kinetis periph drivers +# select Kinetis periph drivers +USEMODULE += periph_llwu USEMODULE += periph_mcg USEMODULE += periph_wdog +# Enable power management by default +USEMODULE += pm_layered + # Define a recipe to build the watchdog disable binary, used when flashing $(RIOTCPU)/$(CPU)/dist/wdog-disable.bin: $(RIOTCPU)/$(CPU)/dist/wdog-disable.s $(Q)$(MAKE) -C $(RIOTCPU)/$(CPU)/dist/ $(notdir $@) diff --git a/cpu/kinetis/cpu.c b/cpu/kinetis/cpu.c index f5785dd8b3e8..3e8152d0f682 100644 --- a/cpu/kinetis/cpu.c +++ b/cpu/kinetis/cpu.c @@ -22,6 +22,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 @@ -39,6 +44,16 @@ void cpu_init(void) /* initialize the CPU clocking provided by the MCG module */ kinetis_mcg_init(); #endif + +#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 6e5dc9cb6fb5..c0ce867a8155 100644 --- a/cpu/kinetis/include/cpu_conf_kinetis.h +++ b/cpu/kinetis/include/cpu_conf_kinetis.h @@ -61,6 +61,93 @@ 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 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; +#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; +#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/include/periph_cpu.h b/cpu/kinetis/include/periph_cpu.h index 8acb05a60bdd..26f437212548 100644 --- a/cpu/kinetis/include/periph_cpu.h +++ b/cpu/kinetis/include/periph_cpu.h @@ -110,9 +110,18 @@ typedef uint16_t gpio_t; #define PERIPH_TIMER_PROVIDES_SET /** - * @brief number of usable power modes + * @name Kinetis power mode configuration + * @{ */ -#define PM_NUM_MODES (1U) +#define PM_NUM_MODES (4U) +enum { + KINETIS_PM_LLS = 0, + KINETIS_PM_VLPS = 1, + KINETIS_PM_STOP = 2, + KINETIS_PM_WAIT = 3, +}; +#define PM_BLOCKER_INITIAL { .val_u32 = 0 } +/** @} */ #ifdef RTC /* All Kinetis CPUs have exactly one RTC hardware module, except for the KL02 @@ -337,6 +346,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 */ @@ -447,9 +458,21 @@ typedef struct { volatile uint32_t *scgc_addr; /**< Clock enable register, in SIM module */ uint8_t scgc_bit; /**< Clock enable bit, within the register */ uart_mode_t mode; /**< UART mode: data bits, parity, stop bits */ - uart_type_t type; /**< Hardware module type (KINETIS_UART or KINETIS_LPUART)*/ + uart_type_t type; /**< Hardware module type (KINETIS_UART or KINETIS_LPUART) */ + /** + * @brief LLWU wakeup source RX pin to allow RX while in LLS mode + * + * Set to @c LLWU_WAKEUP_PIN_UNDEF if the chosen RX pin is not available to + * the LLWU. + */ + llwu_wakeup_pin_t llwu_rx; } 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/llwu.c b/cpu/kinetis/periph/llwu.c new file mode 100644 index 000000000000..b16376c3a4c4 --- /dev/null +++ b/cpu/kinetis/periph/llwu.c @@ -0,0 +1,101 @@ +/* + * 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" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +struct { + gpio_cb_t cb; + void *arg; +} 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 is always on */ + bit_set32(&SIM->SCGC4, SIM_SCGC4_LLWU_SHIFT); /* Enable LLWU clock gate */ +#endif + + /* Enable LLWU interrupt, or else we can never resume from LLS */ + NVIC_EnableIRQ(LLWU_IRQn); + /* LLWU needs to have the lowest possible priority, or it will block the + * other modules from performing their IRQ handling */ + NVIC_SetPriority(LLWU_IRQn, 0xff); +} + +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); + config[pin].cb = cb; + config[pin].arg = arg; + + /* The fields are two bits per pin, and the setting registers are 8 bits wide */ + if(pin < 4) { + unsigned field_offset = pin * 2; + LLWU->PE1 = (LLWU->PE1 & (~(0b11 << field_offset))) | (edge << field_offset); + } + else if(pin < 8) { + unsigned field_offset = (pin - 4) * 2; + LLWU->PE2 = (LLWU->PE2 & (~(0b11 << field_offset))) | (edge << field_offset); + } + else if(pin < 12) { + unsigned field_offset = (pin - 8) * 2; + LLWU->PE3 = (LLWU->PE3 & (~(0b11 << field_offset))) | (edge << field_offset); + } + else { + unsigned field_offset = (pin - 12) * 2; + LLWU->PE4 = (LLWU->PE4 & (~(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 flags = *(&LLWU->F1 + reg); + if (flags == 0) { + continue; + } + /* Clear pin interrupt flags */ + *(&LLWU->F1 + reg) = flags; + DEBUG("llwu: F%u = %02x\n", reg + 1, (unsigned) flags); + for (unsigned i = 0; i < 8; ++i) { + if (flags & 1) { + DEBUG("llwu: pin %u\n", reg * 8 + i); + if (config[i].cb != NULL) { + config[i].cb(config[i].arg); + } + } + flags >>= 1; + } + } + DEBUG("llwu: F3 = %02x\n", LLWU->F3); + /* Read only register F3, the flag will need to be cleared in the peripheral + * instead of writing a 1 to the MWUFx bit. */ + + cortexm_isr_end(); +} diff --git a/cpu/kinetis/periph/pm.c b/cpu/kinetis/periph/pm.c index 4a9961ba7ab0..1aac45fe927a 100644 --- a/cpu/kinetis/periph/pm.c +++ b/cpu/kinetis/periph/pm.c @@ -26,17 +26,60 @@ #define ENABLE_DEBUG (0) #include "debug.h" -/** - * @note The current PM implementation is very much simplified down to only - * using the 'WAIT' mode. This implementation must be further expanded - * to make use of the available and more efficient (deep) sleep modes - * of the Kinetis CPUs. - */ +/* Set to 1 to use the LEDx macros to show which sleep mode is entered */ +#define ENABLE_LEDS (0) + +#if ENABLE_LEDS +#include "board.h" +#define PM_LED(x, state) LED ## x ## _ ## state +#else +#define PM_LED(x, state) +#endif + +/* SMC_PMCTRL_STOPM masks */ +enum { + SMC_PMCTRL_STOPM_STOP = 0, + /* 1 is reserved */ + SMC_PMCTRL_STOPM_VLPS = 2, + SMC_PMCTRL_STOPM_LLS = 3, + /* VLLS is not supported */ +}; + +/** Configure which stop mode will be entered when cortexm_sleep(1) is called */ +static inline void pm_stopm(uint8_t stopm) +{ + SMC->PMCTRL = (SMC->PMCTRL & ~(SMC_PMCTRL_STOPM_MASK)) | SMC_PMCTRL_STOPM(stopm); +} + void pm_set(unsigned mode) { + unsigned deep = 1; switch (mode) { - case 0: - cortexm_sleep(0); + case KINETIS_PM_WAIT: + /* WAIT */ + deep = 0; + PM_LED(0, ON); + break; + case KINETIS_PM_STOP: + /* STOP */ + pm_stopm(SMC_PMCTRL_STOPM_STOP); + PM_LED(1, ON); + break; + case KINETIS_PM_VLPS: + /* VLPS */ + pm_stopm(SMC_PMCTRL_STOPM_VLPS); + PM_LED(2, ON); + break; + case KINETIS_PM_LLS: + /* LLSx */ + pm_stopm(SMC_PMCTRL_STOPM_LLS); + PM_LED(0, ON); + PM_LED(2, ON); break; } + DEBUG("pm_set(%u)\n", mode); + cortexm_sleep(deep); + PM_LED(0, OFF); + PM_LED(1, OFF); + PM_LED(2, OFF); } diff --git a/cpu/kinetis/periph/timer.c b/cpu/kinetis/periph/timer.c index 021c6bee3ea6..cb74c8690eaa 100644 --- a/cpu/kinetis/periph/timer.c +++ b/cpu/kinetis/periph/timer.c @@ -30,7 +30,20 @@ #include "bit.h" #include "board.h" #include "periph_conf.h" +#if MODULE_PERIPH_LLWU +#include "llwu.h" +#endif #include "periph/timer.h" +#if MODULE_PM_LAYERED +#include "pm_layered.h" +#define PM_BLOCK(x) pm_block(x) +#define PM_UNBLOCK(x) pm_unblock(x) +#else +#define PM_BLOCK(x) +#define PM_UNBLOCK(x) +#endif + + #ifdef PIT_LTMR64H_LTH_MASK /* The KW41Z PIT module provides only one IRQ for all PIT channels combined. */ @@ -176,6 +189,8 @@ static inline int pit_init(uint8_t dev, uint32_t freq, timer_cb_t cb, void *arg) /* Start the prescaler counter */ PIT->CHANNEL[pit_config[dev].prescaler_ch].TCTRL = (PIT_TCTRL_TEN_MASK); PIT->CHANNEL[count_ch].TCTRL = PIT_TCTRL_CHN_MASK | PIT_TCTRL_TEN_MASK; + /* PIT is halted in STOP mode, we need to block it */ + PM_BLOCK(KINETIS_PM_STOP); irq_restore(mask); return 0; @@ -253,12 +268,22 @@ static inline uint32_t pit_read(uint8_t dev) 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; + } + PM_BLOCK(KINETIS_PM_STOP); PIT->CHANNEL[ch].TCTRL = PIT_TCTRL_TEN_MASK; } 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; + } + PM_UNBLOCK(KINETIS_PM_STOP); PIT->CHANNEL[ch].TCTRL = 0; } @@ -355,6 +380,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); diff --git a/cpu/kinetis/periph/uart.c b/cpu/kinetis/periph/uart.c index f1c5edf378cd..a5eef6569900 100644 --- a/cpu/kinetis/periph/uart.c +++ b/cpu/kinetis/periph/uart.c @@ -28,6 +28,17 @@ #include "bit.h" #include "periph_conf.h" #include "periph/uart.h" +#if MODULE_PERIPH_LLWU +#include "llwu.h" +#endif +#if MODULE_PM_LAYERED +#include "pm_layered.h" +#define PM_BLOCK(x) pm_block(x) +#define PM_UNBLOCK(x) pm_unblock(x) +#else +#define PM_BLOCK(x) +#define PM_UNBLOCK(x) +#endif #define ENABLE_DEBUG (0) #include "debug.h" @@ -77,6 +88,19 @@ #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 + +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 */ +} uart_isr_ctx_t; + /** * @brief Runtime configuration space, holds pointers to callback functions for RX */ @@ -110,9 +134,14 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) { assert(uart < UART_NUMOF); + if (config[uart].rx_cb) { + PM_UNBLOCK(KINETIS_PM_LLS); + } + /* remember callback addresses */ config[uart].rx_cb = rx_cb; config[uart].arg = arg; + config[uart].active = 0; uart_init_pins(uart); @@ -133,6 +162,22 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) default: return UART_NODEV; } + + if (config[uart].rx_cb) { +#if MODULE_PERIPH_LLWU + if (uart_config[uart].llwu_rx != LLWU_WAKEUP_PIN_UNDEF) { + /* Configure the RX pin for LLWU wakeup to be able to use RX in LLS mode */ + llwu_wakeup_pin_set(uart_config[uart].llwu_rx, LLWU_WAKEUP_EDGE_FALLING, NULL, NULL); + } + else +#endif + { + /* UART and LPUART receivers are stopped in LLS, prevent LLS when there + * is a configured RX callback and no LLWU wakeup pin configured */ + DEBUG("uart: Blocking LLS\n"); + PM_BLOCK(KINETIS_PM_LLS); + } + } return UART_OK; } @@ -189,7 +234,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; @@ -197,8 +242,8 @@ static inline void uart_init_uart(uart_t uart, uint32_t baudrate) /* calculate baudrate */ ubd = (uint16_t)(clk / (baudrate * 16)); - /* set baudrate */ - dev->BDH = (uint8_t)UART_BDH_SBR(ubd >> 8); + /* set baudrate, enable RX active edge detection */ + dev->BDH = (uint8_t)UART_BDH_SBR(ubd >> 8) | UART_BDH_RXEDGIE_MASK; dev->BDL = (uint8_t)UART_BDL_SBR(ubd); #if KINETIS_UART_ADVANCED @@ -228,15 +273,15 @@ 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 transmitter and receiver + RX interrupt + IDLE interrupt */ + dev->C2 = UART_C2_TE_MASK | UART_C2_RE_MASK | 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; /* enable receive interrupt */ NVIC_EnableIRQ(uart_config[uart].irqn); } -#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 +290,8 @@ 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) {} } #if defined(UART_0_ISR) || defined(UART_1_ISR) || defined(UART_2_ISR) || \ @@ -253,31 +300,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 CPU on until we are finished with RX */ + DEBUG("UART ACTIVE\n"); + PM_BLOCK(KINETIS_PM_STOP); + } + } + 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; + /* Let the CPU sleep when idle */ + PM_UNBLOCK(KINETIS_PM_STOP); + 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,11 +438,13 @@ static inline void uart_init_lpuart(uart_t uart, uint32_t baudrate) } uint32_t sbr = clk / (best_osr * baudrate); - /* set baud rate */ - dev->BAUD = LPUART_BAUD_OSR(best_osr - 1) | LPUART_BAUD_SBR(sbr); + /* set baud rate, enable RX active edge interrupt */ + dev->BAUD = LPUART_BAUD_OSR(best_osr - 1) | LPUART_BAUD_SBR(sbr) | LPUART_BAUD_RXEDGIE_MASK; /* enable transmitter and receiver + RX interrupt */ - dev->CTRL |= LPUART_CTRL_TE_MASK | LPUART_CTRL_RE_MASK | LPUART_CTRL_RIE_MASK; + dev->CTRL |= LPUART_CTRL_TE_MASK | LPUART_CTRL_RE_MASK | + LPUART_CTRL_RIE_MASK | LPUART_CTRL_IDLECFG(LPUART_IDLECFG) | + LPUART_CTRL_ILIE_MASK; /* enable receive interrupt */ NVIC_EnableIRQ(uart_config[uart].irqn); @@ -376,6 +458,8 @@ 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) {} } #if defined(LPUART_0_ISR) || defined(LPUART_1_ISR) || defined(LPUART_2_ISR) || \ @@ -387,6 +471,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 CPU on until we are finished with RX */ + DEBUG("LPUART ACTIVE\n"); + PM_BLOCK(KINETIS_PM_LLS); + } + } if (stat & LPUART_STAT_RDRF_MASK) { /* RDRF flag will be cleared when LPUART_DATA is read */ uint8_t data = dev->DATA; @@ -413,6 +505,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; + /* Let the CPU sleep when idle */ + PM_UNBLOCK(KINETIS_PM_LLS); + DEBUG("LPUART IDLE\n"); + } + } cortexm_isr_end(); } From 1a6d5261986af20b177c3ef285a8e1e8fd2ec307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Fri, 2 Nov 2018 10:07:20 +0100 Subject: [PATCH 03/34] fixup! kinetis: Add low power support --- cpu/kinetis/periph/llwu.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cpu/kinetis/periph/llwu.c b/cpu/kinetis/periph/llwu.c index b16376c3a4c4..af72d85b14a9 100644 --- a/cpu/kinetis/periph/llwu.c +++ b/cpu/kinetis/periph/llwu.c @@ -83,14 +83,16 @@ void isr_llwu(void) /* Clear pin interrupt flags */ *(&LLWU->F1 + reg) = flags; DEBUG("llwu: F%u = %02x\n", reg + 1, (unsigned) flags); - for (unsigned i = 0; i < 8; ++i) { + unsigned pin = reg * 8; + while (flags) { if (flags & 1) { - DEBUG("llwu: pin %u\n", reg * 8 + i); - if (config[i].cb != NULL) { - config[i].cb(config[i].arg); + DEBUG("llwu: wakeup pin %u\n", pin); + if (config[pin].cb != NULL) { + config[pin].cb(config[pin].arg); } } flags >>= 1; + ++pin; } } DEBUG("llwu: F3 = %02x\n", LLWU->F3); From 8b241f9935cf3fcba28b25cdd6be84ae63a2f61f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Fri, 2 Nov 2018 10:09:05 +0100 Subject: [PATCH 04/34] fixup! kinetis: Add low power support --- 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 4bbf3240d0c9..0a7d7a8c816a 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 fe9b294c5ac8dc97d27507b6e22da794fa3357bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Fri, 2 Nov 2018 11:05:49 +0100 Subject: [PATCH 05/34] fixup! fixup! kinetis: Add low power support --- cpu/kinetis/include/cpu_conf_kinetis.h | 56 +++++++++++++++++++++++++- cpu/kinetis/periph/llwu.c | 30 ++++++++------ cpu/kinetis/periph/pm.c | 2 + 3 files changed, 74 insertions(+), 14 deletions(-) diff --git a/cpu/kinetis/include/cpu_conf_kinetis.h b/cpu/kinetis/include/cpu_conf_kinetis.h index c0ce867a8155..68a4d1ece48f 100644 --- a/cpu/kinetis/include/cpu_conf_kinetis.h +++ b/cpu/kinetis/include/cpu_conf_kinetis.h @@ -85,7 +85,15 @@ typedef enum llwu_wakeup_module { } llwu_wakeup_module_t; /** - * @brief Mapping physical pins to LLWU wakeup pin source numbers + * @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. @@ -115,6 +123,28 @@ typedef enum llwu_wakeup_pin { 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, @@ -136,10 +166,32 @@ typedef enum llwu_wakeup_pin { 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 + * @brief LLWU wakeup pin edge settings */ typedef enum llwu_wakeup_edge { LLWU_WAKEUP_EDGE_NONE = 0, diff --git a/cpu/kinetis/periph/llwu.c b/cpu/kinetis/periph/llwu.c index af72d85b14a9..8528f8c302f8 100644 --- a/cpu/kinetis/periph/llwu.c +++ b/cpu/kinetis/periph/llwu.c @@ -28,29 +28,28 @@ struct { gpio_cb_t cb; void *arg; -} config[LLWU_WAKEUP_PIN_NUMOF]; +} 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 is always on */ + * for all others the LLWU clock is always on */ bit_set32(&SIM->SCGC4, SIM_SCGC4_LLWU_SHIFT); /* Enable LLWU clock gate */ #endif - /* Enable LLWU interrupt, or else we can never resume from LLS */ - NVIC_EnableIRQ(LLWU_IRQn); - /* LLWU needs to have the lowest possible priority, or it will block the - * other modules from performing their IRQ handling */ - NVIC_SetPriority(LLWU_IRQn, 0xff); + /* 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); - config[pin].cb = cb; - config[pin].arg = arg; + 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 */ if(pin < 4) { @@ -87,17 +86,24 @@ void isr_llwu(void) while (flags) { if (flags & 1) { DEBUG("llwu: wakeup pin %u\n", pin); - if (config[pin].cb != NULL) { - config[pin].cb(config[pin].arg); + 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 */ + 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; } flags >>= 1; ++pin; } } - DEBUG("llwu: F3 = %02x\n", LLWU->F3); /* 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(); } diff --git a/cpu/kinetis/periph/pm.c b/cpu/kinetis/periph/pm.c index 1aac45fe927a..ac0ffa0fc30f 100644 --- a/cpu/kinetis/periph/pm.c +++ b/cpu/kinetis/periph/pm.c @@ -75,6 +75,8 @@ void pm_set(unsigned mode) pm_stopm(SMC_PMCTRL_STOPM_LLS); PM_LED(0, ON); PM_LED(2, ON); + /* Enable LLWU interrupt, or else we can never resume from LLS */ + NVIC_EnableIRQ(LLWU_IRQn); break; } DEBUG("pm_set(%u)\n", mode); From f4d2a53f36d48535208432feb8926cdb39a58efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Tue, 11 Sep 2018 15:37:49 +0200 Subject: [PATCH 06/34] squash kinetis: PIT only block low power when a target is set --- cpu/kinetis/periph/timer.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/cpu/kinetis/periph/timer.c b/cpu/kinetis/periph/timer.c index cb74c8690eaa..7d1de57e32e9 100644 --- a/cpu/kinetis/periph/timer.c +++ b/cpu/kinetis/periph/timer.c @@ -189,8 +189,6 @@ static inline int pit_init(uint8_t dev, uint32_t freq, timer_cb_t cb, void *arg) /* Start the prescaler counter */ PIT->CHANNEL[pit_config[dev].prescaler_ch].TCTRL = (PIT_TCTRL_TEN_MASK); PIT->CHANNEL[count_ch].TCTRL = PIT_TCTRL_CHN_MASK | PIT_TCTRL_TEN_MASK; - /* PIT is halted in STOP mode, we need to block it */ - PM_BLOCK(KINETIS_PM_STOP); irq_restore(mask); return 0; @@ -201,6 +199,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 */ @@ -222,6 +225,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; /* Set new timeout */ @@ -243,6 +251,10 @@ static inline int pit_clear(uint8_t dev) 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) { + /* 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 */ @@ -272,7 +284,6 @@ static inline void pit_start(uint8_t dev) /* Already running */ return; } - PM_BLOCK(KINETIS_PM_STOP); PIT->CHANNEL[ch].TCTRL = PIT_TCTRL_TEN_MASK; } @@ -283,7 +294,6 @@ static inline void pit_stop(uint8_t dev) /* Already stopped */ return; } - PM_UNBLOCK(KINETIS_PM_STOP); PIT->CHANNEL[ch].TCTRL = 0; } @@ -303,6 +313,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 17f71b90c2bc3836aa324818f3db024c57654927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Sun, 18 Mar 2018 09:43:31 +0100 Subject: [PATCH 07/34] cpu/kinetis: Implement uart_poweron/uart_poweroff --- cpu/kinetis/periph/uart.c | 173 +++++++++++++++++++++++++++++++------- 1 file changed, 141 insertions(+), 32 deletions(-) diff --git a/cpu/kinetis/periph/uart.c b/cpu/kinetis/periph/uart.c index a5eef6569900..b662c62e6754 100644 --- a/cpu/kinetis/periph/uart.c +++ b/cpu/kinetis/periph/uart.c @@ -98,7 +98,8 @@ 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 */ + 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; /** @@ -110,9 +111,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 @@ -142,6 +147,7 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) config[uart].rx_cb = rx_cb; config[uart].arg = arg; config[uart].active = 0; + config[uart].enabled = 0; uart_init_pins(uart); @@ -163,34 +169,86 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) return UART_NODEV; } - if (config[uart].rx_cb) { -#if MODULE_PERIPH_LLWU - if (uart_config[uart].llwu_rx != LLWU_WAKEUP_PIN_UNDEF) { - /* Configure the RX pin for LLWU wakeup to be able to use RX in LLS mode */ - llwu_wakeup_pin_set(uart_config[uart].llwu_rx, LLWU_WAKEUP_EDGE_FALLING, NULL, NULL); - } - else -#endif - { - /* UART and LPUART receivers are stopped in LLS, prevent LLS when there - * is a configured RX callback and no LLWU wakeup pin configured */ - DEBUG("uart: Blocking LLS\n"); - PM_BLOCK(KINETIS_PM_LLS); - } - } + uart_poweron(uart); + return UART_OK; } void uart_poweron(uart_t uart) { - (void)uart; - /* not implemented (yet) */ + assert(uart < UART_NUMOF); + if (config[uart].rx_cb) { + unsigned state = irq_disable(); + if (!config[uart].enabled) { + config[uart].enabled = 1; +#if MODULE_PERIPH_LLWU + if (uart_config[uart].llwu_rx != LLWU_WAKEUP_PIN_UNDEF) { + /* Configure the RX pin for LLWU wakeup to be able to use RX in LLS mode */ + llwu_wakeup_pin_set(uart_config[uart].llwu_rx, LLWU_WAKEUP_EDGE_FALLING, NULL, NULL); + } + else +#endif + { + /* UART and LPUART receivers are stopped in LLS, prevent LLS when there + * is a configured RX callback and no LLWU wakeup pin configured */ + DEBUG("uart: Blocking LLS\n"); + PM_BLOCK(KINETIS_PM_LLS); + } + } + irq_restore(state); + } + 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: + return; + } } void uart_poweroff(uart_t uart) { - (void)uart; - /* not implemented (yet) */ + assert(uart < UART_NUMOF); + if (config[uart].rx_cb) { + unsigned state = irq_disable(); + if (config[uart].enabled) { + config[uart].enabled = 0; +#if MODULE_PERIPH_LLWU + if (uart_config[uart].llwu_rx != LLWU_WAKEUP_PIN_UNDEF) { + /* Disable LLWU wakeup for the RX pin */ + llwu_wakeup_pin_set(uart_config[uart].llwu_rx, LLWU_WAKEUP_EDGE_NONE, NULL, NULL); + } + else +#endif + { + /* re-enable LLS since we are not listening anymore */ + DEBUG("uart: unblocking LLS\n"); + PM_UNBLOCK(KINETIS_PM_LLS); + } + } + irq_restore(state); + } + 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: + return; + } } #if KINETIS_HAVE_UART && KINETIS_HAVE_LPUART @@ -258,8 +316,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 { @@ -273,11 +331,12 @@ 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 + IDLE interrupt */ - dev->C2 = UART_C2_TE_MASK | UART_C2_RE_MASK | 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; - + /* enable receiver + RX interrupt + IDLE interrupt */ + if (config[uart].rx_cb) { + dev->C2 = UART_C2_TE_MASK | UART_C2_RE_MASK | 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; + } /* enable receive interrupt */ NVIC_EnableIRQ(uart_config[uart].irqn); } @@ -294,6 +353,28 @@ KINETIS_UART_WRITE_INLINE void uart_write_uart(uart_t uart, const uint8_t *data, 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) || \ defined(UART_3_ISR) || defined(UART_4_ISR) static inline void irq_handler_uart(uart_t uart) @@ -441,12 +522,18 @@ static inline void uart_init_lpuart(uart_t uart, uint32_t baudrate) /* set baud rate, enable RX active edge interrupt */ dev->BAUD = LPUART_BAUD_OSR(best_osr - 1) | LPUART_BAUD_SBR(sbr) | LPUART_BAUD_RXEDGIE_MASK; - /* enable transmitter and receiver + RX interrupt */ - dev->CTRL |= LPUART_CTRL_TE_MASK | LPUART_CTRL_RE_MASK | - LPUART_CTRL_RIE_MASK | LPUART_CTRL_IDLECFG(LPUART_IDLECFG) | - LPUART_CTRL_ILIE_MASK; + /* Enable transmitter */ + dev->CTRL |= LPUART_CTRL_TE_MASK; - /* enable receive interrupt */ + if (config[uart].rx_cb) { + /* enable receiver + RX interrupt + error interrupts */ + dev->CTRL |= LPUART_CTRL_RE_MASK | 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; + } + + /* enable interrupts from LPUART module */ NVIC_EnableIRQ(uart_config[uart].irqn); } @@ -462,6 +549,28 @@ KINETIS_UART_WRITE_INLINE void uart_write_lpuart(uart_t uart, const uint8_t *dat 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) || \ defined(LPUART_3_ISR) || defined(LPUART_4_ISR) static inline void irq_handler_lpuart(uart_t uart) From 4041c4d0423029eaebec58d600e064036520d555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Sat, 17 Mar 2018 10:08:41 +0100 Subject: [PATCH 08/34] boards/mulle: Add UART LLWU config --- boards/mulle/include/periph_conf.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/boards/mulle/include/periph_conf.h b/boards/mulle/include/periph_conf.h index 491cf2ca89db..657d17a7ffe4 100644 --- a/boards/mulle/include/periph_conf.h +++ b/boards/mulle/include/periph_conf.h @@ -137,6 +137,7 @@ static const uart_conf_t uart_config[] = { .scgc_bit = SIM_SCGC4_UART0_SHIFT, .mode = UART_MODE_8N1, .type = KINETIS_UART, + .llwu_rx = LLWU_WAKEUP_PIN_UNDEF, }, { .dev = UART1, @@ -150,6 +151,7 @@ static const uart_conf_t uart_config[] = { .scgc_bit = SIM_SCGC4_UART1_SHIFT, .mode = UART_MODE_8N1, .type = KINETIS_UART, + .llwu_rx = LLWU_WAKEUP_PIN_PTC3, }, }; From c03644ad2c6cada6a3ac3193c6b47e8e1ebc8409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Sun, 18 Mar 2018 09:45:00 +0100 Subject: [PATCH 09/34] boards/frdm-kw41z: Add UART LLWU config --- boards/frdm-kw41z/include/periph_conf.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/boards/frdm-kw41z/include/periph_conf.h b/boards/frdm-kw41z/include/periph_conf.h index 435fac503f05..e3015a744c83 100644 --- a/boards/frdm-kw41z/include/periph_conf.h +++ b/boards/frdm-kw41z/include/periph_conf.h @@ -114,6 +114,9 @@ static const uart_conf_t uart_config[] = { .scgc_bit = SIM_SCGC5_LPUART0_SHIFT, .mode = UART_MODE_8N1, .type = KINETIS_LPUART, + /* Using LLWU requires using lower baud rates */ + /* LLWU_WAKEUP_PIN_PTC6 is the correct setting on this dev board if using LLWU */ + .llwu_rx = LLWU_WAKEUP_PIN_UNDEF, }, }; #define UART_NUMOF (sizeof(uart_config) / sizeof(uart_config[0])) From b41c6d939a1f8b3597f92e5bd70e1756f0c6c2d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Sun, 18 Mar 2018 09:46:40 +0100 Subject: [PATCH 10/34] boards/frdm-k64f: Add UART LLWU config --- boards/frdm-k64f/include/periph_conf.h | 1 + 1 file changed, 1 insertion(+) diff --git a/boards/frdm-k64f/include/periph_conf.h b/boards/frdm-k64f/include/periph_conf.h index 54770d93bf33..93ec7f545a16 100644 --- a/boards/frdm-k64f/include/periph_conf.h +++ b/boards/frdm-k64f/include/periph_conf.h @@ -114,6 +114,7 @@ static const uart_conf_t uart_config[] = { .scgc_bit = SIM_SCGC4_UART0_SHIFT, .mode = UART_MODE_8N1, .type = KINETIS_UART, + .llwu_rx = LLWU_WAKEUP_PIN_UNDEF, }, }; From bc92d8b8db750924427ba6c5d90a71605a8a36a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Sat, 30 Jun 2018 19:04:53 +0200 Subject: [PATCH 11/34] boards/teensy31: Add UART LLWU config --- boards/teensy31/include/periph_conf.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/boards/teensy31/include/periph_conf.h b/boards/teensy31/include/periph_conf.h index c9024e04dd80..2cd22c52b8e1 100644 --- a/boards/teensy31/include/periph_conf.h +++ b/boards/teensy31/include/periph_conf.h @@ -113,6 +113,7 @@ static const uart_conf_t uart_config[] = { .scgc_bit = SIM_SCGC4_UART0_SHIFT, .mode = UART_MODE_8N1, .type = KINETIS_UART, + .llwu_rx = LLWU_WAKEUP_PIN_UNDEF, }, { .dev = UART1, @@ -126,6 +127,7 @@ static const uart_conf_t uart_config[] = { .scgc_bit = SIM_SCGC4_UART1_SHIFT, .mode = UART_MODE_8N1, .type = KINETIS_UART, + .llwu_rx = LLWU_WAKEUP_PIN_UNDEF, }, }; From da8a4d9b2993a990d23a07558ca8c1744f57b99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Sat, 30 Jun 2018 19:05:10 +0200 Subject: [PATCH 12/34] boards/pba-d-01-kw2x: Add UART LLWU config --- boards/pba-d-01-kw2x/include/periph_conf.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/boards/pba-d-01-kw2x/include/periph_conf.h b/boards/pba-d-01-kw2x/include/periph_conf.h index c8afe2c38c3e..5d467e46c323 100644 --- a/boards/pba-d-01-kw2x/include/periph_conf.h +++ b/boards/pba-d-01-kw2x/include/periph_conf.h @@ -108,6 +108,7 @@ static const uart_conf_t uart_config[] = { .scgc_bit = SIM_SCGC4_UART2_SHIFT, .mode = UART_MODE_8N1, .type = KINETIS_UART, + .llwu_rx = LLWU_WAKEUP_PIN_UNDEF, }, { .dev = UART0, @@ -121,6 +122,7 @@ static const uart_conf_t uart_config[] = { .scgc_bit = SIM_SCGC4_UART0_SHIFT, .mode = UART_MODE_8N1, .type = KINETIS_UART, + .llwu_rx = LLWU_WAKEUP_PIN_UNDEF, } }; From 4eb0db588d4be2e255892c6f3d5fda4cffd133ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Sat, 30 Jun 2018 19:06:38 +0200 Subject: [PATCH 13/34] boards/frdm-k22f: Add UART LLWU config --- boards/frdm-k22f/include/periph_conf.h | 1 + 1 file changed, 1 insertion(+) diff --git a/boards/frdm-k22f/include/periph_conf.h b/boards/frdm-k22f/include/periph_conf.h index 3d769c85f929..a2ee0669b18e 100644 --- a/boards/frdm-k22f/include/periph_conf.h +++ b/boards/frdm-k22f/include/periph_conf.h @@ -113,6 +113,7 @@ static const uart_conf_t uart_config[] = { .scgc_bit = SIM_SCGC4_UART1_SHIFT, .mode = UART_MODE_8N1, .type = KINETIS_UART, + .llwu_rx = LLWU_WAKEUP_PIN_PTE1, }, }; From 177298240651e03b4b6233d2c053fbc77a071de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Sat, 30 Jun 2018 19:41:37 +0200 Subject: [PATCH 14/34] boards/mulle: Update LPTMR config with LLWU --- boards/mulle/include/periph_conf.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/boards/mulle/include/periph_conf.h b/boards/mulle/include/periph_conf.h index 657d17a7ffe4..2f776bdc5d23 100644 --- a/boards/mulle/include/periph_conf.h +++ b/boards/mulle/include/periph_conf.h @@ -106,9 +106,10 @@ static const clock_config_t clock_config = { #define LPTMR_CONFIG { \ { \ .dev = LPTMR0, \ - .irqn = LPTMR0_IRQn, \ - .src = 2, \ .base_freq = 32768u, \ + .llwu = LLWU_WAKEUP_MODULE_LPTMR0, \ + .src = 2, \ + .irqn = LPTMR0_IRQn, \ } \ } #define TIMER_NUMOF ((PIT_NUMOF) + (LPTMR_NUMOF)) From 70a3ef8c60da5b8c7485c4751c87d41c35cdab6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Sat, 30 Jun 2018 19:41:46 +0200 Subject: [PATCH 15/34] boards/frdm-k22f: Update LPTMR config with LLWU --- boards/frdm-k22f/include/periph_conf.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/boards/frdm-k22f/include/periph_conf.h b/boards/frdm-k22f/include/periph_conf.h index a2ee0669b18e..452980045875 100644 --- a/boards/frdm-k22f/include/periph_conf.h +++ b/boards/frdm-k22f/include/periph_conf.h @@ -80,14 +80,15 @@ static const clock_config_t clock_config = { }, \ } #define LPTMR_NUMOF (1U) -#define LPTMR_CONFIG { \ - { \ - .dev = LPTMR0, \ - .irqn = LPTMR0_IRQn, \ - .src = 2, \ - .base_freq = 32768u, \ - }, \ -} +#define LPTMR_CONFIG { \ + { \ + .dev = LPTMR0, \ + .base_freq = 32768u, \ + .llwu = LLWU_WAKEUP_MODULE_LPTMR0, \ + .src = 2, \ + .irqn = LPTMR0_IRQn, \ + } \ + } #define TIMER_NUMOF ((PIT_NUMOF) + (LPTMR_NUMOF)) #define PIT_BASECLOCK (CLOCK_BUSCLOCK) From 196de13040204dde2fc82706410f90bc18af85a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Sat, 30 Jun 2018 19:41:54 +0200 Subject: [PATCH 16/34] boards/frdm-kw41z: Update LPTMR config with LLWU --- boards/frdm-kw41z/include/periph_conf.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/boards/frdm-kw41z/include/periph_conf.h b/boards/frdm-kw41z/include/periph_conf.h index e3015a744c83..a3252de84034 100644 --- a/boards/frdm-kw41z/include/periph_conf.h +++ b/boards/frdm-kw41z/include/periph_conf.h @@ -87,9 +87,10 @@ static const clock_config_t clock_config = { #define LPTMR_CONFIG { \ { \ .dev = LPTMR0, \ - .irqn = LPTMR0_IRQn, \ - .src = 2, \ .base_freq = 32768u, \ + .llwu = LLWU_WAKEUP_MODULE_LPTMR0, \ + .src = 2, \ + .irqn = LPTMR0_IRQn, \ } \ } #define TIMER_NUMOF ((PIT_NUMOF) + (LPTMR_NUMOF)) From 9e92ff892e6a2d94e97d9fdcadf7d480da27422a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Mon, 11 Jun 2018 07:08:25 +0200 Subject: [PATCH 17/34] frdm-kw41z: Initialize DCDC module during board_init --- boards/frdm-kw41z/board.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/boards/frdm-kw41z/board.c b/boards/frdm-kw41z/board.c index 9251c626a17e..68a81a9bcb78 100644 --- a/boards/frdm-kw41z/board.c +++ b/boards/frdm-kw41z/board.c @@ -23,6 +23,42 @@ void board_init(void) { + bit_set32(&SIM->SCGC5, SIM_SCGC5_DCDC_SHIFT); + + /* We don't have a status bit for checking if the DCDC is in bypass mode + * so we check if the PSWITCH bit is set, assuming that the power switch + * has not been released since the DCDC was started. This is run early in + * the boot process so it should be fine. */ + if (DCDC->REG0 & DCDC_REG0_PSWITCH_STATUS_MASK) { + /* DCDC is on */ + /* Spin until DCDC output is stable */ + while (!(DCDC->REG0 & DCDC_REG0_DCDC_STS_DC_OK_MASK)) {} + /* Set up DCDC for pulsed mode in VLPS, LLS */ + DCDC->REG0 = (DCDC->REG0 & ~(DCDC_REG0_DCDC_VBAT_DIV_CTRL_MASK | DCDC_REG0_VLPS_CONFIG_DCDC_HP_MASK)) | + DCDC_REG0_DCDC_VBAT_DIV_CTRL(0b10) | /* provide VBAT/2 to ADC input to measure battery level */ + DCDC_REG0_DCDC_LP_DF_CMP_ENABLE_MASK; + DCDC->REG1 = DCDC->REG1 | + DCDC_REG1_DCDC_LOOPCTRL_EN_CM_HYST_MASK | + DCDC_REG1_DCDC_LOOPCTRL_EN_DF_HYST_MASK; + DCDC->REG2 = DCDC->REG2 | + DCDC_REG2_DCDC_LOOPCTRL_HYST_SIGN_MASK; + DCDC->REG3 = (DCDC->REG3 & + ~(DCDC_REG3_DCDC_MINPWR_HALF_FETS_MASK | + DCDC_REG3_DCDC_MINPWR_DOUBLE_FETS_MASK | + DCDC_REG3_DCDC_MINPWR_DOUBLE_FETS_PULSED_MASK)) | + DCDC_REG3_DCDC_MINPWR_HALF_FETS_PULSED_MASK; + + /* Spin until DCDC is stable */ + while (!(DCDC->REG0 & DCDC_REG0_DCDC_STS_DC_OK_MASK)) {} + + /* DCDC has stabilized, halt stepping + * This must be set before entering low power modes */ + DCDC->REG3 |= DCDC_REG3_DCDC_VDD1P5CTRL_DISABLE_STEP_MASK | + DCDC_REG3_DCDC_VDD1P8CTRL_DISABLE_STEP_MASK; + + /* Spin until DCDC is stable */ + while (!(DCDC->REG0 & DCDC_REG0_DCDC_STS_DC_OK_MASK)) {} + } /* initialize the CPU core */ cpu_init(); From 859baa7345033942de91b079f73af19a4c9ab245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Sun, 18 Mar 2018 09:48:02 +0100 Subject: [PATCH 18/34] kinetis: Discard noisy bytes from LPUART --- cpu/kinetis/periph/uart.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cpu/kinetis/periph/uart.c b/cpu/kinetis/periph/uart.c index b662c62e6754..d4b0eeee5110 100644 --- a/cpu/kinetis/periph/uart.c +++ b/cpu/kinetis/periph/uart.c @@ -591,13 +591,16 @@ static inline void irq_handler_lpuart(uart_t uart) if (stat & LPUART_STAT_RDRF_MASK) { /* RDRF flag will be cleared when LPUART_DATA is read */ uint8_t data = dev->DATA; - if (stat & (LPUART_STAT_FE_MASK | LPUART_STAT_PF_MASK)) { + if (stat & (LPUART_STAT_FE_MASK | LPUART_STAT_PF_MASK | LPUART_STAT_NF_MASK)) { if (stat & LPUART_STAT_FE_MASK) { DEBUG("LPUART framing error %08" PRIx32 "\n", stat); } if (stat & LPUART_STAT_PF_MASK) { DEBUG("LPUART parity error %08" PRIx32 "\n", stat); } + if (stat & LPUART_STAT_NF_MASK) { + DEBUG("LPUART noise flag %08" PRIx32 "\n", stat); + } /* FE is set whenever the next character to be read from LPUART_DATA * was received with logic 0 detected where a stop bit was expected. */ /* PF is set whenever the next character to be read from LPUART_DATA From 26b776ae773d6c4c553d4435fdceefc99755159c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Wed, 10 Oct 2018 15:19:37 +0200 Subject: [PATCH 19/34] Revert "kinetis: Discard noisy bytes from LPUART" This reverts commit 1f21fe6796ee8fd7f6637e9b43f3b4ce5feb4e2e. --- cpu/kinetis/periph/uart.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cpu/kinetis/periph/uart.c b/cpu/kinetis/periph/uart.c index d4b0eeee5110..b662c62e6754 100644 --- a/cpu/kinetis/periph/uart.c +++ b/cpu/kinetis/periph/uart.c @@ -591,16 +591,13 @@ static inline void irq_handler_lpuart(uart_t uart) if (stat & LPUART_STAT_RDRF_MASK) { /* RDRF flag will be cleared when LPUART_DATA is read */ uint8_t data = dev->DATA; - if (stat & (LPUART_STAT_FE_MASK | LPUART_STAT_PF_MASK | LPUART_STAT_NF_MASK)) { + if (stat & (LPUART_STAT_FE_MASK | LPUART_STAT_PF_MASK)) { if (stat & LPUART_STAT_FE_MASK) { DEBUG("LPUART framing error %08" PRIx32 "\n", stat); } if (stat & LPUART_STAT_PF_MASK) { DEBUG("LPUART parity error %08" PRIx32 "\n", stat); } - if (stat & LPUART_STAT_NF_MASK) { - DEBUG("LPUART noise flag %08" PRIx32 "\n", stat); - } /* FE is set whenever the next character to be read from LPUART_DATA * was received with logic 0 detected where a stop bit was expected. */ /* PF is set whenever the next character to be read from LPUART_DATA From 120c379d1c785e6052adc944faee8702ed5f38a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Fri, 2 Nov 2018 12:14:10 +0100 Subject: [PATCH 20/34] fixup! fixup! fixup! kinetis: Add low power support --- cpu/kinetis/periph/pm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpu/kinetis/periph/pm.c b/cpu/kinetis/periph/pm.c index ac0ffa0fc30f..5c7b1efe3b7a 100644 --- a/cpu/kinetis/periph/pm.c +++ b/cpu/kinetis/periph/pm.c @@ -76,6 +76,10 @@ void pm_set(unsigned mode) PM_LED(0, ON); PM_LED(2, ON); /* Enable LLWU interrupt, or else we can never resume from LLS */ + /* Clear pending flag first, the LLWU has no purpose in RUN mode, so + * if the flag is set then it must be a remainder from handling the + * previous wakeup. */ + NVIC_ClearPendingIRQ(LLWU_IRQn); NVIC_EnableIRQ(LLWU_IRQn); break; } From 19cdf6cbb33bbd25ceb1e531620c5ce08d7ea344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Fri, 2 Nov 2018 16:11:42 +0100 Subject: [PATCH 21/34] squash kinetis-pm improve LPUART with LLS --- cpu/kinetis/periph/uart.c | 40 +++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/cpu/kinetis/periph/uart.c b/cpu/kinetis/periph/uart.c index b662c62e6754..291672eb3e95 100644 --- a/cpu/kinetis/periph/uart.c +++ b/cpu/kinetis/periph/uart.c @@ -135,12 +135,40 @@ KINETIS_UART_WRITE_INLINE void uart_write_lpuart(uart_t uart, const uint8_t *dat #endif #endif +#ifdef MODULE_PERIPH_LLWU +/** + * @brief LLWU callback for UART RX pin + * + * This function is called only when the CPU is in LLS mode and a falling edge + * occurs on the RX pin. + */ +static void uart_llwu_cb(void *arg) +{ + uart_t uart = (uart_t)arg; + if (!config[uart].active) { + config[uart].active = 1; + /* Keep CPU on until we are finished with RX */ + DEBUG("LLS UART\n"); + PM_BLOCK(KINETIS_PM_STOP); + } +} +#endif + int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) { assert(uart < UART_NUMOF); - if (config[uart].rx_cb) { + if (config[uart].rx_cb && config[uart].enabled) { + /* Re-initialization of an already configured UART instance */ + /* Release PM blocker from previous run */ +#ifdef MODULE_PERIPH_LLWU + /* LLS was only blocked before if not using an LLWU pin for RX */ + if (uart_config[uart].llwu_rx == LLWU_WAKEUP_PIN_UNDEF) { + PM_UNBLOCK(KINETIS_PM_LLS); + } +#else /* MODULE_PERIPH_LLWU */ PM_UNBLOCK(KINETIS_PM_LLS); +#endif /* MODULE_PERIPH_LLWU */ } /* remember callback addresses */ @@ -181,10 +209,10 @@ void uart_poweron(uart_t uart) unsigned state = irq_disable(); if (!config[uart].enabled) { config[uart].enabled = 1; -#if MODULE_PERIPH_LLWU +#ifdef MODULE_PERIPH_LLWU if (uart_config[uart].llwu_rx != LLWU_WAKEUP_PIN_UNDEF) { /* Configure the RX pin for LLWU wakeup to be able to use RX in LLS mode */ - llwu_wakeup_pin_set(uart_config[uart].llwu_rx, LLWU_WAKEUP_EDGE_FALLING, NULL, NULL); + llwu_wakeup_pin_set(uart_config[uart].llwu_rx, LLWU_WAKEUP_EDGE_FALLING, uart_llwu_cb, (void*)uart); } else #endif @@ -220,7 +248,7 @@ void uart_poweroff(uart_t uart) unsigned state = irq_disable(); if (config[uart].enabled) { config[uart].enabled = 0; -#if MODULE_PERIPH_LLWU +#ifdef MODULE_PERIPH_LLWU if (uart_config[uart].llwu_rx != LLWU_WAKEUP_PIN_UNDEF) { /* Disable LLWU wakeup for the RX pin */ llwu_wakeup_pin_set(uart_config[uart].llwu_rx, LLWU_WAKEUP_EDGE_NONE, NULL, NULL); @@ -585,7 +613,7 @@ static inline void irq_handler_lpuart(uart_t uart) config[uart].active = 1; /* Keep CPU on until we are finished with RX */ DEBUG("LPUART ACTIVE\n"); - PM_BLOCK(KINETIS_PM_LLS); + PM_BLOCK(KINETIS_PM_STOP); } } if (stat & LPUART_STAT_RDRF_MASK) { @@ -618,7 +646,7 @@ static inline void irq_handler_lpuart(uart_t uart) if (config[uart].active) { config[uart].active = 0; /* Let the CPU sleep when idle */ - PM_UNBLOCK(KINETIS_PM_LLS); + PM_UNBLOCK(KINETIS_PM_STOP); DEBUG("LPUART IDLE\n"); } } From e43d783fd7a554b23555fb172d0c3be01cf6c183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Mon, 5 Nov 2018 11:02:12 +0100 Subject: [PATCH 22/34] fixup! boards/frdm-kw41z: Add UART LLWU config --- boards/frdm-kw41z/include/periph_conf.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/boards/frdm-kw41z/include/periph_conf.h b/boards/frdm-kw41z/include/periph_conf.h index a3252de84034..f1419dc39a89 100644 --- a/boards/frdm-kw41z/include/periph_conf.h +++ b/boards/frdm-kw41z/include/periph_conf.h @@ -115,8 +115,12 @@ static const uart_conf_t uart_config[] = { .scgc_bit = SIM_SCGC5_LPUART0_SHIFT, .mode = UART_MODE_8N1, .type = KINETIS_LPUART, - /* Using LLWU requires using lower baud rates */ - /* LLWU_WAKEUP_PIN_PTC6 is the correct setting on this dev board if using LLWU */ + /* Undocumented behavior: LPUART fails to detect the START bit at wake up + * with LLWU sometimes. This seem to be related to using the builtin + * DCDC for powering the MCU. */ + /* LLWU_WAKEUP_PIN_PTC6 is the correct setting on this dev board if you + * want to try using LLS mode, or if it does not matter that the UART RX + * byte is sometimes is corrupt */ .llwu_rx = LLWU_WAKEUP_PIN_UNDEF, }, }; From 015f654006bdcb99c7989a32cce53942336fae74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Mon, 5 Nov 2018 11:04:34 +0100 Subject: [PATCH 23/34] fixup! boards/teensy31: Add UART LLWU config --- boards/teensy31/include/periph_conf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/teensy31/include/periph_conf.h b/boards/teensy31/include/periph_conf.h index 2cd22c52b8e1..c87900b6fb6f 100644 --- a/boards/teensy31/include/periph_conf.h +++ b/boards/teensy31/include/periph_conf.h @@ -127,7 +127,7 @@ static const uart_conf_t uart_config[] = { .scgc_bit = SIM_SCGC4_UART1_SHIFT, .mode = UART_MODE_8N1, .type = KINETIS_UART, - .llwu_rx = LLWU_WAKEUP_PIN_UNDEF, + .llwu_rx = LLWU_WAKEUP_PIN_PTC3, }, }; From 9c04a18a7e0bdbc5f4d9a6e204fc880f01a44a2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Mon, 5 Nov 2018 11:18:14 +0100 Subject: [PATCH 24/34] fixup! boards/frdm-k22f: Update LPTMR config with LLWU --- boards/frdm-k22f/include/periph_conf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/frdm-k22f/include/periph_conf.h b/boards/frdm-k22f/include/periph_conf.h index 452980045875..336bc21e5a81 100644 --- a/boards/frdm-k22f/include/periph_conf.h +++ b/boards/frdm-k22f/include/periph_conf.h @@ -87,7 +87,7 @@ static const clock_config_t clock_config = { .llwu = LLWU_WAKEUP_MODULE_LPTMR0, \ .src = 2, \ .irqn = LPTMR0_IRQn, \ - } \ + }, \ } #define TIMER_NUMOF ((PIT_NUMOF) + (LPTMR_NUMOF)) From 90e7e96a646183ec31ddd4a861b3287ae30d0979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Mon, 5 Nov 2018 11:18:37 +0100 Subject: [PATCH 25/34] frdm-k64f: Update LPTMR config with LLWU --- boards/frdm-k64f/include/periph_conf.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/boards/frdm-k64f/include/periph_conf.h b/boards/frdm-k64f/include/periph_conf.h index 93ec7f545a16..1540676f15d7 100644 --- a/boards/frdm-k64f/include/periph_conf.h +++ b/boards/frdm-k64f/include/periph_conf.h @@ -81,14 +81,15 @@ static const clock_config_t clock_config = { }, \ } #define LPTMR_NUMOF (1U) -#define LPTMR_CONFIG { \ - { \ - .dev = LPTMR0, \ - .irqn = LPTMR0_IRQn, \ - .src = 2, \ - .base_freq = 32768u, \ - }, \ -} +#define LPTMR_CONFIG { \ + { \ + .dev = LPTMR0, \ + .base_freq = 32768u, \ + .llwu = LLWU_WAKEUP_MODULE_LPTMR0, \ + .src = 2, \ + .irqn = LPTMR0_IRQn, \ + }, \ + } #define TIMER_NUMOF ((PIT_NUMOF) + (LPTMR_NUMOF)) #define PIT_BASECLOCK (CLOCK_BUSCLOCK) From 44d15e2dae0ff152c81185d8abbf55a48741848f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Mon, 5 Nov 2018 11:19:31 +0100 Subject: [PATCH 26/34] fixup! boards/frdm-kw41z: Update LPTMR config with LLWU --- boards/frdm-kw41z/include/periph_conf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/frdm-kw41z/include/periph_conf.h b/boards/frdm-kw41z/include/periph_conf.h index f1419dc39a89..1c6721a83e02 100644 --- a/boards/frdm-kw41z/include/periph_conf.h +++ b/boards/frdm-kw41z/include/periph_conf.h @@ -91,7 +91,7 @@ static const clock_config_t clock_config = { .llwu = LLWU_WAKEUP_MODULE_LPTMR0, \ .src = 2, \ .irqn = LPTMR0_IRQn, \ - } \ + }, \ } #define TIMER_NUMOF ((PIT_NUMOF) + (LPTMR_NUMOF)) #define PIT_BASECLOCK (CLOCK_BUSCLOCK) From 14c0243fe6612863e5c962d3bb80beb5faa7a307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Mon, 5 Nov 2018 11:20:24 +0100 Subject: [PATCH 27/34] fixup! boards/mulle: Update LPTMR config with LLWU --- boards/mulle/include/periph_conf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/mulle/include/periph_conf.h b/boards/mulle/include/periph_conf.h index 2f776bdc5d23..de4048c1e8b0 100644 --- a/boards/mulle/include/periph_conf.h +++ b/boards/mulle/include/periph_conf.h @@ -110,7 +110,7 @@ static const clock_config_t clock_config = { .llwu = LLWU_WAKEUP_MODULE_LPTMR0, \ .src = 2, \ .irqn = LPTMR0_IRQn, \ - } \ + }, \ } #define TIMER_NUMOF ((PIT_NUMOF) + (LPTMR_NUMOF)) From f2368d19bf6bc658786159e05f5944b71af65b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Wed, 7 Nov 2018 14:41:57 +0100 Subject: [PATCH 28/34] squash kinetis llwu clean up --- cpu/kinetis/periph/llwu.c | 73 +++++++++++++++++++-------------------- cpu/kinetis/periph/pm.c | 2 ++ 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/cpu/kinetis/periph/llwu.c b/cpu/kinetis/periph/llwu.c index 8528f8c302f8..df94a829c3da 100644 --- a/cpu/kinetis/periph/llwu.c +++ b/cpu/kinetis/periph/llwu.c @@ -25,10 +25,12 @@ #define ENABLE_DEBUG (0) #include "debug.h" -struct { +typedef struct { gpio_cb_t cb; void *arg; -} llwu_pin_config[LLWU_WAKEUP_PIN_NUMOF]; +} llwu_pin_config_t; + +static llwu_pin_config_t llwu_pin_config[LLWU_WAKEUP_PIN_NUMOF]; void llwu_init(void) { @@ -51,23 +53,13 @@ void llwu_wakeup_pin_set(llwu_wakeup_pin_t pin, llwu_wakeup_edge_t edge, gpio_cb 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 */ - if(pin < 4) { - unsigned field_offset = pin * 2; - LLWU->PE1 = (LLWU->PE1 & (~(0b11 << field_offset))) | (edge << field_offset); - } - else if(pin < 8) { - unsigned field_offset = (pin - 4) * 2; - LLWU->PE2 = (LLWU->PE2 & (~(0b11 << field_offset))) | (edge << field_offset); - } - else if(pin < 12) { - unsigned field_offset = (pin - 8) * 2; - LLWU->PE3 = (LLWU->PE3 & (~(0b11 << field_offset))) | (edge << field_offset); - } - else { - unsigned field_offset = (pin - 12) * 2; - LLWU->PE4 = (LLWU->PE4 & (~(0b11 << field_offset))) | (edge << field_offset); - } + /* 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) @@ -75,27 +67,32 @@ void isr_llwu(void) DEBUG("LLWU IRQ\n"); for (unsigned reg = 0; reg < ((LLWU_WAKEUP_PIN_NUMOF + 7) / 8); ++reg) { - uint8_t flags = *(&LLWU->F1 + reg); - if (flags == 0) { - continue; - } + uint8_t status = *(&LLWU->F1 + reg); /* Clear pin interrupt flags */ - *(&LLWU->F1 + reg) = flags; - DEBUG("llwu: F%u = %02x\n", reg + 1, (unsigned) flags); - unsigned pin = reg * 8; - while (flags) { - if (flags & 1) { - 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 */ - 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; + *(&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); } - flags >>= 1; - ++pin; + /* 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 diff --git a/cpu/kinetis/periph/pm.c b/cpu/kinetis/periph/pm.c index 5c7b1efe3b7a..726e13b88b13 100644 --- a/cpu/kinetis/periph/pm.c +++ b/cpu/kinetis/periph/pm.c @@ -79,6 +79,8 @@ void pm_set(unsigned mode) /* Clear pending flag first, the LLWU has no purpose in RUN mode, so * if the flag is set then it must be a remainder from handling the * previous wakeup. */ + LLWU->F1 = 0xffu; + LLWU->F2 = 0xffu; NVIC_ClearPendingIRQ(LLWU_IRQn); NVIC_EnableIRQ(LLWU_IRQn); break; From a34d7ac3493e1b18d49c08eeada88725434e7b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Tue, 27 Nov 2018 15:11:33 +0100 Subject: [PATCH 29/34] frdm: Set connect_assert_srst in default OpenOCD configuration This avoids problems when trying to flash a device which is already running an application which uses low power modes. --- boards/common/frdm/dist/openocd-klx.cfg | 5 ++++- boards/common/frdm/dist/openocd-kx.cfg | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/boards/common/frdm/dist/openocd-klx.cfg b/boards/common/frdm/dist/openocd-klx.cfg index 47a447b7b8d5..5d7f29cb13ec 100644 --- a/boards/common/frdm/dist/openocd-klx.cfg +++ b/boards/common/frdm/dist/openocd-klx.cfg @@ -16,7 +16,10 @@ transport select swd # Kinetis L series CPUs source [find target/klx.cfg] -reset_config srst_only +# The debug module is stopped in low leakage modes, we use connect_assert_srst +# to hold the device in reset while connecting with OpenOCD to make sure that we +# always get a connection even when the application uses low power modes. +reset_config srst_only connect_assert_srst $_TARGETNAME configure -event gdb-attach { halt diff --git a/boards/common/frdm/dist/openocd-kx.cfg b/boards/common/frdm/dist/openocd-kx.cfg index 021f22074e26..2e128dcb908a 100644 --- a/boards/common/frdm/dist/openocd-kx.cfg +++ b/boards/common/frdm/dist/openocd-kx.cfg @@ -13,7 +13,10 @@ # Kinetis K series CPUs source [find target/kx.cfg] -reset_config srst_only +# The debug module is stopped in low leakage modes, we use connect_assert_srst +# to hold the device in reset while connecting with OpenOCD to make sure that we +# always get a connection even when the application uses low power modes. +reset_config srst_only connect_assert_srst $_TARGETNAME configure -event gdb-attach { halt From 0215897ff785088483ca24a8a6d0d4585f9b7ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Sat, 1 Dec 2018 14:49:00 +0100 Subject: [PATCH 30/34] usb-kw41z: Update periph_conf.h for low power support --- boards/usb-kw41z/include/periph_conf.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/boards/usb-kw41z/include/periph_conf.h b/boards/usb-kw41z/include/periph_conf.h index 570527b90737..2b0575fbbb2f 100644 --- a/boards/usb-kw41z/include/periph_conf.h +++ b/boards/usb-kw41z/include/periph_conf.h @@ -87,10 +87,11 @@ static const clock_config_t clock_config = { #define LPTMR_CONFIG { \ { \ .dev = LPTMR0, \ - .irqn = LPTMR0_IRQn, \ - .src = 2, \ .base_freq = 32768u, \ - } \ + .llwu = LLWU_WAKEUP_MODULE_LPTMR0, \ + .src = 2, \ + .irqn = LPTMR0_IRQn, \ + }, \ } #define TIMER_NUMOF ((PIT_NUMOF) + (LPTMR_NUMOF)) #define PIT_BASECLOCK (CLOCK_BUSCLOCK) @@ -114,6 +115,10 @@ static const uart_conf_t uart_config[] = { .scgc_bit = SIM_SCGC5_LPUART0_SHIFT, .mode = UART_MODE_8N1, .type = KINETIS_LPUART, + /* LLWU_WAKEUP_PIN_PTC6 is the correct setting on this dev board if you + * want to try using LLS mode. The current setting will block LLS mode + * if UART RX is used, which is true in most applications */ + .llwu_rx = LLWU_WAKEUP_PIN_UNDEF, }, }; #define UART_NUMOF (sizeof(uart_config) / sizeof(uart_config[0])) From 74ecf4eafa9d9f8056e6c4f02fcf1944a68a389c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Fri, 14 Dec 2018 00:05:55 +0100 Subject: [PATCH 31/34] squash kinetis uart pm_block handling clean up This should fix issues with pm_blockers being left when re-init --- cpu/kinetis/periph/uart.c | 74 +++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/cpu/kinetis/periph/uart.c b/cpu/kinetis/periph/uart.c index 291672eb3e95..f7e28181d5e7 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 * @@ -158,18 +158,15 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) { assert(uart < UART_NUMOF); - if (config[uart].rx_cb && config[uart].enabled) { - /* Re-initialization of an already configured UART instance */ - /* Release PM blocker from previous run */ -#ifdef MODULE_PERIPH_LLWU - /* LLS was only blocked before if not using an LLWU pin for RX */ - if (uart_config[uart].llwu_rx == LLWU_WAKEUP_PIN_UNDEF) { - PM_UNBLOCK(KINETIS_PM_LLS); - } -#else /* MODULE_PERIPH_LLWU */ - PM_UNBLOCK(KINETIS_PM_LLS); -#endif /* MODULE_PERIPH_LLWU */ - } + /* 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; @@ -179,9 +176,6 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) 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: @@ -197,16 +191,20 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) 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) { assert(uart < UART_NUMOF); + unsigned state = irq_disable(); if (config[uart].rx_cb) { - unsigned state = irq_disable(); if (!config[uart].enabled) { config[uart].enabled = 1; #ifdef MODULE_PERIPH_LLWU @@ -223,7 +221,6 @@ void uart_poweron(uart_t uart) PM_BLOCK(KINETIS_PM_LLS); } } - irq_restore(state); } switch (uart_config[uart].type) { #if KINETIS_HAVE_UART @@ -237,15 +234,16 @@ void uart_poweron(uart_t uart) break; #endif default: - return; + break; } + irq_restore(state); } void uart_poweroff(uart_t uart) { assert(uart < UART_NUMOF); + unsigned state = irq_disable(); if (config[uart].rx_cb) { - unsigned state = irq_disable(); if (config[uart].enabled) { config[uart].enabled = 0; #ifdef MODULE_PERIPH_LLWU @@ -260,8 +258,13 @@ void uart_poweroff(uart_t uart) DEBUG("uart: unblocking LLS\n"); PM_UNBLOCK(KINETIS_PM_LLS); } + if (config[uart].active) { + /* We were in the middle of a RX sequence, need to release that + * PM blocker as well */ + PM_UNBLOCK(KINETIS_PM_STOP); + config[uart].active = 0; + } } - irq_restore(state); } switch (uart_config[uart].type) { #if KINETIS_HAVE_UART @@ -275,8 +278,9 @@ void uart_poweroff(uart_t uart) break; #endif default: - return; + break; } + irq_restore(state); } #if KINETIS_HAVE_UART && KINETIS_HAVE_LPUART @@ -328,8 +332,8 @@ static inline void uart_init_uart(uart_t uart, uint32_t baudrate) /* calculate baudrate */ ubd = (uint16_t)(clk / (baudrate * 16)); - /* set baudrate, enable RX active edge detection */ - dev->BDH = (uint8_t)UART_BDH_SBR(ubd >> 8) | UART_BDH_RXEDGIE_MASK; + /* set baudrate */ + dev->BDH = (uint8_t)UART_BDH_SBR(ubd >> 8); dev->BDL = (uint8_t)UART_BDL_SBR(ubd); #if KINETIS_UART_ADVANCED @@ -359,14 +363,14 @@ 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 receiver + RX interrupt + IDLE interrupt */ if (config[uart].rx_cb) { - dev->C2 = UART_C2_TE_MASK | UART_C2_RE_MASK | UART_C2_RIE_MASK | UART_C2_ILIE_MASK; + /* 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; } - /* enable receive interrupt */ - NVIC_EnableIRQ(uart_config[uart].irqn); } KINETIS_UART_WRITE_INLINE void uart_write_uart(uart_t uart, const uint8_t *data, size_t len) @@ -548,21 +552,17 @@ static inline void uart_init_lpuart(uart_t uart, uint32_t baudrate) uint32_t sbr = clk / (best_osr * baudrate); /* set baud rate, enable RX active edge interrupt */ - dev->BAUD = LPUART_BAUD_OSR(best_osr - 1) | LPUART_BAUD_SBR(sbr) | LPUART_BAUD_RXEDGIE_MASK; - - /* Enable transmitter */ - dev->CTRL |= LPUART_CTRL_TE_MASK; + dev->BAUD = LPUART_BAUD_OSR(best_osr - 1) | LPUART_BAUD_SBR(sbr); if (config[uart].rx_cb) { - /* enable receiver + RX interrupt + error interrupts */ - dev->CTRL |= LPUART_CTRL_RE_MASK | LPUART_CTRL_RIE_MASK | + /* 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; } - - /* enable interrupts from LPUART module */ - NVIC_EnableIRQ(uart_config[uart].irqn); } KINETIS_UART_WRITE_INLINE void uart_write_lpuart(uart_t uart, const uint8_t *data, size_t len) From bcbe91d821d8d920218562ce039f60ddea9fbd93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Fri, 14 Dec 2018 00:35:42 +0100 Subject: [PATCH 32/34] squash kinetis: Move PM_BLOCK to periph_cpu.h --- cpu/kinetis/include/periph_cpu.h | 16 ++++++++++++++++ cpu/kinetis/periph/uart.c | 8 -------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/cpu/kinetis/include/periph_cpu.h b/cpu/kinetis/include/periph_cpu.h index 26f437212548..614377b7253c 100644 --- a/cpu/kinetis/include/periph_cpu.h +++ b/cpu/kinetis/include/periph_cpu.h @@ -26,6 +26,22 @@ #include "cpu.h" +#if MODULE_PM_LAYERED +#include "pm_layered.h" +/** + * @brief pm_block iff pm_layered is used + */ +#define PM_BLOCK(x) pm_block(x) +/** + * @brief pm_unblock iff pm_layered is used + */ +#define PM_UNBLOCK(x) pm_unblock(x) +#else +/* ignore these calls when not using pm_layered */ +#define PM_BLOCK(x) +#define PM_UNBLOCK(x) +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/cpu/kinetis/periph/uart.c b/cpu/kinetis/periph/uart.c index f7e28181d5e7..0cec73ac5b50 100644 --- a/cpu/kinetis/periph/uart.c +++ b/cpu/kinetis/periph/uart.c @@ -31,14 +31,6 @@ #if MODULE_PERIPH_LLWU #include "llwu.h" #endif -#if MODULE_PM_LAYERED -#include "pm_layered.h" -#define PM_BLOCK(x) pm_block(x) -#define PM_UNBLOCK(x) pm_unblock(x) -#else -#define PM_BLOCK(x) -#define PM_UNBLOCK(x) -#endif #define ENABLE_DEBUG (0) #include "debug.h" From 42be2565621a19c5183f3e007289d1dc35013bd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Fri, 14 Dec 2018 00:36:04 +0100 Subject: [PATCH 33/34] squash kinetis: Add low power handling in i2c periph --- 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 2e5eeb0f7820..e910948cf6ed 100644 --- a/cpu/kinetis/periph/i2c.c +++ b/cpu/kinetis/periph/i2c.c @@ -382,9 +382,12 @@ int i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, uint8_t fla volatile uint8_t dummy; dummy = i2c->D; ++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)) { @@ -432,8 +435,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 7ab0e94f2aaa6240fb30a963498e9238c9631b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Fri, 14 Dec 2018 09:42:15 +0100 Subject: [PATCH 34/34] fixup! squash kinetis uart pm_block handling clean up --- cpu/kinetis/periph/uart.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cpu/kinetis/periph/uart.c b/cpu/kinetis/periph/uart.c index 0cec73ac5b50..83dbc0a21334 100644 --- a/cpu/kinetis/periph/uart.c +++ b/cpu/kinetis/periph/uart.c @@ -363,6 +363,9 @@ static inline void uart_init_uart(uart_t uart, uint32_t baudrate) /* 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; } KINETIS_UART_WRITE_INLINE void uart_write_uart(uart_t uart, const uint8_t *data, size_t len) @@ -555,6 +558,9 @@ static inline void uart_init_lpuart(uart_t uart, uint32_t baudrate) 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)