diff --git a/boards/st/nucleo_wb09ke/CMakeLists.txt b/boards/st/nucleo_wb09ke/CMakeLists.txt new file mode 100644 index 000000000000..945a0f1adae4 --- /dev/null +++ b/boards/st/nucleo_wb09ke/CMakeLists.txt @@ -0,0 +1,5 @@ +# Copyright (c) 2025 STMicroelectronics +# +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library_sources_ifdef(CONFIG_PM board.c) diff --git a/boards/st/nucleo_wb09ke/board.c b/boards/st/nucleo_wb09ke/board.c new file mode 100644 index 000000000000..9f3fb6f3f684 --- /dev/null +++ b/boards/st/nucleo_wb09ke/board.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "stm32wb0x_ll_pwr.h" + +/* Implement the correct Pull-up/pull-down for GPIOA and GPIOB + * to reach the minimum power consumption. + */ +static int board_pupd_init(void) +{ + LL_PWR_EnableGPIOPullUp(LL_PWR_GPIO_A, + LL_PWR_GPIO_BIT_0| + LL_PWR_GPIO_BIT_1| + LL_PWR_GPIO_BIT_2| + LL_PWR_GPIO_BIT_3); + + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_A, + LL_PWR_GPIO_BIT_8| + LL_PWR_GPIO_BIT_9| + LL_PWR_GPIO_BIT_10| + LL_PWR_GPIO_BIT_11); + + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, + LL_PWR_GPIO_BIT_0| + LL_PWR_GPIO_BIT_3| + LL_PWR_GPIO_BIT_6| + LL_PWR_GPIO_BIT_7); + + LL_PWR_EnableGPIOPullUp(LL_PWR_GPIO_B, + LL_PWR_GPIO_BIT_1| + LL_PWR_GPIO_BIT_2| + LL_PWR_GPIO_BIT_4| + LL_PWR_GPIO_BIT_5| + LL_PWR_GPIO_BIT_14| + LL_PWR_GPIO_BIT_15); + + return 0; +} + +SYS_INIT(board_pupd_init, PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); diff --git a/drivers/adc/adc_stm32wb0.c b/drivers/adc/adc_stm32wb0.c index e71357364d26..db7c80780cf3 100644 --- a/drivers/adc/adc_stm32wb0.c +++ b/drivers/adc/adc_stm32wb0.c @@ -97,8 +97,10 @@ LOG_MODULE_REGISTER(adc_stm32wb0, CONFIG_ADC_LOG_LEVEL); #define ADC_CHANNEL_TYPE_DIFF (0x02U) /* Differential */ #define ADC_CHANNEL_TYPE_INVALID (0xFFU) /* Invalid */ +#define STM32_HCLK_FREQUENCY DT_PROP(DT_NODELABEL(rcc), clock_frequency) + /** See RM0505 §6.2.1 "System clock details" */ -BUILD_ASSERT(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC >= (8 * 1000 * 1000), +BUILD_ASSERT(STM32_HCLK_FREQUENCY >= (8 * 1000 * 1000), "STM32WB0: system clock frequency must be at least 8MHz to use ADC"); /** diff --git a/drivers/bluetooth/hci/hci_stm32wb0.c b/drivers/bluetooth/hci/hci_stm32wb0.c index e6d3e16dd32d..7c272d6bd105 100644 --- a/drivers/bluetooth/hci/hci_stm32wb0.c +++ b/drivers/bluetooth/hci/hci_stm32wb0.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include "bleplat_cntr.h" #include "ble_stack.h" #include "stm32wb0x_hal_radio_timer.h" @@ -31,15 +33,8 @@ LOG_MODULE_REGISTER(bt_driver); #define DT_DRV_COMPAT st_hci_stm32wb0 /* Max HS startup time expressed in system time (1953 us / 2.4414 us) */ -#define MAX_HS_STARTUP_TIME 320 -#define BLE_WKUP_PRIO 0 -#define BLE_WKUP_FLAGS 0 #define BLE_TX_RX_PRIO 0 #define BLE_TX_RX_FLAGS 0 -#define CPU_WKUP_PRIO 1 -#define CPU_WKUP_FLAGS 0 -#define BLE_ERROR_PRIO 3 -#define BLE_ERROR_FLAGS 0 #define BLE_RXTX_SEQ_PRIO 3 #define BLE_RXTX_SEQ_FLAGS 0 #define PKA_PRIO 2 @@ -56,9 +51,29 @@ LOG_MODULE_REGISTER(bt_driver); #define EVT_VENDOR_CODE_LSB 3 #define EVT_VENDOR_CODE_MSB 4 +#if (defined(CONFIG_SOC_STM32WB06) || defined(CONFIG_SOC_STM32WB07)) && defined(CONFIG_PM) +#error "At the moment, PM is not supported for WB06 & WB07" +#endif /* (CONFIG_SOC_STM32WB06 || CONFIG_SOC_STM32WB07) && CONFIG_PM */ + static uint32_t __noinit dyn_alloc_a[BLE_DYN_ALLOC_SIZE >> 2]; static uint8_t buffer_out_mem[MAX_EVENT_SIZE]; -static struct k_work_delayable hal_radio_timer_work, ble_stack_work; +static struct k_work_delayable ble_stack_work; + +#if CONFIG_PM_DEVICE +/* ST Proprietary extended event */ +#define HCI_EXT_EVT 0x82 +#define ACI_HAL_END_OF_RADIO_ACTIVITY_VSEVT_CODE 0x0004 +#define STATE_ALL_BITMASK 0xFFFF +#define STATE_IDLE 0x00 +struct bt_hci_ext_evt_hdr { + uint8_t type; + uint8_t evt; + uint16_t len; + uint16_t vs_code; + uint8_t last_state; + uint8_t next_state; +} __packed; +#endif /* CONFIG_PM_DEVICE */ static struct net_buf *get_rx(uint8_t *msg); static PKA_HandleTypeDef hpka; @@ -77,6 +92,43 @@ int BLEPLAT_NvmGet(void) return 0; } +#if CONFIG_PM_DEVICE +/* Inform Zephyr PM about wakeup event from radio */ +static void register_radio_event(uint32_t time, bool unregister) +{ + int64_t value_ms, ticks; + static struct pm_policy_event radio_evt; + static bool first_time = true; + + if (unregister) { + if (!first_time) { + first_time = true; + pm_policy_event_unregister(&radio_evt); + } + } else { + value_ms = HAL_RADIO_TIMER_DiffSysTimeMs(time, HAL_RADIO_TIMER_GetCurrentSysTime()); + ticks = k_ms_to_ticks_floor64(value_ms) + k_uptime_ticks(); + if (first_time) { + pm_policy_event_register(&radio_evt, ticks); + first_time = false; + } else { + pm_policy_event_update(&radio_evt, ticks); + } + } +} +#endif /* CONFIG_PM_DEVICE */ + +uint8_t BLEPLAT_SetRadioTimerValue(uint32_t Time, uint8_t EventType, uint8_t CalReq) +{ + uint8_t retval; + + retval = HAL_RADIO_TIMER_SetRadioTimerValue(Time, EventType, CalReq); +#if CONFIG_PM_DEVICE + register_radio_event(Time, false); +#endif /* CONFIG_PM_DEVICE */ + return retval; +} + static void blestack_process(struct k_work *work) { BLE_STACK_Tick(); @@ -85,11 +137,6 @@ static void blestack_process(struct k_work *work) } } -static void vtimer_process(struct k_work *work) -{ - HAL_RADIO_TIMER_Tick(); -} - /* "If, since the last power-on or reset, the Host has ever issued a legacy * advertising command and then issues an extended advertising command, or * has ever issued an extended advertising command and then issues a legacy @@ -196,9 +243,24 @@ void send_event(uint8_t *buffer_out, uint16_t buffer_out_length, int8_t overflow const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0)); struct hci_data *hci = dev->data; - /* Construct net_buf from event data */ - struct net_buf *buf = get_rx(buffer_out); + struct net_buf *buf; + +#if CONFIG_PM_DEVICE + struct bt_hci_ext_evt_hdr *vs_evt = (struct bt_hci_ext_evt_hdr *)(buffer_out); + + if (vs_evt->type == HCI_EXT_EVT) { + if (vs_evt->evt == BT_HCI_EVT_VENDOR) { + if ((vs_evt->vs_code == ACI_HAL_END_OF_RADIO_ACTIVITY_VSEVT_CODE) && + (vs_evt->next_state == STATE_IDLE)) { + register_radio_event(0, true); + } + return; + } + } +#endif /* CONFIG_PM_DEVICE */ + /* Construct net_buf from event data */ + buf = get_rx(buffer_out); if (buf) { /* Handle the received HCI data */ LOG_DBG("New event %p len %u type %u", buf, buf->len, buf->data[0]); @@ -210,13 +272,6 @@ void send_event(uint8_t *buffer_out, uint16_t buffer_out_length, int8_t overflow void HAL_RADIO_TIMER_TxRxWakeUpCallback(void) { - k_work_schedule(&hal_radio_timer_work, K_NO_WAIT); - k_work_schedule(&ble_stack_work, K_NO_WAIT); -} - -void HAL_RADIO_TIMER_CpuWakeUpCallback(void) -{ - k_work_schedule(&hal_radio_timer_work, K_NO_WAIT); k_work_schedule(&ble_stack_work, K_NO_WAIT); } @@ -224,14 +279,6 @@ void HAL_RADIO_TxRxCallback(uint32_t flags) { BLE_STACK_RadioHandler(flags); k_work_schedule(&ble_stack_work, K_NO_WAIT); - k_work_schedule(&hal_radio_timer_work, K_NO_WAIT); -} - -ISR_DIRECT_DECLARE(RADIO_TIMER_TXRX_WKUP_IRQHandler) -{ - HAL_RADIO_TIMER_TXRX_WKUP_IRQHandler(); - ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency */ - return 1; } ISR_DIRECT_DECLARE(RADIO_TXRX_IRQHandler) @@ -248,59 +295,57 @@ ISR_DIRECT_DECLARE(RADIO_TXRX_SEQ_IRQHandler) return 1; } -ISR_DIRECT_DECLARE(RADIO_TIMER_CPU_WKUP_IRQHandler) -{ - HAL_RADIO_TIMER_TimeoutCallback(); - HAL_RADIO_TIMER_CpuWakeUpCallback(); - ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency */ - return 1; -} - -ISR_DIRECT_DECLARE(RADIO_TIMER_ERROR_IRQHandler) -{ - volatile uint32_t debug_cmd; - - BLUE->DEBUGCMDREG |= 1; - /* If the device is configured with CLK_SYS = 64MHz - * and BLE clock = 16MHz, a register read is necessary - * to ensure interrupt register is properly cleared - * due to AHB down converter latency - */ - debug_cmd = BLUE->DEBUGCMDREG; - LOG_ERR("Timer error"); - ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency */ - return 1; -} - /* Function called from PKA_IRQHandler() context. */ void PKAMGR_IRQCallback(void) { k_work_schedule(&ble_stack_work, K_NO_WAIT); } -static int _PKA_IRQHandler(void *args) +static void _PKA_IRQHandler(void *args) { ARG_UNUSED(args); HAL_PKA_IRQHandler(&hpka); - ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency */ - return 1; } static void ble_isr_installer(void) { - IRQ_DIRECT_CONNECT(RADIO_TIMER_TXRX_WKUP_IRQn, BLE_WKUP_PRIO, - RADIO_TIMER_TXRX_WKUP_IRQHandler, BLE_WKUP_FLAGS); IRQ_DIRECT_CONNECT(RADIO_TXRX_IRQn, BLE_TX_RX_PRIO, RADIO_TXRX_IRQHandler, BLE_TX_RX_FLAGS); - IRQ_DIRECT_CONNECT(RADIO_TIMER_CPU_WKUP_IRQn, CPU_WKUP_PRIO, - RADIO_TIMER_CPU_WKUP_IRQHandler, CPU_WKUP_FLAGS); IRQ_DIRECT_CONNECT(RADIO_TXRX_SEQ_IRQn, BLE_RXTX_SEQ_PRIO, RADIO_TXRX_SEQ_IRQHandler, BLE_RXTX_SEQ_FLAGS); - IRQ_DIRECT_CONNECT(RADIO_TIMER_ERROR_IRQn, BLE_ERROR_PRIO, RADIO_TIMER_ERROR_IRQHandler, - BLE_ERROR_FLAGS); IRQ_CONNECT(PKA_IRQn, PKA_PRIO, _PKA_IRQHandler, NULL, PKA_FLAGS); } +#if defined(CONFIG_PM_DEVICE) +static int ble_pm_action(const struct device *dev, + enum pm_device_action action) +{ + static uint32_t PKA_CR_vr; + + switch (action) { + case PM_DEVICE_ACTION_SUSPEND: + LL_PWR_EnableWU_EWBLE(); + PKA_CR_vr = PKA->CR; + /* TBD: Manage PKA save for WB06 & WB07 */ + break; + case PM_DEVICE_ACTION_RESUME: + LL_PWR_DisableWU_EWBLE(); + /* TBD: Manage PKA restore for WB06 & WB07 */ + PKA->CLRFR = PKA_CLRFR_PROCENDFC | PKA_CLRFR_RAMERRFC | PKA_CLRFR_ADDRERRFC; + PKA->CR = PKA_CR_vr; + ble_isr_installer(); + irq_enable(RADIO_TXRX_IRQn); + irq_enable(RADIO_TXRX_SEQ_IRQn); + irq_enable(PKA_IRQn); + break; + default: + return -ENOTSUP; + } + + return 0; +} +#endif /* CONFIG_PM_DEVICE */ + static void rng_get_random(void *num, size_t size) { const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0)); @@ -450,7 +495,6 @@ static int bt_hci_stm32wb0_send(const struct device *dev, struct net_buf *buf) static int bt_hci_stm32wb0_open(const struct device *dev, bt_hci_recv_t recv) { struct hci_data *data = dev->data; - RADIO_TIMER_InitTypeDef VTIMER_InitStruct = {MAX_HS_STARTUP_TIME, 0, 0}; RADIO_HandleTypeDef hradio = {0}; BLE_STACK_InitTypeDef BLE_STACK_InitParams = { .BLEStartRamAddress = (uint8_t *)dyn_alloc_a, @@ -487,8 +531,6 @@ static int bt_hci_stm32wb0_open(const struct device *dev, bt_hci_recv_t recv) ble_isr_installer(); hradio.Instance = RADIO; HAL_RADIO_Init(&hradio); - HAL_RADIO_TIMER_Init(&VTIMER_InitStruct); - HW_AES_Init(); hpka.Instance = PKA; HAL_PKA_Init(&hpka); @@ -503,9 +545,10 @@ static int bt_hci_stm32wb0_open(const struct device *dev, bt_hci_recv_t recv) #endif /* CONFIG_BT_EXT_ADV */ aci_adv_nwk_init(); - +#if CONFIG_PM_DEVICE + aci_hal_set_radio_activity_mask(STATE_ALL_BITMASK); +#endif /* CONFIG_PM_DEVICE */ data->recv = recv; - k_work_init_delayable(&hal_radio_timer_work, vtimer_process); k_work_init_delayable(&ble_stack_work, blestack_process); k_work_schedule(&ble_stack_work, K_NO_WAIT); @@ -517,10 +560,12 @@ static DEVICE_API(bt_hci, drv) = { .send = bt_hci_stm32wb0_send, }; +PM_DEVICE_DT_INST_DEFINE(0, ble_pm_action); + #define HCI_DEVICE_INIT(inst) \ static struct hci_data hci_data_##inst = { \ }; \ - DEVICE_DT_INST_DEFINE(inst, NULL, NULL, &hci_data_##inst, NULL, \ + DEVICE_DT_INST_DEFINE(inst, NULL, PM_DEVICE_DT_INST_GET(0), &hci_data_##inst, NULL, \ POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &drv) /* Only one instance supported */ diff --git a/drivers/clock_control/clock_stm32_ll_wb0.c b/drivers/clock_control/clock_stm32_ll_wb0.c index e4e856c784d1..227a2d2d9b8a 100644 --- a/drivers/clock_control/clock_stm32_ll_wb0.c +++ b/drivers/clock_control/clock_stm32_ll_wb0.c @@ -28,6 +28,7 @@ #define CLOCK_FREQ_64MHZ (64000000U) #define CLOCK_FREQ_32MHZ (32000000U) #define CLOCK_FREQ_16MHZ (16000000U) +#define STM32_HCLK_FREQUENCY DT_PROP(DT_NODELABEL(rcc), clock_frequency) /* Device tree node definitions */ #define DT_RCC_SLOWCLK_NODE DT_PHANDLE(STM32_CLOCK_CONTROL_NODE, slow_clock) @@ -74,11 +75,11 @@ BUILD_ASSERT(!IS_ENABLED(STM32_SYSCLK_SRC_HSE) || STM32_WB0_CLKSYS_PRESCALER != * the RC64M generator is imprecise. In this configuration, MR_BLE is broken. * The CPU and MR_BLE must be running at 32MHz for MR_BLE to work with HSI. */ - BUILD_ASSERT(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC >= CLOCK_FREQ_32MHZ, + BUILD_ASSERT(STM32_HCLK_FREQUENCY >= CLOCK_FREQ_32MHZ, "System clock frequency must be at least 32MHz to use LSI"); # else /* In PLL or Direct HSE mode, the clock is stable, so 16MHz can be used. */ - BUILD_ASSERT(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC >= CLOCK_FREQ_16MHZ, + BUILD_ASSERT(STM32_HCLK_FREQUENCY >= CLOCK_FREQ_16MHZ, "System clock frequency must be at least 16MHz to use LSI"); # endif /* STM32_SYSCLK_SRC_HSI */ @@ -711,7 +712,7 @@ int stm32_clock_control_init(const struct device *dev) * - 0 wait states otherwise (CLK_SYS <= 32MHz) */ LL_FLASH_SetLatency( - (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC >= CLOCK_FREQ_32MHZ) + (STM32_HCLK_FREQUENCY >= CLOCK_FREQ_32MHZ) ? LL_FLASH_LATENCY_1 : LL_FLASH_LATENCY_0 ); @@ -755,7 +756,7 @@ BUILD_ASSERT(IS_ENABLED(STM32_HSE_ENABLED), LL_RCC_SetRC64MPLLPrescaler( kconfig_to_ll_prescaler(STM32_WB0_CLKSYS_PRESCALER)); - SystemCoreClock = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC; + SystemCoreClock = STM32_HCLK_FREQUENCY; #if defined(STM32_LSI_ENABLED) /* Enable MR_BLE clock for LSI measurement. diff --git a/drivers/entropy/entropy_stm32.c b/drivers/entropy/entropy_stm32.c index a4e8cf40f03c..5134233dc861 100644 --- a/drivers/entropy/entropy_stm32.c +++ b/drivers/entropy/entropy_stm32.c @@ -36,6 +36,8 @@ #define STM32_CONDRST_SUPPORT #endif +#define STM32_HCLK_FREQUENCY DT_PROP(DT_NODELABEL(rcc), clock_frequency) + /* * This driver need to take into account all STM32 family: * - simple rng without hardware fifo and no DMA. @@ -84,7 +86,7 @@ BUILD_ASSERT((CONFIG_ENTROPY_STM32_THR_POOL_SIZE & * at least 32 MHz. See also: §6.2.2 "Peripheral clock details". */ BUILD_ASSERT(!IS_ENABLED(CONFIG_SOC_STM32WB09XX) || - CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC >= (32 * 1000 * 1000), + STM32_HCLK_FREQUENCY >= (32 * 1000 * 1000), "STM32WB09: TRNG requires system clock frequency >= 32MHz"); struct entropy_stm32_rng_dev_cfg { diff --git a/drivers/rtc/rtc_ll_stm32.c b/drivers/rtc/rtc_ll_stm32.c index 2e5edb85921b..2c697a8034fd 100644 --- a/drivers/rtc/rtc_ll_stm32.c +++ b/drivers/rtc/rtc_ll_stm32.c @@ -42,6 +42,8 @@ LOG_MODULE_REGISTER(rtc_stm32, CONFIG_RTC_LOG_LEVEL); +#define STM32_HCLK_FREQUENCY DT_PROP(DT_NODELABEL(rcc), clock_frequency) + #if (defined(CONFIG_SOC_SERIES_STM32L1X) && !defined(RTC_SUBSECOND_SUPPORT)) \ || defined(CONFIG_SOC_SERIES_STM32F2X) /* subsecond counting is not supported by some STM32L1x MCUs (Cat.1) & by STM32F2x SoC series */ @@ -358,7 +360,7 @@ static int rtc_stm32_init(const struct device *dev) * as time base, but SysTick is initialized after the RTC... */ const uint32_t cycles_to_waste = - 84 * (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / USEC_PER_SEC); + 84 * (STM32_HCLK_FREQUENCY / USEC_PER_SEC); volatile uint32_t i = cycles_to_waste; while (--i > 0) { diff --git a/drivers/serial/uart_stm32.c b/drivers/serial/uart_stm32.c index 0177bcb029ff..ace0e77dc2f9 100644 --- a/drivers/serial/uart_stm32.c +++ b/drivers/serial/uart_stm32.c @@ -37,6 +37,7 @@ #include #include #if defined(CONFIG_PM) && defined(IS_UART_WAKEUP_FROMSTOP_INSTANCE) +#include #include #endif /* CONFIG_PM */ @@ -111,6 +112,25 @@ static void uart_stm32_pm_policy_state_lock_get_unconditional(void) } } +static inline void uart_stm32_pm_enable_wakeup_line(uint32_t wakeup_line) +{ +#if defined(CONFIG_SOC_SERIES_STM32WB0X) + ARG_UNUSED(wakeup_line); +#if defined(PWR_CR3_EIWL2) + /** + * If SoC is equipped with LPUART instance, + * enable the associated wake-up line in PWRC. + */ + LL_PWR_EnableInternWU2(); +#endif /* PWR_CR3_EIWL2 */ +#elif defined(IS_UART_WAKEUP_FROMSTOP_INSTANCE) + if (wakeup_line != STM32_WAKEUP_LINE_NONE) { + /* Enable EXTI line associated to UART wake-up event */ + LL_EXTI_EnableIT_0_31(BIT(wakeup_line)); + } +#endif /* CONFIG_SOC_SERIES_STM32WB0X */ +} + static void uart_stm32_pm_policy_state_lock_get(const struct device *dev) { struct uart_stm32_data *data = dev->data; @@ -2160,12 +2180,12 @@ static int uart_stm32_registers_configure(const struct device *dev) LL_USART_EnableIT_WKUP(usart); LL_USART_ClearFlag_WKUP(usart); #endif +#if !defined(CONFIG_SOC_SERIES_STM32WB0X) || defined(USART_CR1_UESM) LL_USART_EnableInStopMode(usart); +#endif /* !CONFIG_SOC_SERIES_STM32WB0X || USART_CR1_UESM */ - if (config->wakeup_line != STM32_WAKEUP_LINE_NONE) { - /* Prepare the WAKEUP with the expected EXTI line */ - LL_EXTI_EnableIT_0_31(BIT(config->wakeup_line)); - } + /* Enable the wake-up line signal (if applicable to hardware) */ + uart_stm32_pm_enable_wakeup_line(config->wakeup_line); } #endif /* CONFIG_PM */ diff --git a/drivers/timer/CMakeLists.txt b/drivers/timer/CMakeLists.txt index 3c488da9a3e7..0ba19a1de8db 100644 --- a/drivers/timer/CMakeLists.txt +++ b/drivers/timer/CMakeLists.txt @@ -41,6 +41,7 @@ zephyr_library_sources_ifdef(CONFIG_REALTEK_RTS5912_RTMR realtek_rts5912_rtmr.c) zephyr_library_sources_ifdef(CONFIG_SAM0_RTC_TIMER sam0_rtc_timer.c) zephyr_library_sources_ifdef(CONFIG_SILABS_SLEEPTIMER_TIMER silabs_sleeptimer_timer.c) zephyr_library_sources_ifdef(CONFIG_STM32_LPTIM_TIMER stm32_lptim_timer.c) +zephyr_library_sources_ifdef(CONFIG_STM32_RADIO_TIMER stm32_radio_timer.c) zephyr_library_sources_ifdef(CONFIG_TI_DM_TIMER ti_dmtimer.c) zephyr_library_sources_ifdef(CONFIG_XLNX_PSTTC_TIMER xlnx_psttc_timer.c) zephyr_library_sources_ifdef(CONFIG_XTENSA_TIMER xtensa_sys_timer.c) diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index c154370c4563..5a16047ab18b 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -99,6 +99,7 @@ source "drivers/timer/Kconfig.sam0_rtc" source "drivers/timer/Kconfig.silabs" source "drivers/timer/Kconfig.smartbond" source "drivers/timer/Kconfig.stm32_lptim" +source "drivers/timer/Kconfig.stm32_radio" source "drivers/timer/Kconfig.ti_dm_timer" source "drivers/timer/Kconfig.xlnx_psttc" source "drivers/timer/Kconfig.xtensa" diff --git a/drivers/timer/Kconfig.stm32_radio b/drivers/timer/Kconfig.stm32_radio new file mode 100644 index 000000000000..df0249a28721 --- /dev/null +++ b/drivers/timer/Kconfig.stm32_radio @@ -0,0 +1,13 @@ +# STM32WB0x radio timer configuration options + +# Copyright (c) 2025 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +config STM32_RADIO_TIMER + bool "STM32 Radio Timer for WB0x" + default y if PM || BT + depends on SOC_SERIES_STM32WB0X + select USE_STM32_HAL_RADIO_TIMER + select TICKLESS_CAPABLE + select HAS_STM32LIB + select SYSTEM_TIMER_HAS_DISABLE_SUPPORT diff --git a/drivers/timer/stm32_radio_timer.c b/drivers/timer/stm32_radio_timer.c new file mode 100644 index 000000000000..dfb7c43005b8 --- /dev/null +++ b/drivers/timer/stm32_radio_timer.c @@ -0,0 +1,203 @@ +/* stm32_radio_timer.c - STM32WB0x Radio Timer */ + +/* + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stm32wb0x_hal_radio_timer.h" + +#include +LOG_MODULE_REGISTER(radio_timer_driver); + +/* Max HS startup time expressed in system time (1953 us / 2.4414 us) */ +#define MAX_HS_STARTUP_TIME 320 +#define BLE_WKUP_PRIO 0 +#define BLE_WKUP_FLAGS 0 +#define CPU_WKUP_PRIO 1 +#define CPU_WKUP_FLAGS 0 +#define RADIO_TIMER_ERROR_PRIO 3 +#define RADIO_TIMER_ERROR_FLAGS 0 + +#define MULT64_THR_FREQ (806) +#define TIMER_MAX_VALUE (0xFFFFFFFFU) +#define TIMER_WRAPPING_MARGIN (4096) +#define MAX_ALLOWED_DELAY (TIMER_MAX_VALUE - TIMER_WRAPPING_MARGIN) + + +BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_NODELABEL(clk_lsi), disabled), + "At the moment, LSI is not supported"); + +#if (defined(CONFIG_SOC_STM32WB06) || defined(CONFIG_SOC_STM32WB07)) && defined(CONFIG_PM) +#error "At the moment, PM is not supported for WB06 & WB07" +#endif /* (CONFIG_SOC_STM32WB06 || CONFIG_SOC_STM32WB07) && CONFIG_PM */ + +/* Translate STU to MTU and vice versa. It is implemented by using integer operations. */ +uint32_t blue_unit_conversion(uint32_t time, uint32_t period_freq, uint32_t thr); +static uint64_t announced_cycles; + +static void radio_timer_error_isr(void *args) +{ + volatile uint32_t debug_cmd; + + ARG_UNUSED(args); + + BLUE->DEBUGCMDREG |= 1; + /* If the device is configured with CLK_SYS = 64MHz + * and BLE clock = 16MHz, a register read is necessary + * to ensure interrupt register is properly cleared + * due to AHB down converter latency + */ + debug_cmd = BLUE->DEBUGCMDREG; + LOG_ERR("Timer error"); +} + +static void radio_timer_cpu_wkup_isr(void *args) +{ + int32_t dticks; + uint64_t diff_cycles; + + ARG_UNUSED(args); + + HAL_RADIO_TIMER_TimeoutCallback(); + if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + diff_cycles = HAL_RADIO_TIMER_GetCurrentSysTime() - announced_cycles; + dticks = (int32_t)k_cyc_to_ticks_near64(diff_cycles); + announced_cycles += k_ticks_to_cyc_near32(dticks); + sys_clock_announce(dticks); + } else { + sys_clock_announce(1); + } +} + +volatile uint32_t ciccio_k = 0; +#if defined(CONFIG_SOC_STM32WB06) || defined(CONFIG_SOC_STM32WB07) +static void radio_timer_txrx_wkup_isr(void *args) +{ + ARG_UNUSED(args); + + HAL_RADIO_TIMER_WakeUpCallback(); +} +#endif /* CONFIG_SOC_STM32WB06 || CONFIG_SOC_STM32WB07 */ + +void sys_clock_set_timeout(int32_t ticks, bool idle) +{ + ARG_UNUSED(idle); + + if (ticks == K_TICKS_FOREVER) { + return; + } + +#if defined(CONFIG_TICKLESS_KERNEL) + uint32_t current_time, delay; + + /* This value is only valid for the LSE with a frequency of 32768 Hz. + * The implementation for the LSI will be done in the future. + */ + uint32_t calibrationData_freq1 = 0x0028F5C2; + + if (ticks < 1) { + ticks = 1; + } + //CLAMP(ticks, 1, (int32_t)MAX_TICKS); + delay = blue_unit_conversion(k_ticks_to_cyc_near32(ticks), calibrationData_freq1, MULT64_THR_FREQ) ; + if (delay > MAX_ALLOWED_DELAY) { + delay = MAX_ALLOWED_DELAY; + } else { + /* If the delay is too small round to minimum 2 ticks */ + delay = MAX(32, delay); + } + current_time = LL_RADIO_TIMER_GetAbsoluteTime(WAKEUP); + /* 4 least significant bits are not taken into account. Then let's round the value */ + LL_RADIO_TIMER_SetCPUWakeupTime(WAKEUP, ((current_time + (delay + 8)) & TIMER_MAX_VALUE)); + LL_RADIO_TIMER_EnableWakeupTimerLowPowerMode(WAKEUP); + LL_RADIO_TIMER_EnableCPUWakeupTimer(WAKEUP); + +#endif /* CONFIG_TICKLESS_KERNEL */ +} + +uint32_t sys_clock_elapsed(void) +{ + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + return 0; + } + + return k_cyc_to_ticks_near32(HAL_RADIO_TIMER_GetCurrentSysTime() - announced_cycles); +} + +uint32_t sys_clock_cycle_get_32(void) +{ + uint64_t val64; + + val64 = sys_clock_cycle_get_64(); + return val64 & 0xFFFFFFFF; +} + +uint64_t sys_clock_cycle_get_64(void) +{ + uint64_t cycles; + + cycles = HAL_RADIO_TIMER_GetCurrentSysTime(); + return cycles; +} + +void sys_clock_disable(void) +{ +#if defined(CONFIG_SOC_STM32WB06) || defined(CONFIG_SOC_STM32WB07) + irq_disable(RADIO_TIMER_TXRX_WKUP_IRQn); +#endif /* CONFIG_SOC_STM32WB06 || CONFIG_SOC_STM32WB07 */ + irq_disable(RADIO_TIMER_CPU_WKUP_IRQn); + irq_disable(RADIO_TIMER_ERROR_IRQn); + __HAL_RCC_RADIO_CLK_DISABLE(); +} + +static int sys_clock_driver_init(void) +{ + RADIO_TIMER_InitTypeDef VTIMER_InitStruct = {MAX_HS_STARTUP_TIME, 0, 0}; + +#if defined(CONFIG_SOC_STM32WB06) || defined(CONFIG_SOC_STM32WB07) + IRQ_CONNECT(RADIO_TIMER_TXRX_WKUP_IRQn, + BLE_WKUP_PRIO, + radio_timer_txrx_wkup_isr, + NULL, + BLE_WKUP_FLAGS); +#endif /* CONFIG_SOC_STM32WB06 || CONFIG_SOC_STM32WB07 */ + + IRQ_CONNECT(RADIO_TIMER_CPU_WKUP_IRQn, + CPU_WKUP_PRIO, + radio_timer_cpu_wkup_isr, + NULL, + CPU_WKUP_FLAGS); + IRQ_CONNECT(RADIO_TIMER_ERROR_IRQn, + RADIO_TIMER_ERROR_PRIO, + radio_timer_error_isr, + NULL, + RADIO_TIMER_ERROR_FLAGS); + + /* Peripheral clock enable */ + if (__HAL_RCC_RADIO_IS_CLK_DISABLED()) { + /* Radio reset */ + __HAL_RCC_RADIO_FORCE_RESET(); + __HAL_RCC_RADIO_RELEASE_RESET(); + + /* Enable Radio peripheral clock */ + __HAL_RCC_RADIO_CLK_ENABLE(); + } + + /* Wait to be sure that the Radio Timer is active */ + while (LL_RADIO_TIMER_GetAbsoluteTime(WAKEUP) < 0x10) { + } + HAL_RADIO_TIMER_Init(&VTIMER_InitStruct); + return 0; +} + +SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY); diff --git a/dts/arm/st/wb0/stm32wb0.dtsi b/dts/arm/st/wb0/stm32wb0.dtsi index 0900f8f02a0f..26c863b647b7 100644 --- a/dts/arm/st/wb0/stm32wb0.dtsi +++ b/dts/arm/st/wb0/stm32wb0.dtsi @@ -33,8 +33,27 @@ cpu0: cpu@0 { device_type = "cpu"; compatible = "arm,cortex-m0+"; + cpu-power-states = <&deepstop>; reg = <0>; }; + + power-states { + deepstop: state0 { + compatible = "zephyr,power-state"; + power-state-name = "suspend-to-ram"; + substate-id = <0>; + + /* One milisecond seems reasonable for now. */ + min-residency-us = <1000>; + + /** + * tWUDEEPSTOP value from datasheets: + * STM32WB06/WB07: typ. 110µs + * STM32WB05/WB09: typ. 170µs + */ + exit-latency-us = <550>; + }; + }; }; sram0: memory@20000000 { diff --git a/soc/st/stm32/Kconfig.defconfig b/soc/st/stm32/Kconfig.defconfig index aafb321151a4..94d28ed50817 100644 --- a/soc/st/stm32/Kconfig.defconfig +++ b/soc/st/stm32/Kconfig.defconfig @@ -35,6 +35,7 @@ DT_FLASH_PARENT_IS_XSPI := $(dt_node_has_compat,$(DT_CHOSEN_FLASH_PARENT),$(DT_C config SYS_CLOCK_HW_CYCLES_PER_SEC default "$(DT_STM32_RCC_CLOCK_FREQ)" if "$(dt_nodelabel_enabled,rcc)" + depends on !STM32_RADIO_TIMER config LOG_BACKEND_SWO_REF_FREQ_HZ default "$(DT_STM32_RCC_CLOCK_FREQ)" if "$(dt_nodelabel_enabled,rcc)" diff --git a/soc/st/stm32/stm32wb0x/CMakeLists.txt b/soc/st/stm32/stm32wb0x/CMakeLists.txt index 8cdf2982e149..4d41b3f618dd 100644 --- a/soc/st/stm32/stm32wb0x/CMakeLists.txt +++ b/soc/st/stm32/stm32wb0x/CMakeLists.txt @@ -3,6 +3,7 @@ zephyr_include_directories(${ZEPHYR_BASE}/drivers) zephyr_sources(soc.c) +zephyr_sources_ifdef(CONFIG_PM power.c s2ram_marking.S) zephyr_include_directories(.) zephyr_linker_sources(RAM_SECTIONS ram_sections.ld) diff --git a/soc/st/stm32/stm32wb0x/Kconfig b/soc/st/stm32/stm32wb0x/Kconfig index 2533ed349e9e..e0844c2fff30 100644 --- a/soc/st/stm32/stm32wb0x/Kconfig +++ b/soc/st/stm32/stm32wb0x/Kconfig @@ -9,8 +9,20 @@ config SOC_SERIES_STM32WB0X select CPU_CORTEX_M_HAS_VTOR select CPU_CORTEX_M_HAS_SYSTICK select CPU_HAS_ARM_MPU + select HAS_PM select HAS_STM32CUBE select SOC_EARLY_INIT_HOOK # WB0x has a ROM bootloader executed at reset, # which makes the following option required select INIT_ARCH_HW_AT_BOOT + + # STM32WB0 series only supports suspend-to-RAM as low-power mode. + # Make sure that S2RAM support is enabled if Power Management is + # selected by the user. PM_DEVICE is also required because devices + # need to be reinitialized after resuming. CUSTOM_MARKING is enabled + # to use SoC-specific hardware registers for resume detection, and + # CORTEX_M_SYSTICK_RESET_BY_LOW_POWER is required because the SoC + # is reset upon wake-up from Deepstop. + imply PM_DEVICE if PM + imply PM_S2RAM + imply PM_S2RAM_CUSTOM_MARKING diff --git a/soc/st/stm32/stm32wb0x/Kconfig.defconfig b/soc/st/stm32/stm32wb0x/Kconfig.defconfig index 79951bd061dc..c7821ba17680 100644 --- a/soc/st/stm32/stm32wb0x/Kconfig.defconfig +++ b/soc/st/stm32/stm32wb0x/Kconfig.defconfig @@ -8,6 +8,18 @@ if SOC_SERIES_STM32WB0X config NUM_IRQS default 32 +config SYS_CLOCK_HW_CYCLES_PER_SEC + default 409600 + depends on STM32_RADIO_TIMER + +config SYS_CLOCK_TICKS_PER_SEC + default 10240 + depends on STM32_RADIO_TIMER + +config CORTEX_M_SYSTICK + default n + depends on STM32_RADIO_TIMER + if BT config BT_AUTO_PHY_UPDATE diff --git a/soc/st/stm32/stm32wb0x/power.c b/soc/st/stm32/stm32wb0x/power.c new file mode 100644 index 000000000000..3e778e0cc6da --- /dev/null +++ b/soc/st/stm32/stm32wb0x/power.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2025 STMicroelectronics. + * + * SPDX-License-Identifier: Apache-2.0 + */ +/** + * STM32WB0 Deepstop implementation for Power Management framework + * + * TODO: + * - document the control flow on PM transitions + * - assertions around system configuration + * (e.g., valid slow clock selected, RTC enabled, ...) + * - ... + */ +#include +#include +#include +#include +#include + +/* Private headers in zephyr/drivers/... */ +#include + +#include +#include +#include +#include +#include + +#include +LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL); + +#if defined(CONFIG_SOC_STM32WB05XX) || defined(CONFIG_SOC_STM32WB09XX) +#define HAS_GPIO_RETENTION 1 +#else +#define HAS_GPIO_RETENTION 0 +#endif /* CONFIG_SOC_STM32WB05XX || CONFIG_SOC_STM32WB09XX */ + +/** + * System-level state managed by PM callbacks + * + * Things that need to be preserved across Deepstop, but + * have no associated driver to backup and restore them. + */ +#define SRAM DT_CHOSEN(zephyr_sram) +#define BL_STK_SIZ (20 * 4) /* in bytes */ +#define BL_STK_TOP ((void *)(DT_REG_ADDR(SRAM) + DT_REG_SIZE(SRAM) - BL_STK_SIZ)) +static uint8_t bl_stk_area_backup[BL_STK_SIZ]; + +uint32_t RCC_APB1ENR_vr, RCC_AHBENR_vr; + +static void save_system_level_state(void) +{ + /** + * The STM32WB0 bootloader uses the end of SRAM as stack. + * Since it is executed on every reset, including wakeup + * from Deepstop, any data placed at the end of SRAM would + * be corrupted. + * + * Backup these words for later restoration to avoid data + * corruption. A much better solution would mark this part + * of SRAM as unusable, but no easy solution was found to + * achieve this. + */ + memcpy(bl_stk_area_backup, BL_STK_TOP, BL_STK_SIZ); +} + +static void restore_system_level_state(void) +{ + /* Restore bootloader stack area */ + memcpy(BL_STK_TOP, bl_stk_area_backup, BL_STK_SIZ); +} + +/* Callback for arch_pm_s2ram_suspend */ +static int suspend_system_to_deepstop(void) +{ + /* Enable SLEEPDEEP to allow entry in Deepstop */ + LL_LPM_EnableDeepSleep(); + + /* Complete all memory transactions */ + __DSB(); + + + /* Attempt entry in Deepstop */ + __WFI(); + + /** + * Make sure no meaningful instruction is + * executed during the two cycles latency + * it takes to power-gate the CPU. + */ + __NOP(); + __NOP(); + + /** + * This code is reached only if the device did not + * enter Deepstop mode (e.g., because an interrupt + * became pending during preparatory work). + * + * Disable SLEEPDEEP and return the appropriate error. + */ + LL_LPM_EnableSleep(); + + return -EBUSY; +} + +/** + * Backup system state to save and configure power + * controller before entry in Deepstop mode + */ +static void prepare_for_deepstop_entry(void) +{ + /** + * DEEPSTOP2 configuration is performed in familiy-wide code + * instead of here (see `soc/st/stm32/common/soc_config.c`). + * + * RAMRET configuration is performed once during SoC init, + * since it is retained across Deepstop (see `soc.c`). + **/ + + /* Save the clock configuration. */ + RCC_APB1ENR_vr = RCC->APB1ENR; + RCC_AHBENR_vr = RCC->AHBENR; + + /* Clear wakeup reason flags (which inhibit Deepstop) */ + LL_PWR_ClearWakeupSource(LL_PWR_WAKEUP_ALL); + LL_SYSCFG_PWRC_ClearIT(LL_SYSCFG_PWRC_WKUP); + LL_PWR_ClearDeepstopSeqFlag(); + LL_PWR_EnableWU_EWBLEHCPU(); + +#if HAS_GPIO_RETENTION + /** + * Enable GPIO state retention in Deepstop if available. + * + * Do not enable this if low-power mode debugging has been + * enabled via Kconfig, because it prevents the debugger + * from staying connected to the SoC. + */ + if (!IS_ENABLED(CONFIG_STM32_ENABLE_DEBUG_SLEEP_STOP)) { + LL_PWR_EnableGPIORET(); + LL_PWR_EnableDBGRET(); + } +#endif /* HAS_GPIO_RETENTION */ + save_system_level_state(); +} + +/** + * @brief Restore SoC-level configuration lost in Deepstop + * @note This function must be called right after wakeup. + */ +static void post_resume_configuration(void) +{ + __ASSERT_NO_MSG(LL_PWR_GetDeepstopSeqFlag() == 1); + + /** + * VTOR has been reset to its default value: restore it. + * (Note that RAM_VR.AppBase was filled during SoC init) + */ + SCB->VTOR = RAM_VR.AppBase; + + /* Restore the clock configuration. */ + RCC->AHBENR = RCC_AHBENR_vr; + RCC->APB1ENR = RCC_APB1ENR_vr; + + /** + * Restore other miscellanous system-level things. + */ + restore_system_level_state(); +} + +/** + * Power Management subsystem callbacks + */ +void pm_state_set(enum pm_state state, uint8_t substate_id) +{ + /* Ignore substate: STM32WB0 has only one low-power mode */ + ARG_UNUSED(substate_id); + + int res; + + if (state != PM_STATE_SUSPEND_TO_RAM) { + /** + * Deepstop is a suspend-to-RAM state. + * Something is wrong if a different + * power state has been requested. + */ + LOG_ERR("Unsupported power state %u", state); + } + prepare_for_deepstop_entry(); + + /* Select Deepstop low-power mode and suspend system */ + LL_PWR_SetPowerMode(LL_PWR_MODE_DEEPSTOP); + + res = arch_pm_s2ram_suspend(suspend_system_to_deepstop); + + if (res >= 0) { + /** + * Restore system configuration only if the SoC actually + * entered Deepstop - otherwise, no state has been lost + * and it would be a waste of time to do so. + */ + post_resume_configuration(); + } +} + +void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id) +{ + ARG_UNUSED(state); + ARG_UNUSED(substate_id); + + /** + * We restore system state in @ref{post_resume_configuration}. + * The only thing we may have to do is release GPIO retention, + * which we have not done yet because we wanted the driver to + * restore all configuration first. + * + * We also need to enable IRQs to fullfill the API contract. + */ +#if HAS_GPIO_RETENTION + LL_PWR_DisableGPIORET(); + LL_PWR_DisableDBGRET(); +#endif /* HAS_GPIO_RETENTION */ + +#if defined(CONFIG_SOC_STM32WB06) || defined(CONFIG_SOC_STM32WB07) + irq_enable(RADIO_TIMER_TXRX_WKUP_IRQn); +#endif /* CONFIG_SOC_STM32WB06 || CONFIG_SOC_STM32WB07 */ + irq_enable(RADIO_TIMER_CPU_WKUP_IRQn); + irq_enable(RADIO_TIMER_ERROR_IRQn); + __enable_irq(); +} diff --git a/soc/st/stm32/stm32wb0x/s2ram_marking.S b/soc/st/stm32/stm32wb0x/s2ram_marking.S new file mode 100644 index 000000000000..d5187b6ce492 --- /dev/null +++ b/soc/st/stm32/stm32wb0x/s2ram_marking.S @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * STM32WB0-specific support code for suspend-to-RAM + */ + +#include +#include +#include +#include + +/* Read RCC and PWRC base from Device Tree */ +#include + +#define RCC_CSR (DT_REG_ADDR(DT_NODELABEL(rcc)) + 0x94) +#define PWR_BASE DT_REG_ADDR(DT_NODELABEL(pwrc)) +#define PWR_SR1 0x10 +#define PWR_SR3 0x38 + +_ASM_FILE_PROLOGUE + +GTEXT(pm_s2ram_mark_set) +SECTION_FUNC(TEXT, pm_s2ram_mark_set) + /* + * Managed by hardware - nothing to do here. + */ + bx lr + +/** + * @brief Check whether SoC is waking up from Deepstop + * @returns 1 if SoC is waking up from Deepstop, 0 otherwise. + * @note Registers are cleared by hardware upon reset, or + * the SoC PM code layer upon entry in Deepstop, so + * this function does not clear the registers. + */ +GTEXT(pm_s2ram_mark_check_and_clear) +SECTION_FUNC(TEXT, pm_s2ram_mark_check_and_clear) + /* + * Check for Deepstop exit on wakeup event: + * - RCC_CSR is zero + * - PWRC.EXTSRR has bit DEEPSTOPF set + * (Redundant => unchecked) + * - Either PWRC_SR1 or PWRC_SR3 is non-zero + * + * Note that we don't have to clear any register since + * they are automatically updated on reset/wake-up. + * + * IMPLEMENTATIONS DETAILS: + * r1 must not be modified and the stack must not be + * used by this function as of writing, due to the + * current implementation of arch_pm_s2ram_resume. + * As such, we can only use r0, r2 and r3 here. + * + * N.B.: r12 is also volatile for the ARM ABI, but it + * cannot be used for most operations on ARMv6-M due + * to 16-bit Thumb limitations, so we might as well + * avoid using it entirely. + */ + ldr r0, =RCC_CSR + ldr r2, [r0] + cmp r2, #0 + bne not_deepstop_wakeup + + ldr r0, =PWR_BASE + ldr r2, [r0, #PWR_SR1] + ldr r3, [r0, #PWR_SR3] + orrs r2, r2, r3 + beq not_deepstop_wakeup + + /** + * All conditions met: this is a wakeup from Deepstop. + */ + movs r0, #1 + bx lr + +not_deepstop_wakeup: + movs r0, #0 + bx lr diff --git a/west.yml b/west.yml index 8851147b8c33..e4f6c2b33c15 100644 --- a/west.yml +++ b/west.yml @@ -245,7 +245,7 @@ manifest: groups: - hal - name: hal_stm32 - revision: 126cbbee19208b7aaca5ad8b287cf104e8bc837a + revision: pull/303/head path: modules/hal/stm32 groups: - hal