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 00000000000000..694f64a3632ac6 Binary files /dev/null and b/boards/st/nucleo_wb05kz/doc/img/nucleo_wb05kz.webp differ diff --git a/boards/st/nucleo_wb05kz/doc/index.rst b/boards/st/nucleo_wb05kz/doc/index.rst new file mode 100644 index 00000000000000..b7434039c7277a --- /dev/null +++ b/boards/st/nucleo_wb05kz/doc/index.rst @@ -0,0 +1,152 @@ +.. _nucleo_wb05kz_board: + +ST Nucleo WB05KZ +################ + +Overview +******** + +The Nucleo WB05KZ board is a Bluetooth |reg| Low Energy wireless and ultra-low-power +board featuring an ARM Cortex |reg|-M0+ based STM32WB05KZV MCU, embedding a +powerful and ultra-low-power radio compliant with the Bluetooth® Low Energy +SIG specification v5.4. + +.. image:: img/nucleo_wb05kz.webp + :align: center + :alt: Nucleo WB05KZ + +More information about the board can be found on the `Nucleo WB05KZ webpage`_. + +Hardware +******** + +Nucleo WB05KZ provides the following hardware components: + +- STM32WB05KZV in VFQFPN32 package +- ARM |reg| 32-bit Cortex |reg|-M0+ CPU +- 64 MHz maximal CPU frequebct +- 192 KB Flash +- 24 KB SRAM + +More information about STM32WB05KZV can be found here: + +- `WB05KZ on www.st.com`_ +- `STM32WB05 reference manual`_ + + +Supported Features +================== + +The Zephyr ``nucleo_wb05kz`` board target supports the following hardware features: + ++-----------+------------+-------------------------------------+ +| Interface | Controller | Driver/Component | ++===========+============+=====================================+ +| NVIC | on-chip | nested vector interrupt controller | ++-----------+------------+-------------------------------------+ +| UART | on-chip | serial port-polling; | +| | | serial port-interrupt | ++-----------+------------+-------------------------------------+ +| PINMUX | on-chip | pinmux | ++-----------+------------+-------------------------------------+ +| GPIO | on-chip | gpio | ++-----------+------------+-------------------------------------+ +| FLASH | on-chip | internal flash memory | ++-----------+------------+-------------------------------------+ + + +Other hardware features are not yet supported on this Zephyr port. + +The default configuration can be found in the defconfig file: +:zephyr_file:`boards/st/nucleo_wb09ke/nucleo_wb09ke_defconfig` + + +Connections and IOs +=================== + +Default Zephyr Peripheral Mapping: +---------------------------------- + +- USART1 TX/RX : PA1/PB0 (ST-Link Virtual COM Port) +- BUTTON (B1) : PA0 +- BUTTON (B2) : PB5 +- BUTTON (B3) : PB14 +- LED (LD1/BLUE) : PB1 +- LED (LD2/GREEN) : PB4 +- LED (LD3/RED) : PB2 + +For more details, please refer to the `Nucleo WB05KZ board User Manual`_. + +Programming and Debugging +************************* + +Nucleo WB05KZ board includes an ST-LINK-V3EC embedded debug tool interface. + +Applications for the ``nucleo_w05kz`` board target can be built and flashed +in the usual way (see :ref:`build_an_application` and :ref:`application_run` +for more details). + +Flashing +======== + +The board is configured to be flashed using the west `STM32CubeProgrammer`_ runner, +so :ref:`it must be installed ` 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] 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 00000000000000..f940b29713a76d Binary files /dev/null and b/boards/st/nucleo_wb09ke/doc/img/nucleo_wb09ke.webp differ diff --git a/boards/st/nucleo_wb09ke/doc/index.rst b/boards/st/nucleo_wb09ke/doc/index.rst new file mode 100644 index 00000000000000..af6a5f7d0277a9 --- /dev/null +++ b/boards/st/nucleo_wb09ke/doc/index.rst @@ -0,0 +1,152 @@ +.. _nucleo_wb09ke_board: + +ST Nucleo WB09KE +################ + +Overview +******** + +The Nucleo WB09KE board is a Bluetooth |reg| Low Energy wireless and ultra-low-power +board featuring an ARM Cortex |reg|-M0+ based STM32WB09KEV MCU, embedding a +powerful and ultra-low-power radio compliant with the Bluetooth® Low Energy +SIG specification v5.4. + +.. image:: img/nucleo_wb09ke.webp + :align: center + :alt: Nucleo WB09KE + +More information about the board can be found on the `Nucleo WB09KE webpage`_. + +Hardware +******** + +Nucleo WB09KE provides the following hardware components: + +- STM32WB09KEV in VFQFPN32 package +- ARM |reg| 32-bit Cortex |reg|-M0+ CPU +- 64 MHz maximal CPU frequebct +- 512 KB Flash +- 64 KB SRAM + +More information about STM32WB09KEV can be found here: + +- `WB09KE on www.st.com`_ +- `STM32WB09 reference manual`_ + + +Supported Features +================== + +The Zephyr ``nucleo_wb09ke`` board target supports the following hardware features: + ++-----------+------------+-------------------------------------+ +| Interface | Controller | Driver/Component | ++===========+============+=====================================+ +| NVIC | on-chip | nested vector interrupt controller | ++-----------+------------+-------------------------------------+ +| UART | on-chip | serial port-polling; | +| | | serial port-interrupt | ++-----------+------------+-------------------------------------+ +| PINMUX | on-chip | pinmux | ++-----------+------------+-------------------------------------+ +| GPIO | on-chip | gpio | ++-----------+------------+-------------------------------------+ +| FLASH | on-chip | internal flash memory | ++-----------+------------+-------------------------------------+ + + +Other hardware features are not yet supported on this Zephyr port. + +The default configuration can be found in the defconfig file: +:zephyr_file:`boards/st/nucleo_wb09ke/nucleo_wb09ke_defconfig` + + +Connections and IOs +=================== + +Default Zephyr Peripheral Mapping: +---------------------------------- + +- USART1 TX/RX : PA1/PB0 (ST-Link Virtual COM Port) +- BUTTON (B1) : PA0 +- BUTTON (B2) : PB5 +- BUTTON (B3) : PB14 +- LED (LD1/BLUE) : PB1 +- LED (LD2/GREEN) : PB4 +- LED (LD3/RED) : PB2 + +For more details, please refer to the `Nucleo WB09KE board User Manual`_. + +Programming and Debugging +************************* + +Nucleo WB09KE board includes an ST-LINK-V3EC embedded debug tool interface. + +Applications for the ``nucleo_w09ke`` board target can be built and flashed +in the usual way (see :ref:`build_an_application` and :ref:`application_run` +for more details). + +Flashing +======== + +The board is configured to be flashed using the west `STM32CubeProgrammer`_ runner, +so :ref:`it must be installed ` 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] 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/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); 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); 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. 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/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)>; + }; +}; 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 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 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" 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" 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/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 }; /** 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_ */ 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_ */ 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 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_ */ 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