From 793774f47c914342974b63e0a77baac5eda59fcb Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Wed, 15 May 2024 14:15:58 +0200 Subject: [PATCH 01/16] west.yml: hal_stm32: Update to HAL integrating STM32WB0 Update the West manifest to point to an updated version of the STM32 HAL compatible with WB0 series thank to inclusion of STM32CubeWB0 package. Signed-off-by: Mathieu Choplain --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 18211e41cb1be8..a7ac8e3d78f758 100644 --- a/west.yml +++ b/west.yml @@ -233,7 +233,7 @@ manifest: groups: - hal - name: hal_stm32 - revision: 4c1adf8a2e2e9888f3b43374bf6521b0788aa82d + revision: 1e6116bd2a36db976d955ee772d21f329e529873 path: modules/hal/stm32 groups: - hal From 8e51cf77dcddd34a73bac5bf1ed1c96b1c2e13b9 Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Wed, 17 Jul 2024 08:42:31 +0200 Subject: [PATCH 02/16] modules: Kconfig.stm32: Add Kconfig symbols for RADIO The RADIO and RADIO_TIMER HAL modules will be required to implement BLE support for STM32WB0 series. Signed-off-by: Mathieu Choplain --- modules/Kconfig.stm32 | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/Kconfig.stm32 b/modules/Kconfig.stm32 index ad598f6300685d..beab5491185959 100644 --- a/modules/Kconfig.stm32 +++ b/modules/Kconfig.stm32 @@ -442,6 +442,16 @@ config USE_STM32_HAL_RAMCFG Enable STM32Cube RAMs configuration controller (RAMCFG) HAL module driver +config USE_STM32_HAL_RADIO + bool + help + Enable STM32Cube Radio HAL module driver + +config USE_STM32_HAL_RADIO_TIMER + bool + help + Enable STM32Cube Radio Timer HAL module driver + config USE_STM32_HAL_RAMECC bool help From 44e7a32766e781f7c41731c040d6779e8c449950 Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Fri, 17 May 2024 09:23:12 +0200 Subject: [PATCH 03/16] soc: st: stm32: add STM32WB0 series Adds support for the STM32WB0 MCU series. Signed-off-by: Mathieu Choplain --- soc/st/stm32/common/soc_config.c | 5 + soc/st/stm32/soc.yml | 6 + soc/st/stm32/stm32wb0x/CMakeLists.txt | 10 ++ soc/st/stm32/stm32wb0x/Kconfig | 15 ++ soc/st/stm32/stm32wb0x/Kconfig.defconfig | 11 ++ soc/st/stm32/stm32wb0x/Kconfig.soc | 33 +++++ soc/st/stm32/stm32wb0x/ram_sections.ld | 22 +++ soc/st/stm32/stm32wb0x/soc.c | 178 +++++++++++++++++++++++ soc/st/stm32/stm32wb0x/soc.h | 22 +++ 9 files changed, 302 insertions(+) create mode 100644 soc/st/stm32/stm32wb0x/CMakeLists.txt create mode 100644 soc/st/stm32/stm32wb0x/Kconfig create mode 100644 soc/st/stm32/stm32wb0x/Kconfig.defconfig create mode 100644 soc/st/stm32/stm32wb0x/Kconfig.soc create mode 100644 soc/st/stm32/stm32wb0x/ram_sections.ld create mode 100644 soc/st/stm32/stm32wb0x/soc.c create mode 100644 soc/st/stm32/stm32wb0x/soc.h diff --git a/soc/st/stm32/common/soc_config.c b/soc/st/stm32/common/soc_config.c index 6e5650850b402a..02bff9495440bc 100644 --- a/soc/st/stm32/common/soc_config.c +++ b/soc/st/stm32/common/soc_config.c @@ -14,6 +14,7 @@ #include #include #include +#include /** * @brief Perform SoC configuration at boot. @@ -80,6 +81,8 @@ static int st_stm32_common_config(void) LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_DBGMCU); LL_DBGMCU_EnableDBGStopMode(); LL_APB2_GRP1_DisableClock(LL_APB2_GRP1_PERIPH_DBGMCU); +#elif defined(CONFIG_SOC_SERIES_STM32WB0X) + LL_PWR_EnableDEEPSTOP2(); #else /* all other parts */ LL_DBGMCU_EnableDBGStopMode(); #endif @@ -102,6 +105,8 @@ static int st_stm32_common_config(void) LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_DBGMCU); LL_DBGMCU_DisableDBGStopMode(); LL_APB2_GRP1_DisableClock(LL_APB2_GRP1_PERIPH_DBGMCU); +#elif defined(CONFIG_SOC_SERIES_STM32WB0X) + LL_PWR_DisableDEEPSTOP2(); #else /* all other parts */ LL_DBGMCU_DisableDBGStopMode(); #endif diff --git a/soc/st/stm32/soc.yml b/soc/st/stm32/soc.yml index d9d818818485cc..f1c625a156952f 100644 --- a/soc/st/stm32/soc.yml +++ b/soc/st/stm32/soc.yml @@ -197,6 +197,12 @@ family: - name: stm32wbx socs: - name: stm32wb55xx + - name: stm32wb0x + socs: + - name: stm32wb05 + - name: stm32wb06 + - name: stm32wb07 + - name: stm32wb09 - name: stm32wbax socs: - name: stm32wba52xx diff --git a/soc/st/stm32/stm32wb0x/CMakeLists.txt b/soc/st/stm32/stm32wb0x/CMakeLists.txt new file mode 100644 index 00000000000000..8cdf2982e14987 --- /dev/null +++ b/soc/st/stm32/stm32wb0x/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_include_directories(${ZEPHYR_BASE}/drivers) + +zephyr_sources(soc.c) +zephyr_include_directories(.) + +zephyr_linker_sources(RAM_SECTIONS ram_sections.ld) + +set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/arm/cortex_m/scripts/linker.ld CACHE INTERNAL "") diff --git a/soc/st/stm32/stm32wb0x/Kconfig b/soc/st/stm32/stm32wb0x/Kconfig new file mode 100644 index 00000000000000..5816f0b83b4093 --- /dev/null +++ b/soc/st/stm32/stm32wb0x/Kconfig @@ -0,0 +1,15 @@ +# STMicroelectronics STM32W0 MCU series + +# Copyright (c) 2024 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +config SOC_SERIES_STM32WB0X + select ARM + select CPU_CORTEX_M0PLUS + select CPU_CORTEX_M_HAS_VTOR + select CPU_CORTEX_M_HAS_SYSTICK + select CPU_HAS_ARM_MPU + select HAS_STM32CUBE + # WB0x has a ROM bootloader executed at reset, + # which makes the following option required + select INIT_ARCH_HW_AT_BOOT diff --git a/soc/st/stm32/stm32wb0x/Kconfig.defconfig b/soc/st/stm32/stm32wb0x/Kconfig.defconfig new file mode 100644 index 00000000000000..01c32f340464df --- /dev/null +++ b/soc/st/stm32/stm32wb0x/Kconfig.defconfig @@ -0,0 +1,11 @@ +# STMicroelectronics STM32WB0 MCU series + +# Copyright (c) 2024 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +if SOC_SERIES_STM32WB0X + +config NUM_IRQS + default 32 + +endif # SOC_SERIES_STM32WB0X diff --git a/soc/st/stm32/stm32wb0x/Kconfig.soc b/soc/st/stm32/stm32wb0x/Kconfig.soc new file mode 100644 index 00000000000000..78a50047002a29 --- /dev/null +++ b/soc/st/stm32/stm32wb0x/Kconfig.soc @@ -0,0 +1,33 @@ +# STMicroelectronics STM32WB0 MCU series + +# Copyright (c) 2024 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +config SOC_SERIES_STM32WB0X + bool + select SOC_FAMILY_STM32 + +config SOC_SERIES + default "stm32wb0x" if SOC_SERIES_STM32WB0X + +config SOC_STM32WB05XX + bool + select SOC_SERIES_STM32WB0X + +config SOC_STM32WB06XX + bool + select SOC_SERIES_STM32WB0X + +config SOC_STM32WB07XX + bool + select SOC_SERIES_STM32WB0X + +config SOC_STM32WB09XX + bool + select SOC_SERIES_STM32WB0X + +config SOC + default "stm32wb05" if SOC_STM32WB05XX + default "stm32wb06" if SOC_STM32WB06XX + default "stm32wb07" if SOC_STM32WB07XX + default "stm32wb09" if SOC_STM32WB09XX diff --git a/soc/st/stm32/stm32wb0x/ram_sections.ld b/soc/st/stm32/stm32wb0x/ram_sections.ld new file mode 100644 index 00000000000000..c85b3301a3229c --- /dev/null +++ b/soc/st/stm32/stm32wb0x/ram_sections.ld @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** Refer to `soc.c` for more information about these areas. */ +SECTION_PROLOGUE(stm32wb0_RAM_VR, 0x20000000 (NOLOAD), ) +{ + /* For historical reasons, leave the first word of + * SRAM0 unused, even though it could store data. + * The structure MUST start at address 0x2000_0004. + */ + . += 4; + + KEEP(*(stm32wb0_RAM_VR)); +} GROUP_LINK_IN(RAMABLE_REGION) + +SECTION_PROLOGUE(stm32wb0_BLUE_RAM, 0x200000C0 (NOLOAD), ) +{ + KEEP(*(stm32wb0_BLUE_RAM)); +} GROUP_LINK_IN(RAMABLE_REGION) diff --git a/soc/st/stm32/stm32wb0x/soc.c b/soc/st/stm32/stm32wb0x/soc.c new file mode 100644 index 00000000000000..fbc8dc212c183a --- /dev/null +++ b/soc/st/stm32/stm32wb0x/soc.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief System/hardware module for STM32WB0 processor + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define LOG_LEVEL CONFIG_SOC_LOG_LEVEL +LOG_MODULE_REGISTER(soc); + +/** + * CMSIS System Core Clock: global variable holding the system core clock, + * which is the frequency supplied to the SysTick timer and processor core. + * + * On STM32WB0 series, after RESET, the system clock frequency is 16MHz. + */ +uint32_t SystemCoreClock = 16000000U; + +/** + * RAM Virtual Register: special structure located at the start + * of SRAM0; used by the UART bootloader and the Low Power Manager. + * Data type definition comes from @ref system_stm32wb0xx.h + */ +Z_GENERIC_SECTION("stm32wb0_RAM_VR") +__used RAM_VR_TypeDef RAM_VR; + +/** Power Controller node */ +#define PWRC DT_INST(0, st_stm32wb0_pwr) + +/** SMPS modes */ +#define STM32WB0_SMPS_MODE_OFF 0 +#define STM32WB0_SMPS_MODE_PRECHARGE 1 +#define STM32WB0_SMPS_MODE_RUN 2 + +#define SMPS_MODE _CONCAT(STM32WB0_SMPS_MODE_, DT_STRING_UNQUOTED(PWRC, smps_mode)) + +/* Convert DTS properties to LL macros */ +#define SMPS_PRESCALER _CONCAT(LL_RCC_SMPS_DIV_, DT_PROP(PWRC, smps_clock_prescaler)) + +#if SMPS_MODE != STM32WB0_SMPS_MODE_OFF + BUILD_ASSERT(DT_NODE_HAS_PROP(PWRC, smps_bom), + "smps-bom must be specified"); + + #define SMPS_BOM \ + _CONCAT(LL_PWR_SMPS_BOM, DT_PROP(PWRC, smps_bom)) + + #define SMPS_LP_MODE \ + COND_CODE_1( \ + DT_PROP(PWRC, smps_lp_floating), \ + (LL_PWR_SMPS_LPOPEN), \ + (LL_PWR_NO_SMPS_LPOPEN)) + + #define SMPS_CURRENT_LIMIT \ + _CONCAT(LL_PWR_SMPS_PRECH_LIMIT_CUR_, \ + DT_STRING_UNQUOTED(PWRC, smps_current_limit)) + + #define SMPS_OUTPUT_VOLTAGE \ + _CONCAT(LL_PWR_SMPS_OUTPUT_VOLTAGE_, \ + DT_STRING_UNQUOTED(PWRC, smps_output_voltage)) +#endif /* SMPS_MODE != STM32WB0_SMPS_MODE_OFF */ + +static void configure_smps(void) +{ + /* Configure SMPS clock prescaler */ + LL_RCC_SetSMPSPrescaler(SMPS_PRESCALER); + +#if SMPS_MODE == STM32WB0_SMPS_MODE_OFF + /* Disable SMPS */ + LL_PWR_SetSMPSMode(LL_PWR_NO_SMPS); + + while (LL_PWR_IsSMPSReady()) { + /* Wait for SMPS to turn off */ + } +#else + /* Select correct BOM */ + LL_PWR_SetSMPSBOM(SMPS_BOM); + + /* Configure low-power mode */ + LL_PWR_SetSMPSOpenMode(SMPS_LP_MODE); + + /* Enable SMPS */ + LL_PWR_SetSMPSMode(LL_PWR_SMPS); + + while (!LL_PWR_IsSMPSReady()) { + /* Wait for SMPS to turn on */ + } + + /* Place SMPS in PRECHARGE (BYPASS) mode. + * This is required to change SMPS output voltage, + * so we can do it unconditionally. + */ + LL_PWR_SetSMPSPrechargeMode(LL_PWR_SMPS_PRECHARGE); + while (LL_PWR_IsSMPSinRUNMode()) { + /* Wait for SMPS to enter PRECHARGE mode */ + } + + if (SMPS_MODE == STM32WB0_SMPS_MODE_PRECHARGE) { + /** + * SMPS should remain in PRECHARGE mode, but + * we still have to configure the current limit. + */ + LL_PWR_SetSMPSPrechargeLimitCurrent(SMPS_CURRENT_LIMIT); + } else { + /** + * SMPS mode requested is RUN mode. Configure the output + * voltage to the desired value then exit PRECHARGE mode. + */ + LL_PWR_SMPS_SetOutputVoltageLevel(SMPS_OUTPUT_VOLTAGE); + + /* Exit PRECHARGE mode (returns in RUN mode) */ + LL_PWR_SetSMPSPrechargeMode(LL_PWR_NO_SMPS_PRECHARGE); + while (!LL_PWR_IsSMPSinRUNMode()) { + /* Wait for SMPS to enter RUN mode */ + } + } +#endif /* SMPS_MODE == STM32WB0_SMPS_MODE_OFF */ +} + +/** + * @brief Perform basic hardware initialization at boot. + * + * This needs to be run from the very beginning, + * so the init priority has to be 0 (zero). + * + * @return 0 + */ +static int stm32wb0_init(void) +{ + /* Update CMSIS SystemCoreClock variable (CLK_SYS) */ + /* On reset, the 64MHz HSI is selected as input to + * the SYSCLKPRE prescaler, set to 4, resulting in + * CLK_SYS being equal to 16MHz. + */ + SystemCoreClock = 16000000U; + + /* Remap address 0 to user flash memory */ + LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_FLASH); + + /** + * Save application exception vector address in RAM_VR. + * By now, SCB->VTOR should point to _vector_table, + * so use that value instead of _vector_table directly. + */ + RAM_VR.AppBase = SCB->VTOR; + + /* Enable retention of all RAM banks in Deepstop */ + LL_PWR_EnableRAMBankRet(LL_PWR_RAMRET_1); +#if defined(LL_PWR_RAMRET_2) + LL_PWR_EnableRAMBankRet(LL_PWR_RAMRET_2); +#endif +#if defined(LL_PWR_RAMRET_3) + LL_PWR_EnableRAMBankRet(LL_PWR_RAMRET_3); +#endif + + /* Configure SMPS step-down converter */ + configure_smps(); + + return 0; +} + +SYS_INIT(stm32wb0_init, PRE_KERNEL_1, 0); diff --git a/soc/st/stm32/stm32wb0x/soc.h b/soc/st/stm32/stm32wb0x/soc.h new file mode 100644 index 00000000000000..57d14e761c3bc6 --- /dev/null +++ b/soc/st/stm32/stm32wb0x/soc.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file SoC configuration macros for the STM32WB0 family processors. + * + */ + + +#ifndef _STM32WB0_SOC_H_ +#define _STM32WB0_SOC_H_ + +#ifndef _ASMLANGUAGE + +#include + +#endif /* !_ASMLANGUAGE */ + +#endif /* _STM32WB0_SOC_H_ */ From d6e53f9f16a0f11a809e269072f7d1809a02b030 Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Fri, 17 May 2024 14:04:27 +0200 Subject: [PATCH 04/16] dts: bindings: clock: add STM32WB0 RCC and LSI Add the Device Tree bindings for the RCC and LSI clock of STM32WB0 series. Signed-off-by: Mathieu Choplain --- dts/bindings/clock/st,stm32wb0-lsi-clock.yaml | 16 ++++++ dts/bindings/clock/st,stm32wb0-rcc.yaml | 56 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 dts/bindings/clock/st,stm32wb0-lsi-clock.yaml create mode 100644 dts/bindings/clock/st,stm32wb0-rcc.yaml diff --git a/dts/bindings/clock/st,stm32wb0-lsi-clock.yaml b/dts/bindings/clock/st,stm32wb0-lsi-clock.yaml new file mode 100644 index 00000000000000..e08eca3656f480 --- /dev/null +++ b/dts/bindings/clock/st,stm32wb0-lsi-clock.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2024 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: | + STM32WB0 LSI Clock + + The STM32WB0 MCUs are equipped with a regular RC LSI clock with a frequency of 24~40kHz. + The SoCs are also equipped with hardware to perform LSI frequency measurement, which + allows to adapt all frequency-based calculations to a somewhat accurate value, ensuring + that the software does not get too much out of sync with real-world time. + + Several LSI frequency measurement options can be configured via Kconfig. + +compatible: "st,stm32wb0-lsi-clock" + +include: [fixed-clock.yaml] diff --git a/dts/bindings/clock/st,stm32wb0-rcc.yaml b/dts/bindings/clock/st,stm32wb0-rcc.yaml new file mode 100644 index 00000000000000..bfebe71de2bd2f --- /dev/null +++ b/dts/bindings/clock/st,stm32wb0-rcc.yaml @@ -0,0 +1,56 @@ +# Copyright (c) 2024 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: | + STM32WB0 Reset and Clock controller node for STM32WB0 devices + This node is in charge of the system clock ('SYSCLK') source + selection and generation. + +compatible: "st,stm32wb0-rcc" + +include: [clock-controller.yaml, base.yaml] + +properties: + reg: + required: true + + "#clock-cells": + const: 2 + + clock-frequency: + required: true + type: int + description: | + default frequency in Hz for clock output + + slow-clock: + type: phandle + description: | + Slow clock source selection. + On STM32WB0, all slow clock devices are clocked from the same + slow clock source, which is selected by this property. + + The slow clock can be either clk_lsi, clk_lse, or clk_16mhz_div512. + + clksys-prescaler: + type: int + required: true + enum: + - 1 + - 2 + - 4 + - 8 + - 16 + - 32 + - 64 + description: | + CLK_SYS prescaler. Defines actual core clock frequency (CLK_SYS) based + on system frequency input (SYSCLK). + The CLK_SYS is used to clock the CPU, AHB, APB, memories and PKA. + + NOTE: if the 32MHz HSE is used as SYSCLK source, the prescaler cannot + be set to 64. + +clock-cells: + - bus + - bits From 28abbb674162aff1332dd2adea8fc37fec53b2cd Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Mon, 2 Sep 2024 17:50:01 +0200 Subject: [PATCH 05/16] dts: bindings: intc: add STM32WB0 GPIO interrupt controller Add the Device Tree binding for the STM32WB0 GPIO interrupt controller. Signed-off-by: Mathieu Choplain --- .../interrupt-controller/st,stm32wb0-gpio-intc.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 dts/bindings/interrupt-controller/st,stm32wb0-gpio-intc.yaml diff --git a/dts/bindings/interrupt-controller/st,stm32wb0-gpio-intc.yaml b/dts/bindings/interrupt-controller/st,stm32wb0-gpio-intc.yaml new file mode 100644 index 00000000000000..c100d75233b5d2 --- /dev/null +++ b/dts/bindings/interrupt-controller/st,stm32wb0-gpio-intc.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: STM32WB0 GPIO Interrupt Controller + +compatible: "st,stm32wb0-gpio-intc" + +include: "st,stm32-exti.yaml" From c66f0f0b195da943a7da5bf17c70d59c25ea55d3 Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Thu, 11 Jul 2024 18:18:31 +0200 Subject: [PATCH 06/16] dts: bindings: flash: add STM32WB0 flash controller Add the Device Tree binding for the STM32WB0 flash controller. Signed-off-by: Mathieu Choplain --- .../flash_controller/st,stm32wb0-flash-controller.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 dts/bindings/flash_controller/st,stm32wb0-flash-controller.yaml diff --git a/dts/bindings/flash_controller/st,stm32wb0-flash-controller.yaml b/dts/bindings/flash_controller/st,stm32wb0-flash-controller.yaml new file mode 100644 index 00000000000000..4523bf923bb7ed --- /dev/null +++ b/dts/bindings/flash_controller/st,stm32wb0-flash-controller.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: STM32WB0 series flash controller + +compatible: "st,stm32wb0-flash-controller" + +include: flash-controller.yaml From 6305444bba2953ecdc091bc7e2bf916d980d2bfc Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Fri, 6 Sep 2024 14:32:08 +0200 Subject: [PATCH 07/16] dts: bindings: power: add STM32WB0 power controller Add a Device Tree binding for the STM32WB0 power controller. Signed-off-by: Mathieu Choplain --- dts/bindings/power/st,stm32wb0-pwr.yaml | 136 ++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 dts/bindings/power/st,stm32wb0-pwr.yaml diff --git a/dts/bindings/power/st,stm32wb0-pwr.yaml b/dts/bindings/power/st,stm32wb0-pwr.yaml new file mode 100644 index 00000000000000..e5239ae80cdd04 --- /dev/null +++ b/dts/bindings/power/st,stm32wb0-pwr.yaml @@ -0,0 +1,136 @@ +# Copyright (c) 2024 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: STM32WB0 power controller + +compatible: "st,stm32wb0-pwr" + +include: "st,stm32-pwr.yaml" + +properties: + smps-mode: + type: string + required: true + description: | + SMPS mode selection + + OFF: + - SMPS converter disabled + - LDOs supply voltage: VDD + + WARNING: The SMPS must not be disabled on board using the + 'SMPS supply configuration', so this mode should NEVER be + selected on these boards. Only use this mode if your board + is using the 'NOSMPS supply configuration'. + + Refer to RM0505 §5.5 "SMPS step down regulator" for details. + + PRECHARGE: (also called BYPASS) + - SMPS converter enabled - clock disabled + - LDOs supply voltage: VDD (though SMPS) + - Supplied current limitation can be programmed + + RUN: (also called ON) + - SMPS converter enabled - clock enabled + - LDOs supply voltage: regulated SMPS output + - Target output voltage can be programmed + enum: + - "OFF" + - "PRECHARGE" + - "RUN" + + smps-bom: + type: int + description: | + SMPS L/C BOM selection + + Indicates which L/C BOM is present on the board: + 1: 1.5µH inductance, 2.2µF output capacitance + 2: 2.2µH inductance, 4.7µF output capacitance + 3: 10µH inductance, 4.7µF output capacitance + Refer to RM0505 §5.5 for more details about L/C BOM. + + This property is required if `smps-mode` is not "OFF". + enum: + - 1 + - 2 + - 3 + + smps-clock-prescaler: + type: int + default: 4 + description: | + SMPS clock prescaler factor + + The SMPS clock, CLK_SMPS, comes from a 16 MHz source that + goes through one of two prescalers, with a respective + division factor of 2 and 4. This property selects which + prescaler should be used. + + Setting this property to 2 results in CLK_SMPS = 8 MHz. + Setting this property to 4 results in CLK_SMPS = 4 MHz. + + All features of the SMPS can be used regardless of which + prescaler has been chosen. Since a slower clock results + in less power consumption, this property defaults to 4. + + This property is only used if `smps-mode` is not "OFF". + enum: + - 2 + - 4 + + smps-lp-floating: + type: boolean + description: | + Floating SMPS output in low-power state + + If this property is set, the SMPS output pin (VFBSD) + is left floating when the SoC is in low-power state. + + If this property is not set, the SMPS is placed in + PRECHARGE mode when the SoC is in low-power state. + (i.e., VFBSD voltage is equal to VDD) + + smps-current-limit: + type: string + default: "20" + description: | + SMPS output current limit (in mA) + + The default value corresponds to the hardware reset + configuration of 20 mA output current limit. + + This property is only used if `smps-mode` is "PRECHARGE". + enum: + - "2_5" + - "5" + - "10" + - "20" + + smps-output-voltage: + type: string + default: "1V40" + description: | + SMPS regulated output voltage + + The default value corresponds to the hardware reset + configuration of 1.40V regulated output. + + This property is only used if `smps-mode` is "RUN". + enum: + - "1V20" + - "1V25" + - "1V30" + - "1V35" + - "1V40" + - "1V45" + - "1V50" + - "1V55" + - "1V60" + - "1V65" + - "1V70" + - "1V75" + - "1V80" + - "1V85" + - "1V90" + - "1V95" From 05a0950281581dcb39279d69cef3afd72e7819f3 Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Tue, 28 May 2024 10:14:11 +0200 Subject: [PATCH 08/16] include: dt-bindings: add STM32WB0 reset header Adds the reset controller dt-bindings header for STM32WB0 series. Signed-off-by: Mathieu Choplain --- .../zephyr/dt-bindings/reset/stm32wb0_reset.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 include/zephyr/dt-bindings/reset/stm32wb0_reset.h diff --git a/include/zephyr/dt-bindings/reset/stm32wb0_reset.h b/include/zephyr/dt-bindings/reset/stm32wb0_reset.h new file mode 100644 index 00000000000000..8fe2a84a5f7320 --- /dev/null +++ b/include/zephyr/dt-bindings/reset/stm32wb0_reset.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_RESET_STM32WB0_RESET_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_RESET_STM32WB0_RESET_H_ + +#include "stm32-common.h" + +/* RCC bus reset register offset */ +#define STM32_RESET_BUS_AHB0 0x30 +#define STM32_RESET_BUS_APB0 0x34 +#define STM32_RESET_BUS_APB1 0x38 +#define STM32_RESET_BUS_APB2 0x3C + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_RESET_STM32WB0_RESET_H_ */ From 3eb08bcab9e93cc5b77d2eb1126717606f6974f4 Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Tue, 28 May 2024 10:10:22 +0200 Subject: [PATCH 09/16] drivers: clock: add STM32WB0 clock control Add control driver for STM32WB0 series, with support for all clock sources. Signed-off-by: Mathieu Choplain --- drivers/clock_control/CMakeLists.txt | 2 + drivers/clock_control/Kconfig.stm32 | 45 + drivers/clock_control/clock_stm32_ll_wb0.c | 792 ++++++++++++++++++ .../clock_control/stm32_clock_control.h | 23 + .../zephyr/dt-bindings/clock/stm32wb0_clock.h | 70 ++ 5 files changed, 932 insertions(+) create mode 100644 drivers/clock_control/clock_stm32_ll_wb0.c create mode 100644 include/zephyr/dt-bindings/clock/stm32wb0_clock.h diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index a6cce73e6964b1..10fcc33f01a706 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -56,6 +56,8 @@ elseif(CONFIG_SOC_SERIES_STM32H5X) zephyr_library_sources(clock_stm32_ll_h5.c) elseif(CONFIG_SOC_SERIES_STM32U5X) zephyr_library_sources(clock_stm32_ll_u5.c) +elseif(CONFIG_SOC_SERIES_STM32WB0X) + zephyr_library_sources(clock_stm32_ll_wb0.c) elseif(CONFIG_SOC_SERIES_STM32WBAX) zephyr_library_sources(clock_stm32_ll_wba.c) else() diff --git a/drivers/clock_control/Kconfig.stm32 b/drivers/clock_control/Kconfig.stm32 index 9c2606e96a1837..487fc00541fd36 100644 --- a/drivers/clock_control/Kconfig.stm32 +++ b/drivers/clock_control/Kconfig.stm32 @@ -47,6 +47,51 @@ config CLOCK_STM32_MUX for a specific domain. For instance per_ck clock on STM32H7 or CLK48 clock +menu "STM32WB0 LSI options" + depends on DT_HAS_ST_STM32WB0_LSI_CLOCK_ENABLED + +config STM32WB0_LSI_MEASUREMENT_WINDOW + int "Size of LSI measurement window (in periods)" + default 32 + range 23 256 + help + Size of the LSI measurement window (# of LSI periods) + + The measurement process involves waiting for a certain amount of LSI periods + to occur, in order to determine precisely the LSI period, and thus frequency. + + This property controls how much LSI periods are required for each measure. + Bigger window sizes increase accuracy of the measure, but increase the time + needed to complete it. Since fLSI >= 24kHz, increasing the measurement window + size makes each measure roughly 42µs slower in the worst case. + + Minimal value is a recommendation from RM0505 Rev.1 §25.8.2, and maximum + value is a hardware limitation. + +config STM32WB0_LSI_RUNTIME_MEASUREMENT_INTERVAL + int "LSI run-time measurement interval (ms)" + default 0 + help + Interval at which runtime measurements should be performed, in milliseconds + + Since the LSI RC frequency is affected by temperature, which is not stable + across time, it is recommended to perform measurements of the LSI frequency + at regular intervals to obtain better accuracy. + + This property enables runtime LSI measurement if present. In this case, + a background thread is created and performs LSI measurements, sleeping + the amount of time specified in this property between each measure. This + thread is also tasked with updating the control registers of peripherals + affected by slow clock drift such as RTC or IWDG, in collaboration with + the peripherals' drivers. Note that this increases the memory footprint + of the clock control driver, and may increase power consumption. + + Setting this option to the default value of "0" disables runtime frequency + measurements - the result of a single measure performed at boot will be + treated as LSI frequency for the lifetime of the application. + +endmenu # DT_HAS_ST_STM32WB0_LSI_CLOCK_ENABLED + # Micro-controller Clock output configuration options choice diff --git a/drivers/clock_control/clock_stm32_ll_wb0.c b/drivers/clock_control/clock_stm32_ll_wb0.c new file mode 100644 index 00000000000000..6654867e01e757 --- /dev/null +++ b/drivers/clock_control/clock_stm32_ll_wb0.c @@ -0,0 +1,792 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Driver definitions */ +#define RCC_REG(_reg_offset) (DT_REG_ADDR(STM32_CLOCK_CONTROL_NODE) + (_reg_offset)) +#define RADIO_CTRL_IRQn 21 /* Not provided by CMSIS; must be declared manually */ + +#define CLOCK_FREQ_64MHZ (64000000U) +#define CLOCK_FREQ_32MHZ (32000000U) +#define CLOCK_FREQ_16MHZ (16000000U) + +/* Device tree node definitions */ +#define DT_RCC_SLOWCLK_NODE DT_PHANDLE(STM32_CLOCK_CONTROL_NODE, slow_clock) +#define DT_LSI_NODE DT_NODELABEL(clk_lsi) + +/* Device tree properties definitions */ +#define STM32_WB0_CLKSYS_PRESCALER \ + DT_PROP(STM32_CLOCK_CONTROL_NODE, clksys_prescaler) + +#if DT_NODE_HAS_PROP(STM32_CLOCK_CONTROL_NODE, slow_clock) + +# if !DT_NODE_HAS_STATUS(DT_RCC_SLOWCLK_NODE, okay) +# error slow-clock source is not enabled +# endif + +# if DT_SAME_NODE(DT_RCC_SLOWCLK_NODE, DT_LSI_NODE) +# define STM32_WB0_SLOWCLK_SRC LL_RCC_LSCO_CLKSOURCE_LSI +# elif DT_SAME_NODE(DT_RCC_SLOWCLK_NODE, DT_NODELABEL(clk_lse)) +# define STM32_WB0_SLOWCLK_SRC LL_RCC_LSCO_CLKSOURCE_LSE +# elif DT_SAME_NODE(DT_RCC_SLOWCLK_NODE, DT_NODELABEL(clk_16mhz_div512)) +# define STM32_WB0_SLOWCLK_SRC LL_RCC_LSCO_CLKSOURCE_HSI64M_DIV2048 +# else +# error Invalid device selected as slow-clock +# endif + +#endif /* DT_NODE_HAS_PROP(STM32_CLOCK_CONTROL_NODE, slow_clock) */ + +#if DT_NODE_HAS_PROP(DT_LSI_NODE, runtime_measurement_interval) + #define STM32_WB0_RUNTIME_LSI_CALIBRATION 1 + #define STM32_WB0_LSI_RUNTIME_CALIB_INTERVAL \ + DT_PROP(DT_LSI_NODE, runtime_measurement_interval) +#endif /* DT_NODE_HAS_PROP(clk_lsi, runtime_calibration_settings) */ + +/* Verify device tree properties are correct */ +BUILD_ASSERT(!IS_ENABLED(STM32_SYSCLK_SRC_HSE) || STM32_WB0_CLKSYS_PRESCALER != 64, + "clksys-prescaler cannot be 64 when SYSCLK source is Direct HSE"); + +#if defined(STM32_LSI_ENABLED) +/* Check clock configuration allows MR_BLE IP to work. + * This IP is required to perform LSI measurements. + */ +# if defined(STM32_SYSCLK_SRC_HSI) + /* When using HSI without PLL, the "16MHz" output is not actually 16MHz, since + * 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, + "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, + "System clock frequency must be at least 16MHz to use LSI"); +# endif /* STM32_SYSCLK_SRC_HSI */ + +/** + * @brief Variable holding the "current frequency of LSI", according + * to the measurement process. This variable is updated each time + * a new measurement of the LSI frequency is performed. + */ +static volatile uint32_t stm32wb0_lsi_frequency = STM32_LSI_FREQ; + +/** + * @brief Perform a measurement of the LSI frequency and updates + * the @p stm32wb0_lsi_frequency global variable based on the results. + * + * @param wait_event Semaphore to wait for completion of the measurement + * If NULL, RADIO_CONTROL registers are polled instead. + */ +static void measure_lsi_frequency(struct k_sem *wait_event) +{ + uint32_t fast_clock_cycles_elapsed; + + /* Ensure calibration flag is clear */ + LL_RADIO_TIMER_ClearFlag_LSICalibrationEnded(RADIO_CTRL); + + /* Setup the calibration parameters + * + * NOTE: (size - 1) is required to get the correct count, + * because the value in the register is one less than the + * actual number of periods requested for calibration. + */ + LL_RADIO_TIMER_SetLSIWindowCalibrationLength(RADIO_CTRL, + (CONFIG_STM32WB0_LSI_MEASUREMENT_WINDOW - 1)); + + /* Start LSI calibration */ + LL_RADIO_TIMER_StartLSICalibration(RADIO_CTRL); + + if (wait_event) { + /* Wait for semaphore to be signaled */ + k_sem_take(wait_event, K_FOREVER); + } else { + while (!LL_RADIO_TIMER_IsActiveFlag_LSICalibrationEnded(RADIO_CTRL)) { + /* Wait for calibration to finish (polling) */ + } + + /* Clear calibration complete flag / interrupt */ + LL_RADIO_TIMER_ClearFlag_LSICalibrationEnded(RADIO_CTRL); + } + + /* Read calibration results */ + fast_clock_cycles_elapsed = LL_RADIO_TIMER_GetLSIPeriod(RADIO_CTRL); + + /** + * Calculate LSI frequency from calibration results and update + * the corresponding global variable + * + * LSI calibration counts the amount of 16MHz clock half-periods that + * occur until a certain amount of slow clock cycles have been observed. + * + * @p fast_clock_cycles_elapsed is the number of 16MHz clock half-periods + * elapsed while waiting for @p STM32_WB0_LSI_MEASURE_WINDOW_SIZE LSI periods + * to occur. The LSI frequency can be calculated the following way: + * + * t = / + * + * ==> Time taken for calibration: + * + * tCALIB = @p fast_clock_cycles_elapsed / (2 * 16MHz) + * + * ==> LSI period: + * + * tLSI = tCALIB / @p STM32_WB0_LSI_MEASURE_WINDOW_SIZE + * + * ( @p fast_clock_cycles_elapsed / (2 * 16MHz) ) + * = ------------------------------------------------ + * @p STM32_WB0_LSI_MEASURE_WINDOW_SIZE + * + * ==> LSI frequency: + * + * fLSI = (1 / tLSI) + * + * @p STM32_WB0_LSI_MEASURE_WINDOW_SIZE + * = ------------------------------------------------ + * ( @p fast_clock_cycles_elapsed / (2 * 16MHz) ) + * + * (2 * 16MHz) * @p STM32_WB0_LSI_MEASURE_WINDOW_SIZE + * = ----------------------------------------------------- + * @p fast_clock_cycles_elapsed + * + * NOTE: The division must be performed first to avoid 32-bit overflow. + */ + stm32wb0_lsi_frequency = + (CLOCK_FREQ_32MHZ / fast_clock_cycles_elapsed) + * CONFIG_STM32WB0_LSI_MEASUREMENT_WINDOW; +} +#endif /* STM32_LSI_ENABLED */ + +/** @brief Verifies if provided domain / bus clock is currently active */ +int enabled_clock(uint32_t src_clk) +{ + int r = 0; + + switch (src_clk) { + case STM32_SRC_SYSCLK: + break; + case STM32_SRC_LSE: + if (!IS_ENABLED(STM32_LSE_ENABLED)) { + r = -ENOTSUP; + } + break; + case STM32_SRC_LSI: + if (!IS_ENABLED(STM32_LSI_ENABLED)) { + r = -ENOTSUP; + } + break; + case STM32_SRC_CLKSLOWMUX: + break; + case STM32_SRC_CLK16MHZ: + break; + case STM32_SRC_CLK32MHZ: + break; + default: + return -ENOTSUP; + } + + return r; +} + +static inline int stm32_clock_control_on(const struct device *dev, + clock_control_subsys_t sub_system) +{ + struct stm32_pclken *pclken = (struct stm32_pclken *)(sub_system); + const mem_addr_t reg = RCC_REG(pclken->bus); + volatile uint32_t temp; + + ARG_UNUSED(dev); + if (!IN_RANGE(pclken->bus, STM32_PERIPH_BUS_MIN, STM32_PERIPH_BUS_MAX)) { + /* Attempting to change domain clock */ + return -ENOTSUP; + } + + sys_set_bits(reg, pclken->enr); + + /* Read back register to be blocked by RCC + * until peripheral clock enabling is complete + */ + temp = sys_read32(reg); + UNUSED(temp); + + return 0; +} + +static inline int stm32_clock_control_off(const struct device *dev, + clock_control_subsys_t sub_system) +{ + struct stm32_pclken *pclken = (struct stm32_pclken *)(sub_system); + const mem_addr_t reg = RCC_REG(pclken->bus); + + ARG_UNUSED(dev); + if (!IN_RANGE(pclken->bus, STM32_PERIPH_BUS_MIN, STM32_PERIPH_BUS_MAX)) { + /* Attempting to change domain clock */ + return -ENOTSUP; + } + + sys_clear_bits(reg, pclken->enr); + + return 0; +} + +static inline int stm32_clock_control_configure(const struct device *dev, + clock_control_subsys_t sub_system, + void *data) +{ + struct stm32_pclken *pclken = (struct stm32_pclken *)sub_system; + const uint32_t shift = STM32_CLOCK_SHIFT_GET(pclken->enr); + mem_addr_t reg = RCC_REG(STM32_CLOCK_REG_GET(pclken->enr)); + int err; + + ARG_UNUSED(dev); + ARG_UNUSED(data); + + err = enabled_clock(pclken->bus); + if (err < 0) { + /* Attempting to configure an unavailable or invalid clock */ + return err; + } + + sys_clear_bits(reg, STM32_CLOCK_MASK_GET(pclken->enr) << shift); + sys_set_bits(reg, STM32_CLOCK_VAL_GET(pclken->enr) << shift); + + return 0; +} + +static inline int get_apb0_periph_clkrate(uint32_t enr, uint32_t *rate, + uint32_t slow_clock, uint32_t sysclk, uint32_t clk_sys) +{ + switch (enr) { + /* Slow clock peripherals: RTC & IWDG */ + case LL_APB0_GRP1_PERIPH_RTC: + case LL_APB0_GRP1_PERIPH_WDG: + *rate = slow_clock; + break; + + /* SYSCLK peripherals: all timers */ +#if defined(TIM1) + case LL_APB0_GRP1_PERIPH_TIM1: +#endif +#if defined(TIM2) + case LL_APB0_GRP1_PERIPH_TIM2: +#endif +#if defined(TIM16) + case LL_APB0_GRP1_PERIPH_TIM16: +#endif +#if defined(TIM17) + case LL_APB0_GRP1_PERIPH_TIM17: +#endif + *rate = sysclk; + break; + + /* CLK_SYS peripherals: SYSCFG */ + case LL_APB0_GRP1_PERIPH_SYSCFG: + *rate = clk_sys; + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static inline int get_apb1_periph_clkrate(uint32_t enr, uint32_t *rate, + uint32_t clk_sys) +{ + switch (enr) { +#if defined(SPI1) + case LL_APB1_GRP1_PERIPH_SPI1: + *rate = clk_sys; + break; +#endif +#if defined(SPI2) + case LL_APB1_GRP1_PERIPH_SPI2: + switch (LL_RCC_GetSPI2I2SClockSource()) { + case LL_RCC_SPI2_I2S_CLK16M: + *rate = CLOCK_FREQ_16MHZ; + break; + case LL_RCC_SPI2_I2S_CLK32M: + *rate = CLOCK_FREQ_32MHZ; + break; + default: + CODE_UNREACHABLE; + } + break; +#endif + case LL_APB1_GRP1_PERIPH_SPI3: + switch (LL_RCC_GetSPI3I2SClockSource()) { + case LL_RCC_SPI3_I2S_CLK16M: + *rate = CLOCK_FREQ_16MHZ; + break; + case LL_RCC_SPI3_I2S_CLK32M: + *rate = CLOCK_FREQ_32MHZ; + break; +#if defined(LL_RCC_SPI3_I2S_CLK64M) + case LL_RCC_SPI3_I2S_CLK64M: + *rate = CLOCK_FREQ_64MHZ; + break; +#endif + default: + CODE_UNREACHABLE; + } + break; + + case LL_APB1_GRP1_PERIPH_I2C1: +#if defined(I2C2) + case LL_APB1_GRP1_PERIPH_I2C2: +#endif + *rate = CLOCK_FREQ_16MHZ; + break; + + case LL_APB1_GRP1_PERIPH_ADCDIG: + case LL_APB1_GRP1_PERIPH_ADCANA: + case (LL_APB1_GRP1_PERIPH_ADCDIG | LL_APB1_GRP1_PERIPH_ADCANA): + /* ADC has two enable bits - accept all combinations. */ + *rate = CLOCK_FREQ_16MHZ; + break; + + case LL_APB1_GRP1_PERIPH_USART1: + *rate = CLOCK_FREQ_16MHZ; + break; + + case LL_APB1_GRP1_PERIPH_LPUART1: +#if !defined(RCC_CFGR_LPUCLKSEL) + *rate = CLOCK_FREQ_16MHZ; +#else + switch (LL_RCC_GetLPUARTClockSource()) { + case LL_RCC_LPUCLKSEL_CLK16M: + *rate = CLOCK_FREQ_16MHZ; + break; + case LL_RCC_LPUCLKSEL_CLKLSE: + *rate = STM32_LSE_FREQ; + break; + default: + CODE_UNREACHABLE; + } +#endif + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static int stm32_clock_control_get_subsys_rate(const struct device *dev, + clock_control_subsys_t sub_system, + uint32_t *rate) +{ + + struct stm32_pclken *pclken = (struct stm32_pclken *)(sub_system); + uint32_t sysclk, slow_clock, clk_sys; +#if defined(STM32_LSI_ENABLED) + const uint32_t clk_lsi = stm32wb0_lsi_frequency; +#else + const uint32_t clk_lsi = 0; +#endif + + + ARG_UNUSED(dev); + + /* Obtain SYSCLK frequency by checking which source drives high-speed clock tree. + * If Direct HSE is enabled, the high-speed tree is clocked by HSE @ 32MHz. + * Otherwise, the high-speed tree is clocked by the RC64MPLL clock @ 64MHz. + * + * NOTE: it is NOT possible to use the usual 'SystemCoreClock * Prescaler' approach on + * STM32WB0 because the prescaler configuration is not affected by input clock variation: + * setting CLKSYSDIV = 1 results in 32MHz CLK_SYS, regardless of SYSCLK being 32 or 64MHZ. + */ + if (LL_RCC_DIRECT_HSE_IsEnabled()) { + sysclk = STM32_HSE_FREQ; + } else { + sysclk = STM32_HSI_FREQ; + } + + /* Obtain CLK_SYS (AHB0) frequency by using the CLKSYSDIV prescaler value. + * + * NOTE: LL_RCC_GetRC64MPLLPrescaler is strictly identical to LL_RCC_GetDirectHSEPrescaler + * and can be used regardless of which source is driving the high-speed clock tree. + * + * NOTE: the prescaler value must be interpreted as if source clock is 64MHz, regardless + * of which source is actually driving the high-speed clock tree. This allows using the + * following formula for calculations. + * + * NOTE: (x >> y) is equivalent to (x / 2^y) or (x / (1 << y)). + */ + clk_sys = CLOCK_FREQ_64MHZ >> LL_RCC_GetRC64MPLLPrescaler(); + + /* Obtain slow clock tree source by reading RCC_CFGR->LCOSEL. + * From this, we can deduce at which frequency the slow clock tree is running. + */ + switch (LL_RCC_LSCO_GetSource()) { + case LL_RCC_LSCO_CLKSOURCE_LSE: + slow_clock = STM32_LSE_FREQ; + break; + case LL_RCC_LSCO_CLKSOURCE_LSI: + slow_clock = clk_lsi; + break; + case LL_RCC_LSCO_CLKSOURCE_HSI64M_DIV2048: + slow_clock = CLOCK_FREQ_64MHZ / 2048; + break; + default: + __ASSERT(0, "Illegal slow clock source!"); + CODE_UNREACHABLE; + } + + switch (pclken->bus) { + case STM32_CLOCK_BUS_AHB0: + /* All peripherals on AHB0 are clocked by CLK_SYS. */ + *rate = clk_sys; + break; + case STM32_CLOCK_BUS_APB0: + return get_apb0_periph_clkrate(pclken->enr, rate, + slow_clock, sysclk, clk_sys); + case STM32_CLOCK_BUS_APB1: + return get_apb1_periph_clkrate(pclken->enr, rate, + clk_sys); + case STM32_SRC_SYSCLK: + *rate = sysclk; + break; + case STM32_SRC_LSE: + *rate = STM32_LSE_FREQ; + break; + case STM32_SRC_LSI: + *rate = clk_lsi; + break; + case STM32_SRC_CLKSLOWMUX: + *rate = slow_clock; + break; + case STM32_SRC_CLK16MHZ: + *rate = CLOCK_FREQ_16MHZ; + break; + case STM32_SRC_CLK32MHZ: + *rate = CLOCK_FREQ_32MHZ; + break; + default: + case STM32_CLOCK_BUS_APB2: + /* The only periperhal on APB2 is the MR_BLE radio. However, + * it is clocked by two sources that run at different frequencies, + * and we are unable to determine which one this API's caller cares + * about. For this reason, return ENOTSUP anyways. + * + * Note that since the only driver that might call this API is the + * Bluetooth driver, and since it can already determine both frequencies + * very easily, this should not pose any problem. + */ + return -ENOTSUP; + } + + return 0; +} + +static enum clock_control_status stm32_clock_control_get_status(const struct device *dev, + clock_control_subsys_t sub_system) +{ + struct stm32_pclken *pclken = (struct stm32_pclken *)sub_system; + + ARG_UNUSED(dev); + + if (IN_RANGE(pclken->bus, STM32_PERIPH_BUS_MIN, STM32_PERIPH_BUS_MAX)) { + /* Bus / gated clock */ + if ((sys_read32(RCC_REG(pclken->bus)) & pclken->enr) == pclken->enr) { + return CLOCK_CONTROL_STATUS_ON; + } else { + return CLOCK_CONTROL_STATUS_OFF; + } + } else { + /* Domain clock */ + if (enabled_clock(pclken->bus) == 0) { + return CLOCK_CONTROL_STATUS_ON; + } else { + return CLOCK_CONTROL_STATUS_OFF; + } + } +} + +static struct clock_control_driver_api stm32_clock_control_api = { + .on = stm32_clock_control_on, + .off = stm32_clock_control_off, + .get_rate = stm32_clock_control_get_subsys_rate, + .get_status = stm32_clock_control_get_status, + .configure = stm32_clock_control_configure, +}; + +static void set_up_fixed_clock_sources(void) +{ + if (IS_ENABLED(STM32_HSE_ENABLED)) { + /* Enable HSE */ + LL_RCC_HSE_Enable(); + while (LL_RCC_HSE_IsReady() != 1) { + /* Wait for HSE ready */ + } + } + + if (IS_ENABLED(STM32_HSI_ENABLED)) { + /* Enable HSI if not enabled */ + if (LL_RCC_HSI_IsReady() != 1) { + /* Enable HSI */ + LL_RCC_HSI_Enable(); + while (LL_RCC_HSI_IsReady() != 1) { + /* Wait for HSI ready */ + } + } + } + + if (IS_ENABLED(STM32_LSI_ENABLED)) { + LL_RCC_LSI_Enable(); + while (LL_RCC_LSI_IsReady() != 1) { + /* Wait for LSI ready */ + } + } + + if (IS_ENABLED(STM32_LSE_ENABLED)) { +#if STM32_LSE_DRIVING + /* Configure driving capability */ + LL_RCC_LSE_SetDriveCapability(STM32_LSE_DRIVING << RCC_CSSWCR_LSEDRV_Pos); +#endif + /* Unconditionally disable pull-up & pull-down on LSE pins */ + LL_PWR_SetNoPullB(LL_PWR_GPIO_BIT_12 | LL_PWR_GPIO_BIT_13); + + if (IS_ENABLED(STM32_LSE_BYPASS)) { + /* Configure LSE bypass */ + LL_RCC_LSE_EnableBypass(); + } + + /* Enable LSE Oscillator (32.768 kHz) */ + LL_RCC_LSE_Enable(); + while (!LL_RCC_LSE_IsReady()) { + /* Wait for LSE ready */ + } + } +} + +/* The STM32WB0 prescaler division factor defines vary depending on + * whether SYSCLK runs at 32MHz (Direct HSE) or 64MHz (RC64MPLL). + * The following helper macro wraps this difference. + */ +#if defined(STM32_SYSCLK_SRC_HSE) +#define LL_PRESCALER(x) LL_RCC_DIRECT_HSE_DIV_ ##x +#else +#define LL_PRESCALER(x) LL_RCC_RC64MPLL_DIV_ ##x +#endif + +/** + * @brief Converts the Kconfig STM32_WB0_CLKSYS_PRESCALER option + * to a LL_RCC_RC64MPLL_DIV_x value understandable by the LL. + */ +static inline uint32_t kconfig_to_ll_prescaler(uint32_t kcfg_pre) +{ + switch (kcfg_pre) { + case 1: + return LL_PRESCALER(1); + case 2: + return LL_PRESCALER(2); + case 4: + return LL_PRESCALER(4); + case 8: + return LL_PRESCALER(8); + case 16: + return LL_PRESCALER(16); + case 32: + return LL_PRESCALER(32); +#if !defined(STM32_SYSCLK_SRC_HSE) + /* A prescaler value of 64 is only valid when running + * off RC64MPLL because CLK_SYS must be at least 1MHz + */ + case 64: + return LL_RCC_RC64MPLL_DIV_64; +#endif + } + CODE_UNREACHABLE; +} +#undef LL_PRESCALER /* Undefine helper macro */ + +#if CONFIG_STM32WB0_LSI_RUNTIME_MEASUREMENT_INTERVAL != 0 +K_SEM_DEFINE(lsi_measurement_sema, 0, 1); + +#define NUM_SLOW_CLOCK_PERIPHERALS 3 + +/** + * Reserve one slot for each slow clock peripheral to ensure each + * peripheral's driver can register a callback to cope with clock drift. + */ +static lsi_update_cb_t lsi_update_callbacks[NUM_SLOW_CLOCK_PERIPHERALS]; + +int stm32wb0_register_lsi_update_callback(lsi_update_cb_t cb) +{ + for (size_t i = 0; i < ARRAY_SIZE(lsi_update_callbacks); i++) { + if (lsi_update_callbacks[i] == NULL) { + lsi_update_callbacks[i] = cb; + return 0; + } + } + return -ENOMEM; +} + +static void radio_ctrl_isr(void) +{ + /* Clear calibration complete flag / interrupt */ + LL_RADIO_TIMER_ClearFlag_LSICalibrationEnded(RADIO_CTRL); + + /* Release the measurement thread */ + k_sem_give(&lsi_measurement_sema); +} + +static void lsi_rt_measure_loop(void) +{ + uint32_t old, new; + + while (1) { + /* Sleep until calibration interval elapses */ + k_sleep(K_MSEC(CONFIG_STM32WB0_LSI_RUNTIME_MEASUREMENT_INTERVAL)); + + old = stm32wb0_lsi_frequency; + + /* Ensure the MR_BLE IP clock is enabled. */ + if (!LL_APB2_GRP1_IsEnabledClock(LL_APB2_GRP1_PERIPH_MRBLE)) { + LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_MRBLE); + } + + /* Perform measurement, making sure we sleep on the semaphore + * signaled by the "measurement complete" interrupt handler + */ + measure_lsi_frequency(&lsi_measurement_sema); + + new = stm32wb0_lsi_frequency; + + /* If LSI frequency changed, invoke all registered callbacks */ + if (new != old) { + for (size_t i = 0; i < ARRAY_SIZE(lsi_update_callbacks); i++) { + if (lsi_update_callbacks[i]) { + lsi_update_callbacks[i](stm32wb0_lsi_frequency); + } + } + } + } +} + +#define LSI_RTM_THREAD_STACK_SIZE 512 +#define LSI_RTM_THREAD_PRIORITY K_LOWEST_APPLICATION_THREAD_PRIO + +K_KERNEL_THREAD_DEFINE(lsi_rt_measurement_thread, + LSI_RTM_THREAD_STACK_SIZE, + lsi_rt_measure_loop, + NULL, NULL, NULL, + LSI_RTM_THREAD_PRIORITY, /* No options */ 0, + /* No delay (automatic start by kernel) */ 0); +#endif + +int stm32_clock_control_init(const struct device *dev) +{ + ARG_UNUSED(dev); + + /* Set flash latency according to target CLK_SYS frequency: + * - 1 wait state when CLK_SYS > 32MHz (i.e., 64MHz configuration) + * - 0 wait states otherwise (CLK_SYS <= 32MHz) + */ + LL_FLASH_SetLatency( + (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC >= CLOCK_FREQ_32MHZ) + ? LL_FLASH_LATENCY_1 + : LL_FLASH_LATENCY_0 + ); + + /* Unconditionally enable SYSCFG clock for other drivers */ + LL_APB0_GRP1_EnableClock(LL_APB0_GRP1_PERIPH_SYSCFG); + + /* Set up indiviual enabled clocks */ + set_up_fixed_clock_sources(); + + /* Set up the slow clock mux */ +#if defined(STM32_WB0_SLOWCLK_SRC) + LL_RCC_LSCO_SetSource(STM32_WB0_SLOWCLK_SRC); +#endif + +#if defined(STM32_SYSCLK_SRC_HSE) + /* Select Direct HSE as SYSCLK source */ + LL_RCC_DIRECT_HSE_Enable(); + + while (LL_RCC_DIRECT_HSE_IsEnabled() == 0) { + /* Wait until Direct HSE is ready */ + } +#elif defined(STM32_SYSCLK_SRC_HSI) || defined(STM32_SYSCLK_SRC_PLL) + /* Select RC64MPLL (HSI/PLL) block as SYSCLK source. */ + LL_RCC_DIRECT_HSE_Disable(); + +# if defined(STM32_SYSCLK_SRC_PLL) +BUILD_ASSERT(IS_ENABLED(STM32_HSE_ENABLED), + "STM32WB0 PLL requires HSE to be enabled!"); + + /* Turn on the PLL part of RC64MPLL block */ + LL_RCC_RC64MPLL_Enable(); + while (LL_RCC_RC64MPLL_IsReady() == 0) { + /* Wait until PLL is ready */ + } + +# endif /* STM32_SYSCLK_SRC_PLL */ +#endif /* STM32_SYSCLK_SRC_* */ + + /* Set CLK_SYS prescaler */ + LL_RCC_SetRC64MPLLPrescaler( + kconfig_to_ll_prescaler(STM32_WB0_CLKSYS_PRESCALER)); + + SystemCoreClock = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC; + +#if defined(STM32_LSI_ENABLED) + /* Enable MR_BLE clock for LSI measurement. + * This is needed because we use part of the MR_BLE hardware + * to perform this measurement. + */ + LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_MRBLE); + + /* Perform a measure of the LSI frequency */ + measure_lsi_frequency(NULL); + +#if CONFIG_STM32WB0_LSI_RUNTIME_MEASUREMENT_INTERVAL == 0 + /* Disable the MR_BLE clock after the measurement */ + LL_APB2_GRP1_DisableClock(LL_APB2_GRP1_PERIPH_MRBLE); +#else + /* MR_BLE clock must not be disabled, as we're + * about to access registers of the IP again. + */ + + /* Enable LSI measurement complete IRQ at NVIC level */ + IRQ_CONNECT(RADIO_CTRL_IRQn, IRQ_PRIO_LOWEST, + radio_ctrl_isr, NULL, 0); + irq_enable(RADIO_CTRL_IRQn); + + /* Unmask IRQ at peripheral level */ + LL_RADIO_TIMER_EnableLSICalibrationIT(RADIO_CTRL); +#endif /* CONFIG_STM32WB0_LSI_RUNTIME_MEASUREMENT_INTERVAL == 0 */ +#endif /* STM32_LSI_ENABLED*/ + return 0; +} + +/** + * @brief Reset & Clock Controller device + * Note that priority is intentionally set to 1, + * so that RCC init runs just after SoC init. + */ +DEVICE_DT_DEFINE(STM32_CLOCK_CONTROL_NODE, + &stm32_clock_control_init, + NULL, NULL, NULL, + PRE_KERNEL_1, + CONFIG_CLOCK_CONTROL_INIT_PRIORITY, + &stm32_clock_control_api); diff --git a/include/zephyr/drivers/clock_control/stm32_clock_control.h b/include/zephyr/drivers/clock_control/stm32_clock_control.h index 262c7f940ac644..e9a2685623f234 100644 --- a/include/zephyr/drivers/clock_control/stm32_clock_control.h +++ b/include/zephyr/drivers/clock_control/stm32_clock_control.h @@ -38,6 +38,8 @@ #include #elif defined(CONFIG_SOC_SERIES_STM32WBX) #include +#elif defined(CONFIG_SOC_SERIES_STM32WB0X) +#include #elif defined(CONFIG_SOC_SERIES_STM32WLX) #include #elif defined(CONFIG_SOC_SERIES_STM32H5X) @@ -499,4 +501,25 @@ struct stm32_pclken { void stm32_hse_css_callback(void); #endif +#ifdef CONFIG_SOC_SERIES_STM32WB0X +/** + * @internal + * @brief Type definition for LSI frequency update callbacks + */ +typedef void (*lsi_update_cb_t)(uint32_t new_lsi_frequency); + +/** + * @internal + * @brief Registers a callback to invoke after each runtime measure and + * update of the LSI frequency is completed. + * + * @param cb Callback to invoke + * @return 0 Registration successful + * @return ENOMEM Too many callbacks registered + * + * @note Callbacks are NEVER invoked if runtime LSI measurement is disabled + */ +int stm32wb0_register_lsi_update_callback(lsi_update_cb_t cb); +#endif /* CONFIG_SOC_SERIES_STM32WB0X */ + #endif /* ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_STM32_CLOCK_CONTROL_H_ */ diff --git a/include/zephyr/dt-bindings/clock/stm32wb0_clock.h b/include/zephyr/dt-bindings/clock/stm32wb0_clock.h new file mode 100644 index 00000000000000..0505526e9683d5 --- /dev/null +++ b/include/zephyr/dt-bindings/clock/stm32wb0_clock.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_STM32WB0_CLOCK_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_STM32WB0_CLOCK_H_ + +/** Define system & low-speed clocks */ +#include "stm32_common_clocks.h" + +/** Other fixed clocks. + * - CLKSLOWMUX: used to query slow clock tree frequency + * - CLK16MHZ: secondary clock for LPUART, SPI3/I2S and BLE + * - CLK32MHZ: secondary clock for SPI3/I2S and BLE + */ +#define STM32_SRC_CLKSLOWMUX (STM32_SRC_LSI + 1) +#define STM32_SRC_CLK16MHZ (STM32_SRC_CLKSLOWMUX + 1) +#define STM32_SRC_CLK32MHZ (STM32_SRC_CLK16MHZ + 1) + +/** Bus clocks */ +#define STM32_CLOCK_BUS_AHB0 0x50 +#define STM32_CLOCK_BUS_APB0 0x54 +#define STM32_CLOCK_BUS_APB1 0x58 +#define STM32_CLOCK_BUS_APB2 0x60 + +#define STM32_PERIPH_BUS_MIN STM32_CLOCK_BUS_AHB0 +#define STM32_PERIPH_BUS_MAX STM32_CLOCK_BUS_APB2 + +#define STM32_CLOCK_REG_MASK (0xFFFFU) +#define STM32_CLOCK_REG_SHIFT (0U) +#define STM32_CLOCK_SHIFT_MASK (0x3FU) +#define STM32_CLOCK_SHIFT_SHIFT (16U) +#define STM32_CLOCK_MASK_MASK (0x1FU) +#define STM32_CLOCK_MASK_SHIFT (22U) +#define STM32_CLOCK_VAL_MASK STM32_CLOCK_MASK_MASK +#define STM32_CLOCK_VAL_SHIFT (27U) + +/** + * @brief STM32 clock configuration bit field + * + * @param reg Offset to target configuration register in RCC + * @param shift Position of field within RCC register (= field LSB's index) + * @param mask Mask of field in RCC register + * @param val Field value + * + * @note 'reg' range: 0x0~0xFFFF [ 00 : 15 ] + * @note 'shift' range: 0~63 [ 16 : 21 ] + * @note 'mask' range: 0x00~0x1F [ 22 : 26 ] + * @note 'val' range: 0x00~0x1F [ 27 : 31 ] + */ +#define STM32_CLOCK(val, mask, shift, reg) \ + ((((reg) & STM32_CLOCK_REG_MASK) << STM32_CLOCK_REG_SHIFT) | \ + (((shift) & STM32_CLOCK_SHIFT_MASK) << STM32_CLOCK_SHIFT_SHIFT) | \ + (((mask) & STM32_CLOCK_MASK_MASK) << STM32_CLOCK_MASK_SHIFT) | \ + (((val) & STM32_CLOCK_VAL_MASK) << STM32_CLOCK_VAL_SHIFT)) + +/** @brief RCC_CFGR register offset */ +#define CFGR_REG 0x08 + +/** @brief RCC_APB2ENR register offset */ +#define APB2ENR_REG 0x60 + +/** @brief Device clk sources selection helpers */ +#define LPUART1_SEL(val) STM32_CLOCK(val, 1, 13, CFGR_REG) /* WB05/WB09 only */ +#define SPI2_I2S2_SEL(val) STM32_CLOCK(val, 1, 22, CFGR_REG) /* WB06/WB07 only */ +/* `mask` is only 0x1 for WB06/WB07, but a single definition with mask=0x3 is acceptable */ +#define SPI3_I2S3_SEL(val) STM32_CLOCK(val, 3, 22, CFGR_REG) + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_STM32WB0_CLOCK_H_ */ From ac395c2be9899366fa755eef193c7a0a7f0296b0 Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Mon, 2 Sep 2024 16:43:42 +0200 Subject: [PATCH 10/16] drivers: intc: add STM32WB0 GPIO interrupt controller Adds a driver for the STM32WB0 series GPIO interrupt controller. This driver implements the STM32 GPIO INTC API, along with an extension function used to check if a specific line is available on current board. This also extends the GPIO INTC API to support level-sensitive interrupts, as this feature is available on STM32WB0. Signed-off-by: Mathieu Choplain --- drivers/interrupt_controller/CMakeLists.txt | 1 + drivers/interrupt_controller/Kconfig.stm32 | 7 + .../interrupt_controller/intc_gpio_stm32wb0.c | 295 ++++++++++++++++++ .../interrupt_controller/gpio_intc_stm32.h | 4 + 4 files changed, 307 insertions(+) create mode 100644 drivers/interrupt_controller/intc_gpio_stm32wb0.c diff --git a/drivers/interrupt_controller/CMakeLists.txt b/drivers/interrupt_controller/CMakeLists.txt index e9b7488f05b5f3..edc0d23a633f2e 100644 --- a/drivers/interrupt_controller/CMakeLists.txt +++ b/drivers/interrupt_controller/CMakeLists.txt @@ -12,6 +12,7 @@ zephyr_library_sources_ifdef(CONFIG_GIC_V1 intc_gic.c) zephyr_library_sources_ifdef(CONFIG_GIC_V2 intc_gic.c) zephyr_library_sources_ifdef(CONFIG_GIC_V3 intc_gicv3.c) zephyr_library_sources_ifdef(CONFIG_GIC_V3_ITS intc_gicv3_its.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_INTC_STM32WB0 intc_gpio_stm32wb0.c) zephyr_library_sources_ifdef(CONFIG_INTEL_VTD_ICTL intc_intel_vtd.c) zephyr_library_sources_ifdef(CONFIG_IOAPIC intc_ioapic.c) zephyr_library_sources_ifdef(CONFIG_ITE_IT8XXX2_INTC intc_ite_it8xxx2.c) diff --git a/drivers/interrupt_controller/Kconfig.stm32 b/drivers/interrupt_controller/Kconfig.stm32 index d7672a8ee400a9..27e0841f89e3f6 100644 --- a/drivers/interrupt_controller/Kconfig.stm32 +++ b/drivers/interrupt_controller/Kconfig.stm32 @@ -12,4 +12,11 @@ config EXTI_STM32 help Enable EXTI driver for STM32 line of MCUs +config GPIO_INTC_STM32WB0 + bool "GPIO Interrupt Controller Driver for STM32WB0 series" + default y + depends on DT_HAS_ST_STM32WB0_GPIO_INTC_ENABLED + help + Enable GPIO interrupt controller driver for STM32WB0 series + endif # SOC_FAMILY_STM32 diff --git a/drivers/interrupt_controller/intc_gpio_stm32wb0.c b/drivers/interrupt_controller/intc_gpio_stm32wb0.c new file mode 100644 index 00000000000000..bcc7407d834340 --- /dev/null +++ b/drivers/interrupt_controller/intc_gpio_stm32wb0.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Driver for STM32WB0 GPIO interrupt controller + * + * In this file, "EXTI" should be understood as "GPIO interrupt controller". + * There is no "External interrupt/event controller (EXTI)" in STM32WB0 MCUs. + */ + +#define DT_DRV_COMPAT st_stm32wb0_gpio_intc + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include /* For PORTA/PORTB defines */ + +#define INTC_NODE DT_DRV_INST(0) + +#define NUM_GPIO_PORTS (2) +#define NUM_PINS_PER_GPIO_PORT (16) + +#define GPIO_PORT_TABLE_INDEX(port) \ + DT_PROP_BY_IDX(INTC_NODE, line_ranges, UTIL_X2(port)) + +/* For good measure only */ +#define _NUM_GPIOS_ON_PORT_X(x) \ + DT_PROP_BY_IDX(INTC_NODE, line_ranges, UTIL_INC(UTIL_X2(x))) +BUILD_ASSERT(DT_PROP_LEN(INTC_NODE, line_ranges) == (2 * NUM_GPIO_PORTS)); +BUILD_ASSERT(_NUM_GPIOS_ON_PORT_X(STM32_PORTA) == NUM_PINS_PER_GPIO_PORT); +BUILD_ASSERT(_NUM_GPIOS_ON_PORT_X(STM32_PORTB) == NUM_PINS_PER_GPIO_PORT); +BUILD_ASSERT(GPIO_PORT_TABLE_INDEX(STM32_PORTB) == NUM_PINS_PER_GPIO_PORT); +#undef _NUM_GPIOS_ON_PORT_X + +/* wrapper for user callback */ +struct gpio_irq_cb_wrp { + stm32_gpio_irq_cb_t fn; + void *data; +}; + +/* wrapper for ISR argument block */ +struct wb0_gpio_isr_argblock { + /* LL define for first line on GPIO port + * (= least significant bit of the port's defines) + */ + uint32_t port_first_line; + /* Pointer to first element of irq_callbacks_table + * array that corresponds to this GPIO line + */ + struct gpio_irq_cb_wrp *cb_table; +}; + +/* driver data */ +struct stm32wb0_gpio_intc_data { + /* per-port user callbacks */ + struct gpio_irq_cb_wrp irq_cb_table[ + NUM_GPIO_PORTS * NUM_PINS_PER_GPIO_PORT]; +}; + +/** + * @returns the LL_EXTI_LINE_Pxy define for pin @p pin on GPIO port @p port + */ +static inline stm32_gpio_irq_line_t portpin_to_ll_exti_line(uint32_t port, gpio_pin_t pin) +{ + stm32_gpio_irq_line_t line = (1U << pin); + + if (port == STM32_PORTB) { + line <<= SYSCFG_IO_DTR_PB0_DT_Pos; + } else if (port == STM32_PORTA) { + line <<= SYSCFG_IO_DTR_PA0_DT_Pos; + } else { + __ASSERT_NO_MSG(0); + } + + return line; +} + +/** + * @returns a 32-bit value contaning: + * - <5:5> port number (0 = PORTA, 1 = PORTB) + * - <4:0> pin number (0~15) + * + * The returned value is always between 0~31. + */ +static inline uint32_t ll_exti_line_to_portpin(stm32_gpio_irq_line_t line) +{ + return LOG2(line); +} + +/** + * @brief Retrieves the user callback block for a given line + */ +static struct gpio_irq_cb_wrp *irq_cb_wrp_for_line(stm32_gpio_irq_line_t line) +{ + const struct device *const dev = DEVICE_DT_GET(INTC_NODE); + struct stm32wb0_gpio_intc_data *const data = dev->data; + const uint32_t index = ll_exti_line_to_portpin(line); + + return data->irq_cb_table + index; +} + +/* Interrupt subroutines */ +static void stm32wb0_gpio_isr(const void *userdata) +{ + const struct wb0_gpio_isr_argblock *arg = userdata; + const struct gpio_irq_cb_wrp *cb_table = arg->cb_table; + + uint32_t line = arg->port_first_line; + + for (uint32_t i = 0; i < NUM_PINS_PER_GPIO_PORT; i++, line <<= 1) { + if (LL_EXTI_IsActiveFlag(line) != 0) { + /* clear pending interrupt */ + LL_EXTI_ClearFlag(line); + + /* execute user callback if registered */ + if (cb_table[i].fn != NULL) { + const gpio_port_pins_t pin = (1U << i); + + cb_table[i].fn(pin, cb_table[i].data); + } + } + } +} + +/** + * Define the driver data early so that the macro that follows can + * refer to it directly instead of indirecting through drv->data. + */ +static struct stm32wb0_gpio_intc_data gpio_intc_data; + + /** + * This macro creates the ISR argument block for the @p pidx GPIO port, + * connects the ISR to the interrupt line and enable IRQ at NVIC level. + * + * @param node GPIO INTC device tree node + * @param pidx GPIO port index + * @param plin LL define of first line on GPIO port + */ +#define INIT_INTC_PORT_INNER(node, pidx, plin) \ + static const struct wb0_gpio_isr_argblock \ + port ##pidx ##_argblock = { \ + .port_first_line = plin, \ + .cb_table = gpio_intc_data.irq_cb_table + \ + GPIO_PORT_TABLE_INDEX(pidx) \ + }; \ + \ + IRQ_CONNECT(DT_IRQN_BY_IDX(node, pidx), \ + DT_IRQ_BY_IDX(node, pidx, priority), \ + stm32wb0_gpio_isr, &port ##pidx ##_argblock, 0); \ + \ + irq_enable(DT_IRQN_BY_IDX(node, pidx)) + +#define STM32WB0_INIT_INTC_FOR_PORT(_PORT) \ + INIT_INTC_PORT_INNER(INTC_NODE, \ + STM32_PORT ##_PORT, LL_EXTI_LINE_P ##_PORT ## 0) + +/** + * @brief Initializes the GPIO interrupt controller driver + */ +static int stm32wb0_gpio_intc_init(const struct device *dev) +{ + ARG_UNUSED(dev); + + STM32WB0_INIT_INTC_FOR_PORT(A); + + STM32WB0_INIT_INTC_FOR_PORT(B); + + return 0; +} + +DEVICE_DT_DEFINE(INTC_NODE, &stm32wb0_gpio_intc_init, + NULL, &gpio_intc_data, NULL, PRE_KERNEL_1, + CONFIG_INTC_INIT_PRIORITY, NULL); + +/** + * @brief STM32 GPIO interrupt controller API implementation + */ + +/** + * @internal + * STM32WB0 GPIO interrupt controller driver: + * The type @ref stm32_gpio_irq_line_t is used to hold the LL_EXTI_LINE_Pxy + * defines that corresponds to the specified pin. Note that these defines + * also contain the target GPIO port. + * @endinternal + */ +stm32_gpio_irq_line_t stm32_gpio_intc_get_pin_irq_line(uint32_t port, gpio_pin_t pin) +{ + return portpin_to_ll_exti_line(port, pin); +} + +void stm32_gpio_intc_enable_line(stm32_gpio_irq_line_t line) +{ + /* Enable line interrupt at INTC level */ + LL_EXTI_EnableIT(line); + + /** + * Nothing else to do; INTC interrupt line + * is enabled at NVIC level during init. + */ +} + +void stm32_gpio_intc_disable_line(stm32_gpio_irq_line_t line) +{ + /* Disable line interrupt at INTC level */ + LL_EXTI_DisableIT(line); +} + +void stm32_gpio_intc_select_line_trigger(stm32_gpio_irq_line_t line, uint32_t trg) +{ + switch (trg) { + case STM32_GPIO_IRQ_TRIG_NONE: + /** + * There is no NONE trigger on STM32WB0. + * We could disable the line interrupts here, but it isn't + * really necessary: the GPIO driver already does it by + * calling @ref stm32_gpio_intc_disable_line before calling + * us with @p trigger = STM32_EXTI_TRIG_NONE. + */ + break; + case STM32_GPIO_IRQ_TRIG_RISING: + LL_EXTI_EnableEdgeDetection(line); + LL_EXTI_DisableBothEdgeTrig(line); + LL_EXTI_EnableRisingTrig(line); + break; + case STM32_GPIO_IRQ_TRIG_FALLING: + LL_EXTI_EnableEdgeDetection(line); + LL_EXTI_DisableBothEdgeTrig(line); + LL_EXTI_DisableRisingTrig(line); + break; + case STM32_GPIO_IRQ_TRIG_BOTH: + LL_EXTI_EnableEdgeDetection(line); + LL_EXTI_EnableBothEdgeTrig(line); + break; + case STM32_GPIO_IRQ_TRIG_HIGH_LEVEL: + LL_EXTI_DisableEdgeDetection(line); + LL_EXTI_EnableRisingTrig(line); + break; + case STM32_GPIO_IRQ_TRIG_LOW_LEVEL: + LL_EXTI_DisableEdgeDetection(line); + LL_EXTI_DisableRisingTrig(line); + break; + default: + __ASSERT_NO_MSG(0); + break; + } + + /* Since it is not possible to disable triggers on STM32WB0, + * unlike in other STM32 series, activity on GPIO pin may have + * set the "event occurred" bit spuriously. + * + * Clear the bit now after reconfiguration to make sure that no + * spurious interrupt is delivered. (This works because interrupts + * are enabled *after* trigger selection by the GPIO driver, which + * is the only sensical order to do things in) + */ + LL_EXTI_ClearFlag(line); +} + +int stm32_gpio_intc_set_irq_callback(stm32_gpio_irq_line_t line, + stm32_gpio_irq_cb_t cb, void *data) +{ + struct gpio_irq_cb_wrp *cb_wrp = irq_cb_wrp_for_line(line); + + if ((cb_wrp->fn == cb) && (cb_wrp->data == data)) { + return 0; + } + + /* If line already has a callback, return EBUSY */ + if (cb_wrp->fn != NULL) { + return -EBUSY; + } + + cb_wrp->fn = cb; + cb_wrp->data = data; + + return 0; +} + +void stm32_gpio_intc_remove_irq_callback(uint32_t line) +{ + struct gpio_irq_cb_wrp *cb_wrp = irq_cb_wrp_for_line(line); + + cb_wrp->fn = cb_wrp->data = NULL; +} diff --git a/include/zephyr/drivers/interrupt_controller/gpio_intc_stm32.h b/include/zephyr/drivers/interrupt_controller/gpio_intc_stm32.h index 9199b8bad94905..cd3c5ca810a525 100644 --- a/include/zephyr/drivers/interrupt_controller/gpio_intc_stm32.h +++ b/include/zephyr/drivers/interrupt_controller/gpio_intc_stm32.h @@ -55,6 +55,10 @@ enum stm32_gpio_irq_trigger { STM32_GPIO_IRQ_TRIG_FALLING = 0x2, /* Trigger on both rising and falling edge */ STM32_GPIO_IRQ_TRIG_BOTH = 0x3, + /* Trigger on high level */ + STM32_GPIO_IRQ_TRIG_HIGH_LEVEL = 0x4, + /* Trigger on low level */ + STM32_GPIO_IRQ_TRIG_LOW_LEVEL = 0x5 }; /** From 65abda49f1fce928d41372d0fb009898c0aff631 Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Mon, 2 Sep 2024 17:19:56 +0200 Subject: [PATCH 11/16] drivers: gpio: stm32: add support for STM32WB0 Adds support for the STM32WB0 series in the existing STM32 GPIO driver. Signed-off-by: Mathieu Choplain --- drivers/gpio/gpio_stm32.c | 101 +++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 22 deletions(-) diff --git a/drivers/gpio/gpio_stm32.c b/drivers/gpio/gpio_stm32.c index 9687bbb34cb5b0..fafa9b8d340a06 100644 --- a/drivers/gpio/gpio_stm32.c +++ b/drivers/gpio/gpio_stm32.c @@ -156,6 +156,49 @@ static inline uint32_t stm32_pinval_get(gpio_pin_t pin) return pinval; } +static inline void ll_gpio_set_pin_pull(GPIO_TypeDef *GPIOx, uint32_t Pin, uint32_t Pull) +{ +#if defined(CONFIG_SOC_SERIES_STM32WB0X) + /* On STM32WB0, the PWRC PU/PD control registers should be used instead + * of the GPIO controller registers, so we cannot use LL_GPIO_SetPinPull. + */ + const uint32_t gpio = (GPIOx == GPIOA) ? LL_PWR_GPIO_A : LL_PWR_GPIO_B; + + if (Pull == LL_GPIO_PULL_UP) { + LL_PWR_EnableGPIOPullUp(gpio, Pin); + LL_PWR_DisableGPIOPullDown(gpio, Pin); + } else if (Pull == LL_GPIO_PULL_DOWN) { + LL_PWR_EnableGPIOPullDown(gpio, Pin); + LL_PWR_DisableGPIOPullUp(gpio, Pin); + } else if (Pull == LL_GPIO_PULL_NO) { + LL_PWR_DisableGPIOPullUp(gpio, Pin); + LL_PWR_DisableGPIOPullDown(gpio, Pin); + } +#else + LL_GPIO_SetPinPull(GPIOx, Pin, Pull); +#endif /* CONFIG_SOC_SERIES_STM32WB0X */ +} + +static inline uint32_t ll_gpio_get_pin_pull(GPIO_TypeDef *GPIOx, uint32_t Pin) +{ +#if defined(CONFIG_SOC_SERIES_STM32WB0X) + /* On STM32WB0, the PWRC PU/PD control registers should be used instead + * of the GPIO controller registers, so we cannot use LL_GPIO_GetPinPull. + */ + const uint32_t gpio = (GPIOx == GPIOA) ? LL_PWR_GPIO_A : LL_PWR_GPIO_B; + + if (LL_PWR_IsEnabledGPIOPullDown(gpio, Pin)) { + return LL_GPIO_PULL_DOWN; + } else if (LL_PWR_IsEnabledGPIOPullUp(gpio, Pin)) { + return LL_GPIO_PULL_UP; + } else { + return LL_GPIO_PULL_NO; + } +#else + return LL_GPIO_GetPinPull(GPIOx, Pin); +#endif /* CONFIG_SOC_SERIES_STM32WB0X */ +} + static inline void gpio_stm32_disable_pin_irqs(uint32_t port, gpio_pin_t pin) { #if defined(CONFIG_EXTI_STM32) @@ -267,7 +310,7 @@ static void gpio_stm32_configure_raw(const struct device *dev, gpio_pin_t pin, LL_GPIO_SetPinSpeed(gpio, pin_ll, ospeed >> STM32_OSPEEDR_SHIFT); - LL_GPIO_SetPinPull(gpio, pin_ll, pupd >> STM32_PUPDR_SHIFT); + ll_gpio_set_pin_pull(gpio, pin_ll, pupd >> STM32_PUPDR_SHIFT); if (mode == STM32_MODER_ALT_MODE) { if (pin < 8) { @@ -503,7 +546,7 @@ static int gpio_stm32_get_config(const struct device *dev, pin_ll = stm32_pinval_get(pin); pin_config.type = LL_GPIO_GetPinOutputType(gpio, pin_ll); - pin_config.pupd = LL_GPIO_GetPinPull(gpio, pin_ll); + pin_config.pupd = ll_gpio_get_pin_pull(gpio, pin_ll); pin_config.mode = LL_GPIO_GetPinMode(gpio, pin_ll); pin_config.out_state = LL_GPIO_IsOutputPinSet(gpio, pin_ll); @@ -521,7 +564,7 @@ static int gpio_stm32_pin_interrupt_configure(const struct device *dev, const struct gpio_stm32_config *cfg = dev->config; struct gpio_stm32_data *data = dev->data; const stm32_gpio_irq_line_t irq_line = stm32_gpio_intc_get_pin_irq_line(cfg->port, pin); - uint32_t edge = 0; + uint32_t irq_trigger = 0; int err = 0; #ifdef CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT @@ -539,10 +582,39 @@ static int gpio_stm32_pin_interrupt_configure(const struct device *dev, goto exit; } - /* Level trigger interrupts not supported */ if (mode == GPIO_INT_MODE_LEVEL) { - err = -ENOTSUP; - goto exit; + /* Level-sensitive interrupts are only supported on STM32WB0. */ + if (!IS_ENABLED(CONFIG_SOC_SERIES_STM32WB0X)) { + err = -ENOTSUP; + goto exit; + } else { + switch (trig) { + case GPIO_INT_TRIG_LOW: + irq_trigger = STM32_GPIO_IRQ_TRIG_LOW_LEVEL; + break; + case GPIO_INT_TRIG_HIGH: + irq_trigger = STM32_GPIO_IRQ_TRIG_HIGH_LEVEL; + break; + default: + err = -EINVAL; + goto exit; + } + } + } else { + switch (trig) { + case GPIO_INT_TRIG_LOW: + irq_trigger = STM32_GPIO_IRQ_TRIG_FALLING; + break; + case GPIO_INT_TRIG_HIGH: + irq_trigger = STM32_GPIO_IRQ_TRIG_RISING; + break; + case GPIO_INT_TRIG_BOTH: + irq_trigger = STM32_GPIO_IRQ_TRIG_BOTH; + break; + default: + err = -EINVAL; + goto exit; + } } if (stm32_gpio_intc_set_irq_callback(irq_line, gpio_stm32_isr, data) != 0) { @@ -550,26 +622,11 @@ static int gpio_stm32_pin_interrupt_configure(const struct device *dev, goto exit; } - switch (trig) { - case GPIO_INT_TRIG_LOW: - edge = STM32_GPIO_IRQ_TRIG_FALLING; - break; - case GPIO_INT_TRIG_HIGH: - edge = STM32_GPIO_IRQ_TRIG_RISING; - break; - case GPIO_INT_TRIG_BOTH: - edge = STM32_GPIO_IRQ_TRIG_BOTH; - break; - default: - err = -EINVAL; - goto exit; - } - #if defined(CONFIG_EXTI_STM32) stm32_exti_set_line_src_port(pin, cfg->port); #endif - stm32_gpio_intc_select_line_trigger(irq_line, edge); + stm32_gpio_intc_select_line_trigger(irq_line, irq_trigger); stm32_gpio_intc_enable_line(irq_line); From 1db47b2cdfd6781e08ea191bd18c265ba9c473bc Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Thu, 11 Jul 2024 18:17:06 +0200 Subject: [PATCH 12/16] drivers: flash: stm32: add STM32WB0 flash controller Adds a basic driver for the STM32WB0 flash controller (read/erase/write). Extended operations are not supported by this driver. Signed-off-by: Mathieu Choplain --- drivers/flash/CMakeLists.txt | 2 + drivers/flash/flash_stm32wb0x.c | 459 ++++++++++++++++++++++++++++++++ 2 files changed, 461 insertions(+) create mode 100644 drivers/flash/flash_stm32wb0x.c diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index c82276611af737..64b9c149db39f7 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -84,6 +84,8 @@ if(CONFIG_SOC_FLASH_STM32) else() zephyr_library_sources_ifdef(CONFIG_DT_HAS_ST_STM32_FLASH_CONTROLLER_ENABLED flash_stm32.c flash_stm32wbax.c) endif() + elseif(CONFIG_SOC_SERIES_STM32WB0X) + zephyr_library_sources_ifdef(CONFIG_DT_HAS_ST_STM32WB0_FLASH_CONTROLLER_ENABLED flash_stm32wb0x.c) else() if(CONFIG_DT_HAS_ST_STM32_FLASH_CONTROLLER_ENABLED) zephyr_library_sources(flash_stm32.c) diff --git a/drivers/flash/flash_stm32wb0x.c b/drivers/flash/flash_stm32wb0x.c new file mode 100644 index 00000000000000..3de8136aa0d81f --- /dev/null +++ b/drivers/flash/flash_stm32wb0x.c @@ -0,0 +1,459 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT st_stm32wb0_flash_controller + +#include +#include +#include +#include +#include +#include + +/* also brings "stm32wb0x_hal_flash.h" + * and "system_stm32wb0x.h", which provide macros + * used by the driver, such as FLASH_PAGE_SIZE or + * _MEMORY_FLASH_SIZE_ respectively. + */ +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(flash_stm32wb0x, CONFIG_FLASH_LOG_LEVEL); + +/** + * Driver private definitions & assertions + */ +#define SYSTEM_FLASH_SIZE _MEMORY_FLASH_SIZE_ +#define PAGES_IN_FLASH (SYSTEM_FLASH_SIZE / FLASH_PAGE_SIZE) + +#define WRITE_BLOCK_SIZE \ + DT_PROP(DT_INST(0, soc_nv_flash), write_block_size) + +/* Size of flash words, in bytes (equal to write block size) */ +#define WORD_SIZE WRITE_BLOCK_SIZE + +#define ERASE_BLOCK_SIZE \ + DT_PROP(DT_INST(0, soc_nv_flash), erase_block_size) + +/** + * Driver private structures + */ +struct flash_wb0x_data { + /** Used to serialize write/erase operations */ + struct k_sem write_lock; + + /** Flash size, in bytes */ + size_t flash_size; +}; + +/** + * Driver private utility functions + */ +static inline uint32_t read_mem_u32(const uint32_t *ptr) +{ + /** + * Fetch word using sys_get_le32, which performs byte-sized + * reads instead of word-sized. This is important as ptr may + * be unaligned. We also want to use le32 because the data is + * stored in little-endian inside the flash. + */ + return sys_get_le32((const uint8_t *)ptr); +} + +static inline size_t get_flash_size_in_bytes(void) +{ + /* FLASH.SIZE contains the highest flash address supported + * on this MCU, which is also the number of words in flash + * minus one. + */ + const uint32_t words_in_flash = + READ_BIT(FLASH->SIZE, FLASH_FLASH_SIZE_FLASH_SIZE) + 1; + + return words_in_flash * WORD_SIZE; +} + +/** + * @brief Returns the associated error to IRQ flags. + * + * @returns a negative error value + */ +static int error_from_irq_flags(uint32_t flags) +{ + /** + * Only two errors are expected: + * - illegal command + * - command error + */ + if (flags & FLASH_FLAG_ILLCMD) { + return -EINVAL; + } + + if (flags & FLASH_FLAG_CMDERR) { + return -EIO; + } + + /* + * Unexpected error flag -> "out of domain" + * In practice, this should never be reached. + */ + return -EDOM; +} + +static bool is_valid_flash_range(const struct device *dev, + off_t offset, uint32_t len) +{ + const struct flash_wb0x_data *data = dev->data; + uint32_t offset_plus_len; + + /* (offset + len) must not overflow */ + return !u32_add_overflow(offset, len, &offset_plus_len) + /* offset must be a valid offset in flash */ + && IN_RANGE(offset, 0, data->flash_size - 1) + /* (offset + len) must be in [0; flash size] + * because it is equal to the last accessed + * byte in flash plus one (an access of `len` + * bytes starting at `offset` touches bytes + * `offset` to `offset + len` EXCLUDED) + */ + && IN_RANGE(offset_plus_len, 0, data->flash_size); +} + +static bool is_writeable_flash_range(const struct device *dev, + off_t offset, uint32_t len) +{ + if ((offset % WRITE_BLOCK_SIZE) != 0 + || (len % WRITE_BLOCK_SIZE) != 0) { + return false; + } + + return is_valid_flash_range(dev, offset, len); +} + +static bool is_erasable_flash_range(const struct device *dev, + off_t offset, uint32_t len) +{ + if ((offset % ERASE_BLOCK_SIZE) != 0 + || (len % ERASE_BLOCK_SIZE) != 0) { + return false; + } + + return is_valid_flash_range(dev, offset, len); +} + +/** + * Driver private functions + */ + +static uint32_t poll_flash_controller(void) +{ + uint32_t flags; + + /* Poll until an interrupt flag is raised */ + do { + flags = FLASH->IRQRAW; + } while (flags == 0); + + /* Acknowledge the flag(s) we have seen */ + FLASH->IRQRAW = flags; + + return flags; +} + +static int execute_flash_command(uint8_t cmd) +{ + uint32_t irq_flags; + + /* Clear all pending interrupt bits */ + FLASH->IRQRAW = FLASH->IRQRAW; + + /* Start command */ + FLASH->COMMAND = cmd; + + /* Wait for CMDSTART */ + irq_flags = poll_flash_controller(); + + /* If command didn't start, an error occurred */ + if (!(irq_flags & FLASH_IT_CMDSTART)) { + return error_from_irq_flags(irq_flags); + } + + /** + * Both CMDSTART and CMDDONE may be set if the command was + * executed fast enough. In this case, we're already done. + * Otherwise, we need to poll again until CMDDONE/error occurs. + */ + if (!(irq_flags & FLASH_IT_CMDDONE)) { + irq_flags = poll_flash_controller(); + } + + if (!(irq_flags & FLASH_IT_CMDDONE)) { + return error_from_irq_flags(irq_flags); + } else { + return 0; + } +} + +int erase_page_range(uint32_t start_page, uint32_t page_count) +{ + int res = 0; + + __ASSERT_NO_MSG(start_page < PAGES_IN_FLASH); + __ASSERT_NO_MSG((start_page + page_count - 1) < PAGES_IN_FLASH); + + for (uint32_t i = start_page; + i < (start_page + page_count); + i++) { + /* ADDRESS[16:9] = XADR[10:3] (address of page to erase) + * ADDRESS[8:0] = 0 (row & word address, must be 0) + */ + FLASH->ADDRESS = (i << 9); + + res = execute_flash_command(FLASH_CMD_ERASE_PAGES); + if (res < 0) { + break; + } + } + + return res; +} + +int write_word_range(const void *buf, uint32_t start_word, uint32_t num_words) +{ + /* Special value to load in DATAx registers to skip + * writing corresponding word with BURSTWRITE command. + */ + const uint32_t BURST_IGNORE_VALUE = 0xFFFFFFFF; + const size_t WORDS_IN_BURST = 4; + uint32_t dst_addr = start_word; + uint32_t remaining = num_words; + /** + * Note that @p buf may not be aligned to 32-bit boundary. + * However, declaring src_ptr as uint32_t* makes the address + * increment by 4 every time we do src_ptr++, which makes it + * behave like the other counters in this function. + */ + const uint32_t *src_ptr = buf; + int res = 0; + + /** + * Write to flash is performed as a 3 step process: + * - write single words using WRITE commands until the write + * write address is aligned to flash quadword boundary + * + * - after write address is aligned to quadword, we can use + * the BURSTWRITE commands to write 4 words at a time + * + * - once less than 4 words remain to write, a last BURSTWRITE + * is used with unneeded DATAx registers filled with 0xFFFFFFFF + * (this makes BURSTWRITE ignore write to these addresses) + */ + + /* (1) Align to quadword boundary with WRITE commands */ + while (remaining > 0 && (dst_addr % WORDS_IN_BURST) != 0) { + FLASH->ADDRESS = dst_addr; + FLASH->DATA0 = read_mem_u32(src_ptr); + + res = execute_flash_command(FLASH_CMD_WRITE); + if (res < 0) { + return res; + } + + src_ptr++; + dst_addr++; + remaining--; + } + + /* (2) Write bursts of quadwords */ + while (remaining >= WORDS_IN_BURST) { + __ASSERT_NO_MSG((dst_addr % WORDS_IN_BURST) == 0); + + FLASH->ADDRESS = dst_addr; + FLASH->DATA0 = read_mem_u32(src_ptr + 0); + FLASH->DATA1 = read_mem_u32(src_ptr + 1); + FLASH->DATA2 = read_mem_u32(src_ptr + 2); + FLASH->DATA3 = read_mem_u32(src_ptr + 3); + + res = execute_flash_command(FLASH_CMD_BURSTWRITE); + if (res < 0) { + return res; + } + + src_ptr += WORDS_IN_BURST; + dst_addr += WORDS_IN_BURST; + remaining -= WORDS_IN_BURST; + } + + /* (3) Write trailing (between 1 and 3 words) */ + if (remaining > 0) { + __ASSERT_NO_MSG(remaining < WORDS_IN_BURST); + __ASSERT_NO_MSG((dst_addr % WORDS_IN_BURST) == 0); + + FLASH->ADDRESS = dst_addr; + FLASH->DATA0 = read_mem_u32(src_ptr + 0); + + FLASH->DATA1 = (remaining >= 2) + ? read_mem_u32(src_ptr + 1) + : BURST_IGNORE_VALUE; + + FLASH->DATA2 = (remaining == 3) + ? read_mem_u32(src_ptr + 2) + : BURST_IGNORE_VALUE; + + FLASH->DATA3 = BURST_IGNORE_VALUE; + + remaining = 0; + + res = execute_flash_command(FLASH_CMD_BURSTWRITE); + } + + return res; +} + +/** + * Driver subsystem API implementation + */ +int flash_wb0x_read(const struct device *dev, off_t offset, + void *buffer, size_t len) +{ + if (!len) { + return 0; + } + + if (!is_valid_flash_range(dev, offset, len)) { + return -EINVAL; + } + + const uint8_t *flash_base = ((void *)DT_REG_ADDR(DT_INST(0, st_stm32_nv_flash))); + + memcpy(buffer, flash_base + (uint32_t)offset, len); + + return 0; +} + +int flash_wb0x_write(const struct device *dev, off_t offset, + const void *buffer, size_t len) +{ + struct flash_wb0x_data *data = dev->data; + int res; + + if (!len) { + return 0; + } + + if (!is_writeable_flash_range(dev, offset, len)) { + return -EINVAL; + } + + /* Acquire driver lock */ + res = k_sem_take(&data->write_lock, K_NO_WAIT); + if (res < 0) { + return res; + } + + const uint32_t start_word = (uint32_t)offset / WORD_SIZE; + const uint32_t num_words = len / WORD_SIZE; + + res = write_word_range(buffer, start_word, num_words); + + /* Release driver lock */ + k_sem_give(&data->write_lock); + + return res; +} + +int flash_wb0x_erase(const struct device *dev, off_t offset, size_t size) +{ + struct flash_wb0x_data *data = dev->data; + int res; + + if (!size) { + return 0; + } + + if (!is_erasable_flash_range(dev, offset, size)) { + return -EINVAL; + } + + /* Acquire driver lock */ + res = k_sem_take(&data->write_lock, K_NO_WAIT); + if (res < 0) { + return res; + } + + const uint32_t start_page = (uint32_t)offset / ERASE_BLOCK_SIZE; + const uint32_t page_count = size / ERASE_BLOCK_SIZE; + + res = erase_page_range(start_page, page_count); + + /* Release driver lock */ + k_sem_give(&data->write_lock); + + return res; +} + +const struct flash_parameters *flash_wb0x_get_parameters( + const struct device *dev) +{ + static const struct flash_parameters fp = { + .write_block_size = WRITE_BLOCK_SIZE, + .erase_value = 0xff, + }; + + return &fp; +} + +#if defined(CONFIG_FLASH_PAGE_LAYOUT) +void flash_wb0x_pages_layout(const struct device *dev, + const struct flash_pages_layout **layout, + size_t *layout_size) +{ + /** + * STM32WB0 flash: single bank, 2KiB pages + * (the number of pages depends on MCU) + */ + static const struct flash_pages_layout fpl[] = {{ + .pages_count = PAGES_IN_FLASH, + .pages_size = FLASH_PAGE_SIZE + }}; + + *layout = fpl; + *layout_size = ARRAY_SIZE(fpl); +} +#endif /* CONFIG_FLASH_PAGE_LAYOUT */ + +static const struct flash_driver_api flash_wb0x_api = { + .erase = flash_wb0x_erase, + .write = flash_wb0x_write, + .read = flash_wb0x_read, + .get_parameters = flash_wb0x_get_parameters, +#ifdef CONFIG_FLASH_PAGE_LAYOUT + .page_layout = flash_wb0x_pages_layout, +#endif + /* extended operations not supported */ +}; + +int stm32wb0x_flash_init(const struct device *dev) +{ + struct flash_wb0x_data *data = dev->data; + + k_sem_init(&data->write_lock, 1, 1); + + data->flash_size = get_flash_size_in_bytes(); + + return 0; +} + +/** + * Driver device instantiation + */ +static struct flash_wb0x_data wb0x_flash_drv_data; + +DEVICE_DT_INST_DEFINE(0, stm32wb0x_flash_init, NULL, + &wb0x_flash_drv_data, NULL, POST_KERNEL, + CONFIG_FLASH_INIT_PRIORITY, &flash_wb0x_api); From d4c3401254b1f3c36fd236fd12a5a79e2b93234f Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Mon, 9 Sep 2024 13:44:02 +0200 Subject: [PATCH 13/16] drivers: hwinfo: stm32: mark STM32WB0 series as incompatible The existing hwinfo driver for STM32 is incompatible with STM32WB0 series. Prevent compiling the driver if the target's series is STM32WB0. This fixes the build failure on the drivers.hwinfo.api test. Signed-off-by: Mathieu Choplain --- drivers/hwinfo/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwinfo/Kconfig b/drivers/hwinfo/Kconfig index 53296839540eff..2e27074061929b 100644 --- a/drivers/hwinfo/Kconfig +++ b/drivers/hwinfo/Kconfig @@ -65,7 +65,7 @@ endif # HWINFO_CC13XX_CC26XX config HWINFO_STM32 bool "STM32 hwinfo" default y - depends on SOC_FAMILY_STM32 + depends on SOC_FAMILY_STM32 && !SOC_SERIES_STM32WB0X select HWINFO_HAS_DRIVER help Enable STM32 hwinfo driver. From ed7b62dc6e1716f2bf22715241e3e755d04cb384 Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Tue, 28 May 2024 12:17:02 +0200 Subject: [PATCH 14/16] dts: arm: st: wb0: add DTSI for STM32WB0 series Adds Device Tree include files for all MCUs in the STM32WB0 series. These DTSI files only contain the supported peripherals for now. Signed-off-by: Mathieu Choplain --- dts/arm/st/wb0/stm32wb0.dtsi | 203 ++++++++++++++++++++++++++++++++ dts/arm/st/wb0/stm32wb05.dtsi | 13 ++ dts/arm/st/wb0/stm32wb05Xz.dtsi | 21 ++++ dts/arm/st/wb0/stm32wb06.dtsi | 14 +++ dts/arm/st/wb0/stm32wb06Xc.dtsi | 21 ++++ dts/arm/st/wb0/stm32wb07.dtsi | 19 +++ dts/arm/st/wb0/stm32wb07Xc.dtsi | 21 ++++ dts/arm/st/wb0/stm32wb09.dtsi | 18 +++ dts/arm/st/wb0/stm32wb09Xe.dtsi | 21 ++++ 9 files changed, 351 insertions(+) create mode 100644 dts/arm/st/wb0/stm32wb0.dtsi create mode 100644 dts/arm/st/wb0/stm32wb05.dtsi create mode 100644 dts/arm/st/wb0/stm32wb05Xz.dtsi create mode 100644 dts/arm/st/wb0/stm32wb06.dtsi create mode 100644 dts/arm/st/wb0/stm32wb06Xc.dtsi create mode 100644 dts/arm/st/wb0/stm32wb07.dtsi create mode 100644 dts/arm/st/wb0/stm32wb07Xc.dtsi create mode 100644 dts/arm/st/wb0/stm32wb09.dtsi create mode 100644 dts/arm/st/wb0/stm32wb09Xe.dtsi diff --git a/dts/arm/st/wb0/stm32wb0.dtsi b/dts/arm/st/wb0/stm32wb0.dtsi new file mode 100644 index 00000000000000..7a8454fa0116b1 --- /dev/null +++ b/dts/arm/st/wb0/stm32wb0.dtsi @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* NOTE: for some registers, the reserved size in the memory map differs + * between WB05/09 and WB06/07. In these cases, if all the registers fit + * in the smallest size, the peripheral is defined in this file with the + * smallest size in the 'reg' property. + */ +/ { + chosen { + zephyr,flash-controller = &flash; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-m0+"; + reg = <0>; + }; + }; + + sram0: memory@20000000 { + compatible = "mmio-sram"; + }; + + clocks { + /* High-speed clock nodes + * These nodes must only be used as input to + * the RCC node (i.e., 'clocks' property) + */ + clk_hse: clk-hse { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = ; + status = "disabled"; + }; + + clk_hsi: clk-hsi { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = ; + status = "disabled"; + }; + + /* Dummy node representing the RC64MPLL block in PLL mode + * Using this node as RCC node input requires HSE to be enabled. + */ + pll: pll64m { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = ; + status = "disabled"; + }; + + /* Slow speed clock nodes + * These nodes must only be used for the + * 'slow-clock' property of the RCC node. + */ + clk_lse: clk-lse { + #clock-cells = <0>; + compatible = "st,stm32-lse-clock"; + clock-frequency = <32768>; + driving-capability = <1>; + status = "disabled"; + }; + + clk_lsi: clk-lsi { + /* "fixed-clock" compatible is required for compatibility with the + * macros in `include/drivers/clock_control/stm32_clock_control.h` + */ + #clock-cells = <0>; + compatible = "st,stm32wb0-lsi-clock", "fixed-clock"; + clock-frequency = ; + status = "disabled"; + }; + + /* Dummy node representing the "CLK_16MHz/512" slow clock source. + * WARNING: this clock is not active in DEEPSTOP, so all slow clock peripherals + * are stopped, and cannot wake up the SoC, if this is selected as slow-clock! + */ + clk_16mhz_div512: clk-16mhz-div512 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <(DT_FREQ_M(16) / 512)>; + status = "disabled"; + }; + }; + + soc { + flash: flash-controller@40001000 { + compatible = "st,stm32wb0-flash-controller", "st,stm32-flash-controller"; + reg = <0x40001000 DT_SIZE_K(1)>; + interrupts = <0 0>; + + #address-cells = <1>; + #size-cells = <1>; + + flash0: flash@10040000 { + compatible = "st,stm32-nv-flash", "soc-nv-flash"; + write-block-size = <4>; + erase-block-size = <2048>; + /* maximum erase time(ms) for a page (2K) */ + max-erase-time = <40>; + }; + }; + + rcc: rcc@48400000 { + compatible = "st,stm32wb0-rcc"; + reg = <0x48400000 DT_SIZE_K(1)>; + #clock-cells = <2>; + + rctl: reset-controller { + compatible = "st,stm32-rcc-rctl"; + #reset-cells = <1>; + }; + }; + + pwrc: power@48500000 { + compatible = "st,stm32wb0-pwr"; + reg = <0x48500000 DT_SIZE_K(1)>; + }; + + /* STM32WB0 GPIO interrupt controller + * + * The 'reg' property corresponds to the SYSCFG memory range, + * because that's where the GPIO INTC registers are located. + */ + gpio_intc: interrupt-controller@40000000 { + compatible = "st,stm32wb0-gpio-intc"; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + reg = <0x40000000 64>; + num-lines = <32>; + interrupts = <15 0>, <16 0>; + interrupt-names = "gpioa", "gpiob"; + line-ranges = <0 16>, <16 16>; + }; + + pinctrl: pin-controller@48000000 { + compatible = "st,stm32-pinctrl"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x48000000 DT_SIZE_M(2)>; + + gpioa: gpio@48000000 { + compatible = "st,stm32-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x48000000 DT_SIZE_K(1)>; + clocks = <&rcc STM32_CLOCK_BUS_AHB0 (1 << 2)>; + }; + + gpiob: gpio@48100000 { + compatible = "st,stm32-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x48100000 DT_SIZE_K(1)>; + clocks = <&rcc STM32_CLOCK_BUS_AHB0 (1 << 3)>; + }; + }; + + usart1: serial@41004000 { + compatible = "st,stm32-usart", "st,stm32-uart"; + reg = <0x41004000 DT_SIZE_K(1)>; + + clocks = <&rcc STM32_CLOCK_BUS_APB1 (1 << 10)>; + resets = <&rctl STM32_RESET(APB1, 10)>; + interrupts = <8 0>; + status = "disabled"; + }; + + lpuart1: serial@41005000 { + compatible = "st,stm32-lpuart", "st,stm32-uart"; + reg = <0x41005000 DT_SIZE_K(1)>; + clocks = <&rcc STM32_CLOCK_BUS_APB1 (1 << 8)>; + resets = <&rctl STM32_RESET(APB1, 8)>; + interrupts = <9 0>; + status = "disabled"; + }; + }; +}; + +&nvic { + arm,num-irq-priority-bits = <2>; +}; diff --git a/dts/arm/st/wb0/stm32wb05.dtsi b/dts/arm/st/wb0/stm32wb05.dtsi new file mode 100644 index 00000000000000..15dfabf83b9205 --- /dev/null +++ b/dts/arm/st/wb0/stm32wb05.dtsi @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + compatible = "st,stm32wb05", "st,stm32wb0", "simple-bus"; + }; +}; diff --git a/dts/arm/st/wb0/stm32wb05Xz.dtsi b/dts/arm/st/wb0/stm32wb05Xz.dtsi new file mode 100644 index 00000000000000..470309ec99f9bf --- /dev/null +++ b/dts/arm/st/wb0/stm32wb05Xz.dtsi @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + flash: flash-controller@40001000 { + flash0: flash@10040000 { + reg = <0x10040000 DT_SIZE_K(192)>; + }; + }; + }; + + sram0: memory@20000000 { + reg = <0x20000000 DT_SIZE_K(24)>; + }; +}; diff --git a/dts/arm/st/wb0/stm32wb06.dtsi b/dts/arm/st/wb0/stm32wb06.dtsi new file mode 100644 index 00000000000000..180478d5562e4f --- /dev/null +++ b/dts/arm/st/wb0/stm32wb06.dtsi @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** STM32WB06 is identical to STM32WB07, except for RAM size */ +#include + +/ { + soc { + compatible = "st,stm32wb06", "st,stm32wb0", "simple-bus"; + }; +}; diff --git a/dts/arm/st/wb0/stm32wb06Xc.dtsi b/dts/arm/st/wb0/stm32wb06Xc.dtsi new file mode 100644 index 00000000000000..f6d489b7ad5b42 --- /dev/null +++ b/dts/arm/st/wb0/stm32wb06Xc.dtsi @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + flash: flash-controller@40001000 { + flash0: flash@10040000 { + reg = <0x10040000 DT_SIZE_K(256)>; + }; + }; + }; + + sram0: memory@20000000 { + reg = <0x20000000 DT_SIZE_K(32)>; + }; +}; diff --git a/dts/arm/st/wb0/stm32wb07.dtsi b/dts/arm/st/wb0/stm32wb07.dtsi new file mode 100644 index 00000000000000..b69008ed2d8f63 --- /dev/null +++ b/dts/arm/st/wb0/stm32wb07.dtsi @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + compatible = "st,stm32wb07", "st,stm32wb0", "simple-bus"; + + flash: flash-controller@40001000 { + flash0: flash@10040000 { + reg = <0x10040000 DT_SIZE_K(256)>; + }; + }; + }; +}; diff --git a/dts/arm/st/wb0/stm32wb07Xc.dtsi b/dts/arm/st/wb0/stm32wb07Xc.dtsi new file mode 100644 index 00000000000000..ef3656fd6a9592 --- /dev/null +++ b/dts/arm/st/wb0/stm32wb07Xc.dtsi @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + flash: flash-controller@40001000 { + flash0: flash@10040000 { + reg = <0x10040000 DT_SIZE_K(256)>; + }; + }; + }; + + sram0: memory@20000000 { + reg = <0x20000000 DT_SIZE_K(64)>; + }; +}; diff --git a/dts/arm/st/wb0/stm32wb09.dtsi b/dts/arm/st/wb0/stm32wb09.dtsi new file mode 100644 index 00000000000000..604ac43322baf5 --- /dev/null +++ b/dts/arm/st/wb0/stm32wb09.dtsi @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * STM32WB09 is mostly identical to STM32WB05. + * The only differences are RAM size, flash size and TRNG IP. + * Flash and RAM size are defined in the package-level DTSI. + */ +#include + +/ { + soc { + compatible = "st,stm32wb09", "st,stm32wb0", "simple-bus"; + }; +}; diff --git a/dts/arm/st/wb0/stm32wb09Xe.dtsi b/dts/arm/st/wb0/stm32wb09Xe.dtsi new file mode 100644 index 00000000000000..155c8d8186c677 --- /dev/null +++ b/dts/arm/st/wb0/stm32wb09Xe.dtsi @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + soc { + flash: flash-controller@40001000 { + flash0: flash@10040000 { + reg = <0x10040000 DT_SIZE_K(512)>; + }; + }; + }; + + sram0: memory@20000000 { + reg = <0x20000000 DT_SIZE_K(64)>; + }; +}; From c8cf93b675bfa9d5d5098782716360e3158e1860 Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Wed, 4 Sep 2024 14:05:25 +0200 Subject: [PATCH 15/16] boards: st: nucleo_wb05kz: add support for Nucleo-WB05KZ board Add support for the STMicroelectronics Nucleo-WB05KZ board. Signed-off-by: Mathieu Choplain --- boards/st/nucleo_wb05kz/Kconfig.nucleo_wb05kz | 5 + .../nucleo_wb05kz/arduino_r3_connector.dtsi | 28 ++++ boards/st/nucleo_wb05kz/board.cmake | 5 + boards/st/nucleo_wb05kz/board.yml | 5 + .../nucleo_wb05kz/doc/img/nucleo_wb05kz.webp | Bin 0 -> 40290 bytes boards/st/nucleo_wb05kz/doc/index.rst | 152 ++++++++++++++++++ boards/st/nucleo_wb05kz/nucleo_wb05kz.dts | 103 ++++++++++++ boards/st/nucleo_wb05kz/nucleo_wb05kz.yaml | 14 ++ .../st/nucleo_wb05kz/nucleo_wb05kz_defconfig | 11 ++ boards/st/nucleo_wb05kz/support/openocd.cfg | 5 + 10 files changed, 328 insertions(+) create mode 100644 boards/st/nucleo_wb05kz/Kconfig.nucleo_wb05kz create mode 100644 boards/st/nucleo_wb05kz/arduino_r3_connector.dtsi create mode 100644 boards/st/nucleo_wb05kz/board.cmake create mode 100644 boards/st/nucleo_wb05kz/board.yml create mode 100644 boards/st/nucleo_wb05kz/doc/img/nucleo_wb05kz.webp create mode 100644 boards/st/nucleo_wb05kz/doc/index.rst create mode 100644 boards/st/nucleo_wb05kz/nucleo_wb05kz.dts create mode 100644 boards/st/nucleo_wb05kz/nucleo_wb05kz.yaml create mode 100644 boards/st/nucleo_wb05kz/nucleo_wb05kz_defconfig create mode 100644 boards/st/nucleo_wb05kz/support/openocd.cfg diff --git a/boards/st/nucleo_wb05kz/Kconfig.nucleo_wb05kz b/boards/st/nucleo_wb05kz/Kconfig.nucleo_wb05kz new file mode 100644 index 00000000000000..e1646cb0bdbba1 --- /dev/null +++ b/boards/st/nucleo_wb05kz/Kconfig.nucleo_wb05kz @@ -0,0 +1,5 @@ +# Copyright (c) 2024 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_NUCLEO_WB05KZ + select SOC_STM32WB05XX diff --git a/boards/st/nucleo_wb05kz/arduino_r3_connector.dtsi b/boards/st/nucleo_wb05kz/arduino_r3_connector.dtsi new file mode 100644 index 00000000000000..75fb046f29f8c8 --- /dev/null +++ b/boards/st/nucleo_wb05kz/arduino_r3_connector.dtsi @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + arduino_header: connector { + compatible = "arduino-header-r3"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + + /* Most pins are not connected to the Arduino + * connector in default hardware configuration. + * Only the connected pins are provided here. + */ + gpio-map = <14 0 &gpiob 15 0>, /* D8 */ + <16 0 &gpioa 9 0>, /* D10 */ + <17 0 &gpioa 11 0>, /* D11 */ + <18 0 &gpioa 8 0>, /* D12 */ + <19 0 &gpiob 3 0>, /* D13 */ + <20 0 &gpiob 7 0>, /* D14 */ + <21 0 &gpiob 6 0>; /* D15 */ + }; +}; + +arduino_serial: &usart1 {}; diff --git a/boards/st/nucleo_wb05kz/board.cmake b/boards/st/nucleo_wb05kz/board.cmake new file mode 100644 index 00000000000000..15bdb8e444dec0 --- /dev/null +++ b/boards/st/nucleo_wb05kz/board.cmake @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 +board_runner_args(stm32cubeprogrammer "--port=swd" "--reset-mode=sw") + +include(${ZEPHYR_BASE}/boards/common/stm32cubeprogrammer.board.cmake) +include(${ZEPHYR_BASE}/boards/common/openocd.board.cmake) diff --git a/boards/st/nucleo_wb05kz/board.yml b/boards/st/nucleo_wb05kz/board.yml new file mode 100644 index 00000000000000..645506c7953a8d --- /dev/null +++ b/boards/st/nucleo_wb05kz/board.yml @@ -0,0 +1,5 @@ +board: + name: nucleo_wb05kz + vendor: st + socs: + - name: stm32wb05 diff --git a/boards/st/nucleo_wb05kz/doc/img/nucleo_wb05kz.webp b/boards/st/nucleo_wb05kz/doc/img/nucleo_wb05kz.webp new file mode 100644 index 0000000000000000000000000000000000000000..694f64a3632ac68de1a2f4aeb7a8371fbdcf0be7 GIT binary patch literal 40290 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 beforehand. + +Alternatively, OpenOCD can also be used to flash the board using the +``--runner`` (or ``-r``) option: + +.. code-block:: console + + $ west flash --runner openocd + +Flashing an application to Nucleo WB05KZ +---------------------------------------- + +Connect the Nucleo WB05KZ to your host computer using the USB port, +then run a serial host program to connect with your Nucleo board: + +.. code-block:: console + + $ minicom -D /dev/ttyACM0 + +Now build and flash an application. Here is an example for +:ref:`hello_world`. + +.. zephyr-app-commands:: + :zephyr-app: samples/hello_world + :board: nucleo_wb05kz + :goals: build flash + +You should see the following message on the console: + +.. code-block:: console + + Hello World! nucleo_wb05kz/stm32wb05 + + +Debugging +========= + +You can debug an application in the usual way. Here is an example for the +:ref:`hello_world` application. + +.. zephyr-app-commands:: + :zephyr-app: samples/hello_world + :board: nucleo_wb05kz + :maybe-skip-config: + :goals: debug + +.. _`Nucleo WB05KZ webpage`: + https://www.st.com/en/evaluation-tools/nucleo-wb05kz.html + +.. _`WB05KZ on www.st.com`: + https://www.st.com/en/microcontrollers-microprocessors/stm32wb05kz.html + +.. _`STM32WB05 reference manual`: + https://www.st.com/resource/en/reference_manual/rm0529-stm32wb05xz-ultralow-power-wireless-32bit-mcu-armbased-cortexm0-with-bluetooth-low-energy-and-24-ghz-radio-solution-stmicroelectronics.pdf + +.. _`Nucleo WB05KZ board User Manual`: + https://www.st.com/resource/en/user_manual/um3343-stm32wb05-nucleo64-board-mb1801-and-mb2032-stmicroelectronics.pdf + +.. _STM32CubeProgrammer: + https://www.st.com/en/development-tools/stm32cubeprog.html diff --git a/boards/st/nucleo_wb05kz/nucleo_wb05kz.dts b/boards/st/nucleo_wb05kz/nucleo_wb05kz.dts new file mode 100644 index 00000000000000..f61f32e06d0c0e --- /dev/null +++ b/boards/st/nucleo_wb05kz/nucleo_wb05kz.dts @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; +#include +#include +#include + +#include "arduino_r3_connector.dtsi" + +/ { + model = "STMicroelectronics STM32WB05KZ-NUCLEO board"; + compatible = "st,stm32wb05kz-nucleo"; + + #address-cells = <1>; + #size-cells = <1>; + + chosen { + zephyr,console = &usart1; + zephyr,shell-uart = &usart1; + zephyr,sram = &sram0; + zephyr,flash = &flash0; + }; + + leds: leds { + compatible ="gpio-leds"; + blue_led_1: led_0 { + gpios = <&gpiob 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + }; + green_led_1: led_1 { + gpios = <&gpiob 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + }; + red_led_1: led_2 { + gpios = <&gpiob 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + }; + }; + + gpio_keys { + compatible = "gpio-keys"; + user_button_1: button_0 { + label = "SW1"; + gpios = <&gpioa 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + zephyr,code = ; + }; + user_button_2: button_1 { + label = "SW2"; + gpios = <&gpiob 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + zephyr,code = ; + }; + user_button_3: button_2 { + label = "SW3"; + gpios = <&gpiob 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + zephyr,code = ; + }; + }; + + aliases { + led0 = &blue_led_1; + led1 = &green_led_1; + led2 = &red_led_1; + sw0 = &user_button_1; + sw1 = &user_button_2; + sw2 = &user_button_3; + }; +}; + +&pwrc { + smps-mode = "RUN"; + smps-bom = <3>; +}; + +&clk_lse { + status = "okay"; +}; + +&clk_hse { + status = "okay"; +}; + +&clk_hsi { + status = "okay"; +}; + +&pll { + status = "okay"; +}; + +&rcc { + clocks = <&pll>; + clock-frequency = ; + clksys-prescaler = <1>; + slow-clock = <&clk_lse>; +}; + +&usart1 { + pinctrl-0 = <&usart1_tx_pa1 &usart1_rx_pb0>; + pinctrl-names = "default"; + current-speed = <115200>; + status = "okay"; +}; diff --git a/boards/st/nucleo_wb05kz/nucleo_wb05kz.yaml b/boards/st/nucleo_wb05kz/nucleo_wb05kz.yaml new file mode 100644 index 00000000000000..ce75ada4a463d5 --- /dev/null +++ b/boards/st/nucleo_wb05kz/nucleo_wb05kz.yaml @@ -0,0 +1,14 @@ +identifier: nucleo_wb05kz +name: ST Nucleo WB05KZ +type: mcu +arch: arm +toolchain: + - zephyr + - gnuarmemb + - xtools +ram: 24 +flash: 192 +supported: + - gpio + - arduino_gpio +vendor: st diff --git a/boards/st/nucleo_wb05kz/nucleo_wb05kz_defconfig b/boards/st/nucleo_wb05kz/nucleo_wb05kz_defconfig new file mode 100644 index 00000000000000..0a45ef65e1153a --- /dev/null +++ b/boards/st/nucleo_wb05kz/nucleo_wb05kz_defconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: Apache-2.0 + +# Enable GPIO +CONFIG_GPIO=y + +# Enable UART driver +CONFIG_SERIAL=y + +# Enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y diff --git a/boards/st/nucleo_wb05kz/support/openocd.cfg b/boards/st/nucleo_wb05kz/support/openocd.cfg new file mode 100644 index 00000000000000..6d294443fcface --- /dev/null +++ b/boards/st/nucleo_wb05kz/support/openocd.cfg @@ -0,0 +1,5 @@ +source [find interface/stlink-dap.cfg] + +transport select "dapdirect_swd" + +source [find target/stm32wb0x.cfg] From b9d9f8924c37c427d43987662aaf7dd56adf0f28 Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Tue, 28 May 2024 12:18:02 +0200 Subject: [PATCH 16/16] boards: st: nucleo_wb09ke: add support for Nucleo-WB09KE board This commit adds support for the ST Nucleo-WB09KE board. Signed-off-by: Mathieu Choplain --- boards/st/nucleo_wb09ke/Kconfig.nucleo_wb09ke | 5 + .../nucleo_wb09ke/arduino_r3_connector.dtsi | 28 ++++ boards/st/nucleo_wb09ke/board.cmake | 5 + boards/st/nucleo_wb09ke/board.yml | 5 + .../nucleo_wb09ke/doc/img/nucleo_wb09ke.webp | Bin 0 -> 43708 bytes boards/st/nucleo_wb09ke/doc/index.rst | 152 ++++++++++++++++++ boards/st/nucleo_wb09ke/nucleo_wb09ke.dts | 103 ++++++++++++ boards/st/nucleo_wb09ke/nucleo_wb09ke.yaml | 14 ++ .../st/nucleo_wb09ke/nucleo_wb09ke_defconfig | 11 ++ boards/st/nucleo_wb09ke/support/openocd.cfg | 5 + 10 files changed, 328 insertions(+) create mode 100644 boards/st/nucleo_wb09ke/Kconfig.nucleo_wb09ke create mode 100644 boards/st/nucleo_wb09ke/arduino_r3_connector.dtsi create mode 100644 boards/st/nucleo_wb09ke/board.cmake create mode 100644 boards/st/nucleo_wb09ke/board.yml create mode 100644 boards/st/nucleo_wb09ke/doc/img/nucleo_wb09ke.webp create mode 100644 boards/st/nucleo_wb09ke/doc/index.rst create mode 100644 boards/st/nucleo_wb09ke/nucleo_wb09ke.dts create mode 100644 boards/st/nucleo_wb09ke/nucleo_wb09ke.yaml create mode 100644 boards/st/nucleo_wb09ke/nucleo_wb09ke_defconfig create mode 100644 boards/st/nucleo_wb09ke/support/openocd.cfg diff --git a/boards/st/nucleo_wb09ke/Kconfig.nucleo_wb09ke b/boards/st/nucleo_wb09ke/Kconfig.nucleo_wb09ke new file mode 100644 index 00000000000000..9dbfd544b27efb --- /dev/null +++ b/boards/st/nucleo_wb09ke/Kconfig.nucleo_wb09ke @@ -0,0 +1,5 @@ +# Copyright (c) 2024 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_NUCLEO_WB09KE + select SOC_STM32WB09XX diff --git a/boards/st/nucleo_wb09ke/arduino_r3_connector.dtsi b/boards/st/nucleo_wb09ke/arduino_r3_connector.dtsi new file mode 100644 index 00000000000000..75fb046f29f8c8 --- /dev/null +++ b/boards/st/nucleo_wb09ke/arduino_r3_connector.dtsi @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + arduino_header: connector { + compatible = "arduino-header-r3"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + + /* Most pins are not connected to the Arduino + * connector in default hardware configuration. + * Only the connected pins are provided here. + */ + gpio-map = <14 0 &gpiob 15 0>, /* D8 */ + <16 0 &gpioa 9 0>, /* D10 */ + <17 0 &gpioa 11 0>, /* D11 */ + <18 0 &gpioa 8 0>, /* D12 */ + <19 0 &gpiob 3 0>, /* D13 */ + <20 0 &gpiob 7 0>, /* D14 */ + <21 0 &gpiob 6 0>; /* D15 */ + }; +}; + +arduino_serial: &usart1 {}; diff --git a/boards/st/nucleo_wb09ke/board.cmake b/boards/st/nucleo_wb09ke/board.cmake new file mode 100644 index 00000000000000..15bdb8e444dec0 --- /dev/null +++ b/boards/st/nucleo_wb09ke/board.cmake @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 +board_runner_args(stm32cubeprogrammer "--port=swd" "--reset-mode=sw") + +include(${ZEPHYR_BASE}/boards/common/stm32cubeprogrammer.board.cmake) +include(${ZEPHYR_BASE}/boards/common/openocd.board.cmake) diff --git a/boards/st/nucleo_wb09ke/board.yml b/boards/st/nucleo_wb09ke/board.yml new file mode 100644 index 00000000000000..2bff9e912b57bc --- /dev/null +++ b/boards/st/nucleo_wb09ke/board.yml @@ -0,0 +1,5 @@ +board: + name: nucleo_wb09ke + vendor: st + socs: + - name: stm32wb09 diff --git a/boards/st/nucleo_wb09ke/doc/img/nucleo_wb09ke.webp b/boards/st/nucleo_wb09ke/doc/img/nucleo_wb09ke.webp new file mode 100644 index 0000000000000000000000000000000000000000..f940b29713a76d5334e673a8faf8d34656be6d4b GIT binary patch literal 43708 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

RIS#S8^B3OMC`clQMHfA(NRsc0r*g*dnuWE}LWb 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 beforehand. + +Alternatively, OpenOCD can also be used to flash the board using the +``--runner`` (or ``-r``) option: + +.. code-block:: console + + $ west flash --runner openocd + +Flashing an application to Nucleo WB09KE +---------------------------------------- + +Connect the Nucleo WB09KE to your host computer using the USB port, +then run a serial host program to connect with your Nucleo board: + +.. code-block:: console + + $ minicom -D /dev/ttyACM0 + +Now build and flash an application. Here is an example for +:ref:`hello_world`. + +.. zephyr-app-commands:: + :zephyr-app: samples/hello_world + :board: nucleo_wb09ke + :goals: build flash + +You should see the following message on the console: + +.. code-block:: console + + Hello World! nucleo_wb09ke/stm32wb09 + + +Debugging +========= + +You can debug an application in the usual way. Here is an example for the +:ref:`hello_world` application. + +.. zephyr-app-commands:: + :zephyr-app: samples/hello_world + :board: nucleo_wb09ke + :maybe-skip-config: + :goals: debug + +.. _`Nucleo WB09KE webpage`: + https://www.st.com/en/evaluation-tools/nucleo-wb09ke.html + +.. _`WB09KE on www.st.com`: + https://www.st.com/en/microcontrollers-microprocessors/stm32wb09ke.html + +.. _`STM32WB09 reference manual`: + https://www.st.com/resource/en/reference_manual/rm0505-stm32wb09xe-ultralow-power-wireless-32bit-mcu-armbased-cortexm0-with-bluetooth-low-energy-and-24-ghz-radio-solution-stmicroelectronics.pdf + +.. _`Nucleo WB09KE board User Manual`: + https://www.st.com/resource/en/user_manual/um3345-stm32wb09-nucleo64-board-mb1801-and-mb2032-stmicroelectronics.pdf + +.. _STM32CubeProgrammer: + https://www.st.com/en/development-tools/stm32cubeprog.html diff --git a/boards/st/nucleo_wb09ke/nucleo_wb09ke.dts b/boards/st/nucleo_wb09ke/nucleo_wb09ke.dts new file mode 100644 index 00000000000000..733e6c0f717b64 --- /dev/null +++ b/boards/st/nucleo_wb09ke/nucleo_wb09ke.dts @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; +#include +#include +#include + +#include "arduino_r3_connector.dtsi" + +/ { + model = "STMicroelectronics STM32WB09KE-NUCLEO board"; + compatible = "st,stm32wb09ke-nucleo"; + + #address-cells = <1>; + #size-cells = <1>; + + chosen { + zephyr,console = &usart1; + zephyr,shell-uart = &usart1; + zephyr,sram = &sram0; + zephyr,flash = &flash0; + }; + + leds: leds { + compatible ="gpio-leds"; + blue_led_1: led_0 { + gpios = <&gpiob 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + }; + green_led_1: led_1 { + gpios = <&gpiob 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + }; + red_led_1: led_2 { + gpios = <&gpiob 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + }; + }; + + gpio_keys { + compatible = "gpio-keys"; + user_button_1: button_0 { + label = "SW1"; + gpios = <&gpioa 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + zephyr,code = ; + }; + user_button_2: button_1 { + label = "SW2"; + gpios = <&gpiob 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + zephyr,code = ; + }; + user_button_3: button_2 { + label = "SW3"; + gpios = <&gpiob 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + zephyr,code = ; + }; + }; + + aliases { + led0 = &blue_led_1; + led1 = &green_led_1; + led2 = &red_led_1; + sw0 = &user_button_1; + sw1 = &user_button_2; + sw2 = &user_button_3; + }; +}; + +&pwrc { + smps-mode = "RUN"; + smps-bom = <3>; +}; + +&clk_lse { + status = "okay"; +}; + +&clk_hse { + status = "okay"; +}; + +&clk_hsi { + status = "okay"; +}; + +&pll { + status = "okay"; +}; + +&rcc { + clocks = <&pll>; + clock-frequency = ; + clksys-prescaler = <1>; + slow-clock = <&clk_lse>; +}; + +&usart1 { + pinctrl-0 = <&usart1_tx_pa1 &usart1_rx_pb0>; + pinctrl-names = "default"; + current-speed = <115200>; + status = "okay"; +}; diff --git a/boards/st/nucleo_wb09ke/nucleo_wb09ke.yaml b/boards/st/nucleo_wb09ke/nucleo_wb09ke.yaml new file mode 100644 index 00000000000000..908b24ea6da946 --- /dev/null +++ b/boards/st/nucleo_wb09ke/nucleo_wb09ke.yaml @@ -0,0 +1,14 @@ +identifier: nucleo_wb09ke +name: ST Nucleo WB09KE +type: mcu +arch: arm +toolchain: + - zephyr + - gnuarmemb + - xtools +ram: 64 +flash: 512 +supported: + - gpio + - arduino_gpio +vendor: st diff --git a/boards/st/nucleo_wb09ke/nucleo_wb09ke_defconfig b/boards/st/nucleo_wb09ke/nucleo_wb09ke_defconfig new file mode 100644 index 00000000000000..0a45ef65e1153a --- /dev/null +++ b/boards/st/nucleo_wb09ke/nucleo_wb09ke_defconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: Apache-2.0 + +# Enable GPIO +CONFIG_GPIO=y + +# Enable UART driver +CONFIG_SERIAL=y + +# Enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y diff --git a/boards/st/nucleo_wb09ke/support/openocd.cfg b/boards/st/nucleo_wb09ke/support/openocd.cfg new file mode 100644 index 00000000000000..6d294443fcface --- /dev/null +++ b/boards/st/nucleo_wb09ke/support/openocd.cfg @@ -0,0 +1,5 @@ +source [find interface/stlink-dap.cfg] + +transport select "dapdirect_swd" + +source [find target/stm32wb0x.cfg]