You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2022/06/13 12:01:50 UTC

[GitHub] [incubator-nuttx] onegray opened a new pull request, #6422: New chip family stm32wb (WIP)

onegray opened a new pull request, #6422:
URL: https://github.com/apache/incubator-nuttx/pull/6422

   ## Summary
   Work in progress
   
   ## Impact
   None
   
   ## Testing
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] pkarashchenko commented on a diff in pull request #6422: New stm32wb chip family

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on code in PR #6422:
URL: https://github.com/apache/incubator-nuttx/pull/6422#discussion_r909297975


##########
arch/arm/src/stm32wb/hardware/stm32wb_dmamux.h:
##########
@@ -0,0 +1,284 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_dmamux.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_DMAMUX_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_DMAMUX_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "chip.h"
+#include "stm32wb_dma.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DMAMUX1 0
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_DMAMUX_CXCR_OFFSET(x)  (0x0000 + 0x0004*(x)) /* DMAMUX1 request line multiplexer channel x configuration register */

Review Comment:
   Optional
   ```suggestion
   #define STM32WB_DMAMUX_CXCR_OFFSET(x)  (0x0000 + 0x0004 * (x)) /* DMAMUX1 request line multiplexer channel x configuration register */
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_dmamux.h:
##########
@@ -0,0 +1,284 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_dmamux.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_DMAMUX_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_DMAMUX_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "chip.h"
+#include "stm32wb_dma.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DMAMUX1 0
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_DMAMUX_CXCR_OFFSET(x)  (0x0000 + 0x0004*(x)) /* DMAMUX1 request line multiplexer channel x configuration register */
+#define STM32WB_DMAMUX_C0CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(0)
+#define STM32WB_DMAMUX_C1CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(1)
+#define STM32WB_DMAMUX_C2CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(2)
+#define STM32WB_DMAMUX_C3CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(3)
+#define STM32WB_DMAMUX_C4CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(4)
+#define STM32WB_DMAMUX_C5CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(5)
+#define STM32WB_DMAMUX_C6CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(6)
+#define STM32WB_DMAMUX_C7CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(7)
+#define STM32WB_DMAMUX_C8CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(8)
+#define STM32WB_DMAMUX_C9CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(9)
+#define STM32WB_DMAMUX_C10CR_OFFSET    STM32WB_DMAMUX_CXCR_OFFSET(10)
+#define STM32WB_DMAMUX_C11CR_OFFSET    STM32WB_DMAMUX_CXCR_OFFSET(11)
+#define STM32WB_DMAMUX_C12CR_OFFSET    STM32WB_DMAMUX_CXCR_OFFSET(12)
+#define STM32WB_DMAMUX_C13CR_OFFSET    STM32WB_DMAMUX_CXCR_OFFSET(13)
+                                              /* 0x034-0x07C: Reserved */
+#define STM32WB_DMAMUX_CSR_OFFSET      0x0080 /* DMAMUX1 request line multiplexer interrupt channel status register */
+#define STM32WB_DMAMUX_CFR_OFFSET      0x0084 /* DMAMUX1 request line multiplexer interrupt clear flag register */
+                                              /* 0x088-0x0FC: Reserved */
+
+#define STM32WB_DMAMUX_RGXCR_OFFSET(x) (0x0100+0x004*(x)) /* DMAMUX1 request generator channel x configuration register */

Review Comment:
   Optional
   ```suggestion
   #define STM32WB_DMAMUX_RGXCR_OFFSET(x) (0x0100 + 0x004 * (x)) /* DMAMUX1 request generator channel x configuration register */
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] pkarashchenko commented on a diff in pull request #6422: New stm32wb chip family

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on code in PR #6422:
URL: https://github.com/apache/incubator-nuttx/pull/6422#discussion_r908922815


##########
arch/arm/src/stm32wb/stm32wb_irq.c:
##########
@@ -0,0 +1,545 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_irq.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <debug.h>
+#include <assert.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include "arm_internal.h"
+#include "nvic.h"
+#ifdef CONFIG_ARCH_RAMVECTORS
+#  include "ram_vectors.h"
+#endif
+#include "stm32wb_rtc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Get a 32-bit version of the default priority */
+
+#define DEFPRIORITY32 \
+  (NVIC_SYSH_PRIORITY_DEFAULT << 24 | \
+   NVIC_SYSH_PRIORITY_DEFAULT << 16 | \
+   NVIC_SYSH_PRIORITY_DEFAULT << 8  | \
+   NVIC_SYSH_PRIORITY_DEFAULT)

Review Comment:
   We can keep it like it is now. I forgot to tag it as optional. This is just from the common style perspective like in other defines from this PR like `TIM1_SMCR_TS_BITS` for example.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] pkarashchenko commented on a diff in pull request #6422: New stm32wb chip family

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on code in PR #6422:
URL: https://github.com/apache/incubator-nuttx/pull/6422#discussion_r908408057


##########
arch/arm/include/stm32wb/stm32wb_irq.h:
##########
@@ -0,0 +1,220 @@
+/****************************************************************************
+ * arch/arm/include/stm32wb/stm32wb_irq.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* This file should never be included directly but, rather, only indirectly
+ * through arch/irq.h
+ */
+
+#ifndef __ARCH_ARM_INCLUDE_STM32WB_STM32WB55XX_IRQ_H
+#define __ARCH_ARM_INCLUDE_STM32WB_STM32WB55XX_IRQ_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-processor Prototypes
+ ****************************************************************************/
+
+/* IRQ numbers.  The IRQ number corresponds vector number and hence map
+ * directly to bits in the NVIC.  This does, however, waste several words of
+ * memory in the IRQ to handle mapping tables.
+ *
+ * Processor Exceptions (vectors 0-15).  These common definitions can be
+ * found in the file nuttx/arch/arm/include/stm32wb/irq.h which includes
+ * this file
+ *
+ * External interrupts (vectors >= 16)
+ *
+ * These interrupts vectors was implemented based on RM0434 Table 61.
+ *
+ */
+
+#define STM32WB_IRQ_WWDG        (STM32WB_IRQ_FIRST + 0)  /* 0:  Window Watchdog interrupt */
+#define STM32WB_IRQ_PVD         (STM32WB_IRQ_FIRST + 1)  /* 1:  PVD through EXTI[16] Line detection interrupt */
+
+#if defined(CONFIG_STM32WB_STM32WB35) || defined(CONFIG_STM32WB_STM32WB55)
+#  define STM32WB_IRQ_PVM1      (STM32WB_IRQ_FIRST + 1)  /* 1:  PVM1 through EXTI[31] Line detection interrupt */
+#endif
+
+#if defined(CONFIG_STM32WB_STM32WB35) || defined(CONFIG_STM32WB_STM32WB55) \
+    || defined(CONFIG_STM32WB_STM32WB15)
+#  define STM32WB_IRQ_PVM3      (STM32WB_IRQ_FIRST + 1)  /* 1:  PVM3 through EXTI[33] Line detection interrupt */
+#endif
+
+#define STM32WB_IRQ_TAMPER      (STM32WB_IRQ_FIRST + 2)  /* 2:  Tamper through EXTI[18] interrupts */
+#define STM32WB_IRQ_TIMESTAMP   (STM32WB_IRQ_FIRST + 2)  /* 2:  Time stamp through EXTI[18] interrupts */
+#define STM32WB_IRQ_LSECSS      (STM32WB_IRQ_FIRST + 2)  /* 2:  LSECSS through EXTI[18] interrupts */
+#define STM32WB_IRQ_RTC_WKUP    (STM32WB_IRQ_FIRST + 3)  /* 3:  RTC global interrupt */
+#define STM32WB_IRQ_FLASH       (STM32WB_IRQ_FIRST + 4)  /* 4:  Flash global interrupt */
+#define STM32WB_IRQ_RCC         (STM32WB_IRQ_FIRST + 5)  /* 5:  RCC global interrupt */
+#define STM32WB_IRQ_EXTI0       (STM32WB_IRQ_FIRST + 6)  /* 6:  EXTI Line 0 interrupt */
+#define STM32WB_IRQ_EXTI1       (STM32WB_IRQ_FIRST + 7)  /* 7:  EXTI Line 1 interrupt */
+#define STM32WB_IRQ_EXTI2       (STM32WB_IRQ_FIRST + 8)  /* 8:  EXTI Line 2 interrupt */
+#define STM32WB_IRQ_EXTI3       (STM32WB_IRQ_FIRST + 9)  /* 9:  EXTI Line 3 interrupt */
+#define STM32WB_IRQ_EXTI4       (STM32WB_IRQ_FIRST + 10) /* 10: EXTI Line 4 interrupt */
+#define STM32WB_IRQ_DMA1CH1     (STM32WB_IRQ_FIRST + 11) /* 11: DMA1 Channel 1 global interrupt */
+#define STM32WB_IRQ_DMA1CH2     (STM32WB_IRQ_FIRST + 12) /* 12: DMA1 Channel 2 global interrupt */
+#define STM32WB_IRQ_DMA1CH3     (STM32WB_IRQ_FIRST + 13) /* 13: DMA1 Channel 3 global interrupt */
+#define STM32WB_IRQ_DMA1CH4     (STM32WB_IRQ_FIRST + 14) /* 14: DMA1 Channel 4 global interrupt */
+#define STM32WB_IRQ_DMA1CH5     (STM32WB_IRQ_FIRST + 15) /* 15: DMA1 Channel 5 global interrupt */
+#define STM32WB_IRQ_DMA1CH6     (STM32WB_IRQ_FIRST + 16) /* 16: DMA1 Channel 6 global interrupt */
+#define STM32WB_IRQ_DMA1CH7     (STM32WB_IRQ_FIRST + 17) /* 17: DMA1 Channel 7 global interrupt */
+#define STM32WB_IRQ_ADC1        (STM32WB_IRQ_FIRST + 18) /* 18: ADC1 global interrupt */
+
+#if defined(CONFIG_STM32WB_STM32WB35) || defined(CONFIG_STM32WB_STM32WB55)
+  #define STM32WB_IRQ_USB_HP    (STM32WB_IRQ_FIRST + 19) /* 19: USB High Priority Interrupt */
+  #define STM32WB_IRQ_USB_LP    (STM32WB_IRQ_FIRST + 20) /* 20: USB Low Priority Interrupt */
+#endif
+
+#define STM32WB_IRQ_C2SEV       (STM32WB_IRQ_FIRST + 21) /* 21: CPU2 SEV Interrupt */
+
+#if defined(CONFIG_STM32WB_STM32WB35) || defined(CONFIG_STM32WB_STM32WB55) \
+    || defined(CONFIG_STM32WB_STM32WB15)
+#  define STM32WB_IRQ_COMP      (STM32WB_IRQ_FIRST + 22) /* 22: COMP1/COMP2 Interrupts */
+#endif
+
+#define STM32WB_IRQ_EXTI95      (STM32WB_IRQ_FIRST + 23) /* 23: EXTI Lines [9:5] Interrupt */
+#define STM32WB_IRQ_TIM1BRK     (STM32WB_IRQ_FIRST + 24) /* 24: TIM1 Break interrupt */
+#define STM32WB_IRQ_TIM1UP      (STM32WB_IRQ_FIRST + 25) /* 25: TIM1 Update interrupt */
+#define STM32WB_IRQ_TIM1TRGCOM  (STM32WB_IRQ_FIRST + 26) /* 26: TIM1 Trigger and Communication Interrupts */
+
+#if defined(CONFIG_STM32WB_STM32WB30) || defined(CONFIG_STM32WB_STM32WB50) \
+    || defined(CONFIG_STM32WB_STM32WB35) || defined(CONFIG_STM32WB_STM32WB55)
+#  define STM32WB_IRQ_TIM16     (STM32WB_IRQ_FIRST + 25) /* 25: TIM16 global interrupt */
+#  define STM32WB_IRQ_TIM17     (STM32WB_IRQ_FIRST + 26) /* 26: TIM17 global interrupt */
+#endif
+
+#define STM32WB_IRQ_TIM1CC      (STM32WB_IRQ_FIRST + 27) /* 27: TIM1 Capture Compare interrupt */
+#define STM32WB_IRQ_TIM2        (STM32WB_IRQ_FIRST + 28) /* 28: TIM2 global interrupt */
+#define STM32WB_IRQ_PKA         (STM32WB_IRQ_FIRST + 29) /* 29: PKA Interrupt */
+#define STM32WB_IRQ_I2C1EV      (STM32WB_IRQ_FIRST + 30) /* 30: I2C1 event interrupt */
+#define STM32WB_IRQ_I2C1ER      (STM32WB_IRQ_FIRST + 31) /* 31: I2C1 error interrupt */
+
+#if defined(CONFIG_STM32WB_STM32WB35) || defined(CONFIG_STM32WB_STM32WB55)
+#  define STM32WB_IRQ_I2C3EV    (STM32WB_IRQ_FIRST + 32) /* 32: I2C3 event interrupt */
+#  define STM32WB_IRQ_I2C3ER    (STM32WB_IRQ_FIRST + 33) /* 33: I2C3 error interrupt */
+#endif
+
+#define STM32WB_IRQ_SPI1        (STM32WB_IRQ_FIRST + 34) /* 34: SPI1 global interrupt */
+
+#if defined(CONFIG_STM32WB_STM32WB55)
+#  define STM32WB_IRQ_SPI2      (STM32WB_IRQ_FIRST + 35) /* 35: SPI2 global interrupt */
+#endif
+
+#define STM32WB_IRQ_USART1      (STM32WB_IRQ_FIRST + 36) /* 36: USART1 global interrupt */
+
+#if defined(CONFIG_STM32WB_STM32WB35) || defined(CONFIG_STM32WB_STM32WB55) \
+    || defined(CONFIG_STM32WB_STM32WB15)
+#  define STM32WB_IRQ_LPUART1   (STM32WB_IRQ_FIRST + 37) /* 37: LPUART1 global interrupt */
+#endif
+
+#if defined(CONFIG_STM32WB_STM32WB35) || defined(CONFIG_STM32WB_STM32WB55)
+#  define STM32WB_IRQ_SAI1      (STM32WB_IRQ_FIRST + 38) /* 38: SAI1 A/B global interrupt */
+#endif
+
+#if defined(CONFIG_STM32WB_STM32WB10) || defined(CONFIG_STM32WB_STM32WB15) \
+    || defined(CONFIG_STM32WB_STM32WB55)
+#  define STM32WB_IRQ_TSC       (STM32WB_IRQ_FIRST + 39) /* 39: TSC global interrupt */
+#endif
+
+#define STM32WB_IRQ_EXTI1510    (STM32WB_IRQ_FIRST + 40) /* 40: EXTI Line[15:10] interrupts */
+#define STM32WB_IRQ_RTCALRM     (STM32WB_IRQ_FIRST + 41) /* 41: RTC alarm A/B interrupt */
+
+#if defined(CONFIG_STM32WB_STM32WB35) || defined(CONFIG_STM32WB_STM32WB55)
+#  define STM32WB_IRQ_CRS       (STM32WB_IRQ_FIRST + 42) /* 42: CRS interrupt */
+#endif
+
+#define STM32WB_IRQ_PWRSOTF     (STM32WB_IRQ_FIRST + 43) /* 43: PWR switching on the fly interrupt */
+#define STM32WB_IRQ_PWRBLEACT   (STM32WB_IRQ_FIRST + 43) /* 43: PWR end of BLE activity interrupt */
+#define STM32WB_IRQ_PWRRFPHASE  (STM32WB_IRQ_FIRST + 43) /* 43: PWR end of critical radio phase interrupt */
+
+#if defined(CONFIG_STM32WB_STM32WB30) || defined(CONFIG_STM32WB_STM32WB50) \
+    || defined(CONFIG_STM32WB_STM32WB35) || defined(CONFIG_STM32WB_STM32WB55)
+#  define STM32WB_IRQ_PWR802ACT (STM32WB_IRQ_FIRST + 43) /* 43: PWR end of 802.15.4 activity interrupt */
+#endif
+
+#define STM32WB_IRQ_IPCCRX      (STM32WB_IRQ_FIRST + 44) /* 44: IPCC RX occupied interrupt */
+#define STM32WB_IRQ_IPCCTX      (STM32WB_IRQ_FIRST + 45) /* 45: IPCC TX free interrupt */
+#define STM32WB_IRQ_HSEM        (STM32WB_IRQ_FIRST + 46) /* 46: Semaphore interrupt 0 to CPU1 */
+#define STM32WB_IRQ_LPTIM1      (STM32WB_IRQ_FIRST + 47) /* 47: LPTIM1 global interrupt */
+#define STM32WB_IRQ_LPTIM2      (STM32WB_IRQ_FIRST + 48) /* 48: LPTIM2 global interrupt */
+
+#if defined(CONFIG_STM32WB_STM32WB55)
+#  define STM32WB_IRQ_LCD       (STM32WB_IRQ_FIRST + 49) /* 49: LCD global interrupt */
+#endif
+
+#if defined(CONFIG_STM32WB_STM32WB35) || defined(CONFIG_STM32WB_STM32WB55)
+#  define STM32WB_IRQ_QUADSPI   (STM32WB_IRQ_FIRST + 50) /* 50: QUADSPI global interrupt */
+#  define STM32WB_IRQ_AES1      (STM32WB_IRQ_FIRST + 51) /* 51: AES1 crypto global interrupt */
+#endif
+
+#define STM32WB_IRQ_AES2        (STM32WB_IRQ_FIRST + 52) /* 52: AES2 crypto global interrupt */
+#define STM32WB_IRQ_RNG         (STM32WB_IRQ_FIRST + 53) /* 53: RNG global interrupt */
+#define STM32WB_IRQ_FPU         (STM32WB_IRQ_FIRST + 54) /* 54: FPU global interrupt */
+
+#if defined(CONFIG_STM32WB_STM32WB35) || defined(CONFIG_STM32WB_STM32WB55)
+#  define STM32WB_IRQ_DMA2CH1   (STM32WB_IRQ_FIRST + 55) /* 55: DMA2 Channel 1 global interrupt */
+#  define STM32WB_IRQ_DMA2CH2   (STM32WB_IRQ_FIRST + 56) /* 56: DMA2 Channel 2 global interrupt */
+#  define STM32WB_IRQ_DMA2CH3   (STM32WB_IRQ_FIRST + 57) /* 57: DMA2 Channel 3 global interrupt */
+#  define STM32WB_IRQ_DMA2CH4   (STM32WB_IRQ_FIRST + 58) /* 58: DMA2 Channel 4 global interrupt */
+#  define STM32WB_IRQ_DMA2CH5   (STM32WB_IRQ_FIRST + 59) /* 59: DMA2 Channel 5 global interrupt */
+#  define STM32WB_IRQ_DMA2CH6   (STM32WB_IRQ_FIRST + 60) /* 60: DMA2 Channel 6 global interrupt */
+#  define STM32WB_IRQ_DMA2CH7   (STM32WB_IRQ_FIRST + 61) /* 61: DMA2 Channel 7 global interrupt */
+#endif
+
+#define STM32WB_IRQ_DMAMUX1     (STM32WB_IRQ_FIRST + 62) /* 62: DMAMUX1 overrun interrupt */
+
+#define STM32WB_IRQ_NEXTINTS    63
+
+/* (EXTI interrupts do not use IRQ numbers) */
+
+#define NR_IRQS                 (STM32WB_IRQ_FIRST + STM32WB_IRQ_NEXTINTS)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+#endif
+
+#endif /* __ARCH_ARM_INCLUDE_STM32WB_STM32WB55XX_IRQ_H */

Review Comment:
   ```suggestion
   #endif /* __ARCH_ARM_INCLUDE_STM32WB_STM32WB_IRQ_H */
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_rcc.h:
##########
@@ -0,0 +1,877 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_rcc.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_RCC_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_RCC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_RCC_CR_OFFSET           0x0000  /* Clock control register */
+#define STM32WB_RCC_ICSCR_OFFSET        0x0004  /* Internal clock sources calibration register */
+#define STM32WB_RCC_CFGR_OFFSET         0x0008  /* Clock configuration register */
+#define STM32WB_RCC_PLLCFG_OFFSET       0x000c  /* PLL configuration register */
+#define STM32WB_RCC_PLLSAI1CFG_OFFSET   0x0010  /* PLLSAI1 configuration register */
+#define STM32WB_RCC_CIER_OFFSET         0x0018  /* Clock interrupt enable register */
+#define STM32WB_RCC_CIFR_OFFSET         0x001c  /* Clock interrupt flag register */
+#define STM32WB_RCC_CICR_OFFSET         0x0020  /* Clock interrupt clear register */
+#define STM32WB_RCC_SMPSCR_OFFSET       0x0024  /* Step-down converter control register */
+#define STM32WB_RCC_AHB1RSTR_OFFSET     0x0028  /* AHB1 peripheral reset register */
+#define STM32WB_RCC_AHB2RSTR_OFFSET     0x002c  /* AHB2 peripheral reset register */
+#define STM32WB_RCC_AHB3RSTR_OFFSET     0x0030  /* AHB3 peripheral reset register */
+#define STM32WB_RCC_APB1RSTR1_OFFSET    0x0038  /* APB1 Peripheral reset register 1 */
+#define STM32WB_RCC_APB1RSTR2_OFFSET    0x003c  /* APB1 Peripheral reset register 2 */
+#define STM32WB_RCC_APB2RSTR_OFFSET     0x0040  /* APB2 Peripheral reset register */
+#define STM32WB_RCC_APB3RSTR_OFFSET     0x0044  /* APB3 Peripheral reset register */
+#define STM32WB_RCC_AHB1ENR_OFFSET      0x0048  /* AHB1 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB2ENR_OFFSET      0x004c  /* AHB2 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB3ENR_OFFSET      0x0050  /* AHB3 Peripheral Clock enable register */
+#define STM32WB_RCC_APB1ENR1_OFFSET     0x0058  /* APB1 Peripheral Clock enable register 1 */
+#define STM32WB_RCC_APB1ENR2_OFFSET     0x005c  /* APB1 Peripheral Clock enable register 2 */
+#define STM32WB_RCC_APB2ENR_OFFSET      0x0060  /* APB2 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB1SMENR_OFFSET    0x0068  /* AHB1 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_AHB2SMENR_OFFSET    0x006c  /* AHB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_AHB3SMENR_OFFSET    0x0070  /* AHB3 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_APB1SMENR1_OFFSET   0x0078  /* APB1 clock enable in sleep and stop modes register 1 */
+#define STM32WB_RCC_APB1SMENR2_OFFSET   0x007c  /* APB1 clock enable in sleep and stop modes register 2 */
+#define STM32WB_RCC_APB2SMENR_OFFSET    0x0080  /* APB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_CCIPR_OFFSET        0x0088  /* Peripherals independent clock configuration register */
+#define STM32WB_RCC_BDCR_OFFSET         0x0090  /* Backup domain control register */
+#define STM32WB_RCC_CSR_OFFSET          0x0094  /* Control/status register */
+#define STM32WB_RCC_CRRCR_OFFSET        0x0098  /* Clock recovery RC register */
+#define STM32WB_RCC_HSECR_OFFSET        0x009c  /* Clock HSE register */
+#define STM32WB_RCC_EXTCFGR_OFFSET      0x0108  /* Extended clock recovery register */
+#define STM32WB_RCC_C2AHB1ENR_OFFSET    0x0148  /* CPU2 AHB1 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB2ENR_OFFSET    0x014c  /* CPU2 AHB2 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB3ENR_OFFSET    0x0150  /* CPU2 AHB3 Peripheral Clock enable register */
+#define STM32WB_RCC_C2APB1ENR1_OFFSET   0x0158  /* CPU2 APB1 Peripheral Clock enable register 1 */
+#define STM32WB_RCC_C2APB1ENR2_OFFSET   0x015c  /* CPU2 APB1 Peripheral Clock enable register 2 */
+#define STM32WB_RCC_C2APB2ENR_OFFSET    0x0160  /* CPU2 APB2 Peripheral Clock enable register */
+#define STM32WB_RCC_C2APB3ENR_OFFSET    0x0164  /* CPU2 APB3 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB1SMENR_OFFSET  0x0168  /* CPU2 AHB1 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2AHB2SMENR_OFFSET  0x016c  /* CPU2 AHB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2AHB3SMENR_OFFSET  0x0170  /* CPU2 AHB3 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2APB1SMENR1_OFFSET 0x0178  /* CPU2 APB1 clock enable in sleep and stop modes register 1 */
+#define STM32WB_RCC_C2APB1SMENR2_OFFSET 0x017c  /* CPU2 APB1 clock enable in sleep and stop modes register 2 */
+#define STM32WB_RCC_C2APB2SMENR_OFFSET  0x0180  /* CPU2 APB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2APB3SMENR_OFFSET  0x0184  /* CPU2 APB3 clock enable in sleep and stop modes register */
+
+/* Register Addresses *******************************************************/
+
+#define STM32WB_RCC_CR                (STM32WB_RCC_BASE + STM32WB_RCC_CR_OFFSET)
+#define STM32WB_RCC_ICSCR             (STM32WB_RCC_BASE + STM32WB_RCC_ICSCR_OFFSET)
+#define STM32WB_RCC_CFGR              (STM32WB_RCC_BASE + STM32WB_RCC_CFGR_OFFSET)
+#define STM32WB_RCC_PLLCFG            (STM32WB_RCC_BASE + STM32WB_RCC_PLLCFG_OFFSET)
+#define STM32WB_RCC_PLLSAI1CFG        (STM32WB_RCC_BASE + STM32WB_RCC_PLLSAI1CFG_OFFSET)
+#define STM32WB_RCC_CIER              (STM32WB_RCC_BASE + STM32WB_RCC_CIER_OFFSET)
+#define STM32WB_RCC_CIFR              (STM32WB_RCC_BASE + STM32WB_RCC_CIFR_OFFSET)
+#define STM32WB_RCC_CICR              (STM32WB_RCC_BASE + STM32WB_RCC_CICR_OFFSET)
+#define STM32WB_RCC_SMPSCR            (STM32WB_RCC_BASE + STM32WB_RCC_SMPSCR_OFFSET)
+#define STM32WB_RCC_AHB1RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB1RSTR_OFFSET)
+#define STM32WB_RCC_AHB2RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB2RSTR_OFFSET)
+#define STM32WB_RCC_AHB3RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB3RSTR_OFFSET)
+#define STM32WB_RCC_APB1RSTR1         (STM32WB_RCC_BASE + STM32WB_RCC_APB1RSTR1_OFFSET)
+#define STM32WB_RCC_APB1RSTR2         (STM32WB_RCC_BASE + STM32WB_RCC_APB1RSTR2_OFFSET)
+#define STM32WB_RCC_APB2RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_APB2RSTR_OFFSET)
+#define STM32WB_RCC_APB3RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_APB3RSTR_OFFSET)
+#define STM32WB_RCC_AHB1ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB1ENR_OFFSET)
+#define STM32WB_RCC_AHB2ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB2ENR_OFFSET)
+#define STM32WB_RCC_AHB3ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB3ENR_OFFSET)
+#define STM32WB_RCC_APB1ENR1          (STM32WB_RCC_BASE + STM32WB_RCC_APB1ENR1_OFFSET)
+#define STM32WB_RCC_APB1ENR2          (STM32WB_RCC_BASE + STM32WB_RCC_APB1ENR2_OFFSET)
+#define STM32WB_RCC_APB2ENR           (STM32WB_RCC_BASE + STM32WB_RCC_APB2ENR_OFFSET)
+#define STM32WB_RCC_AHB1SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB1SMENR_OFFSET)
+#define STM32WB_RCC_AHB2SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB2SMENR_OFFSET)
+#define STM32WB_RCC_AHB3SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB3SMENR_OFFSET)
+#define STM32WB_RCC_APB1SMENR1        (STM32WB_RCC_BASE + STM32WB_RCC_APB1SMENR1_OFFSET)
+#define STM32WB_RCC_APB1SMENR2        (STM32WB_RCC_BASE + STM32WB_RCC_APB1SMENR2_OFFSET)
+#define STM32WB_RCC_APB2SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_APB2SMENR_OFFSET)
+#define STM32WB_RCC_CCIPR             (STM32WB_RCC_BASE + STM32WB_RCC_CCIPR_OFFSET)
+#define STM32WB_RCC_BDCR              (STM32WB_RCC_BASE + STM32WB_RCC_BDCR_OFFSET)
+#define STM32WB_RCC_CSR               (STM32WB_RCC_BASE + STM32WB_RCC_CSR_OFFSET)
+#define STM32WB_RCC_CRRCR             (STM32WB_RCC_BASE + STM32WB_RCC_CRRCR_OFFSET)
+#define STM32WB_RCC_HSECR             (STM32WB_RCC_BASE + STM32WB_RCC_HSECR_OFFSET)
+#define STM32WB_RCC_EXTCFGR           (STM32WB_RCC_BASE + STM32WB_RCC_EXTCFGR_OFFSET)
+#define STM32WB_RCC_C2AHB1ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB1ENR_OFFSET)
+#define STM32WB_RCC_C2AHB2ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB2ENR_OFFSET)
+#define STM32WB_RCC_C2AHB3ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB3ENR_OFFSET)
+#define STM32WB_RCC_C2APB1ENR1        (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1ENR1_OFFSET)
+#define STM32WB_RCC_C2APB1ENR2        (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1ENR2_OFFSET)
+#define STM32WB_RCC_C2APB2ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2APB2ENR_OFFSET)
+#define STM32WB_RCC_C2APB3ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2APB3ENR_OFFSET)
+#define STM32WB_RCC_C2AHB1SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB1SMENR_OFFSET)
+#define STM32WB_RCC_C2AHB2SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB2SMENR_OFFSET)
+#define STM32WB_RCC_C2AHB3SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB3SMENR_OFFSET)
+#define STM32WB_RCC_C2APB1SMENR1      (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1SMENR1_OFFSET)
+#define STM32WB_RCC_C2APB1SMENR2      (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1SMENR2_OFFSET)
+#define STM32WB_RCC_C2APB2SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2APB2SMENR_OFFSET)
+#define STM32WB_RCC_C2APB3SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2APB3SMENR_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Clock control register */
+
+#define RCC_CR_MSION                  (1 << 0)  /* Bit 0: Internal Multi Speed clock enable */
+#define RCC_CR_MSIRDY                 (1 << 1)  /* Bit 1: Internal Multi Speed clock ready flag */
+#define RCC_CR_MSIPLLEN               (1 << 2)  /* Bit 2: MSI clock PLL enable */
+#define RCC_CR_MSIRANGE_SHIFT         (4)       /* Bits 7-4: MSI clock range */
+#define RCC_CR_MSIRANGE_MASK          (0x0f << RCC_CR_MSIRANGE_SHIFT)
+#  define RCC_CR_MSIRANGE_100K        (0  << RCC_CR_MSIRANGE_SHIFT) /* 0000: around 100 kHz */
+#  define RCC_CR_MSIRANGE_200K        (1  << RCC_CR_MSIRANGE_SHIFT) /* 0001: around 200 kHz */
+#  define RCC_CR_MSIRANGE_400K        (2  << RCC_CR_MSIRANGE_SHIFT) /* 0010: around 400 kHz */
+#  define RCC_CR_MSIRANGE_800K        (3  << RCC_CR_MSIRANGE_SHIFT) /* 0011: around 800 kHz */
+#  define RCC_CR_MSIRANGE_1M          (4  << RCC_CR_MSIRANGE_SHIFT) /* 0100: around 1 MHz */
+#  define RCC_CR_MSIRANGE_2M          (5  << RCC_CR_MSIRANGE_SHIFT) /* 0101: around 2 MHz */
+#  define RCC_CR_MSIRANGE_4M          (6  << RCC_CR_MSIRANGE_SHIFT) /* 0110: around 4 MHz */
+#  define RCC_CR_MSIRANGE_8M          (7  << RCC_CR_MSIRANGE_SHIFT) /* 0111: around 8 MHz */
+#  define RCC_CR_MSIRANGE_16M         (8  << RCC_CR_MSIRANGE_SHIFT) /* 1000: around 16 MHz */
+#  define RCC_CR_MSIRANGE_24M         (9  << RCC_CR_MSIRANGE_SHIFT) /* 1001: around 24 MHz */
+#  define RCC_CR_MSIRANGE_32M         (10 << RCC_CR_MSIRANGE_SHIFT) /* 1010: around 32 MHz */
+#  define RCC_CR_MSIRANGE_48M         (11 << RCC_CR_MSIRANGE_SHIFT) /* 1011: around 48 MHz */
+
+#define RCC_CR_HSION                  (1 << 8)  /* Bit 8:  HSI16 clock enable */
+#define RCC_CR_HSIKERON               (1 << 9)  /* Bit 9:  HSI16 always enable for peripheral kernels */
+#define RCC_CR_HSIRDY                 (1 << 10) /* Bit 10: HSI16 clock ready flag */
+#define RCC_CR_HSIASFS                (1 << 11) /* Bit 11: HSI16 automatic start from stop */
+#define RCC_CR_HSEON                  (1 << 16) /* Bit 16: HSE clock enable */
+#define RCC_CR_HSERDY                 (1 << 17) /* Bit 17: HSE clock ready flag */
+#define RCC_CR_CSSON                  (1 << 19) /* Bit 19: Clock Security System enable */
+#define RCC_CR_HSEPRE                 (1 << 20) /* Bit 20: HSE sysclk and PLL M divider prescaler */
+#define RCC_CR_PLLON                  (1 << 24) /* Bit 24: PLL enable */
+#define RCC_CR_PLLRDY                 (1 << 25) /* Bit 25: PLL clock ready flag */
+#define RCC_CR_PLLSAI1ON              (1 << 26) /* Bit 26: PLLSAI1 enable */
+#define RCC_CR_PLLSAI1RDY             (1 << 27) /* Bit 27: PLLSAI1 clock ready flag */
+
+/* Internal Clock Sources Calibration */
+
+#define RCC_CR_HSITRIM_SHIFT          (24)      /* Bits 30-24: HSI16 clock trimming */
+#define RCC_CR_HSITRIM_MASK           (0x7f << RCC_CR_HSITRIM_SHIFT)
+#define RCC_CR_HSICAL_SHIFT           (16)      /* Bits 23-16: HSI16 clock Calibration */
+#define RCC_CR_HSICAL_MASK            (0xff << RCC_CR_HSICAL_SHIFT)
+#define RCC_CR_MSITRIM_SHIFT          (8)       /* Bits 15-8:  Internal Multi Speed clock trimming */
+#define RCC_CR_MSITRIM_MASK           (0xff << RCC_CR_MSITRIM_SHIFT)
+#define RCC_CR_MSICAL_SHIFT           (0)       /* Bits 7-0:   Internal Multi Speed clock Calibration */
+#define RCC_CR_MSICAL_MASK            (0xff << RCC_CR_MSICAL_SHIFT)
+
+/* Clock configuration register */
+
+#define RCC_CFGR_SW_SHIFT             (0)       /* Bits 0-1: System clock Switch */
+#define RCC_CFGR_SW_MASK              (0x3 << RCC_CFGR_SW_SHIFT)
+#  define RCC_CFGR_SW_MSI             (0x0 << RCC_CFGR_SW_SHIFT) /* 00: MSI selected as system clock */
+#  define RCC_CFGR_SW_HSI16           (0x1 << RCC_CFGR_SW_SHIFT) /* 00: HSI16 selected as system clock */
+#  define RCC_CFGR_SW_HSE             (0x2 << RCC_CFGR_SW_SHIFT) /* 01: HSE selected as system clock */
+#  define RCC_CFGR_SW_PLL             (0x3 << RCC_CFGR_SW_SHIFT) /* 10: PLL selected as system clock */
+
+#define RCC_CFGR_SWS_SHIFT            (2)       /* Bits 2-3: System Clock Switch Status */
+#define RCC_CFGR_SWS_MASK             (0x3 << RCC_CFGR_SWS_SHIFT)
+#  define RCC_CFGR_SWS_MSI            (0x0 << RCC_CFGR_SWS_SHIFT) /* 00: MSI oscillator used as system clock */
+#  define RCC_CFGR_SWS_HSI16          (0x1 << RCC_CFGR_SWS_SHIFT) /* 00: HSI16 oscillator used as system clock */
+#  define RCC_CFGR_SWS_HSE            (0x2 << RCC_CFGR_SWS_SHIFT) /* 01: HSE oscillator used as system clock */
+#  define RCC_CFGR_SWS_PLL            (0x3 << RCC_CFGR_SWS_SHIFT) /* 10: PLL used as system clock */
+
+#define RCC_CFGR_HPRE_SHIFT           (4)       /* Bits 4-7: HCLK1 prescaler (AHB1, AHB2, AHB3 and SRAM1) */
+#define RCC_CFGR_HPRE_MASK            (0x0f << RCC_CFGR_HPRE_SHIFT)
+#  define RCC_CFGR_HPRE_SYSCLK        (0x00 << RCC_CFGR_HPRE_SHIFT) /* 0000: SYSCLK not divided */
+#  define RCC_CFGR_HPRE_SYSCLKd3      (0x01 << RCC_CFGR_HPRE_SHIFT) /* 0001: SYSCLK divided by 3 */
+#  define RCC_CFGR_HPRE_SYSCLKd5      (0x02 << RCC_CFGR_HPRE_SHIFT) /* 0010: SYSCLK divided by 5 */
+#  define RCC_CFGR_HPRE_SYSCLKd6      (0x05 << RCC_CFGR_HPRE_SHIFT) /* 0101: SYSCLK divided by 6 */
+#  define RCC_CFGR_HPRE_SYSCLKd10     (0x06 << RCC_CFGR_HPRE_SHIFT) /* 0110: SYSCLK divided by 10 */
+#  define RCC_CFGR_HPRE_SYSCLKd32     (0x07 << RCC_CFGR_HPRE_SHIFT) /* 0111: SYSCLK divided by 32 */
+#  define RCC_CFGR_HPRE_SYSCLKd2      (0x08 << RCC_CFGR_HPRE_SHIFT) /* 1000: SYSCLK divided by 2 */
+#  define RCC_CFGR_HPRE_SYSCLKd4      (0x09 << RCC_CFGR_HPRE_SHIFT) /* 1001: SYSCLK divided by 4 */
+#  define RCC_CFGR_HPRE_SYSCLKd8      (0x10 << RCC_CFGR_HPRE_SHIFT) /* 1010: SYSCLK divided by 8 */
+#  define RCC_CFGR_HPRE_SYSCLKd16     (0x11 << RCC_CFGR_HPRE_SHIFT) /* 1011: SYSCLK divided by 16 */
+#  define RCC_CFGR_HPRE_SYSCLKd64     (0x12 << RCC_CFGR_HPRE_SHIFT) /* 1100: SYSCLK divided by 64 */
+#  define RCC_CFGR_HPRE_SYSCLKd128    (0x13 << RCC_CFGR_HPRE_SHIFT) /* 1101: SYSCLK divided by 128 */
+#  define RCC_CFGR_HPRE_SYSCLKd256    (0x14 << RCC_CFGR_HPRE_SHIFT) /* 1110: SYSCLK divided by 256 */
+#  define RCC_CFGR_HPRE_SYSCLKd512    (0x15 << RCC_CFGR_HPRE_SHIFT) /* 1111: SYSCLK divided by 512 */
+
+#define RCC_CFGR_PPRE1_SHIFT          (8)       /* Bits 8-10: PCLK1 Low speed prescaler (APB1) */
+#define RCC_CFGR_PPRE1_MASK           (0x7 << RCC_CFGR_PPRE1_SHIFT)
+#  define RCC_CFGR_PPRE1_HCLK1        (0x0 << RCC_CFGR_PPRE1_SHIFT) /* 0xx: HCLK1 not divided */
+#  define RCC_CFGR_PPRE1_HCLK1d2      (0x4 << RCC_CFGR_PPRE1_SHIFT) /* 100: HCLK1 divided by 2 */
+#  define RCC_CFGR_PPRE1_HCLK1d4      (0x5 << RCC_CFGR_PPRE1_SHIFT) /* 101: HCLK1 divided by 4 */
+#  define RCC_CFGR_PPRE1_HCLK1d8      (0x6 << RCC_CFGR_PPRE1_SHIFT) /* 110: HCLK1 divided by 8 */
+#  define RCC_CFGR_PPRE1_HCLK1d16     (0x7 << RCC_CFGR_PPRE1_SHIFT) /* 111: HCLK1 divided by 16 */
+
+#define RCC_CFGR_PPRE2_SHIFT          (11)      /* Bits 11-13: PCLK2 High speed prescaler (APB2) */
+#define RCC_CFGR_PPRE2_MASK           (0x7 << RCC_CFGR_PPRE2_SHIFT)
+#  define RCC_CFGR_PPRE2_HCLK1        (0x0 << RCC_CFGR_PPRE2_SHIFT) /* 0xx: HCLK1 not divided */
+#  define RCC_CFGR_PPRE2_HCLK1d2      (0x4 << RCC_CFGR_PPRE2_SHIFT) /* 100: HCLK1 divided by 2 */
+#  define RCC_CFGR_PPRE2_HCLK1d4      (0x5 << RCC_CFGR_PPRE2_SHIFT) /* 101: HCLK1 divided by 4 */
+#  define RCC_CFGR_PPRE2_HCLK1d8      (0x6 << RCC_CFGR_PPRE2_SHIFT) /* 110: HCLK1 divided by 8 */
+#  define RCC_CFGR_PPRE2_HCLK1d16     (0x7 << RCC_CFGR_PPRE2_SHIFT) /* 111: HCLK1 divided by 16 */
+
+#define RCC_CFGR_STOPWUCK             (1 << 15) /* Bit 15: Wakeup from Stop and CSS backup clock selection */
+#  define RCC_CFGR_STOPWUCK_MSI       (0 << 15) /* 0: MSI */
+#  define RCC_CFGR_STOPWUCK_HSI16     (1 << 15) /* 0: HSI16 */
+
+#define RCC_CFGR_HPREF                (1 << 16) /* Bit 16: HCLK1 prescaler flag (AHB1, AHB2, AHB3, SRAM1) */
+#define RCC_CFGR_PPRE1F               (1 << 17) /* Bit 17: PCLK1 prescaler flag (APB1) */
+#define RCC_CFGR_PPRE2F               (1 << 18) /* Bit 18: PCLK2 prescaler flag (APB2) */
+
+#define RCC_CFGR_MCOSEL_SHIFT         (24)      /* Bits 24-27: Microcontroller Clock Output */
+#define RCC_CFGR_MCOSEL_MASK          (0xf << RCC_CFGR_MCOSEL_SHIFT)
+#  define RCC_CFGR_MCOSEL_DISABLED    (0x0 << RCC_CFGR_MCOSEL_SHIFT) /* 0000: Output disabled, no clock on MCO */
+#  define RCC_CFGR_MCOSEL_SYSCLK      (0x1 << RCC_CFGR_MCOSEL_SHIFT) /* 0001: SYSCLK system clock selected */
+#  define RCC_CFGR_MCOSEL_MSI         (0x2 << RCC_CFGR_MCOSEL_SHIFT) /* 0010: MSI clock selected */
+#  define RCC_CFGR_MCOSEL_HSI16       (0x3 << RCC_CFGR_MCOSEL_SHIFT) /* 0011: HSI16 clock selected */
+#  define RCC_CFGR_MCOSEL_HSE_AFT     (0x4 << RCC_CFGR_MCOSEL_SHIFT) /* 0100: HSE clock after stabilization */
+#  define RCC_CFGR_MCOSEL_PLL         (0x5 << RCC_CFGR_MCOSEL_SHIFT) /* 0101: Main PLLRCLK selected  */
+#  define RCC_CFGR_MCOSEL_LSI1        (0x6 << RCC_CFGR_MCOSEL_SHIFT) /* 0110: LSI1 clock selected */
+#  define RCC_CFGR_MCOSEL_LSI2        (0x7 << RCC_CFGR_MCOSEL_SHIFT) /* 0111: LSI2 clock selected */
+#  define RCC_CFGR_MCOSEL_LSE         (0x8 << RCC_CFGR_MCOSEL_SHIFT) /* 1000: LSE clock selected */
+#  define RCC_CFGR_MCOSEL_HSI48       (0x9 << RCC_CFGR_MCOSEL_SHIFT) /* 1001: HSI48 clock selected */
+#  define RCC_CFGR_MCOSEL_HSE_BFR     (0xc << RCC_CFGR_MCOSEL_SHIFT) /* 1100: HSE clock before stabilization */
+
+#define RCC_CFGR_MCOPRE_SHIFT         (28)      /* Bits 28-30: MCO prescaler */
+#define RCC_CFGR_MCOPRE_MASK          (0x7 << RCC_CFGR_MCOPRE_SHIFT)
+#  define RCC_CFGR_MCOPRE_DIV1        (0x0 << RCC_CFGR_MCOPRE_SHIFT) /* 000: no division */
+#  define RCC_CFGR_MCOPRE_DIV2        (0x1 << RCC_CFGR_MCOPRE_SHIFT) /* 001: division by 2 */
+#  define RCC_CFGR_MCOPRE_DIV4        (0x2 << RCC_CFGR_MCOPRE_SHIFT) /* 010: division by 4 */
+#  define RCC_CFGR_MCOPRE_DIV8        (0x3 << RCC_CFGR_MCOPRE_SHIFT) /* 011: division by 8 */
+#  define RCC_CFGR_MCOPRE_DIV16       (0x4 << RCC_CFGR_MCOPRE_SHIFT) /* 100: division by 16 */
+
+#define RCC_CFGR_RESET_MASK           (0x00070000)
+
+/* PLL configuration register */
+
+#define RCC_PLLCFG_PLLSRC_SHIFT       (0)       /* Bits 0-1: Main PLL and audio PLLSAI1 entry clock source */
+#define RCC_PLLCFG_PLLSRC_MASK        (0x3 << RCC_PLLCFG_PLLSRC_SHIFT)
+#  define RCC_PLLCFG_PLLSRC_NONE      (0x0 << RCC_PLLCFG_PLLSRC_SHIFT) /* 00: No clock sent to PLLs */
+#  define RCC_PLLCFG_PLLSRC_MSI       (0x1 << RCC_PLLCFG_PLLSRC_SHIFT) /* 01: MSI selected as PLLs source */
+#  define RCC_PLLCFG_PLLSRC_HSI16     (0x2 << RCC_PLLCFG_PLLSRC_SHIFT) /* 10: HSI16 selected as PLLs source */
+#  define RCC_PLLCFG_PLLSRC_HSE       (0x3 << RCC_PLLCFG_PLLSRC_SHIFT) /* 11: HSE selected as PLLs source */
+
+#define RCC_PLLCFG_PLLM_SHIFT         (4)       /* Bits 4-6: Main PLL and audio PLLSAI1 divider */
+#define RCC_PLLCFG_PLLM_MASK          (0x07 << RCC_PLLCFG_PLLM_SHIFT)
+#  define RCC_PLLCFG_PLLM(n)          ((n-1) << RCC_PLLCFG_PLLM_SHIFT) /* n = 1..8 */
+
+#define RCC_PLLCFG_PLLN_SHIFT         (8)       /* Bits 8-14: Main PLL (PLL) VCO multiplier */
+#define RCC_PLLCFG_PLLN_MASK          (0x7f << RCC_PLLCFG_PLLN_SHIFT)
+#  define RCC_PLLCFG_PLLN(n)          ((n) << RCC_PLLCFG_PLLN_SHIFT) /* n = 6..127 */
+
+#define RCC_PLLCFG_PLLPEN             (1 << 16) /* Bit 16: Main PLL PLLPCLK output enable */
+
+#define RCC_PLLCFG_PLLP_SHIFT         (17)      /* Bits 17-21: Main PLL div factor for PLLPCLK */
+#define RCC_PLLCFG_PLLP_MASK          (0x1f << RCC_PLLCFG_PLLP_SHIFT)
+#  define RCC_PLLCFG_PLLP(n)          ((n-1) << RCC_PLLCFG_PLLP_SHIFT) /* n = 2..32 */

Review Comment:
   ```suggestion
   #  define RCC_PLLCFG_PLLP(n)          (((n) - 1) << RCC_PLLCFG_PLLP_SHIFT) /* n = 2..32 */
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_rcc.h:
##########
@@ -0,0 +1,877 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_rcc.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_RCC_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_RCC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_RCC_CR_OFFSET           0x0000  /* Clock control register */
+#define STM32WB_RCC_ICSCR_OFFSET        0x0004  /* Internal clock sources calibration register */
+#define STM32WB_RCC_CFGR_OFFSET         0x0008  /* Clock configuration register */
+#define STM32WB_RCC_PLLCFG_OFFSET       0x000c  /* PLL configuration register */
+#define STM32WB_RCC_PLLSAI1CFG_OFFSET   0x0010  /* PLLSAI1 configuration register */
+#define STM32WB_RCC_CIER_OFFSET         0x0018  /* Clock interrupt enable register */
+#define STM32WB_RCC_CIFR_OFFSET         0x001c  /* Clock interrupt flag register */
+#define STM32WB_RCC_CICR_OFFSET         0x0020  /* Clock interrupt clear register */
+#define STM32WB_RCC_SMPSCR_OFFSET       0x0024  /* Step-down converter control register */
+#define STM32WB_RCC_AHB1RSTR_OFFSET     0x0028  /* AHB1 peripheral reset register */
+#define STM32WB_RCC_AHB2RSTR_OFFSET     0x002c  /* AHB2 peripheral reset register */
+#define STM32WB_RCC_AHB3RSTR_OFFSET     0x0030  /* AHB3 peripheral reset register */
+#define STM32WB_RCC_APB1RSTR1_OFFSET    0x0038  /* APB1 Peripheral reset register 1 */
+#define STM32WB_RCC_APB1RSTR2_OFFSET    0x003c  /* APB1 Peripheral reset register 2 */
+#define STM32WB_RCC_APB2RSTR_OFFSET     0x0040  /* APB2 Peripheral reset register */
+#define STM32WB_RCC_APB3RSTR_OFFSET     0x0044  /* APB3 Peripheral reset register */
+#define STM32WB_RCC_AHB1ENR_OFFSET      0x0048  /* AHB1 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB2ENR_OFFSET      0x004c  /* AHB2 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB3ENR_OFFSET      0x0050  /* AHB3 Peripheral Clock enable register */
+#define STM32WB_RCC_APB1ENR1_OFFSET     0x0058  /* APB1 Peripheral Clock enable register 1 */
+#define STM32WB_RCC_APB1ENR2_OFFSET     0x005c  /* APB1 Peripheral Clock enable register 2 */
+#define STM32WB_RCC_APB2ENR_OFFSET      0x0060  /* APB2 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB1SMENR_OFFSET    0x0068  /* AHB1 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_AHB2SMENR_OFFSET    0x006c  /* AHB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_AHB3SMENR_OFFSET    0x0070  /* AHB3 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_APB1SMENR1_OFFSET   0x0078  /* APB1 clock enable in sleep and stop modes register 1 */
+#define STM32WB_RCC_APB1SMENR2_OFFSET   0x007c  /* APB1 clock enable in sleep and stop modes register 2 */
+#define STM32WB_RCC_APB2SMENR_OFFSET    0x0080  /* APB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_CCIPR_OFFSET        0x0088  /* Peripherals independent clock configuration register */
+#define STM32WB_RCC_BDCR_OFFSET         0x0090  /* Backup domain control register */
+#define STM32WB_RCC_CSR_OFFSET          0x0094  /* Control/status register */
+#define STM32WB_RCC_CRRCR_OFFSET        0x0098  /* Clock recovery RC register */
+#define STM32WB_RCC_HSECR_OFFSET        0x009c  /* Clock HSE register */
+#define STM32WB_RCC_EXTCFGR_OFFSET      0x0108  /* Extended clock recovery register */
+#define STM32WB_RCC_C2AHB1ENR_OFFSET    0x0148  /* CPU2 AHB1 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB2ENR_OFFSET    0x014c  /* CPU2 AHB2 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB3ENR_OFFSET    0x0150  /* CPU2 AHB3 Peripheral Clock enable register */
+#define STM32WB_RCC_C2APB1ENR1_OFFSET   0x0158  /* CPU2 APB1 Peripheral Clock enable register 1 */
+#define STM32WB_RCC_C2APB1ENR2_OFFSET   0x015c  /* CPU2 APB1 Peripheral Clock enable register 2 */
+#define STM32WB_RCC_C2APB2ENR_OFFSET    0x0160  /* CPU2 APB2 Peripheral Clock enable register */
+#define STM32WB_RCC_C2APB3ENR_OFFSET    0x0164  /* CPU2 APB3 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB1SMENR_OFFSET  0x0168  /* CPU2 AHB1 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2AHB2SMENR_OFFSET  0x016c  /* CPU2 AHB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2AHB3SMENR_OFFSET  0x0170  /* CPU2 AHB3 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2APB1SMENR1_OFFSET 0x0178  /* CPU2 APB1 clock enable in sleep and stop modes register 1 */
+#define STM32WB_RCC_C2APB1SMENR2_OFFSET 0x017c  /* CPU2 APB1 clock enable in sleep and stop modes register 2 */
+#define STM32WB_RCC_C2APB2SMENR_OFFSET  0x0180  /* CPU2 APB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2APB3SMENR_OFFSET  0x0184  /* CPU2 APB3 clock enable in sleep and stop modes register */
+
+/* Register Addresses *******************************************************/
+
+#define STM32WB_RCC_CR                (STM32WB_RCC_BASE + STM32WB_RCC_CR_OFFSET)
+#define STM32WB_RCC_ICSCR             (STM32WB_RCC_BASE + STM32WB_RCC_ICSCR_OFFSET)
+#define STM32WB_RCC_CFGR              (STM32WB_RCC_BASE + STM32WB_RCC_CFGR_OFFSET)
+#define STM32WB_RCC_PLLCFG            (STM32WB_RCC_BASE + STM32WB_RCC_PLLCFG_OFFSET)
+#define STM32WB_RCC_PLLSAI1CFG        (STM32WB_RCC_BASE + STM32WB_RCC_PLLSAI1CFG_OFFSET)
+#define STM32WB_RCC_CIER              (STM32WB_RCC_BASE + STM32WB_RCC_CIER_OFFSET)
+#define STM32WB_RCC_CIFR              (STM32WB_RCC_BASE + STM32WB_RCC_CIFR_OFFSET)
+#define STM32WB_RCC_CICR              (STM32WB_RCC_BASE + STM32WB_RCC_CICR_OFFSET)
+#define STM32WB_RCC_SMPSCR            (STM32WB_RCC_BASE + STM32WB_RCC_SMPSCR_OFFSET)
+#define STM32WB_RCC_AHB1RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB1RSTR_OFFSET)
+#define STM32WB_RCC_AHB2RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB2RSTR_OFFSET)
+#define STM32WB_RCC_AHB3RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB3RSTR_OFFSET)
+#define STM32WB_RCC_APB1RSTR1         (STM32WB_RCC_BASE + STM32WB_RCC_APB1RSTR1_OFFSET)
+#define STM32WB_RCC_APB1RSTR2         (STM32WB_RCC_BASE + STM32WB_RCC_APB1RSTR2_OFFSET)
+#define STM32WB_RCC_APB2RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_APB2RSTR_OFFSET)
+#define STM32WB_RCC_APB3RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_APB3RSTR_OFFSET)
+#define STM32WB_RCC_AHB1ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB1ENR_OFFSET)
+#define STM32WB_RCC_AHB2ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB2ENR_OFFSET)
+#define STM32WB_RCC_AHB3ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB3ENR_OFFSET)
+#define STM32WB_RCC_APB1ENR1          (STM32WB_RCC_BASE + STM32WB_RCC_APB1ENR1_OFFSET)
+#define STM32WB_RCC_APB1ENR2          (STM32WB_RCC_BASE + STM32WB_RCC_APB1ENR2_OFFSET)
+#define STM32WB_RCC_APB2ENR           (STM32WB_RCC_BASE + STM32WB_RCC_APB2ENR_OFFSET)
+#define STM32WB_RCC_AHB1SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB1SMENR_OFFSET)
+#define STM32WB_RCC_AHB2SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB2SMENR_OFFSET)
+#define STM32WB_RCC_AHB3SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB3SMENR_OFFSET)
+#define STM32WB_RCC_APB1SMENR1        (STM32WB_RCC_BASE + STM32WB_RCC_APB1SMENR1_OFFSET)
+#define STM32WB_RCC_APB1SMENR2        (STM32WB_RCC_BASE + STM32WB_RCC_APB1SMENR2_OFFSET)
+#define STM32WB_RCC_APB2SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_APB2SMENR_OFFSET)
+#define STM32WB_RCC_CCIPR             (STM32WB_RCC_BASE + STM32WB_RCC_CCIPR_OFFSET)
+#define STM32WB_RCC_BDCR              (STM32WB_RCC_BASE + STM32WB_RCC_BDCR_OFFSET)
+#define STM32WB_RCC_CSR               (STM32WB_RCC_BASE + STM32WB_RCC_CSR_OFFSET)
+#define STM32WB_RCC_CRRCR             (STM32WB_RCC_BASE + STM32WB_RCC_CRRCR_OFFSET)
+#define STM32WB_RCC_HSECR             (STM32WB_RCC_BASE + STM32WB_RCC_HSECR_OFFSET)
+#define STM32WB_RCC_EXTCFGR           (STM32WB_RCC_BASE + STM32WB_RCC_EXTCFGR_OFFSET)
+#define STM32WB_RCC_C2AHB1ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB1ENR_OFFSET)
+#define STM32WB_RCC_C2AHB2ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB2ENR_OFFSET)
+#define STM32WB_RCC_C2AHB3ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB3ENR_OFFSET)
+#define STM32WB_RCC_C2APB1ENR1        (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1ENR1_OFFSET)
+#define STM32WB_RCC_C2APB1ENR2        (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1ENR2_OFFSET)
+#define STM32WB_RCC_C2APB2ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2APB2ENR_OFFSET)
+#define STM32WB_RCC_C2APB3ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2APB3ENR_OFFSET)
+#define STM32WB_RCC_C2AHB1SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB1SMENR_OFFSET)
+#define STM32WB_RCC_C2AHB2SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB2SMENR_OFFSET)
+#define STM32WB_RCC_C2AHB3SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB3SMENR_OFFSET)
+#define STM32WB_RCC_C2APB1SMENR1      (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1SMENR1_OFFSET)
+#define STM32WB_RCC_C2APB1SMENR2      (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1SMENR2_OFFSET)
+#define STM32WB_RCC_C2APB2SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2APB2SMENR_OFFSET)
+#define STM32WB_RCC_C2APB3SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2APB3SMENR_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Clock control register */
+
+#define RCC_CR_MSION                  (1 << 0)  /* Bit 0: Internal Multi Speed clock enable */
+#define RCC_CR_MSIRDY                 (1 << 1)  /* Bit 1: Internal Multi Speed clock ready flag */
+#define RCC_CR_MSIPLLEN               (1 << 2)  /* Bit 2: MSI clock PLL enable */
+#define RCC_CR_MSIRANGE_SHIFT         (4)       /* Bits 7-4: MSI clock range */
+#define RCC_CR_MSIRANGE_MASK          (0x0f << RCC_CR_MSIRANGE_SHIFT)
+#  define RCC_CR_MSIRANGE_100K        (0  << RCC_CR_MSIRANGE_SHIFT) /* 0000: around 100 kHz */
+#  define RCC_CR_MSIRANGE_200K        (1  << RCC_CR_MSIRANGE_SHIFT) /* 0001: around 200 kHz */
+#  define RCC_CR_MSIRANGE_400K        (2  << RCC_CR_MSIRANGE_SHIFT) /* 0010: around 400 kHz */
+#  define RCC_CR_MSIRANGE_800K        (3  << RCC_CR_MSIRANGE_SHIFT) /* 0011: around 800 kHz */
+#  define RCC_CR_MSIRANGE_1M          (4  << RCC_CR_MSIRANGE_SHIFT) /* 0100: around 1 MHz */
+#  define RCC_CR_MSIRANGE_2M          (5  << RCC_CR_MSIRANGE_SHIFT) /* 0101: around 2 MHz */
+#  define RCC_CR_MSIRANGE_4M          (6  << RCC_CR_MSIRANGE_SHIFT) /* 0110: around 4 MHz */
+#  define RCC_CR_MSIRANGE_8M          (7  << RCC_CR_MSIRANGE_SHIFT) /* 0111: around 8 MHz */
+#  define RCC_CR_MSIRANGE_16M         (8  << RCC_CR_MSIRANGE_SHIFT) /* 1000: around 16 MHz */
+#  define RCC_CR_MSIRANGE_24M         (9  << RCC_CR_MSIRANGE_SHIFT) /* 1001: around 24 MHz */
+#  define RCC_CR_MSIRANGE_32M         (10 << RCC_CR_MSIRANGE_SHIFT) /* 1010: around 32 MHz */
+#  define RCC_CR_MSIRANGE_48M         (11 << RCC_CR_MSIRANGE_SHIFT) /* 1011: around 48 MHz */
+
+#define RCC_CR_HSION                  (1 << 8)  /* Bit 8:  HSI16 clock enable */
+#define RCC_CR_HSIKERON               (1 << 9)  /* Bit 9:  HSI16 always enable for peripheral kernels */
+#define RCC_CR_HSIRDY                 (1 << 10) /* Bit 10: HSI16 clock ready flag */
+#define RCC_CR_HSIASFS                (1 << 11) /* Bit 11: HSI16 automatic start from stop */
+#define RCC_CR_HSEON                  (1 << 16) /* Bit 16: HSE clock enable */
+#define RCC_CR_HSERDY                 (1 << 17) /* Bit 17: HSE clock ready flag */
+#define RCC_CR_CSSON                  (1 << 19) /* Bit 19: Clock Security System enable */
+#define RCC_CR_HSEPRE                 (1 << 20) /* Bit 20: HSE sysclk and PLL M divider prescaler */
+#define RCC_CR_PLLON                  (1 << 24) /* Bit 24: PLL enable */
+#define RCC_CR_PLLRDY                 (1 << 25) /* Bit 25: PLL clock ready flag */
+#define RCC_CR_PLLSAI1ON              (1 << 26) /* Bit 26: PLLSAI1 enable */
+#define RCC_CR_PLLSAI1RDY             (1 << 27) /* Bit 27: PLLSAI1 clock ready flag */
+
+/* Internal Clock Sources Calibration */
+
+#define RCC_CR_HSITRIM_SHIFT          (24)      /* Bits 30-24: HSI16 clock trimming */
+#define RCC_CR_HSITRIM_MASK           (0x7f << RCC_CR_HSITRIM_SHIFT)
+#define RCC_CR_HSICAL_SHIFT           (16)      /* Bits 23-16: HSI16 clock Calibration */
+#define RCC_CR_HSICAL_MASK            (0xff << RCC_CR_HSICAL_SHIFT)
+#define RCC_CR_MSITRIM_SHIFT          (8)       /* Bits 15-8:  Internal Multi Speed clock trimming */
+#define RCC_CR_MSITRIM_MASK           (0xff << RCC_CR_MSITRIM_SHIFT)
+#define RCC_CR_MSICAL_SHIFT           (0)       /* Bits 7-0:   Internal Multi Speed clock Calibration */
+#define RCC_CR_MSICAL_MASK            (0xff << RCC_CR_MSICAL_SHIFT)
+
+/* Clock configuration register */
+
+#define RCC_CFGR_SW_SHIFT             (0)       /* Bits 0-1: System clock Switch */
+#define RCC_CFGR_SW_MASK              (0x3 << RCC_CFGR_SW_SHIFT)
+#  define RCC_CFGR_SW_MSI             (0x0 << RCC_CFGR_SW_SHIFT) /* 00: MSI selected as system clock */
+#  define RCC_CFGR_SW_HSI16           (0x1 << RCC_CFGR_SW_SHIFT) /* 00: HSI16 selected as system clock */
+#  define RCC_CFGR_SW_HSE             (0x2 << RCC_CFGR_SW_SHIFT) /* 01: HSE selected as system clock */
+#  define RCC_CFGR_SW_PLL             (0x3 << RCC_CFGR_SW_SHIFT) /* 10: PLL selected as system clock */
+
+#define RCC_CFGR_SWS_SHIFT            (2)       /* Bits 2-3: System Clock Switch Status */
+#define RCC_CFGR_SWS_MASK             (0x3 << RCC_CFGR_SWS_SHIFT)
+#  define RCC_CFGR_SWS_MSI            (0x0 << RCC_CFGR_SWS_SHIFT) /* 00: MSI oscillator used as system clock */
+#  define RCC_CFGR_SWS_HSI16          (0x1 << RCC_CFGR_SWS_SHIFT) /* 00: HSI16 oscillator used as system clock */
+#  define RCC_CFGR_SWS_HSE            (0x2 << RCC_CFGR_SWS_SHIFT) /* 01: HSE oscillator used as system clock */
+#  define RCC_CFGR_SWS_PLL            (0x3 << RCC_CFGR_SWS_SHIFT) /* 10: PLL used as system clock */
+
+#define RCC_CFGR_HPRE_SHIFT           (4)       /* Bits 4-7: HCLK1 prescaler (AHB1, AHB2, AHB3 and SRAM1) */
+#define RCC_CFGR_HPRE_MASK            (0x0f << RCC_CFGR_HPRE_SHIFT)
+#  define RCC_CFGR_HPRE_SYSCLK        (0x00 << RCC_CFGR_HPRE_SHIFT) /* 0000: SYSCLK not divided */
+#  define RCC_CFGR_HPRE_SYSCLKd3      (0x01 << RCC_CFGR_HPRE_SHIFT) /* 0001: SYSCLK divided by 3 */
+#  define RCC_CFGR_HPRE_SYSCLKd5      (0x02 << RCC_CFGR_HPRE_SHIFT) /* 0010: SYSCLK divided by 5 */
+#  define RCC_CFGR_HPRE_SYSCLKd6      (0x05 << RCC_CFGR_HPRE_SHIFT) /* 0101: SYSCLK divided by 6 */
+#  define RCC_CFGR_HPRE_SYSCLKd10     (0x06 << RCC_CFGR_HPRE_SHIFT) /* 0110: SYSCLK divided by 10 */
+#  define RCC_CFGR_HPRE_SYSCLKd32     (0x07 << RCC_CFGR_HPRE_SHIFT) /* 0111: SYSCLK divided by 32 */
+#  define RCC_CFGR_HPRE_SYSCLKd2      (0x08 << RCC_CFGR_HPRE_SHIFT) /* 1000: SYSCLK divided by 2 */
+#  define RCC_CFGR_HPRE_SYSCLKd4      (0x09 << RCC_CFGR_HPRE_SHIFT) /* 1001: SYSCLK divided by 4 */
+#  define RCC_CFGR_HPRE_SYSCLKd8      (0x10 << RCC_CFGR_HPRE_SHIFT) /* 1010: SYSCLK divided by 8 */
+#  define RCC_CFGR_HPRE_SYSCLKd16     (0x11 << RCC_CFGR_HPRE_SHIFT) /* 1011: SYSCLK divided by 16 */
+#  define RCC_CFGR_HPRE_SYSCLKd64     (0x12 << RCC_CFGR_HPRE_SHIFT) /* 1100: SYSCLK divided by 64 */
+#  define RCC_CFGR_HPRE_SYSCLKd128    (0x13 << RCC_CFGR_HPRE_SHIFT) /* 1101: SYSCLK divided by 128 */
+#  define RCC_CFGR_HPRE_SYSCLKd256    (0x14 << RCC_CFGR_HPRE_SHIFT) /* 1110: SYSCLK divided by 256 */
+#  define RCC_CFGR_HPRE_SYSCLKd512    (0x15 << RCC_CFGR_HPRE_SHIFT) /* 1111: SYSCLK divided by 512 */
+
+#define RCC_CFGR_PPRE1_SHIFT          (8)       /* Bits 8-10: PCLK1 Low speed prescaler (APB1) */
+#define RCC_CFGR_PPRE1_MASK           (0x7 << RCC_CFGR_PPRE1_SHIFT)
+#  define RCC_CFGR_PPRE1_HCLK1        (0x0 << RCC_CFGR_PPRE1_SHIFT) /* 0xx: HCLK1 not divided */
+#  define RCC_CFGR_PPRE1_HCLK1d2      (0x4 << RCC_CFGR_PPRE1_SHIFT) /* 100: HCLK1 divided by 2 */
+#  define RCC_CFGR_PPRE1_HCLK1d4      (0x5 << RCC_CFGR_PPRE1_SHIFT) /* 101: HCLK1 divided by 4 */
+#  define RCC_CFGR_PPRE1_HCLK1d8      (0x6 << RCC_CFGR_PPRE1_SHIFT) /* 110: HCLK1 divided by 8 */
+#  define RCC_CFGR_PPRE1_HCLK1d16     (0x7 << RCC_CFGR_PPRE1_SHIFT) /* 111: HCLK1 divided by 16 */
+
+#define RCC_CFGR_PPRE2_SHIFT          (11)      /* Bits 11-13: PCLK2 High speed prescaler (APB2) */
+#define RCC_CFGR_PPRE2_MASK           (0x7 << RCC_CFGR_PPRE2_SHIFT)
+#  define RCC_CFGR_PPRE2_HCLK1        (0x0 << RCC_CFGR_PPRE2_SHIFT) /* 0xx: HCLK1 not divided */
+#  define RCC_CFGR_PPRE2_HCLK1d2      (0x4 << RCC_CFGR_PPRE2_SHIFT) /* 100: HCLK1 divided by 2 */
+#  define RCC_CFGR_PPRE2_HCLK1d4      (0x5 << RCC_CFGR_PPRE2_SHIFT) /* 101: HCLK1 divided by 4 */
+#  define RCC_CFGR_PPRE2_HCLK1d8      (0x6 << RCC_CFGR_PPRE2_SHIFT) /* 110: HCLK1 divided by 8 */
+#  define RCC_CFGR_PPRE2_HCLK1d16     (0x7 << RCC_CFGR_PPRE2_SHIFT) /* 111: HCLK1 divided by 16 */
+
+#define RCC_CFGR_STOPWUCK             (1 << 15) /* Bit 15: Wakeup from Stop and CSS backup clock selection */
+#  define RCC_CFGR_STOPWUCK_MSI       (0 << 15) /* 0: MSI */
+#  define RCC_CFGR_STOPWUCK_HSI16     (1 << 15) /* 0: HSI16 */
+
+#define RCC_CFGR_HPREF                (1 << 16) /* Bit 16: HCLK1 prescaler flag (AHB1, AHB2, AHB3, SRAM1) */
+#define RCC_CFGR_PPRE1F               (1 << 17) /* Bit 17: PCLK1 prescaler flag (APB1) */
+#define RCC_CFGR_PPRE2F               (1 << 18) /* Bit 18: PCLK2 prescaler flag (APB2) */
+
+#define RCC_CFGR_MCOSEL_SHIFT         (24)      /* Bits 24-27: Microcontroller Clock Output */
+#define RCC_CFGR_MCOSEL_MASK          (0xf << RCC_CFGR_MCOSEL_SHIFT)
+#  define RCC_CFGR_MCOSEL_DISABLED    (0x0 << RCC_CFGR_MCOSEL_SHIFT) /* 0000: Output disabled, no clock on MCO */
+#  define RCC_CFGR_MCOSEL_SYSCLK      (0x1 << RCC_CFGR_MCOSEL_SHIFT) /* 0001: SYSCLK system clock selected */
+#  define RCC_CFGR_MCOSEL_MSI         (0x2 << RCC_CFGR_MCOSEL_SHIFT) /* 0010: MSI clock selected */
+#  define RCC_CFGR_MCOSEL_HSI16       (0x3 << RCC_CFGR_MCOSEL_SHIFT) /* 0011: HSI16 clock selected */
+#  define RCC_CFGR_MCOSEL_HSE_AFT     (0x4 << RCC_CFGR_MCOSEL_SHIFT) /* 0100: HSE clock after stabilization */
+#  define RCC_CFGR_MCOSEL_PLL         (0x5 << RCC_CFGR_MCOSEL_SHIFT) /* 0101: Main PLLRCLK selected  */
+#  define RCC_CFGR_MCOSEL_LSI1        (0x6 << RCC_CFGR_MCOSEL_SHIFT) /* 0110: LSI1 clock selected */
+#  define RCC_CFGR_MCOSEL_LSI2        (0x7 << RCC_CFGR_MCOSEL_SHIFT) /* 0111: LSI2 clock selected */
+#  define RCC_CFGR_MCOSEL_LSE         (0x8 << RCC_CFGR_MCOSEL_SHIFT) /* 1000: LSE clock selected */
+#  define RCC_CFGR_MCOSEL_HSI48       (0x9 << RCC_CFGR_MCOSEL_SHIFT) /* 1001: HSI48 clock selected */
+#  define RCC_CFGR_MCOSEL_HSE_BFR     (0xc << RCC_CFGR_MCOSEL_SHIFT) /* 1100: HSE clock before stabilization */
+
+#define RCC_CFGR_MCOPRE_SHIFT         (28)      /* Bits 28-30: MCO prescaler */
+#define RCC_CFGR_MCOPRE_MASK          (0x7 << RCC_CFGR_MCOPRE_SHIFT)
+#  define RCC_CFGR_MCOPRE_DIV1        (0x0 << RCC_CFGR_MCOPRE_SHIFT) /* 000: no division */
+#  define RCC_CFGR_MCOPRE_DIV2        (0x1 << RCC_CFGR_MCOPRE_SHIFT) /* 001: division by 2 */
+#  define RCC_CFGR_MCOPRE_DIV4        (0x2 << RCC_CFGR_MCOPRE_SHIFT) /* 010: division by 4 */
+#  define RCC_CFGR_MCOPRE_DIV8        (0x3 << RCC_CFGR_MCOPRE_SHIFT) /* 011: division by 8 */
+#  define RCC_CFGR_MCOPRE_DIV16       (0x4 << RCC_CFGR_MCOPRE_SHIFT) /* 100: division by 16 */
+
+#define RCC_CFGR_RESET_MASK           (0x00070000)
+
+/* PLL configuration register */
+
+#define RCC_PLLCFG_PLLSRC_SHIFT       (0)       /* Bits 0-1: Main PLL and audio PLLSAI1 entry clock source */
+#define RCC_PLLCFG_PLLSRC_MASK        (0x3 << RCC_PLLCFG_PLLSRC_SHIFT)
+#  define RCC_PLLCFG_PLLSRC_NONE      (0x0 << RCC_PLLCFG_PLLSRC_SHIFT) /* 00: No clock sent to PLLs */
+#  define RCC_PLLCFG_PLLSRC_MSI       (0x1 << RCC_PLLCFG_PLLSRC_SHIFT) /* 01: MSI selected as PLLs source */
+#  define RCC_PLLCFG_PLLSRC_HSI16     (0x2 << RCC_PLLCFG_PLLSRC_SHIFT) /* 10: HSI16 selected as PLLs source */
+#  define RCC_PLLCFG_PLLSRC_HSE       (0x3 << RCC_PLLCFG_PLLSRC_SHIFT) /* 11: HSE selected as PLLs source */
+
+#define RCC_PLLCFG_PLLM_SHIFT         (4)       /* Bits 4-6: Main PLL and audio PLLSAI1 divider */
+#define RCC_PLLCFG_PLLM_MASK          (0x07 << RCC_PLLCFG_PLLM_SHIFT)
+#  define RCC_PLLCFG_PLLM(n)          ((n-1) << RCC_PLLCFG_PLLM_SHIFT) /* n = 1..8 */
+
+#define RCC_PLLCFG_PLLN_SHIFT         (8)       /* Bits 8-14: Main PLL (PLL) VCO multiplier */
+#define RCC_PLLCFG_PLLN_MASK          (0x7f << RCC_PLLCFG_PLLN_SHIFT)
+#  define RCC_PLLCFG_PLLN(n)          ((n) << RCC_PLLCFG_PLLN_SHIFT) /* n = 6..127 */
+
+#define RCC_PLLCFG_PLLPEN             (1 << 16) /* Bit 16: Main PLL PLLPCLK output enable */
+
+#define RCC_PLLCFG_PLLP_SHIFT         (17)      /* Bits 17-21: Main PLL div factor for PLLPCLK */
+#define RCC_PLLCFG_PLLP_MASK          (0x1f << RCC_PLLCFG_PLLP_SHIFT)
+#  define RCC_PLLCFG_PLLP(n)          ((n-1) << RCC_PLLCFG_PLLP_SHIFT) /* n = 2..32 */
+
+#define RCC_PLLCFG_PLLQEN             (1 << 24) /* Bit 24: Main PLL PLLQCLK output enable */
+
+#define RCC_PLLCFG_PLLQ_SHIFT         (25)      /* Bits 25-27: Main PLL division factor for PLLQCLK */
+#define RCC_PLLCFG_PLLQ_MASK          (0x7 << RCC_PLLCFG_PLLQ_SHIFT)
+#  define RCC_PLLCFG_PLLQ(n)          ((n-1) << RCC_PLLCFG_PLLQ_SHIFT) /* n = 2..8 */
+
+#define RCC_PLLCFG_PLLREN             (1 << 28) /* Bit 28: Main PLL PLLRCLK output enable */
+
+#define RCC_PLLCFG_PLLR_SHIFT         (29)      /* Bits 29-31: Main PLL division factor for PLLRCLK */
+#define RCC_PLLCFG_PLLR_MASK          (0x7 << RCC_PLLCFG_PLLR_SHIFT)
+#  define RCC_PLLCFG_PLLR(n)          ((n-1) << RCC_PLLCFG_PLLR_SHIFT) /* n = 2..8 */
+
+#define RCC_PLLCFG_RESET              (0x22040100) /* PLLCFG reset value */
+
+/* PLLSAI1 Configuration register */
+
+#define RCC_PLLSAI1CFG_PLLN_SHIFT     (8)       /* Bits 8-14: SAI1 PLL (PLLSAI1) VCO multiplier */
+#define RCC_PLLSAI1CFG_PLLN_MASK      (0x7f << RCC_PLLSAI1CFG_PLLN_SHIFT)
+#  define RCC_PLLSAI1CFG_PLLN(n)      ((n) << RCC_PLLSAI1CFG_PLLN_SHIFT) /* n = 4..86 */
+
+#define RCC_PLLSAI1CFG_PLLPEN         (1 << 16) /* Bit 16: SAI1 PLL PLLSAI1CLK output enable */
+
+#define RCC_PLLSAI1CFG_PLLP_SHIFT     (17)      /* Bit 17-21: Main PLL div factor for PLLSAI1CLK */
+#define RCC_PLLSAI1CFG_PLLP_MASK      (0x1f << RCC_PLLSAI1CFG_PLLP_SHIFT)
+#  define RCC_PLLSAI1CFG_PLLP(n)      ((n-1) << RCC_PLLSAI1CFG_PLLP_SHIFT) /* n = 2..32 */
+
+#define RCC_PLLSAI1CFG_PLLQEN         (1 << 24) /* Bit 24: PLLSAI1QCLK output enable */
+
+#define RCC_PLLSAI1CFG_PLLQ_SHIFT     (25)      /* Bits 25-27: PLLSAI1 division factor for PLLSAI1QCLK */
+#define RCC_PLLSAI1CFG_PLLQ_MASK      (0x3 << RCC_PLLSAI1CFG_PLLQ_SHIFT)
+#  define RCC_PLLSAI1CFG_PLLQ(n)      ((n-1) << RCC_PLLSAI1CFG_PLLQ_SHIFT) /* n = 2..8 */

Review Comment:
   ```suggestion
   #  define RCC_PLLSAI1CFG_PLLQ(n)      (((n) - 1) << RCC_PLLSAI1CFG_PLLQ_SHIFT) /* n = 2..8 */
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_spi.h:
##########
@@ -0,0 +1,151 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_spi.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_SPI_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_SPI_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "chip.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Maximum allowed speed as per specifications for all SPIs */
+
+#define STM32WB_SPI_CLK_MAX         32000000ul
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_SPI_CR1_OFFSET      0x0000  /* SPI Control Register 1 (16-bit) */
+#define STM32WB_SPI_CR2_OFFSET      0x0004  /* SPI control register 2 (16-bit) */
+#define STM32WB_SPI_SR_OFFSET       0x0008  /* SPI status register (16-bit) */
+#define STM32WB_SPI_DR_OFFSET       0x000c  /* SPI data register (16-bit) */
+#define STM32WB_SPI_CRCPR_OFFSET    0x0010  /* SPI CRC polynomial register (16-bit) */
+#define STM32WB_SPI_RXCRCR_OFFSET   0x0014  /* SPI Rx CRC register (16-bit) */
+#define STM32WB_SPI_TXCRCR_OFFSET   0x0018  /* SPI Tx CRC register (16-bit) */
+
+/* Register Addresses *******************************************************/
+
+#define STM32WB_SPI1_CR1            (STM32WB_SPI1_BASE + STM32WB_SPI_CR1_OFFSET)
+#define STM32WB_SPI1_CR2            (STM32WB_SPI1_BASE + STM32WB_SPI_CR2_OFFSET)
+#define STM32WB_SPI1_SR             (STM32WB_SPI1_BASE + STM32WB_SPI_SR_OFFSET)
+#define STM32WB_SPI1_DR             (STM32WB_SPI1_BASE + STM32WB_SPI_DR_OFFSET)
+#define STM32WB_SPI1_CRCPR          (STM32WB_SPI1_BASE + STM32WB_SPI_CRCPR_OFFSET)
+#define STM32WB_SPI1_RXCRCR         (STM32WB_SPI1_BASE + STM32WB_SPI_RXCRCR_OFFSET)
+#define STM32WB_SPI1_TXCRCR         (STM32WB_SPI1_BASE + STM32WB_SPI_TXCRCR_OFFSET)
+
+#if CONFIG_STM32WB_HAVE_SPI2
+#  define STM32WB_SPI2_CR1          (STM32WB_SPI2_BASE + STM32WB_SPI_CR1_OFFSET)
+#  define STM32WB_SPI2_CR2          (STM32WB_SPI2_BASE + STM32WB_SPI_CR2_OFFSET)
+#  define STM32WB_SPI2_SR           (STM32WB_SPI2_BASE + STM32WB_SPI_SR_OFFSET)
+#  define STM32WB_SPI2_DR           (STM32WB_SPI2_BASE + STM32WB_SPI_DR_OFFSET)
+#  define STM32WB_SPI2_CRCPR        (STM32WB_SPI2_BASE + STM32WB_SPI_CRCPR_OFFSET)
+#  define STM32WB_SPI2_RXCRCR       (STM32WB_SPI2_BASE + STM32WB_SPI_RXCRCR_OFFSET)
+#  define STM32WB_SPI2_TXCRCR       (STM32WB_SPI2_BASE + STM32WB_SPI_TXCRCR_OFFSET)
+#endif
+
+/* Register Bitfield Definitions ********************************************/
+
+/* SPI Control Register 1 */
+
+#define SPI_CR1_CPHA              (1 << 0)  /* Bit 0: Clock Phase */
+#define SPI_CR1_CPOL              (1 << 1)  /* Bit 1: Clock Polarity */
+#define SPI_CR1_MSTR              (1 << 2)  /* Bit 2: Master Selection */
+#define SPI_CR1_BR_SHIFT          (3)       /* Bits 5:3 Baud Rate Control */
+#define SPI_CR1_BR_MASK           (0x7 << SPI_CR1_BR_SHIFT)
+#  define SPI_CR1_FPCLCKd2        (0x0 << SPI_CR1_BR_SHIFT) /* 000: fPCLK/2 */
+#  define SPI_CR1_FPCLCKd4        (0x1 << SPI_CR1_BR_SHIFT) /* 001: fPCLK/4 */
+#  define SPI_CR1_FPCLCKd8        (0x2 << SPI_CR1_BR_SHIFT) /* 010: fPCLK/8 */
+#  define SPI_CR1_FPCLCKd16       (0x3 << SPI_CR1_BR_SHIFT) /* 011: fPCLK/16 */
+#  define SPI_CR1_FPCLCKd32       (0x4 << SPI_CR1_BR_SHIFT) /* 100: fPCLK/32 */
+#  define SPI_CR1_FPCLCKd64       (0x5 << SPI_CR1_BR_SHIFT) /* 101: fPCLK/64 */
+#  define SPI_CR1_FPCLCKd128      (0x6 << SPI_CR1_BR_SHIFT) /* 110: fPCLK/128 */
+#  define SPI_CR1_FPCLCKd256      (0x7 << SPI_CR1_BR_SHIFT) /* 111: fPCLK/256 */
+
+#define SPI_CR1_SPE               (1 << 6)  /* Bit 6: SPI Enable */
+#define SPI_CR1_LSBFIRST          (1 << 7)  /* Bit 7: Frame Format */
+#define SPI_CR1_SSI               (1 << 8)  /* Bit 8: Internal slave select */
+#define SPI_CR1_SSM               (1 << 9)  /* Bit 9: Software slave management */
+#define SPI_CR1_RXONLY            (1 << 10) /* Bit 10: Receive only */
+#define SPI_CR1_CRCL              (1 << 11) /* Bit 11: CRC length */
+#define SPI_CR1_CRCNEXT           (1 << 12) /* Bit 12: Transmit CRC next */
+#define SPI_CR1_CRCEN             (1 << 13) /* Bit 13: Hardware CRC calculation enable */
+#define SPI_CR1_BIDIOE            (1 << 14) /* Bit 14: Output enable in bidirectional mode */
+#define SPI_CR1_BIDIMODE          (1 << 15) /* Bit 15: Bidirectional data mode enable */
+
+/* SPI Control Register 2 */
+
+#define SPI_CR2_RXDMAEN           (1 << 0)  /* Bit 0: Rx Buffer DMA Enable */
+#define SPI_CR2_TXDMAEN           (1 << 1)  /* Bit 1: Tx Buffer DMA Enable */
+#define SPI_CR2_SSOE              (1 << 2)  /* Bit 2: SS Output Enable */
+#define SPI_CR2_NSSP              (1 << 3)  /* Bit 3: NSS pulse management */
+#define SPI_CR2_FRF               (1 << 4)  /* Bit 4: Frame format */
+#define SPI_CR2_ERRIE             (1 << 5)  /* Bit 5: Error interrupt enable */
+#define SPI_CR2_RXNEIE            (1 << 6)  /* Bit 6: RX buffer not empty interrupt enable */
+#define SPI_CR2_TXEIE             (1 << 7)  /* Bit 7: Tx buffer empty interrupt enable */
+#define SPI_CR2_DS_SHIFT          (8)       /* Bits 8-11: Data size */
+#define SPI_CR2_DS_MASK           (0xf << SPI_CR2_DS_SHIFT)
+#  define SPI_CR2_DS_VAL(bits)    ( ((bits)-1) << SPI_CR2_DS_SHIFT)

Review Comment:
   ```suggestion
   #  define SPI_CR2_DS_VAL(bits)    (((bits) - 1) << SPI_CR2_DS_SHIFT)
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_gpio.h:
##########
@@ -0,0 +1,317 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_gpio.h
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.  The
+ *  ASF licenses this file to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance with the
+ *  License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ *  License for the specific language governing permissions and limitations
+ *  under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_GPIO_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_GPIO_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_GPIO_MODER_OFFSET        0x0000         /* GPIO port mode register */
+#define STM32WB_GPIO_OTYPER_OFFSET       0x0004         /* GPIO port output type register */
+#define STM32WB_GPIO_OSPEED_OFFSET       0x0008         /* GPIO port output speed register */
+#define STM32WB_GPIO_PUPDR_OFFSET        0x000c         /* GPIO port pull-up/pull-down register */
+#define STM32WB_GPIO_IDR_OFFSET          0x0010         /* GPIO port input data register */
+#define STM32WB_GPIO_ODR_OFFSET          0x0014         /* GPIO port output data register */
+#define STM32WB_GPIO_BSRR_OFFSET         0x0018         /* GPIO port bit set/reset register */
+#define STM32WB_GPIO_LCKR_OFFSET         0x001c         /* GPIO port configuration lock register */
+#define STM32WB_GPIO_AFRL_OFFSET         0x0020         /* GPIO alternate function low register */
+#define STM32WB_GPIO_AFRH_OFFSET         0x0024         /* GPIO alternate function high register */
+#define STM32WB_GPIO_BRR_OFFSET          0x0028         /* GPIO port bit reset register */
+
+/* Register Addresses *******************************************************/
+
+#define STM32WB_GPIOA_MODER              (STM32WB_GPIOA_BASE + STM32WB_GPIO_MODER_OFFSET)
+#define STM32WB_GPIOA_OTYPER             (STM32WB_GPIOA_BASE + STM32WB_GPIO_OTYPER_OFFSET)
+#define STM32WB_GPIOA_OSPEED             (STM32WB_GPIOA_BASE + STM32WB_GPIO_OSPEED_OFFSET)
+#define STM32WB_GPIOA_PUPDR              (STM32WB_GPIOA_BASE + STM32WB_GPIO_PUPDR_OFFSET)
+#define STM32WB_GPIOA_IDR                (STM32WB_GPIOA_BASE + STM32WB_GPIO_IDR_OFFSET)
+#define STM32WB_GPIOA_ODR                (STM32WB_GPIOA_BASE + STM32WB_GPIO_ODR_OFFSET)
+#define STM32WB_GPIOA_BSRR               (STM32WB_GPIOA_BASE + STM32WB_GPIO_BSRR_OFFSET)
+#define STM32WB_GPIOA_LCKR               (STM32WB_GPIOA_BASE + STM32WB_GPIO_LCKR_OFFSET)
+#define STM32WB_GPIOA_AFRL               (STM32WB_GPIOA_BASE + STM32WB_GPIO_AFRL_OFFSET)
+#define STM32WB_GPIOA_AFRH               (STM32WB_GPIOA_BASE + STM32WB_GPIO_AFRH_OFFSET)
+#define STM32WB_GPIOA_BRR                (STM32WB_GPIOA_BASE + STM32WB_GPIO_BRR_OFFSET)
+
+#define STM32WB_GPIOB_MODER              (STM32WB_GPIOB_BASE + STM32WB_GPIO_MODER_OFFSET)
+#define STM32WB_GPIOB_OTYPER             (STM32WB_GPIOB_BASE + STM32WB_GPIO_OTYPER_OFFSET)
+#define STM32WB_GPIOB_OSPEED             (STM32WB_GPIOB_BASE + STM32WB_GPIO_OSPEED_OFFSET)
+#define STM32WB_GPIOB_PUPDR              (STM32WB_GPIOB_BASE + STM32WB_GPIO_PUPDR_OFFSET)
+#define STM32WB_GPIOB_IDR                (STM32WB_GPIOB_BASE + STM32WB_GPIO_IDR_OFFSET)
+#define STM32WB_GPIOB_ODR                (STM32WB_GPIOB_BASE + STM32WB_GPIO_ODR_OFFSET)
+#define STM32WB_GPIOB_BSRR               (STM32WB_GPIOB_BASE + STM32WB_GPIO_BSRR_OFFSET)
+#define STM32WB_GPIOB_LCKR               (STM32WB_GPIOB_BASE + STM32WB_GPIO_LCKR_OFFSET)
+#define STM32WB_GPIOB_AFRL               (STM32WB_GPIOB_BASE + STM32WB_GPIO_AFRL_OFFSET)
+#define STM32WB_GPIOB_AFRH               (STM32WB_GPIOB_BASE + STM32WB_GPIO_AFRH_OFFSET)
+#define STM32WB_GPIOB_BRR                (STM32WB_GPIOB_BASE + STM32WB_GPIO_BRR_OFFSET)
+
+#define STM32WB_GPIOC_MODER              (STM32WB_GPIOC_BASE + STM32WB_GPIO_MODER_OFFSET)
+#define STM32WB_GPIOC_OTYPER             (STM32WB_GPIOC_BASE + STM32WB_GPIO_OTYPER_OFFSET)
+#define STM32WB_GPIOC_OSPEED             (STM32WB_GPIOC_BASE + STM32WB_GPIO_OSPEED_OFFSET)
+#define STM32WB_GPIOC_PUPDR              (STM32WB_GPIOC_BASE + STM32WB_GPIO_PUPDR_OFFSET)
+#define STM32WB_GPIOC_IDR                (STM32WB_GPIOC_BASE + STM32WB_GPIO_IDR_OFFSET)
+#define STM32WB_GPIOC_ODR                (STM32WB_GPIOC_BASE + STM32WB_GPIO_ODR_OFFSET)
+#define STM32WB_GPIOC_BSRR               (STM32WB_GPIOC_BASE + STM32WB_GPIO_BSRR_OFFSET)
+#define STM32WB_GPIOC_LCKR               (STM32WB_GPIOC_BASE + STM32WB_GPIO_LCKR_OFFSET)
+#define STM32WB_GPIOC_AFRL               (STM32WB_GPIOC_BASE + STM32WB_GPIO_AFRL_OFFSET)
+#define STM32WB_GPIOC_AFRH               (STM32WB_GPIOC_BASE + STM32WB_GPIO_AFRH_OFFSET)
+#define STM32WB_GPIOC_BRR                (STM32WB_GPIOC_BASE + STM32WB_GPIO_BRR_OFFSET)
+
+#if defined(CONFIG_STM32WB_GPIO_HAVE_PORTD)
+#  define STM32WB_GPIOD_MODER            (STM32WB_GPIOD_BASE + STM32WB_GPIO_MODER_OFFSET)
+#  define STM32WB_GPIOD_OTYPER           (STM32WB_GPIOD_BASE + STM32WB_GPIO_OTYPER_OFFSET)
+#  define STM32WB_GPIOD_OSPEED           (STM32WB_GPIOD_BASE + STM32WB_GPIO_OSPEED_OFFSET)
+#  define STM32WB_GPIOD_PUPDR            (STM32WB_GPIOD_BASE + STM32WB_GPIO_PUPDR_OFFSET)
+#  define STM32WB_GPIOD_IDR              (STM32WB_GPIOD_BASE + STM32WB_GPIO_IDR_OFFSET)
+#  define STM32WB_GPIOD_ODR              (STM32WB_GPIOD_BASE + STM32WB_GPIO_ODR_OFFSET)
+#  define STM32WB_GPIOD_BSRR             (STM32WB_GPIOD_BASE + STM32WB_GPIO_BSRR_OFFSET)
+#  define STM32WB_GPIOD_LCKR             (STM32WB_GPIOD_BASE + STM32WB_GPIO_LCKR_OFFSET)
+#  define STM32WB_GPIOD_AFRL             (STM32WB_GPIOD_BASE + STM32WB_GPIO_AFRL_OFFSET)
+#  define STM32WB_GPIOD_AFRH             (STM32WB_GPIOD_BASE + STM32WB_GPIO_AFRH_OFFSET)
+#  define STM32WB_GPIOD_BRR              (STM32WB_GPIOD_BASE + STM32WB_GPIO_BRR_OFFSET)
+#endif
+
+#define STM32WB_GPIOE_MODER              (STM32WB_GPIOE_BASE + STM32WB_GPIO_MODER_OFFSET)
+#define STM32WB_GPIOE_OTYPER             (STM32WB_GPIOE_BASE + STM32WB_GPIO_OTYPER_OFFSET)
+#define STM32WB_GPIOE_OSPEED             (STM32WB_GPIOE_BASE + STM32WB_GPIO_OSPEED_OFFSET)
+#define STM32WB_GPIOE_PUPDR              (STM32WB_GPIOE_BASE + STM32WB_GPIO_PUPDR_OFFSET)
+#define STM32WB_GPIOE_IDR                (STM32WB_GPIOE_BASE + STM32WB_GPIO_IDR_OFFSET)
+#define STM32WB_GPIOE_ODR                (STM32WB_GPIOE_BASE + STM32WB_GPIO_ODR_OFFSET)
+#define STM32WB_GPIOE_BSRR               (STM32WB_GPIOE_BASE + STM32WB_GPIO_BSRR_OFFSET)
+#define STM32WB_GPIOE_LCKR               (STM32WB_GPIOE_BASE + STM32WB_GPIO_LCKR_OFFSET)
+#define STM32WB_GPIOE_AFRL               (STM32WB_GPIOE_BASE + STM32WB_GPIO_AFRL_OFFSET)
+#define STM32WB_GPIOE_BRR                (STM32WB_GPIOE_BASE + STM32WB_GPIO_BRR_OFFSET)
+
+#define STM32WB_GPIOH_MODER              (STM32WB_GPIOH_BASE + STM32WB_GPIO_MODER_OFFSET)
+#define STM32WB_GPIOH_OTYPER             (STM32WB_GPIOH_BASE + STM32WB_GPIO_OTYPER_OFFSET)
+#define STM32WB_GPIOH_OSPEED             (STM32WB_GPIOH_BASE + STM32WB_GPIO_OSPEED_OFFSET)
+#define STM32WB_GPIOH_PUPDR              (STM32WB_GPIOH_BASE + STM32WB_GPIO_PUPDR_OFFSET)
+#define STM32WB_GPIOH_IDR                (STM32WB_GPIOH_BASE + STM32WB_GPIO_IDR_OFFSET)
+#define STM32WB_GPIOH_ODR                (STM32WB_GPIOH_BASE + STM32WB_GPIO_ODR_OFFSET)
+#define STM32WB_GPIOH_BSRR               (STM32WB_GPIOH_BASE + STM32WB_GPIO_BSRR_OFFSET)
+#define STM32WB_GPIOH_LCKR               (STM32WB_GPIOH_BASE + STM32WB_GPIO_LCKR_OFFSET)
+#define STM32WB_GPIOH_AFRL               (STM32WB_GPIOH_BASE + STM32WB_GPIO_AFRL_OFFSET)
+#define STM32WB_GPIOH_BRR                (STM32WB_GPIOH_BASE + STM32WB_GPIO_BRR_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* GPIO port mode register */
+
+#define GPIO_MODER_INPUT               (0x0)          /* Input mode */
+#define GPIO_MODER_OUTPUT              (0x1)          /* General purpose output mode */
+#define GPIO_MODER_ALT                 (0x2)          /* Alternate mode */
+#define GPIO_MODER_ANALOG              (0x3)          /* Analog mode (reset state) */
+
+#define GPIO_MODER_SHIFT(n)            ((n) << 1)
+#define GPIO_MODER_MASK(n)             (0x3 << GPIO_MODER_SHIFT(n))
+
+#define GPIO_MODER0_SHIFT              (0)
+#define GPIO_MODER0_MASK               (0x3 << GPIO_MODER0_SHIFT)
+#define GPIO_MODER1_SHIFT              (2)
+#define GPIO_MODER1_MASK               (0x3 << GPIO_MODER1_SHIFT)
+#define GPIO_MODER2_SHIFT              (4)
+#define GPIO_MODER2_MASK               (0x3 << GPIO_MODER2_SHIFT)
+#define GPIO_MODER3_SHIFT              (6)
+#define GPIO_MODER3_MASK               (0x3 << GPIO_MODER3_SHIFT)
+#define GPIO_MODER4_SHIFT              (8)
+#define GPIO_MODER4_MASK               (0x3 << GPIO_MODER4_SHIFT)
+#define GPIO_MODER5_SHIFT              (10)
+#define GPIO_MODER5_MASK               (0x3 << GPIO_MODER5_SHIFT)
+#define GPIO_MODER6_SHIFT              (12)
+#define GPIO_MODER6_MASK               (0x3 << GPIO_MODER6_SHIFT)
+#define GPIO_MODER7_SHIFT              (14)
+#define GPIO_MODER7_MASK               (0x3 << GPIO_MODER7_SHIFT)
+#define GPIO_MODER8_SHIFT              (16)
+#define GPIO_MODER8_MASK               (0x3 << GPIO_MODER8_SHIFT)
+#define GPIO_MODER9_SHIFT              (18)
+#define GPIO_MODER9_MASK               (0x3 << GPIO_MODER9_SHIFT)
+#define GPIO_MODER10_SHIFT             (20)
+#define GPIO_MODER10_MASK              (0x3 << GPIO_MODER10_SHIFT)
+#define GPIO_MODER11_SHIFT             (22)
+#define GPIO_MODER11_MASK              (0x3 << GPIO_MODER11_SHIFT)
+#define GPIO_MODER12_SHIFT             (24)
+#define GPIO_MODER12_MASK              (0x3 << GPIO_MODER12_SHIFT)
+#define GPIO_MODER13_SHIFT             (26)
+#define GPIO_MODER13_MASK              (0x3 << GPIO_MODER13_SHIFT)
+#define GPIO_MODER14_SHIFT             (28)
+#define GPIO_MODER14_MASK              (0x3 << GPIO_MODER14_SHIFT)
+#define GPIO_MODER15_SHIFT             (30)
+#define GPIO_MODER15_MASK              (0x3 << GPIO_MODER15_SHIFT)
+
+/* GPIO port output type register */
+
+#define GPIO_OTYPER_PP(n)              (0)            /* 0=Output push-pull (reset state) */
+#define GPIO_OTYPER_OD(n)              (1 << (n))     /* 1=Output open-drain */
+
+/* GPIO port output speed register */
+
+#define GPIO_OSPEED_5MHz               (0x0)          /* 5 MHz Low speed output */
+#define GPIO_OSPEED_25MHz              (0x1)          /* 25 MHz Medium speed output */
+#define GPIO_OSPEED_50MHz              (0x2)          /* 50 MHz Fast speed output */
+#define GPIO_OSPEED_120MHz             (0x3)          /* 120 MHz High speed output */
+
+#define GPIO_OSPEED_SHIFT(n)           ((n) << 1)
+#define GPIO_OSPEED_MASK(n)            (0x3 << GPIO_OSPEED_SHIFT(n))
+
+#define GPIO_OSPEED0_SHIFT             (0)
+#define GPIO_OSPEED0_MASK              (0x3 << GPIO_OSPEED0_SHIFT)
+#define GPIO_OSPEED1_SHIFT             (2)
+#define GPIO_OSPEED1_MASK              (0x3 << GPIO_OSPEED1_SHIFT)
+#define GPIO_OSPEED2_SHIFT             (4)
+#define GPIO_OSPEED2_MASK              (0x3 << GPIO_OSPEED2_SHIFT)
+#define GPIO_OSPEED3_SHIFT             (6)
+#define GPIO_OSPEED3_MASK              (0x3 << GPIO_OSPEED3_SHIFT)
+#define GPIO_OSPEED4_SHIFT             (8)
+#define GPIO_OSPEED4_MASK              (0x3 << GPIO_OSPEED4_SHIFT)
+#define GPIO_OSPEED5_SHIFT             (10)
+#define GPIO_OSPEED5_MASK              (0x3 << GPIO_OSPEED5_SHIFT)
+#define GPIO_OSPEED6_SHIFT             (12)
+#define GPIO_OSPEED6_MASK              (0x3 << GPIO_OSPEED6_SHIFT)
+#define GPIO_OSPEED7_SHIFT             (14)
+#define GPIO_OSPEED7_MASK              (0x3 << GPIO_OSPEED7_SHIFT)
+#define GPIO_OSPEED8_SHIFT             (16)
+#define GPIO_OSPEED8_MASK              (0x3 << GPIO_OSPEED8_SHIFT)
+#define GPIO_OSPEED9_SHIFT             (18)
+#define GPIO_OSPEED9_MASK              (0x3 << GPIO_OSPEED9_SHIFT)
+#define GPIO_OSPEED10_SHIFT            (20)
+#define GPIO_OSPEED10_MASK             (0x3 << GPIO_OSPEED10_SHIFT)
+#define GPIO_OSPEED11_SHIFT            (22)
+#define GPIO_OSPEED11_MASK             (0x3 << GPIO_OSPEED11_SHIFT)
+#define GPIO_OSPEED12_SHIFT            (24)
+#define GPIO_OSPEED12_MASK             (0x3 << GPIO_OSPEED12_SHIFT)
+#define GPIO_OSPEED13_SHIFT            (26)
+#define GPIO_OSPEED13_MASK             (0x3 << GPIO_OSPEED13_SHIFT)
+#define GPIO_OSPEED14_SHIFT            (28)
+#define GPIO_OSPEED14_MASK             (0x3 << GPIO_OSPEED14_SHIFT)
+#define GPIO_OSPEED15_SHIFT            (30)
+#define GPIO_OSPEED15_MASK             (0x3 << GPIO_OSPEED15_SHIFT)
+
+/* GPIO port pull-up/pull-down register */
+
+#define GPIO_PUPDR_NONE                (0x0)          /* No pull-up, pull-down */
+#define GPIO_PUPDR_PULLUP              (0x1)          /* Pull-up */
+#define GPIO_PUPDR_PULLDOWN            (0x2)          /* Pull-down */
+
+#define GPIO_PUPDR_SHIFT(n)            ((n) << 1)
+#define GPIO_PUPDR_MASK(n)             (0x3 << GPIO_PUPDR_SHIFT(n))
+
+#define GPIO_PUPDR0_SHIFT              (0)
+#define GPIO_PUPDR0_MASK               (0x3 << GPIO_PUPDR0_SHIFT)
+#define GPIO_PUPDR1_SHIFT              (2)
+#define GPIO_PUPDR1_MASK               (0x3 << GPIO_PUPDR1_SHIFT)
+#define GPIO_PUPDR2_SHIFT              (4)
+#define GPIO_PUPDR2_MASK               (0x3 << GPIO_PUPDR2_SHIFT)
+#define GPIO_PUPDR3_SHIFT              (6)
+#define GPIO_PUPDR3_MASK               (0x3 << GPIO_PUPDR3_SHIFT)
+#define GPIO_PUPDR4_SHIFT              (8)
+#define GPIO_PUPDR4_MASK               (0x3 << GPIO_PUPDR4_SHIFT)
+#define GPIO_PUPDR5_SHIFT              (10)
+#define GPIO_PUPDR5_MASK               (0x3 << GPIO_PUPDR5_SHIFT)
+#define GPIO_PUPDR6_SHIFT              (12)
+#define GPIO_PUPDR6_MASK               (0x3 << GPIO_PUPDR6_SHIFT)
+#define GPIO_PUPDR7_SHIFT              (14)
+#define GPIO_PUPDR7_MASK               (0x3 << GPIO_PUPDR7_SHIFT)
+#define GPIO_PUPDR8_SHIFT              (16)
+#define GPIO_PUPDR8_MASK               (0x3 << GPIO_PUPDR8_SHIFT)
+#define GPIO_PUPDR9_SHIFT              (18)
+#define GPIO_PUPDR9_MASK               (0x3 << GPIO_PUPDR9_SHIFT)
+#define GPIO_PUPDR10_SHIFT             (20)
+#define GPIO_PUPDR10_MASK              (0x3 << GPIO_PUPDR10_SHIFT)
+#define GPIO_PUPDR11_SHIFT             (22)
+#define GPIO_PUPDR11_MASK              (0x3 << GPIO_PUPDR11_SHIFT)
+#define GPIO_PUPDR12_SHIFT             (24)
+#define GPIO_PUPDR12_MASK              (0x3 << GPIO_PUPDR12_SHIFT)
+#define GPIO_PUPDR13_SHIFT             (26)
+#define GPIO_PUPDR13_MASK              (0x3 << GPIO_PUPDR13_SHIFT)
+#define GPIO_PUPDR14_SHIFT             (28)
+#define GPIO_PUPDR14_MASK              (0x3 << GPIO_PUPDR14_SHIFT)
+#define GPIO_PUPDR15_SHIFT             (30)
+#define GPIO_PUPDR15_MASK              (0x3 << GPIO_PUPDR15_SHIFT)
+
+/* GPIO port input data register */
+
+#define GPIO_IDR(n)                    (1 << (n))
+
+/* GPIO port output data register */
+
+#define GPIO_ODR(n)                    (1 << (n))
+
+/* GPIO port bit set/reset register */
+
+#define GPIO_BSRR_SET(n)               (1 << (n))
+#define GPIO_BSRR_RESET(n)             (1 << ((n) + 16))
+
+/* GPIO port configuration lock register */
+
+#define GPIO_LCKR(n)                   (1 << (n))
+#define GPIO_LCKK                      (1 << 16)      /* Bit 16: Lock key */
+
+/* GPIO alternate function low register */
+
+#define GPIO_AFRL_AFSEL_SHIFT(n)        ((n) << 2)    /* Alt function selection for pins 0 to 7 */
+#define GPIO_AFRL_AFSEL_MASK(n)         (0xf << GPIO_AFRL_AFSEL_SHIFT(n))
+
+#define GPIO_AFRL_AFSEL0_SHIFT          (0)
+#define GPIO_AFRL_AFSEL0_MASK           (0xf << GPIO_AFRL_AFSEL0_SHIFT)
+#define GPIO_AFRL_AFSEL1_SHIFT          (4)
+#define GPIO_AFRL_AFSEL1_MASK           (0xf << GPIO_AFRL_AFSEL1_SHIFT)
+#define GPIO_AFRL_AFSEL2_SHIFT          (8)
+#define GPIO_AFRL_AFSEL2_MASK           (0xf << GPIO_AFRL_AFSEL2_SHIFT)
+#define GPIO_AFRL_AFSEL3_SHIFT          (12)
+#define GPIO_AFRL_AFSEL3_MASK           (0xf << GPIO_AFRL_AFSEL3_SHIFT)
+#define GPIO_AFRL_AFSEL4_SHIFT          (16)
+#define GPIO_AFRL_AFSEL4_MASK           (0xf << GPIO_AFRL_AFSEL4_SHIFT)
+#define GPIO_AFRL_AFSEL5_SHIFT          (20)
+#define GPIO_AFRL_AFSEL5_MASK           (0xf << GPIO_AFRL_AFSEL5_SHIFT)
+#define GPIO_AFRL_AFSEL6_SHIFT          (24)
+#define GPIO_AFRL_AFSEL6_MASK           (0xf << GPIO_AFRL_AFSEL6_SHIFT)
+#define GPIO_AFRL_AFSEL7_SHIFT          (28)
+#define GPIO_AFRL_AFSEL7_MASK           (0xf << GPIO_AFRL_AFSEL7_SHIFT)
+
+/* GPIO alternate function high register */
+
+#define GPIO_AFRH_AFSEL_SHIFT(n)        ((n-8) << 2)  /* Alt function selection for pins 8 to 15 */

Review Comment:
   ```suggestion
   #define GPIO_AFRH_AFSEL_SHIFT(n)        (((n) - 8) << 2)  /* Alt function selection for pins 8 to 15 */
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_rcc.h:
##########
@@ -0,0 +1,877 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_rcc.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_RCC_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_RCC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_RCC_CR_OFFSET           0x0000  /* Clock control register */
+#define STM32WB_RCC_ICSCR_OFFSET        0x0004  /* Internal clock sources calibration register */
+#define STM32WB_RCC_CFGR_OFFSET         0x0008  /* Clock configuration register */
+#define STM32WB_RCC_PLLCFG_OFFSET       0x000c  /* PLL configuration register */
+#define STM32WB_RCC_PLLSAI1CFG_OFFSET   0x0010  /* PLLSAI1 configuration register */
+#define STM32WB_RCC_CIER_OFFSET         0x0018  /* Clock interrupt enable register */
+#define STM32WB_RCC_CIFR_OFFSET         0x001c  /* Clock interrupt flag register */
+#define STM32WB_RCC_CICR_OFFSET         0x0020  /* Clock interrupt clear register */
+#define STM32WB_RCC_SMPSCR_OFFSET       0x0024  /* Step-down converter control register */
+#define STM32WB_RCC_AHB1RSTR_OFFSET     0x0028  /* AHB1 peripheral reset register */
+#define STM32WB_RCC_AHB2RSTR_OFFSET     0x002c  /* AHB2 peripheral reset register */
+#define STM32WB_RCC_AHB3RSTR_OFFSET     0x0030  /* AHB3 peripheral reset register */
+#define STM32WB_RCC_APB1RSTR1_OFFSET    0x0038  /* APB1 Peripheral reset register 1 */
+#define STM32WB_RCC_APB1RSTR2_OFFSET    0x003c  /* APB1 Peripheral reset register 2 */
+#define STM32WB_RCC_APB2RSTR_OFFSET     0x0040  /* APB2 Peripheral reset register */
+#define STM32WB_RCC_APB3RSTR_OFFSET     0x0044  /* APB3 Peripheral reset register */
+#define STM32WB_RCC_AHB1ENR_OFFSET      0x0048  /* AHB1 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB2ENR_OFFSET      0x004c  /* AHB2 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB3ENR_OFFSET      0x0050  /* AHB3 Peripheral Clock enable register */
+#define STM32WB_RCC_APB1ENR1_OFFSET     0x0058  /* APB1 Peripheral Clock enable register 1 */
+#define STM32WB_RCC_APB1ENR2_OFFSET     0x005c  /* APB1 Peripheral Clock enable register 2 */
+#define STM32WB_RCC_APB2ENR_OFFSET      0x0060  /* APB2 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB1SMENR_OFFSET    0x0068  /* AHB1 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_AHB2SMENR_OFFSET    0x006c  /* AHB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_AHB3SMENR_OFFSET    0x0070  /* AHB3 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_APB1SMENR1_OFFSET   0x0078  /* APB1 clock enable in sleep and stop modes register 1 */
+#define STM32WB_RCC_APB1SMENR2_OFFSET   0x007c  /* APB1 clock enable in sleep and stop modes register 2 */
+#define STM32WB_RCC_APB2SMENR_OFFSET    0x0080  /* APB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_CCIPR_OFFSET        0x0088  /* Peripherals independent clock configuration register */
+#define STM32WB_RCC_BDCR_OFFSET         0x0090  /* Backup domain control register */
+#define STM32WB_RCC_CSR_OFFSET          0x0094  /* Control/status register */
+#define STM32WB_RCC_CRRCR_OFFSET        0x0098  /* Clock recovery RC register */
+#define STM32WB_RCC_HSECR_OFFSET        0x009c  /* Clock HSE register */
+#define STM32WB_RCC_EXTCFGR_OFFSET      0x0108  /* Extended clock recovery register */
+#define STM32WB_RCC_C2AHB1ENR_OFFSET    0x0148  /* CPU2 AHB1 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB2ENR_OFFSET    0x014c  /* CPU2 AHB2 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB3ENR_OFFSET    0x0150  /* CPU2 AHB3 Peripheral Clock enable register */
+#define STM32WB_RCC_C2APB1ENR1_OFFSET   0x0158  /* CPU2 APB1 Peripheral Clock enable register 1 */
+#define STM32WB_RCC_C2APB1ENR2_OFFSET   0x015c  /* CPU2 APB1 Peripheral Clock enable register 2 */
+#define STM32WB_RCC_C2APB2ENR_OFFSET    0x0160  /* CPU2 APB2 Peripheral Clock enable register */
+#define STM32WB_RCC_C2APB3ENR_OFFSET    0x0164  /* CPU2 APB3 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB1SMENR_OFFSET  0x0168  /* CPU2 AHB1 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2AHB2SMENR_OFFSET  0x016c  /* CPU2 AHB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2AHB3SMENR_OFFSET  0x0170  /* CPU2 AHB3 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2APB1SMENR1_OFFSET 0x0178  /* CPU2 APB1 clock enable in sleep and stop modes register 1 */
+#define STM32WB_RCC_C2APB1SMENR2_OFFSET 0x017c  /* CPU2 APB1 clock enable in sleep and stop modes register 2 */
+#define STM32WB_RCC_C2APB2SMENR_OFFSET  0x0180  /* CPU2 APB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2APB3SMENR_OFFSET  0x0184  /* CPU2 APB3 clock enable in sleep and stop modes register */
+
+/* Register Addresses *******************************************************/
+
+#define STM32WB_RCC_CR                (STM32WB_RCC_BASE + STM32WB_RCC_CR_OFFSET)
+#define STM32WB_RCC_ICSCR             (STM32WB_RCC_BASE + STM32WB_RCC_ICSCR_OFFSET)
+#define STM32WB_RCC_CFGR              (STM32WB_RCC_BASE + STM32WB_RCC_CFGR_OFFSET)
+#define STM32WB_RCC_PLLCFG            (STM32WB_RCC_BASE + STM32WB_RCC_PLLCFG_OFFSET)
+#define STM32WB_RCC_PLLSAI1CFG        (STM32WB_RCC_BASE + STM32WB_RCC_PLLSAI1CFG_OFFSET)
+#define STM32WB_RCC_CIER              (STM32WB_RCC_BASE + STM32WB_RCC_CIER_OFFSET)
+#define STM32WB_RCC_CIFR              (STM32WB_RCC_BASE + STM32WB_RCC_CIFR_OFFSET)
+#define STM32WB_RCC_CICR              (STM32WB_RCC_BASE + STM32WB_RCC_CICR_OFFSET)
+#define STM32WB_RCC_SMPSCR            (STM32WB_RCC_BASE + STM32WB_RCC_SMPSCR_OFFSET)
+#define STM32WB_RCC_AHB1RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB1RSTR_OFFSET)
+#define STM32WB_RCC_AHB2RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB2RSTR_OFFSET)
+#define STM32WB_RCC_AHB3RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB3RSTR_OFFSET)
+#define STM32WB_RCC_APB1RSTR1         (STM32WB_RCC_BASE + STM32WB_RCC_APB1RSTR1_OFFSET)
+#define STM32WB_RCC_APB1RSTR2         (STM32WB_RCC_BASE + STM32WB_RCC_APB1RSTR2_OFFSET)
+#define STM32WB_RCC_APB2RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_APB2RSTR_OFFSET)
+#define STM32WB_RCC_APB3RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_APB3RSTR_OFFSET)
+#define STM32WB_RCC_AHB1ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB1ENR_OFFSET)
+#define STM32WB_RCC_AHB2ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB2ENR_OFFSET)
+#define STM32WB_RCC_AHB3ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB3ENR_OFFSET)
+#define STM32WB_RCC_APB1ENR1          (STM32WB_RCC_BASE + STM32WB_RCC_APB1ENR1_OFFSET)
+#define STM32WB_RCC_APB1ENR2          (STM32WB_RCC_BASE + STM32WB_RCC_APB1ENR2_OFFSET)
+#define STM32WB_RCC_APB2ENR           (STM32WB_RCC_BASE + STM32WB_RCC_APB2ENR_OFFSET)
+#define STM32WB_RCC_AHB1SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB1SMENR_OFFSET)
+#define STM32WB_RCC_AHB2SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB2SMENR_OFFSET)
+#define STM32WB_RCC_AHB3SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB3SMENR_OFFSET)
+#define STM32WB_RCC_APB1SMENR1        (STM32WB_RCC_BASE + STM32WB_RCC_APB1SMENR1_OFFSET)
+#define STM32WB_RCC_APB1SMENR2        (STM32WB_RCC_BASE + STM32WB_RCC_APB1SMENR2_OFFSET)
+#define STM32WB_RCC_APB2SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_APB2SMENR_OFFSET)
+#define STM32WB_RCC_CCIPR             (STM32WB_RCC_BASE + STM32WB_RCC_CCIPR_OFFSET)
+#define STM32WB_RCC_BDCR              (STM32WB_RCC_BASE + STM32WB_RCC_BDCR_OFFSET)
+#define STM32WB_RCC_CSR               (STM32WB_RCC_BASE + STM32WB_RCC_CSR_OFFSET)
+#define STM32WB_RCC_CRRCR             (STM32WB_RCC_BASE + STM32WB_RCC_CRRCR_OFFSET)
+#define STM32WB_RCC_HSECR             (STM32WB_RCC_BASE + STM32WB_RCC_HSECR_OFFSET)
+#define STM32WB_RCC_EXTCFGR           (STM32WB_RCC_BASE + STM32WB_RCC_EXTCFGR_OFFSET)
+#define STM32WB_RCC_C2AHB1ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB1ENR_OFFSET)
+#define STM32WB_RCC_C2AHB2ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB2ENR_OFFSET)
+#define STM32WB_RCC_C2AHB3ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB3ENR_OFFSET)
+#define STM32WB_RCC_C2APB1ENR1        (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1ENR1_OFFSET)
+#define STM32WB_RCC_C2APB1ENR2        (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1ENR2_OFFSET)
+#define STM32WB_RCC_C2APB2ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2APB2ENR_OFFSET)
+#define STM32WB_RCC_C2APB3ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2APB3ENR_OFFSET)
+#define STM32WB_RCC_C2AHB1SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB1SMENR_OFFSET)
+#define STM32WB_RCC_C2AHB2SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB2SMENR_OFFSET)
+#define STM32WB_RCC_C2AHB3SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB3SMENR_OFFSET)
+#define STM32WB_RCC_C2APB1SMENR1      (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1SMENR1_OFFSET)
+#define STM32WB_RCC_C2APB1SMENR2      (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1SMENR2_OFFSET)
+#define STM32WB_RCC_C2APB2SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2APB2SMENR_OFFSET)
+#define STM32WB_RCC_C2APB3SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2APB3SMENR_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Clock control register */
+
+#define RCC_CR_MSION                  (1 << 0)  /* Bit 0: Internal Multi Speed clock enable */
+#define RCC_CR_MSIRDY                 (1 << 1)  /* Bit 1: Internal Multi Speed clock ready flag */
+#define RCC_CR_MSIPLLEN               (1 << 2)  /* Bit 2: MSI clock PLL enable */
+#define RCC_CR_MSIRANGE_SHIFT         (4)       /* Bits 7-4: MSI clock range */
+#define RCC_CR_MSIRANGE_MASK          (0x0f << RCC_CR_MSIRANGE_SHIFT)
+#  define RCC_CR_MSIRANGE_100K        (0  << RCC_CR_MSIRANGE_SHIFT) /* 0000: around 100 kHz */
+#  define RCC_CR_MSIRANGE_200K        (1  << RCC_CR_MSIRANGE_SHIFT) /* 0001: around 200 kHz */
+#  define RCC_CR_MSIRANGE_400K        (2  << RCC_CR_MSIRANGE_SHIFT) /* 0010: around 400 kHz */
+#  define RCC_CR_MSIRANGE_800K        (3  << RCC_CR_MSIRANGE_SHIFT) /* 0011: around 800 kHz */
+#  define RCC_CR_MSIRANGE_1M          (4  << RCC_CR_MSIRANGE_SHIFT) /* 0100: around 1 MHz */
+#  define RCC_CR_MSIRANGE_2M          (5  << RCC_CR_MSIRANGE_SHIFT) /* 0101: around 2 MHz */
+#  define RCC_CR_MSIRANGE_4M          (6  << RCC_CR_MSIRANGE_SHIFT) /* 0110: around 4 MHz */
+#  define RCC_CR_MSIRANGE_8M          (7  << RCC_CR_MSIRANGE_SHIFT) /* 0111: around 8 MHz */
+#  define RCC_CR_MSIRANGE_16M         (8  << RCC_CR_MSIRANGE_SHIFT) /* 1000: around 16 MHz */
+#  define RCC_CR_MSIRANGE_24M         (9  << RCC_CR_MSIRANGE_SHIFT) /* 1001: around 24 MHz */
+#  define RCC_CR_MSIRANGE_32M         (10 << RCC_CR_MSIRANGE_SHIFT) /* 1010: around 32 MHz */
+#  define RCC_CR_MSIRANGE_48M         (11 << RCC_CR_MSIRANGE_SHIFT) /* 1011: around 48 MHz */
+
+#define RCC_CR_HSION                  (1 << 8)  /* Bit 8:  HSI16 clock enable */
+#define RCC_CR_HSIKERON               (1 << 9)  /* Bit 9:  HSI16 always enable for peripheral kernels */
+#define RCC_CR_HSIRDY                 (1 << 10) /* Bit 10: HSI16 clock ready flag */
+#define RCC_CR_HSIASFS                (1 << 11) /* Bit 11: HSI16 automatic start from stop */
+#define RCC_CR_HSEON                  (1 << 16) /* Bit 16: HSE clock enable */
+#define RCC_CR_HSERDY                 (1 << 17) /* Bit 17: HSE clock ready flag */
+#define RCC_CR_CSSON                  (1 << 19) /* Bit 19: Clock Security System enable */
+#define RCC_CR_HSEPRE                 (1 << 20) /* Bit 20: HSE sysclk and PLL M divider prescaler */
+#define RCC_CR_PLLON                  (1 << 24) /* Bit 24: PLL enable */
+#define RCC_CR_PLLRDY                 (1 << 25) /* Bit 25: PLL clock ready flag */
+#define RCC_CR_PLLSAI1ON              (1 << 26) /* Bit 26: PLLSAI1 enable */
+#define RCC_CR_PLLSAI1RDY             (1 << 27) /* Bit 27: PLLSAI1 clock ready flag */
+
+/* Internal Clock Sources Calibration */
+
+#define RCC_CR_HSITRIM_SHIFT          (24)      /* Bits 30-24: HSI16 clock trimming */
+#define RCC_CR_HSITRIM_MASK           (0x7f << RCC_CR_HSITRIM_SHIFT)
+#define RCC_CR_HSICAL_SHIFT           (16)      /* Bits 23-16: HSI16 clock Calibration */
+#define RCC_CR_HSICAL_MASK            (0xff << RCC_CR_HSICAL_SHIFT)
+#define RCC_CR_MSITRIM_SHIFT          (8)       /* Bits 15-8:  Internal Multi Speed clock trimming */
+#define RCC_CR_MSITRIM_MASK           (0xff << RCC_CR_MSITRIM_SHIFT)
+#define RCC_CR_MSICAL_SHIFT           (0)       /* Bits 7-0:   Internal Multi Speed clock Calibration */
+#define RCC_CR_MSICAL_MASK            (0xff << RCC_CR_MSICAL_SHIFT)
+
+/* Clock configuration register */
+
+#define RCC_CFGR_SW_SHIFT             (0)       /* Bits 0-1: System clock Switch */
+#define RCC_CFGR_SW_MASK              (0x3 << RCC_CFGR_SW_SHIFT)
+#  define RCC_CFGR_SW_MSI             (0x0 << RCC_CFGR_SW_SHIFT) /* 00: MSI selected as system clock */
+#  define RCC_CFGR_SW_HSI16           (0x1 << RCC_CFGR_SW_SHIFT) /* 00: HSI16 selected as system clock */
+#  define RCC_CFGR_SW_HSE             (0x2 << RCC_CFGR_SW_SHIFT) /* 01: HSE selected as system clock */
+#  define RCC_CFGR_SW_PLL             (0x3 << RCC_CFGR_SW_SHIFT) /* 10: PLL selected as system clock */
+
+#define RCC_CFGR_SWS_SHIFT            (2)       /* Bits 2-3: System Clock Switch Status */
+#define RCC_CFGR_SWS_MASK             (0x3 << RCC_CFGR_SWS_SHIFT)
+#  define RCC_CFGR_SWS_MSI            (0x0 << RCC_CFGR_SWS_SHIFT) /* 00: MSI oscillator used as system clock */
+#  define RCC_CFGR_SWS_HSI16          (0x1 << RCC_CFGR_SWS_SHIFT) /* 00: HSI16 oscillator used as system clock */
+#  define RCC_CFGR_SWS_HSE            (0x2 << RCC_CFGR_SWS_SHIFT) /* 01: HSE oscillator used as system clock */
+#  define RCC_CFGR_SWS_PLL            (0x3 << RCC_CFGR_SWS_SHIFT) /* 10: PLL used as system clock */
+
+#define RCC_CFGR_HPRE_SHIFT           (4)       /* Bits 4-7: HCLK1 prescaler (AHB1, AHB2, AHB3 and SRAM1) */
+#define RCC_CFGR_HPRE_MASK            (0x0f << RCC_CFGR_HPRE_SHIFT)
+#  define RCC_CFGR_HPRE_SYSCLK        (0x00 << RCC_CFGR_HPRE_SHIFT) /* 0000: SYSCLK not divided */
+#  define RCC_CFGR_HPRE_SYSCLKd3      (0x01 << RCC_CFGR_HPRE_SHIFT) /* 0001: SYSCLK divided by 3 */
+#  define RCC_CFGR_HPRE_SYSCLKd5      (0x02 << RCC_CFGR_HPRE_SHIFT) /* 0010: SYSCLK divided by 5 */
+#  define RCC_CFGR_HPRE_SYSCLKd6      (0x05 << RCC_CFGR_HPRE_SHIFT) /* 0101: SYSCLK divided by 6 */
+#  define RCC_CFGR_HPRE_SYSCLKd10     (0x06 << RCC_CFGR_HPRE_SHIFT) /* 0110: SYSCLK divided by 10 */
+#  define RCC_CFGR_HPRE_SYSCLKd32     (0x07 << RCC_CFGR_HPRE_SHIFT) /* 0111: SYSCLK divided by 32 */
+#  define RCC_CFGR_HPRE_SYSCLKd2      (0x08 << RCC_CFGR_HPRE_SHIFT) /* 1000: SYSCLK divided by 2 */
+#  define RCC_CFGR_HPRE_SYSCLKd4      (0x09 << RCC_CFGR_HPRE_SHIFT) /* 1001: SYSCLK divided by 4 */
+#  define RCC_CFGR_HPRE_SYSCLKd8      (0x10 << RCC_CFGR_HPRE_SHIFT) /* 1010: SYSCLK divided by 8 */
+#  define RCC_CFGR_HPRE_SYSCLKd16     (0x11 << RCC_CFGR_HPRE_SHIFT) /* 1011: SYSCLK divided by 16 */
+#  define RCC_CFGR_HPRE_SYSCLKd64     (0x12 << RCC_CFGR_HPRE_SHIFT) /* 1100: SYSCLK divided by 64 */
+#  define RCC_CFGR_HPRE_SYSCLKd128    (0x13 << RCC_CFGR_HPRE_SHIFT) /* 1101: SYSCLK divided by 128 */
+#  define RCC_CFGR_HPRE_SYSCLKd256    (0x14 << RCC_CFGR_HPRE_SHIFT) /* 1110: SYSCLK divided by 256 */
+#  define RCC_CFGR_HPRE_SYSCLKd512    (0x15 << RCC_CFGR_HPRE_SHIFT) /* 1111: SYSCLK divided by 512 */
+
+#define RCC_CFGR_PPRE1_SHIFT          (8)       /* Bits 8-10: PCLK1 Low speed prescaler (APB1) */
+#define RCC_CFGR_PPRE1_MASK           (0x7 << RCC_CFGR_PPRE1_SHIFT)
+#  define RCC_CFGR_PPRE1_HCLK1        (0x0 << RCC_CFGR_PPRE1_SHIFT) /* 0xx: HCLK1 not divided */
+#  define RCC_CFGR_PPRE1_HCLK1d2      (0x4 << RCC_CFGR_PPRE1_SHIFT) /* 100: HCLK1 divided by 2 */
+#  define RCC_CFGR_PPRE1_HCLK1d4      (0x5 << RCC_CFGR_PPRE1_SHIFT) /* 101: HCLK1 divided by 4 */
+#  define RCC_CFGR_PPRE1_HCLK1d8      (0x6 << RCC_CFGR_PPRE1_SHIFT) /* 110: HCLK1 divided by 8 */
+#  define RCC_CFGR_PPRE1_HCLK1d16     (0x7 << RCC_CFGR_PPRE1_SHIFT) /* 111: HCLK1 divided by 16 */
+
+#define RCC_CFGR_PPRE2_SHIFT          (11)      /* Bits 11-13: PCLK2 High speed prescaler (APB2) */
+#define RCC_CFGR_PPRE2_MASK           (0x7 << RCC_CFGR_PPRE2_SHIFT)
+#  define RCC_CFGR_PPRE2_HCLK1        (0x0 << RCC_CFGR_PPRE2_SHIFT) /* 0xx: HCLK1 not divided */
+#  define RCC_CFGR_PPRE2_HCLK1d2      (0x4 << RCC_CFGR_PPRE2_SHIFT) /* 100: HCLK1 divided by 2 */
+#  define RCC_CFGR_PPRE2_HCLK1d4      (0x5 << RCC_CFGR_PPRE2_SHIFT) /* 101: HCLK1 divided by 4 */
+#  define RCC_CFGR_PPRE2_HCLK1d8      (0x6 << RCC_CFGR_PPRE2_SHIFT) /* 110: HCLK1 divided by 8 */
+#  define RCC_CFGR_PPRE2_HCLK1d16     (0x7 << RCC_CFGR_PPRE2_SHIFT) /* 111: HCLK1 divided by 16 */
+
+#define RCC_CFGR_STOPWUCK             (1 << 15) /* Bit 15: Wakeup from Stop and CSS backup clock selection */
+#  define RCC_CFGR_STOPWUCK_MSI       (0 << 15) /* 0: MSI */
+#  define RCC_CFGR_STOPWUCK_HSI16     (1 << 15) /* 0: HSI16 */
+
+#define RCC_CFGR_HPREF                (1 << 16) /* Bit 16: HCLK1 prescaler flag (AHB1, AHB2, AHB3, SRAM1) */
+#define RCC_CFGR_PPRE1F               (1 << 17) /* Bit 17: PCLK1 prescaler flag (APB1) */
+#define RCC_CFGR_PPRE2F               (1 << 18) /* Bit 18: PCLK2 prescaler flag (APB2) */
+
+#define RCC_CFGR_MCOSEL_SHIFT         (24)      /* Bits 24-27: Microcontroller Clock Output */
+#define RCC_CFGR_MCOSEL_MASK          (0xf << RCC_CFGR_MCOSEL_SHIFT)
+#  define RCC_CFGR_MCOSEL_DISABLED    (0x0 << RCC_CFGR_MCOSEL_SHIFT) /* 0000: Output disabled, no clock on MCO */
+#  define RCC_CFGR_MCOSEL_SYSCLK      (0x1 << RCC_CFGR_MCOSEL_SHIFT) /* 0001: SYSCLK system clock selected */
+#  define RCC_CFGR_MCOSEL_MSI         (0x2 << RCC_CFGR_MCOSEL_SHIFT) /* 0010: MSI clock selected */
+#  define RCC_CFGR_MCOSEL_HSI16       (0x3 << RCC_CFGR_MCOSEL_SHIFT) /* 0011: HSI16 clock selected */
+#  define RCC_CFGR_MCOSEL_HSE_AFT     (0x4 << RCC_CFGR_MCOSEL_SHIFT) /* 0100: HSE clock after stabilization */
+#  define RCC_CFGR_MCOSEL_PLL         (0x5 << RCC_CFGR_MCOSEL_SHIFT) /* 0101: Main PLLRCLK selected  */
+#  define RCC_CFGR_MCOSEL_LSI1        (0x6 << RCC_CFGR_MCOSEL_SHIFT) /* 0110: LSI1 clock selected */
+#  define RCC_CFGR_MCOSEL_LSI2        (0x7 << RCC_CFGR_MCOSEL_SHIFT) /* 0111: LSI2 clock selected */
+#  define RCC_CFGR_MCOSEL_LSE         (0x8 << RCC_CFGR_MCOSEL_SHIFT) /* 1000: LSE clock selected */
+#  define RCC_CFGR_MCOSEL_HSI48       (0x9 << RCC_CFGR_MCOSEL_SHIFT) /* 1001: HSI48 clock selected */
+#  define RCC_CFGR_MCOSEL_HSE_BFR     (0xc << RCC_CFGR_MCOSEL_SHIFT) /* 1100: HSE clock before stabilization */
+
+#define RCC_CFGR_MCOPRE_SHIFT         (28)      /* Bits 28-30: MCO prescaler */
+#define RCC_CFGR_MCOPRE_MASK          (0x7 << RCC_CFGR_MCOPRE_SHIFT)
+#  define RCC_CFGR_MCOPRE_DIV1        (0x0 << RCC_CFGR_MCOPRE_SHIFT) /* 000: no division */
+#  define RCC_CFGR_MCOPRE_DIV2        (0x1 << RCC_CFGR_MCOPRE_SHIFT) /* 001: division by 2 */
+#  define RCC_CFGR_MCOPRE_DIV4        (0x2 << RCC_CFGR_MCOPRE_SHIFT) /* 010: division by 4 */
+#  define RCC_CFGR_MCOPRE_DIV8        (0x3 << RCC_CFGR_MCOPRE_SHIFT) /* 011: division by 8 */
+#  define RCC_CFGR_MCOPRE_DIV16       (0x4 << RCC_CFGR_MCOPRE_SHIFT) /* 100: division by 16 */
+
+#define RCC_CFGR_RESET_MASK           (0x00070000)
+
+/* PLL configuration register */
+
+#define RCC_PLLCFG_PLLSRC_SHIFT       (0)       /* Bits 0-1: Main PLL and audio PLLSAI1 entry clock source */
+#define RCC_PLLCFG_PLLSRC_MASK        (0x3 << RCC_PLLCFG_PLLSRC_SHIFT)
+#  define RCC_PLLCFG_PLLSRC_NONE      (0x0 << RCC_PLLCFG_PLLSRC_SHIFT) /* 00: No clock sent to PLLs */
+#  define RCC_PLLCFG_PLLSRC_MSI       (0x1 << RCC_PLLCFG_PLLSRC_SHIFT) /* 01: MSI selected as PLLs source */
+#  define RCC_PLLCFG_PLLSRC_HSI16     (0x2 << RCC_PLLCFG_PLLSRC_SHIFT) /* 10: HSI16 selected as PLLs source */
+#  define RCC_PLLCFG_PLLSRC_HSE       (0x3 << RCC_PLLCFG_PLLSRC_SHIFT) /* 11: HSE selected as PLLs source */
+
+#define RCC_PLLCFG_PLLM_SHIFT         (4)       /* Bits 4-6: Main PLL and audio PLLSAI1 divider */
+#define RCC_PLLCFG_PLLM_MASK          (0x07 << RCC_PLLCFG_PLLM_SHIFT)
+#  define RCC_PLLCFG_PLLM(n)          ((n-1) << RCC_PLLCFG_PLLM_SHIFT) /* n = 1..8 */

Review Comment:
   ```suggestion
   #  define RCC_PLLCFG_PLLM(n)          (((n) - 1) << RCC_PLLCFG_PLLM_SHIFT) /* n = 1..8 */
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_syscfg.h:
##########
@@ -0,0 +1,227 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_syscfg.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_SYSCFG_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_SYSCFG_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "chip.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_SYSCFG_MEMRMP_OFFSET    0x0000 /* SYSCFG memory remap register */
+#define STM32WB_SYSCFG_CFGR1_OFFSET     0x0004 /* SYSCFG configuration register 1 */
+
+#define STM32WB_SYSCFG_EXTICR_OFFSET(p) (0x0008 + ((p) & 0x0c)) /* Pin p = 0..15 */
+
+#define STM32WB_SYSCFG_EXTICR1_OFFSET   0x0008 /* SYSCFG external interrupt configuration register 1 */
+#define STM32WB_SYSCFG_EXTICR2_OFFSET   0x000c /* SYSCFG external interrupt configuration register 2 */
+#define STM32WB_SYSCFG_EXTICR3_OFFSET   0x0010 /* SYSCFG external interrupt configuration register 3 */
+#define STM32WB_SYSCFG_EXTICR4_OFFSET   0x0014 /* SYSCFG external interrupt configuration register 4 */
+
+#define STM32WB_SYSCFG_SCSR_OFFSET      0x0018 /* SYSCFG SRAM2 control and status register */
+#define STM32WB_SYSCFG_CFGR2_OFFSET     0x001c /* SYSCFG configuration register 2 */
+#define STM32WB_SYSCFG_SWPR1_OFFSET     0x0020 /* SYSCFG SRAM2 write protection register 1 */
+#define STM32WB_SYSCFG_SKR_OFFSET       0x0024 /* SYSCFG SRAM2 key register */
+#define STM32WB_SYSCFG_SWPR2_OFFSET     0x0028 /* SYSCFG SRAM2 write protection register 2 */
+
+#define STM32WB_SYSCFG_IMR1_OFFSET      0x0100 /* SYSCFG Interrupt mask register 1 */
+#define STM32WB_SYSCFG_IMR2_OFFSET      0x0104 /* SYSCFG Interrupt mask register 2 */
+#define STM32WB_SYSCFG_C2IMR1_OFFSET    0x0108 /* SYSCFG CPU2 Interrupt mask register 1 */
+#define STM32WB_SYSCFG_C2IMR2_OFFSET    0x010c /* SYSCFG CPU2 Interrupt mask register 2 */
+#define STM32WB_SYSCFG_SIPCR_OFFSET     0x0110 /* SYSCFG Secure IP control register */
+
+/* Register Addresses *******************************************************/
+
+#define STM32WB_SYSCFG_MEMRMP           (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_MEMRMP_OFFSET)
+#define STM32WB_SYSCFG_CFGR1            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_CFGR1_OFFSET)
+#define STM32WB_SYSCFG_EXTICR(p)        (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR_OFFSET(p))
+#define STM32WB_SYSCFG_EXTICR1          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR1_OFFSET)
+#define STM32WB_SYSCFG_EXTICR2          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR2_OFFSET)
+#define STM32WB_SYSCFG_EXTICR3          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR3_OFFSET)
+#define STM32WB_SYSCFG_EXTICR4          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR4_OFFSET)
+#define STM32WB_SYSCFG_SCSR             (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SCSR_OFFSET)
+#define STM32WB_SYSCFG_CFGR2            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_CFGR2_OFFSET)
+#define STM32WB_SYSCFG_SWPR1            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SWPR1_OFFSET)
+#define STM32WB_SYSCFG_SKR              (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SKR_OFFSET)
+#define STM32WB_SYSCFG_SWPR2            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SWPR2_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* SYSCFG memory remap register */
+
+#define SYSCFG_MEMRMP_SHIFT           (0)       /* Bits 2-0: Memory mapping selection */
+#define SYSCFG_MEMRMP_MASK            (0x7 << SYSCFG_MEMRMP_SHIFT)
+#  define SYSCFG_MEMRMP_FLASH         (0x0 << SYSCFG_MEMRMP_SHIFT) /* 000: Main Flash memory mapped at 0x00000000 */
+#  define SYSCFG_MEMRMP_SYSTEM        (0x1 << SYSCFG_MEMRMP_SHIFT) /* 001: System Flash memory mapped at 0x00000000 */
+#  define SYSCFG_MEMRMP_SRAM          (0x3 << SYSCFG_MEMRMP_SHIFT) /* 011: SRAM1 mapped at 0x00000000 */
+#  define SYSCFG_MEMRMP_QSPI          (0x6 << SYSCFG_MEMRMP_SHIFT) /* 110: QSPI memory mapped at 0x00000000 */
+
+/* SYSCFG configuration register 1 */
+
+#define SYSCFG_CFGR1_BOOSTEN          (1 << 8)  /* Bit  8: I/O analog switch voltage booster enable */
+#define SYSCFG_CFGR1_I2C_PB6_FMP      (1 << 16) /* Bit 16: Fast-mode Plus (Fm+) driving capability activation on PB6 */
+#define SYSCFG_CFGR1_I2C_PB7_FMP      (1 << 17) /* Bit 17: Fast-mode Plus (Fm+) driving capability activation on PB7 */
+#define SYSCFG_CFGR1_I2C_PB8_FMP      (1 << 18) /* Bit 18: Fast-mode Plus (Fm+) driving capability activation on PB8 */
+#define SYSCFG_CFGR1_I2C_PB9_FMP      (1 << 19) /* Bit 19: Fast-mode Plus (Fm+) driving capability activation on PB9 */
+#define SYSCFG_CFGR1_I2C1_FMP         (1 << 20) /* Bit 20: I2C1 Fast-mode Plus (Fm+) driving capability activation */
+#define SYSCFG_CFGR1_I2C3_FMP         (1 << 22) /* Bit 22: I2C3 Fast-mode Plus (Fm+) driving capability activation */
+#define SYSCFG_CFGR1_FPU_IE0          (1 << 26) /* Bit 26: FPU Invalid operation interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE1          (1 << 27) /* Bit 27: FPU Divide-by-zero interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE2          (1 << 28) /* Bit 28: FPU Underflow interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE3          (1 << 29) /* Bit 29: FPU Overflow interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE4          (1 << 30) /* Bit 30: FPU Input denormal interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE5          (1 << 31) /* Bit 31: FPU Inexact interrupt enable */
+
+/* SYSCFG external interrupt configuration register 1-4 */
+
+#define SYSCFG_EXTICR_PORTA           (0x0)     /* 000: PA[x] pin */
+#define SYSCFG_EXTICR_PORTB           (0x1)     /* 001: PB[x] pin */
+#define SYSCFG_EXTICR_PORTC           (0x2)     /* 010: PC[x] pin */
+#define SYSCFG_EXTICR_PORTD           (0x3)     /* 011: PD[x] pin */
+#define SYSCFG_EXTICR_PORTE           (0x4)     /* 100: PE[x] pin */
+#define SYSCFG_EXTICR_PORTH           (0x7)     /* 111: PH[x] pin */
+
+#define SYSCFG_EXTICR_PORT_MASK       (0x7)
+#define SYSCFG_EXTICR_EXTI_SHIFT(g)   (((g) & 0x3) << 2)
+#define SYSCFG_EXTICR_EXTI_MASK(g)    (SYSCFG_EXTICR_PORT_MASK << (SYSCFG_EXTICR_EXTI_SHIFT(g)))
+
+#define SYSCFG_EXTICR1_EXTI0_SHIFT    (0)       /* Bits 0-2: EXTI 0 configuration */
+#define SYSCFG_EXTICR1_EXTI0_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI0_SHIFT)
+#define SYSCFG_EXTICR1_EXTI1_SHIFT    (4)       /* Bits 4-6: EXTI 1 configuration */
+#define SYSCFG_EXTICR1_EXTI1_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI1_SHIFT)
+#define SYSCFG_EXTICR1_EXTI2_SHIFT    (8)       /* Bits 8-10: EXTI 2 configuration */
+#define SYSCFG_EXTICR1_EXTI2_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI2_SHIFT)
+#define SYSCFG_EXTICR1_EXTI3_SHIFT    (12)      /* Bits 12-14: EXTI 3 configuration */
+#define SYSCFG_EXTICR1_EXTI3_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI3_SHIFT)
+
+#define SYSCFG_EXTICR2_EXTI4_SHIFT    (0)       /* Bits 0-2: EXTI 4 configuration */
+#define SYSCFG_EXTICR2_EXTI4_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI4_SHIFT)
+#define SYSCFG_EXTICR2_EXTI5_SHIFT    (4)       /* Bits 4-6: EXTI 5 configuration */
+#define SYSCFG_EXTICR2_EXTI5_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI5_SHIFT)
+#define SYSCFG_EXTICR2_EXTI6_SHIFT    (8)       /* Bits 8-10: EXTI 6 configuration */
+#define SYSCFG_EXTICR2_EXTI6_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI6_SHIFT)
+#define SYSCFG_EXTICR2_EXTI7_SHIFT    (12)      /* Bits 12-14: EXTI 7 configuration */
+#define SYSCFG_EXTICR2_EXTI7_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI7_SHIFT)
+
+#define SYSCFG_EXTICR3_EXTI8_SHIFT    (0)       /* Bits 0-2: EXTI 8 configuration */
+#define SYSCFG_EXTICR3_EXTI8_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI8_SHIFT)
+#define SYSCFG_EXTICR3_EXTI9_SHIFT    (4)       /* Bits 4-6: EXTI 9 configuration */
+#define SYSCFG_EXTICR3_EXTI9_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI9_SHIFT)
+#define SYSCFG_EXTICR3_EXTI10_SHIFT   (8)       /* Bits 8-10: EXTI 10 configuration */
+#define SYSCFG_EXTICR3_EXTI10_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI10_SHIFT)
+#define SYSCFG_EXTICR3_EXTI11_SHIFT   (12)      /* Bits 12-14: EXTI 11 configuration */
+#define SYSCFG_EXTICR3_EXTI11_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI11_SHIFT)
+
+#define SYSCFG_EXTICR4_EXTI12_SHIFT   (0)       /* Bits 0-2: EXTI 12 configuration */
+#define SYSCFG_EXTICR4_EXTI12_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI12_SHIFT)
+#define SYSCFG_EXTICR4_EXTI13_SHIFT   (4)       /* Bits 4-6: EXTI 13 configuration */
+#define SYSCFG_EXTICR4_EXTI13_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI13_SHIFT)
+#define SYSCFG_EXTICR4_EXTI14_SHIFT   (8)       /* Bits 8-10: EXTI 14 configuration */
+#define SYSCFG_EXTICR4_EXTI14_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI14_SHIFT)
+#define SYSCFG_EXTICR4_EXTI15_SHIFT   (12)      /* Bits 12-14: EXTI 15 configuration */
+#define SYSCFG_EXTICR4_EXTI15_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI15_SHIFT)
+
+/* SYSCFG SRAM2 control and status register */
+
+#define SYSCFG_SCSR_SRAM2ER           (1 << 0)  /* Bit  0: SRAM2 and PKA RAM Erase */
+#define SYSCFG_SCSR_SRAM2BSY          (1 << 1)  /* Bit  1: SRAM2 and PKA RAM busy by erase operation */
+#define SYSCFG_SCSR_C2RFD             (1 << 31) /* Bit 31: CPU2 SRAM fetch disable */
+
+/* SYSCFG configuration register 2 */
+
+#define SYSCFG_CFGR2_CLL              (1 << 0)  /* Bit 0: LOCKUP (Hardfault) output enable */
+#define SYSCFG_CFGR2_SPL              (1 << 1)  /* Bit 1: SRAM2 parity lock enable  */
+#define SYSCFG_CFGR2_PVDL             (1 << 2)  /* Bit 2: PVD lock enable */
+#define SYSCFG_CFGR2_ECCL             (1 << 3)  /* Bit 3: ECC lock enable */
+#define SYSCFG_CFGR2_SPF              (1 << 8)  /* Bit 8: SRAM2 parity error flag */
+
+/* SYSCFG SRAM2 write protection register 1 */
+
+#define SYSCFG_SWPR1_PWP(x)           (1 << (x)) /* Bits 0-31: SRAM2 1Kb page x = 0..31 write protection */
+
+/* SYSCFG SRAM2 key register */
+
+#define SYSCFG_SKR_SHIFT              (0)
+#define SYSCFG_SKR_MASK               (0xff << SYSCFG_SKR_SHIFT)
+#define SYSCFG_SKR_KEY1               0xca
+#define SYSCFG_SKR_KEY2               0x53
+
+/* SYSCFG SRAM2 write protection register 2 */
+
+#define SYSCFG_SWPR2_PWP(x)           (1 << ((x)-32)) /* Bits 0-31: SRAM2 1Kb page x = 32..63 write protection */
+
+/* SYSCFG Interrupt mask register 1 */
+
+#define SYSCFG_IMR1_EXTIIM(n)         (1 << ((n)+16)) /* EXTI[n] interrupt mask, n = 5..15 */
+
+#define SYSCFG_IMR1_TIM1IM            (1 << 13) /* Bit 13: TIM1 interrupt mask */
+#define SYSCFG_IMR1_TIM16IM           (1 << 14) /* Bit 14: TIM16 interrupt mask */
+#define SYSCFG_IMR1_TIM17IM           (1 << 15) /* Bit 15: TIM17 interrupt mask */
+
+/* SYSCFG Interrupt mask register 2 */
+
+#define SYSCFG_IMR2_PVM1IM            (1 << 16) /* Bit 16: PVM1 interrupt mask */
+#define SYSCFG_IMR2_PVM3IM            (1 << 18) /* Bit 18: PVM3 interrupt mask */
+#define SYSCFG_IMR2_PVDIM             (1 << 20) /* Bit 20: PVD interrupt mask */
+
+/* SYSCFG CPU2 Interrupt mask register 1 */
+
+#define SYSCFG_C2IMR1_RTCSTLSECSSIM   (1 << 0)  /* Bit 0: CPU2 RTC, STAMP, TAMP, LSECSS interrupt mask */
+#define SYSCFG_C2IMR1_RTCWKUPIM       (1 << 3)  /* Bit 3: CPU2 RTC Wakeup interrupt mask */
+#define SYSCFG_C2IMR1_RTCALRMIM       (1 << 4)  /* Bit 4: CPU2 RTC Alarm interrupt mask */
+#define SYSCFG_C2IMR1_RCCIM           (1 << 5)  /* Bit 5: CPU2 RCC interrupt mask */
+#define SYSCFG_C2IMR1_FLASHIM         (1 << 6)  /* Bit 6: CPU2 FLASH interrupt mask */
+#define SYSCFG_C2IMR1_PKAIM           (1 << 8)  /* Bit 8: CPU2 PKA interrupt mask */
+#define SYSCFG_C2IMR1_RNGIM           (1 << 9)  /* Bit 9: CPU2 RNG interrupt mask */
+#define SYSCFG_C2IMR1_AES1IM          (1 << 10) /* Bit 10: CPU2 AES1 interrupt mask */
+#define SYSCFG_C2IMR1_COMPIM          (1 << 11) /* Bit 11: CPU2 COMP interrupt mask */
+#define SYSCFG_C2IMR1_ADCIM           (1 << 12) /* Bit 12: CPU2 ADC interrupt mask */
+
+#define SYSCFG_C2IMR1_EXTIIM(n)       (1 << ((n)+16)) /* CPU2 EXTI[n] interrupt mask, n = 0..15 */
+
+/* SYSCFG CPU2 Interrupt mask register 2 */
+
+#define SYSCFG_C2IMR2_DMA1IM(n)       (1 << ((n)-1)) /* CPU2 DMA1[n] interrupt mask, n = 1..7 */
+
+#define SYSCFG_C2IMR2_DMA2IM(n)       (1 << ((n)+7)) /* CPU2 DMA2[n] interrupt mask, n = 1..7 */

Review Comment:
   ```suggestion
   #define SYSCFG_C2IMR2_DMA2IM(n)       (1 << ((n) + 7)) /* CPU2 DMA2[n] interrupt mask, n = 1..7 */
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_tim.h:
##########
@@ -0,0 +1,1385 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_tim.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_TIM_CR1_OFFSET      0x0000  /* Control register 1 */
+#define STM32WB_TIM_CR2_OFFSET      0x0004  /* Control register 2 */
+#define STM32WB_TIM_SMCR_OFFSET     0x0008  /* Slave mode control register (TIM1, TIM2) */
+#define STM32WB_TIM_DIER_OFFSET     0x000c  /* DMA / Interrupt enable register */
+#define STM32WB_TIM_SR_OFFSET       0x0010  /* Status register */
+#define STM32WB_TIM_EGR_OFFSET      0x0014  /* Event generation register */
+#define STM32WB_TIM_CCMR1_OFFSET    0x0018  /* Capture/compare mode register 1 */
+#define STM32WB_TIM_CCMR2_OFFSET    0x001c  /* Capture/compare mode register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCER_OFFSET     0x0020  /* Capture/compare enable register */
+#define STM32WB_TIM_CNT_OFFSET      0x0024  /* Counter */
+#define STM32WB_TIM_PSC_OFFSET      0x0028  /* Prescaler */
+#define STM32WB_TIM_ARR_OFFSET      0x002c  /* Auto-reload register */
+#define STM32WB_TIM_RCR_OFFSET      0x0030  /* Repetition counter register (TIM1, TIM16/TIM17) */
+#define STM32WB_TIM_CCR1_OFFSET     0x0034  /* Capture/compare register 1 */
+#define STM32WB_TIM_CCR2_OFFSET     0x0038  /* Capture/compare register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR3_OFFSET     0x003c  /* Capture/compare register 3 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR4_OFFSET     0x0040  /* Capture/compare register 4 (TIM1, TIM2) */
+#define STM32WB_TIM_BDTR_OFFSET     0x0044  /* Break and dead-time register (TIM1, TIM16/17) */
+#define STM32WB_TIM_DCR_OFFSET      0x0048  /* DMA control register */
+#define STM32WB_TIM_DMAR_OFFSET     0x004c  /* DMA address for burst mode */
+#define STM32WB_TIM_OR1_OFFSET      0x0050  /* Option register 1 */
+#define STM32WB_TIM_CCMR3_OFFSET    0x0054  /* Capture/compare mode register 3 (TIM1) */
+#define STM32WB_TIM_CCR5_OFFSET     0x0058  /* Capture/compare register 5 (TIM1) */
+#define STM32WB_TIM_CCR6_OFFSET     0x005C  /* Capture/compare register 6 (TIM1) */
+#define STM32WB_TIM_AF1_OFFSET      0x0060  /* Alternate function register 1 */
+#define STM32WB_TIM_AF2_OFFSET      0x0064  /* Alternate function register 2 (TIM1) */
+#define STM32WB_TIM_TISEL_OFFSET    0x0068  /* Input selector register */
+
+/* Register Addresses *******************************************************/
+
+/* Advanced Timer TIM1 */
+
+#define STM32WB_TIM1_CR1            (STM32WB_TIM1_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM1_CR2            (STM32WB_TIM1_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM1_SMCR           (STM32WB_TIM1_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM1_DIER           (STM32WB_TIM1_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM1_SR             (STM32WB_TIM1_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM1_EGR            (STM32WB_TIM1_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM1_CCMR1          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM1_CCMR2          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM1_CCER           (STM32WB_TIM1_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM1_CNT            (STM32WB_TIM1_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM1_PSC            (STM32WB_TIM1_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM1_ARR            (STM32WB_TIM1_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM1_RCR            (STM32WB_TIM1_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM1_CCR1           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM1_CCR2           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM1_CCR3           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM1_CCR4           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM1_BDTR           (STM32WB_TIM1_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM1_DCR            (STM32WB_TIM1_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM1_DMAR           (STM32WB_TIM1_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM1_OR1            (STM32WB_TIM1_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM1_CCMR3          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR3_OFFSET)
+#define STM32WB_TIM1_CCR5           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR5_OFFSET)
+#define STM32WB_TIM1_CCR6           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR6_OFFSET)
+#define STM32WB_TIM1_AF1            (STM32WB_TIM1_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM1_AF2            (STM32WB_TIM1_BASE + STM32WB_TIM_AF2_OFFSET)
+#define STM32WB_TIM1_TISEL          (STM32WB_TIM1_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General 32-bit Timer TIM2 */
+
+#define STM32WB_TIM2_CR1            (STM32WB_TIM2_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM2_CR2            (STM32WB_TIM2_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM2_SMCR           (STM32WB_TIM2_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM2_DIER           (STM32WB_TIM2_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM2_SR             (STM32WB_TIM2_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM2_EGR            (STM32WB_TIM2_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM2_CCMR1          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM2_CCMR2          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM2_CCER           (STM32WB_TIM2_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM2_CNT            (STM32WB_TIM2_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM2_PSC            (STM32WB_TIM2_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM2_ARR            (STM32WB_TIM2_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM2_CCR1           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM2_CCR2           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM2_CCR3           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM2_CCR4           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM2_DCR            (STM32WB_TIM2_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM2_DMAR           (STM32WB_TIM2_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM2_OR1            (STM32WB_TIM2_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM2_AF1            (STM32WB_TIM2_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM2_TISEL          (STM32WB_TIM2_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General Timers TIM16/TIM17 */
+
+#define STM32WB_TIM16_CR1           (STM32WB_TIM16_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM16_CR2           (STM32WB_TIM16_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM16_DIER          (STM32WB_TIM16_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM16_SR            (STM32WB_TIM16_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM16_EGR           (STM32WB_TIM16_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM16_CCMR1         (STM32WB_TIM16_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM16_CCER          (STM32WB_TIM16_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM16_CNT           (STM32WB_TIM16_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM16_PSC           (STM32WB_TIM16_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM16_ARR           (STM32WB_TIM16_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM16_RCR           (STM32WB_TIM16_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM16_CCR1          (STM32WB_TIM16_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM16_BDTR          (STM32WB_TIM16_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM16_DCR           (STM32WB_TIM16_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM16_DMAR          (STM32WB_TIM16_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM16_OR1           (STM32WB_TIM16_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM16_AF1           (STM32WB_TIM16_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM16_TISEL         (STM32WB_TIM16_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+#define STM32WB_TIM17_CR1           (STM32WB_TIM17_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM17_CR2           (STM32WB_TIM17_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM17_DIER          (STM32WB_TIM17_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM17_SR            (STM32WB_TIM17_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM17_EGR           (STM32WB_TIM17_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM17_CCMR1         (STM32WB_TIM17_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM17_CCER          (STM32WB_TIM17_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM17_CNT           (STM32WB_TIM17_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM17_PSC           (STM32WB_TIM17_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM17_ARR           (STM32WB_TIM17_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM17_RCR           (STM32WB_TIM17_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM17_CCR1          (STM32WB_TIM17_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM17_BDTR          (STM32WB_TIM17_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM17_DCR           (STM32WB_TIM17_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM17_DMAR          (STM32WB_TIM17_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM17_OR1           (STM32WB_TIM17_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM17_AF1           (STM32WB_TIM17_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM17_TISEL         (STM32WB_TIM17_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* Register Value Constants *************************************************/
+
+/* Digital Filter options */
+
+#define STM32WB_DF_NOFILT           (0x0) /* 0000: No filter */
+#define STM32WB_DF_FCKINTn2         (0x1) /* 0001: fSAMPLING = fCK_INT, N=2 */
+#define STM32WB_DF_FCKINTn4         (0x2) /* 0010: fSAMPLING = fCK_INT, N=4 */
+#define STM32WB_DF_FCKINTn8         (0x3) /* 0011: fSAMPLING = fCK_INT, N=8 */
+#define STM32WB_DF_FDTSd2n6         (0x4) /* 0100: fSAMPLING = fDTS/2, N=6 */
+#define STM32WB_DF_FDTSd2n8         (0x5) /* 0101: fSAMPLING = fDTS/2, N=8 */
+#define STM32WB_DF_FDTSd4n6         (0x6) /* 0110: fSAMPLING = fDTS/4, N=6 */
+#define STM32WB_DF_FDTSd4n8         (0x7) /* 0111: fSAMPLING = fDTS/4, N=8 */
+#define STM32WB_DF_FDTSd8n6         (0x8) /* 1000: fSAMPLING = fDTS/8, N=6 */
+#define STM32WB_DF_FDTSd8n8         (0x9) /* 1001: fSAMPLING = fDTS/8, N=8 */
+#define STM32WB_DF_FDTSd16n5        (0xa) /* 1010: fSAMPLING = fDTS/16, N=5 */
+#define STM32WB_DF_FDTSd16n6        (0xb) /* 1011: fSAMPLING = fDTS/16, N=6 */
+#define STM32WB_DF_FDTSd16n8        (0xc) /* 1100: fSAMPLING = fDTS/16, N=8 */
+#define STM32WB_DF_FDTSd32n5        (0xd) /* 1101: fSAMPLING = fDTS/32, N=5 */
+#define STM32WB_DF_FDTSd32n6        (0xe) /* 1110: fSAMPLING = fDTS/32, N=6 */
+#define STM32WB_DF_FDTSd32n8        (0xf) /* 1111: fSAMPLING = fDTS/32, N=8 */
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Control register 1 */
+
+#define TIM1_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM1_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM1_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM1_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM1_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM1_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM1_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM1_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM1_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM1_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM1_CR1_CMS_MASK           (0x3 << TIM1_CR1_CMS_SHIFT)
+#  define TIM1_CR1_CMS_EDGE         (0x0 << TIM1_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM1_CR1_CMS_CNTR1        (0x1 << TIM1_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM1_CR1_CMS_CNTR2        (0x2 << TIM1_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM1_CR1_CMS_CNTR3        (0x3 << TIM1_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM1_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM1_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM1_CR1_CKD_MASK           (0x3 << TIM1_CR1_CKD_SHIFT)
+#  define TIM1_CR1_CKD_TCKINT       (0x0 << TIM1_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM1_CR1_CKD_2TCKINT      (0x1 << TIM1_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM1_CR1_CKD_4TCKINT      (0x2 << TIM1_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM1_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM2_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM2_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM2_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM2_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM2_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM2_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM2_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM2_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM2_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM2_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM2_CR1_CMS_MASK           (0x3 << TIM2_CR1_CMS_SHIFT)
+#  define TIM2_CR1_CMS_EDGE         (0x0 << TIM2_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM2_CR1_CMS_CNTR1        (0x1 << TIM2_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM2_CR1_CMS_CNTR2        (0x2 << TIM2_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM2_CR1_CMS_CNTR3        (0x3 << TIM2_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM2_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM2_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM2_CR1_CKD_MASK           (0x3 << TIM2_CR1_CKD_SHIFT)
+#  define TIM2_CR1_CKD_TCKINT       (0x0 << TIM2_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM2_CR1_CKD_2TCKINT      (0x1 << TIM2_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM2_CR1_CKD_4TCKINT      (0x2 << TIM2_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM2_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM16_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM16_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM16_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM16_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM16_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM16_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM16_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM16_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM16_CR1_CKD_MASK          (0x3 << TIM16_CR1_CKD_SHIFT)
+#  define TIM16_CR1_CKD_TCKINT      (0x0 << TIM16_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM16_CR1_CKD_2TCKINT     (0x1 << TIM16_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM16_CR1_CKD_4TCKINT     (0x2 << TIM16_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM16_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM17_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM17_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM17_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM17_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM17_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM17_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM17_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM17_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM17_CR1_CKD_MASK          (0x3 << TIM17_CR1_CKD_SHIFT)
+#  define TIM17_CR1_CKD_TCKINT      (0x0 << TIM17_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM17_CR1_CKD_2TCKINT     (0x1 << TIM17_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM17_CR1_CKD_4TCKINT     (0x2 << TIM17_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM17_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+/* Control register 2 */
+
+#define TIM1_CR2_CCPC               (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM1_CR2_CCUS               (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM1_CR2_CCUS_COMG        (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM1_CR2_CCUS_COMG_TRGI   (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM1_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM1_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM1_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM1_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM1_CR2_MMS_MASK           (0x7 << TIM1_CR2_MMS_SHIFT)
+#  define TIM1_CR2_MMS_RESET        (0x0 << TIM1_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM1_CR2_MMS_ENABLE       (0x1 << TIM1_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM1_CR2_MMS_UPDATE       (0x2 << TIM1_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM1_CR2_MMS_COMPP        (0x3 << TIM1_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS_OC1REF       (0x4 << TIM1_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM1_CR2_MMS_OC2REF       (0x5 << TIM1_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM1_CR2_MMS_OC3REF       (0x6 << TIM1_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM1_CR2_MMS_OC4REF       (0x7 << TIM1_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM1_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM1_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM1_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM1_CR2_OIS1               (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM1_CR2_OIS1N              (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+#define TIM1_CR2_OIS2               (1 << 10) /* Bit 10: Output Idle state 2 (OC2 output) */
+#define TIM1_CR2_OIS2N              (1 << 11) /* Bit 11: Output Idle state 2 (OC2N output) */
+#define TIM1_CR2_OIS3               (1 << 12) /* Bit 12: Output Idle state 3 (OC3 output) */
+#define TIM1_CR2_OIS3N              (1 << 13) /* Bit 13: Output Idle state 3 (OC3N output) */
+#define TIM1_CR2_OIS4               (1 << 14) /* Bit 14: Output Idle state 4 (OC4 output) */
+#define TIM1_CR2_OIS5               (1 << 16) /* Bit 16: Output Idle state 5 (OC5 output) */
+#define TIM1_CR2_OIS6               (1 << 18) /* Bit 18: Output Idle state 6 (OC6 output) */
+#define TIM1_CR2_MMS2_SHIFT         (20)      /* Bits 20-23: Master Mode Selection 2 */
+#define TIM1_CR2_MMS2_MASK          (0xf << TIM1_CR2_MMS2_SHIFT)
+#  define TIM1_CR2_MMS2_RESET       (0x0 << TIM1_CR2_MMS2_SHIFT) /* 0000: Reset - TIMx_EGR UG bit is TRG9 */
+#  define TIM1_CR2_MMS2_ENABLE      (0x1 << TIM1_CR2_MMS2_SHIFT) /* 0001: Enable - CNT_EN is TRGO2 */
+#  define TIM1_CR2_MMS2_UPDATE      (0x2 << TIM1_CR2_MMS2_SHIFT) /* 0010: Update event is TRGO2 */
+#  define TIM1_CR2_MMS2_COMPP       (0x3 << TIM1_CR2_MMS2_SHIFT) /* 0011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS2_OC1REF      (0x4 << TIM1_CR2_MMS2_SHIFT) /* 0100: Compare OC1REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC2REF      (0x5 << TIM1_CR2_MMS2_SHIFT) /* 0101: Compare OC2REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC3REF      (0x6 << TIM1_CR2_MMS2_SHIFT) /* 0110: Compare OC3REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC4REF      (0x7 << TIM1_CR2_MMS2_SHIFT) /* 0111: Compare OC4REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC5REF      (0x8 << TIM1_CR2_MMS2_SHIFT) /* 1000: Compare OC5REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC6REF      (0x9 << TIM1_CR2_MMS2_SHIFT) /* 1001: Compare OC6REF is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4      (0xa << TIM1_CR2_MMS2_SHIFT) /* 1010: Compare pulse - OC4REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC6      (0xb << TIM1_CR2_MMS2_SHIFT) /* 1011: Compare pulse - OC6REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4R6R   (0xc << TIM1_CR2_MMS2_SHIFT) /* 1100: Compare pulse - OC4REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC4R6F   (0xd << TIM1_CR2_MMS2_SHIFT) /* 1101: Compare pulse - OC4REF rising/OC6REF falling */
+#  define TIM1_CR2_MMS2_CMPOC5R6R   (0xe << TIM1_CR2_MMS2_SHIFT) /* 1110: Compare pulse - OC5REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC5R6F   (0xf << TIM1_CR2_MMS2_SHIFT) /* 1111: Compare pulse - OC5REF rising/OC6REF falling */
+
+#define TIM2_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM2_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM2_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM2_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM2_CR2_MMS_MASK           (0x7 << TIM2_CR2_MMS_SHIFT)
+#  define TIM2_CR2_MMS_RESET        (0x0 << TIM2_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM2_CR2_MMS_ENABLE       (0x1 << TIM2_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM2_CR2_MMS_UPDATE       (0x2 << TIM2_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM2_CR2_MMS_COMPP        (0x3 << TIM2_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM2_CR2_MMS_OC1REF       (0x4 << TIM2_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM2_CR2_MMS_OC2REF       (0x5 << TIM2_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM2_CR2_MMS_OC3REF       (0x6 << TIM2_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM2_CR2_MMS_OC4REF       (0x7 << TIM2_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM2_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM2_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM2_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM16_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM16_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM16_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM16_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM16_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM16_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM16_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM16_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM16_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+#define TIM17_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM17_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM17_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM17_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM17_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM17_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM17_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM17_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM17_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+/* Slave mode control register */
+
+#define TIM1_SMCR_SMS_LO_SHIFT      (0)       /* Bits 0-2: Slave mode selection, bits [2:0] */
+#define TIM1_SMCR_SMS_HI_SHIFT      (16)      /* Bit 16: Slave mode selection, bits [3] */
+#define TIM1_SMCR_SMS_BITS(h,l)     ((h << TIM1_SMCR_SMS_HI_SHIFT) | (l << TIM1_SMCR_SMS_LO_SHIFT))
+#define TIM1_SMCR_SMS_MASK          TIM1_SMCR_SMS_BITS(0x1, 0x7)
+#  define TIM1_SMCR_DISAB           TIM1_SMCR_SMS_BITS(0x0, 0x0) /* 0,000: Slave mode disabled */
+#  define TIM1_SMCR_ENCMD1          TIM1_SMCR_SMS_BITS(0x0, 0x1) /* 0,001: Encoder mode 1 */
+#  define TIM1_SMCR_ENCMD2          TIM1_SMCR_SMS_BITS(0x0, 0x2) /* 0,010: Encoder mode 2 */
+#  define TIM1_SMCR_ENCMD3          TIM1_SMCR_SMS_BITS(0x0, 0x3) /* 0,011: Encoder mode 3 */
+#  define TIM1_SMCR_RESET           TIM1_SMCR_SMS_BITS(0x0, 0x4) /* 0,100: Reset Mode */
+#  define TIM1_SMCR_GATED           TIM1_SMCR_SMS_BITS(0x0, 0x5) /* 0,101: Gated Mode */
+#  define TIM1_SMCR_TRIGGER         TIM1_SMCR_SMS_BITS(0x0, 0x6) /* 0,110: Trigger Mode */
+#  define TIM1_SMCR_EXTCLK1         TIM1_SMCR_SMS_BITS(0x0, 0x7) /* 0,111: External Clock Mode 1 */
+#  define TIM1_SMCR_SMS_COMBINED    TIM1_SMCR_SMS_BITS(0x1, 0x0) /* 1,000: Combined Reset and Trigger mode */
+
+#define TIM1_SMCR_OCCS              (1 << 3)  /* Bit 3: OCREF clear selection */
+#  define TIM1_SMCR_OCCS_CLR        (0 << 3)  /* 0: OCREF clear triggered by CLR input */
+#  define TIM1_SMCR_OCCS_ETRF       (1 << 3)  /* 1: OCREF clear triggered by ETRF */
+
+#define TIM1_SMCR_TS_LO_SHIFT       (4)       /* Bits 4-6: Trigger selection, bits [2:0] */
+#define TIM1_SMCR_TS_HI_SHIFT       (20)      /* Bits 20-21: Trigger selection, bits [1:0] */
+#define TIM1_SMCR_TS_BITS(h,l)      ((h << TIM1_SMCR_TS_HI_SHIFT) | (l << TIM1_SMCR_TS_LO_SHIFT))
+#define TIM1_SMCR_TS_MASK           TIM1_SMCR_TS_BITS(0x3, 0x7)
+#  define TIM1_SMCR_ITR0            TIM1_SMCR_TS_BITS(0x0, 0x0) /* 00,000: Internal trigger 0 (ITR0) */
+#  define TIM1_SMCR_ITR1            TIM1_SMCR_TS_BITS(0x0, 0x1) /* 00,001: Internal trigger 1 (ITR1) */
+#  define TIM1_SMCR_ITR2            TIM1_SMCR_TS_BITS(0x0, 0x2) /* 00,010: Internal trigger 2 (ITR2) */
+#  define TIM1_SMCR_ITR3            TIM1_SMCR_TS_BITS(0x0, 0x3) /* 00,011: Internal trigger 3 (ITR3) */
+#  define TIM1_SMCR_T1FED           TIM1_SMCR_TS_BITS(0x0, 0x4) /* 00,100: TI1 Edge detector (TI1F_ED) */
+#  define TIM1_SMCR_TI1FP1          TIM1_SMCR_TS_BITS(0x0, 0x5) /* 00,101: Filtered timer input 1 (TI1FP1) */
+#  define TIM1_SMCR_T12FP2          TIM1_SMCR_TS_BITS(0x0, 0x6) /* 00,110: Filtered timer input 2 (TI2FP2) */
+#  define TIM1_SMCR_ETRF            TIM1_SMCR_TS_BITS(0x0, 0x7) /* 00,111: External trigger input (ETRF) */
+
+#define TIM1_SMCR_MSM               (1 << 7)  /* Bit 7: Master/slave mode */
+#define TIM1_SMCR_ETF_SHIFT         (8)       /* Bits 8-11: External trigger filter */
+#define TIM1_SMCR_ETF_MASK          (0xf << TIM1_SMCR_ETF_SHIFT)
+#  define TIM1_SMCR_ETF(f)          ((f) << TIM1_SMCR_ETF_SHIFT) /* f = STM32WB_DF_[digital filter option] */
+
+#define TIM1_SMCR_ETPS_SHIFT        (12)      /* Bits 12-13: External trigger prescaler */
+#define TIM1_SMCR_ETPS_MASK         (0x3 << TIM1_SMCR_ETPS_SHIFT)
+#  define TIM1_SMCR_PSCOFF          (0x0 << TIM1_SMCR_ETPS_SHIFT) /* 00: Prescaler OFF */
+#  define TIM1_SMCR_ETRPd2          (0x1 << TIM1_SMCR_ETPS_SHIFT) /* 01: ETRP frequency divided by 2 */
+#  define TIM1_SMCR_ETRPd4          (0x2 << TIM1_SMCR_ETPS_SHIFT) /* 10: ETRP frequency divided by 4 */
+#  define TIM1_SMCR_ETRPd8          (0x3 << TIM1_SMCR_ETPS_SHIFT) /* 11: ETRP frequency divided by 8 */
+
+#define TIM1_SMCR_ECE               (1 << 14) /* Bit 14: External clock enable */
+#define TIM1_SMCR_ETP               (1 << 15) /* Bit 15: External trigger polarity */
+#  define TIM1_SMCR_ETP_HIGH        (0 << 15) /* 0: ETR is non-inverted, active at high level or rising edge */
+#  define TIM1_SMCR_ETP_LOW         (1 << 15) /* 1: ETR is inverted, active at low level or falling edge */
+
+#define TIM2_SMCR_SMS_LO_SHIFT      (0)       /* Bits 0-2: Slave mode selection, bits [2:0] */
+#define TIM2_SMCR_SMS_HI_SHIFT      (16)      /* Bit 16: Slave mode selection, bits [3] */
+#define TIM2_SMCR_SMS_BITS(h,l)     ((h << TIM2_SMCR_SMS_HI_SHIFT) | (l << TIM2_SMCR_SMS_LO_SHIFT))
+#define TIM2_SMCR_SMS_MASK          TIM2_SMCR_SMS_BITS(0x1, 0x7)
+#  define TIM2_SMCR_DISAB           TIM2_SMCR_SMS_BITS(0x0, 0x0) /* 0,000: Slave mode disabled */
+#  define TIM2_SMCR_ENCMD1          TIM2_SMCR_SMS_BITS(0x0, 0x1) /* 0,001: Encoder mode 1 */
+#  define TIM2_SMCR_ENCMD2          TIM2_SMCR_SMS_BITS(0x0, 0x2) /* 0,010: Encoder mode 2 */
+#  define TIM2_SMCR_ENCMD3          TIM2_SMCR_SMS_BITS(0x0, 0x3) /* 0,011: Encoder mode 3 */
+#  define TIM2_SMCR_RESET           TIM2_SMCR_SMS_BITS(0x0, 0x4) /* 0,100: Reset Mode */
+#  define TIM2_SMCR_GATED           TIM2_SMCR_SMS_BITS(0x0, 0x5) /* 0,101: Gated Mode */
+#  define TIM2_SMCR_TRIGGER         TIM2_SMCR_SMS_BITS(0x0, 0x6) /* 0,110: Trigger Mode */
+#  define TIM2_SMCR_EXTCLK1         TIM2_SMCR_SMS_BITS(0x0, 0x7) /* 0,111: External Clock Mode 1 */
+#  define TIM2_SMCR_SMS_COMBINED    TIM2_SMCR_SMS_BITS(0x1, 0x0) /* 1,000: Combined Reset and Trigger mode */
+
+#define TIM2_SMCR_OCCS              (1 << 3)  /* Bit 3: OCREF clear selection */
+#  define TIM2_SMCR_OCCS_CLR        (0 << 3)  /* 0: OCREF clear triggered by CLR input */
+#  define TIM2_SMCR_OCCS_ETRF       (1 << 3)  /* 1: OCREF clear triggered by ETRF */
+
+#define TIM2_SMCR_TS_LO_SHIFT       (4)       /* Bits 4-6: Trigger selection, bits [2:0] */
+#define TIM2_SMCR_TS_HI_SHIFT       (20)      /* Bits 20-21: Trigger selection, bits [1:0] */
+#define TIM2_SMCR_TS_BITS(h,l)      ((h << TIM2_SMCR_TS_HI_SHIFT) | (l << TIM2_SMCR_TS_LO_SHIFT))
+#define TIM2_SMCR_TS_MASK           TIM2_SMCR_TS_BITS(0x3, 0x7)
+#  define TIM2_SMCR_ITR0            TIM2_SMCR_TS_BITS(0x0, 0x0) /* 00,000: Internal trigger 0 (ITR0) */
+#  define TIM2_SMCR_ITR1            TIM2_SMCR_TS_BITS(0x0, 0x1) /* 00,001: Internal trigger 1 (ITR1) */
+#  define TIM2_SMCR_ITR2            TIM2_SMCR_TS_BITS(0x0, 0x2) /* 00,010: Internal trigger 2 (ITR2) */
+#  define TIM2_SMCR_ITR3            TIM2_SMCR_TS_BITS(0x0, 0x3) /* 00,011: Internal trigger 3 (ITR3) */
+#  define TIM2_SMCR_T1FED           TIM2_SMCR_TS_BITS(0x0, 0x4) /* 00,100: TI1 Edge detector (TI1F_ED) */
+#  define TIM2_SMCR_TI1FP1          TIM2_SMCR_TS_BITS(0x0, 0x5) /* 00,101: Filtered timer Input 1 (TI1FP1) */
+#  define TIM2_SMCR_T12FP2          TIM2_SMCR_TS_BITS(0x0, 0x6) /* 00,110: Filtered timer Input 2 (TI2FP2) */
+#  define TIM2_SMCR_ETRF            TIM2_SMCR_TS_BITS(0x0, 0x7) /* 00,111: External trigger input (ETRF) */
+#  define TIM2_SMCR_ITR4            TIM2_SMCR_TS_BITS(0x1, 0x0) /* 01,000: Internal trigger 4 (ITR4) */
+#  define TIM2_SMCR_ITR5            TIM2_SMCR_TS_BITS(0x1, 0x1) /* 01,001: Internal trigger 5 (ITR5) */
+#  define TIM2_SMCR_ITR6            TIM2_SMCR_TS_BITS(0x1, 0x2) /* 01,010: Internal trigger 6 (ITR6) */
+#  define TIM2_SMCR_ITR7            TIM2_SMCR_TS_BITS(0x1, 0x3) /* 01,011: Internal trigger 7 (ITR7) */
+#  define TIM2_SMCR_ITR8            TIM2_SMCR_TS_BITS(0x1, 0x4) /* 01,100: Internal trigger 8 (ITR8) */
+
+#define TIM2_SMCR_MSM               (1 << 7)  /* Bit 7: Master/slave mode */
+#define TIM2_SMCR_ETF_SHIFT         (8)       /* Bits 8-11: External trigger filter */
+#define TIM2_SMCR_ETF_MASK          (0xf << TIM2_SMCR_ETF_SHIFT)
+#  define TIM2_SMCR_ETF(f)          ((f) << TIM2_SMCR_ETF_SHIFT) /* f = STM32WB_DF_[digital filter option] */
+
+#define TIM2_SMCR_ETPS_SHIFT        (12)      /* Bits 12-13: External trigger prescaler */
+#define TIM2_SMCR_ETPS_MASK         (0x3 << TIM2_SMCR_ETPS_SHIFT)
+#  define TIM2_SMCR_PSCOFF          (0x0 << TIM2_SMCR_ETPS_SHIFT) /* 00: Prescaler OFF */
+#  define TIM2_SMCR_ETRPd2          (0x1 << TIM2_SMCR_ETPS_SHIFT) /* 01: ETRP frequency divided by 2 */
+#  define TIM2_SMCR_ETRPd4          (0x2 << TIM2_SMCR_ETPS_SHIFT) /* 10: ETRP frequency divided by 4 */
+#  define TIM2_SMCR_ETRPd8          (0x3 << TIM2_SMCR_ETPS_SHIFT) /* 11: ETRP frequency divided by 8 */
+
+#define TIM2_SMCR_ECE               (1 << 14) /* Bit 14: External clock enable */
+#define TIM2_SMCR_ETP               (1 << 15) /* Bit 15: External trigger polarity */
+#  define TIM2_SMCR_ETP_HIGH        (0 << 15) /* 0: ETR is non-inverted, active at high level or rising edge */
+#  define TIM2_SMCR_ETP_LOW         (1 << 15) /* 1: ETR is inverted, active at low level or falling edge */
+
+/* Timer DMA / Interrupt enable register */
+
+#define TIM1_DIER_UIE               (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM1_DIER_CC1IE             (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM1_DIER_CC2IE             (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt enable */
+#define TIM1_DIER_CC3IE             (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt enable */
+#define TIM1_DIER_CC4IE             (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt enable */
+#define TIM1_DIER_COMIE             (1 << 5)  /* Bit 5: COM interrupt enable */
+#define TIM1_DIER_TIE               (1 << 6)  /* Bit 6: Trigger interrupt enable */
+#define TIM1_DIER_BIE               (1 << 7)  /* Bit 7: Break interrupt enable */
+#define TIM1_DIER_UDE               (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM1_DIER_CC1DE             (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+#define TIM1_DIER_CC2DE             (1 << 10) /* Bit 10: Capture/Compare 2 DMA request enable */
+#define TIM1_DIER_CC3DE             (1 << 11) /* Bit 11: Capture/Compare 3 DMA request enable */
+#define TIM1_DIER_CC4DE             (1 << 12) /* Bit 12: Capture/Compare 4 DMA request enable */
+#define TIM1_DIER_COMDE             (1 << 13) /* Bit 13: COM DMA request enable */
+#define TIM1_DIER_TDE               (1 << 14) /* Bit 14: Trigger DMA request enable */
+
+#define TIM2_DIER_UIE               (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM2_DIER_CC1IE             (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM2_DIER_CC2IE             (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt enable */
+#define TIM2_DIER_CC3IE             (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt enable */
+#define TIM2_DIER_CC4IE             (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt enable */
+#define TIM2_DIER_TIE               (1 << 6)  /* Bit 6: Trigger interrupt enable */
+#define TIM2_DIER_UDE               (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM2_DIER_CC1DE             (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+#define TIM2_DIER_CC2DE             (1 << 10) /* Bit 10: Capture/Compare 2 DMA request enable */
+#define TIM2_DIER_CC3DE             (1 << 11) /* Bit 11: Capture/Compare 3 DMA request enable */
+#define TIM2_DIER_CC4DE             (1 << 12) /* Bit 12: Capture/Compare 4 DMA request enable */
+
+#define TIM16_DIER_UIE              (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM16_DIER_CC1IE            (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM16_DIER_COMIE            (1 << 5)  /* Bit 5: COM interrupt enable */
+#define TIM16_DIER_BIE              (1 << 7)  /* Bit 7: Break interrupt enable */
+#define TIM16_DIER_UDE              (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM16_DIER_CC1DE            (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+
+#define TIM17_DIER_UIE              (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM17_DIER_CC1IE            (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM17_DIER_COMIE            (1 << 5)  /* Bit 5: COM interrupt enable */
+#define TIM17_DIER_BIE              (1 << 7)  /* Bit 7: Break interrupt enable */
+#define TIM17_DIER_UDE              (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM17_DIER_CC1DE            (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+
+/* Status register */
+
+#define TIM1_SR_UIF                 (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM1_SR_CC1IF               (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM1_SR_CC2IF               (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt Flag */
+#define TIM1_SR_CC3IF               (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt Flag */
+#define TIM1_SR_CC4IF               (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt Flag */
+#define TIM1_SR_COMIF               (1 << 5)  /* Bit 5: COM interrupt Flag */
+#define TIM1_SR_TIF                 (1 << 6)  /* Bit 6: Trigger interrupt Flag */
+#define TIM1_SR_BIF                 (1 << 7)  /* Bit 7: Break interrupt Flag */
+#define TIM1_SR_B2IF                (1 << 8)  /* Bit 8: Break 2 interrupt Flag */
+#define TIM1_SR_CC1OF               (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+#define TIM1_SR_CC2OF               (1 << 10) /* Bit 10: Capture/Compare 2 Overcapture Flag */
+#define TIM1_SR_CC3OF               (1 << 11) /* Bit 11: Capture/Compare 3 Overcapture Flag */
+#define TIM1_SR_CC4OF               (1 << 12) /* Bit 12: Capture/Compare 4 Overcapture Flag */
+#define TIM1_SR_SBIF                (1 << 13) /* Bit 13: System break interrupt Flag */
+#define TIM1_SR_CC5IF               (1 << 16) /* Bit 16: Compare 5 interrupt flag */
+#define TIM1_SR_CC6IF               (1 << 17) /* Bit 17: Compare 6 interrupt flag */
+
+#define TIM2_SR_UIF                 (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM2_SR_CC1IF               (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM2_SR_CC2IF               (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt Flag */
+#define TIM2_SR_CC3IF               (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt Flag */
+#define TIM2_SR_CC4IF               (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt Flag */
+#define TIM2_SR_TIF                 (1 << 6)  /* Bit 6: Trigger interrupt Flag */
+#define TIM2_SR_CC1OF               (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+#define TIM2_SR_CC2OF               (1 << 10) /* Bit 10: Capture/Compare 2 Overcapture Flag */
+#define TIM2_SR_CC3OF               (1 << 11) /* Bit 11: Capture/Compare 3 Overcapture Flag */
+#define TIM2_SR_CC4OF               (1 << 12) /* Bit 12: Capture/Compare 4 Overcapture Flag */
+
+#define TIM16_SR_UIF                (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM16_SR_CC1IF              (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM16_SR_COMIF              (1 << 5)  /* Bit 5: COM interrupt Flag */
+#define TIM16_SR_BIF                (1 << 7)  /* Bit 7: Break interrupt Flag */
+#define TIM16_SR_CC1OF              (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+
+#define TIM17_SR_UIF                (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM17_SR_CC1IF              (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM17_SR_COMIF              (1 << 5)  /* Bit 5: COM interrupt Flag */
+#define TIM17_SR_BIF                (1 << 7)  /* Bit 7: Break interrupt Flag */
+#define TIM17_SR_CC1OF              (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+
+/* Event generation register */
+
+#define TIM1_EGR_UG                 (1 << 0)  /* Bit 0: Update Generation */
+#define TIM1_EGR_CC1G               (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM1_EGR_CC2G               (1 << 2)  /* Bit 2: Capture/Compare 2 Generation */
+#define TIM1_EGR_CC3G               (1 << 3)  /* Bit 3: Capture/Compare 3 Generation */
+#define TIM1_EGR_CC4G               (1 << 4)  /* Bit 4: Capture/Compare 4 Generation */
+#define TIM1_EGR_COMG               (1 << 5)  /* Bit 5: Capture/Compare Control Update Generation */
+#define TIM1_EGR_TG                 (1 << 6)  /* Bit 6: Trigger Generation */
+#define TIM1_EGR_BG                 (1 << 7)  /* Bit 7: Break Generation */
+#define TIM1_EGR_B2G                (1 << 8)  /* Bit 8: Break 2 Generation */
+
+#define TIM2_EGR_UG                 (1 << 0)  /* Bit 0: Update Generation */
+#define TIM2_EGR_CC1G               (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM2_EGR_CC2G               (1 << 2)  /* Bit 2: Capture/Compare 2 Generation */
+#define TIM2_EGR_CC3G               (1 << 3)  /* Bit 3: Capture/Compare 3 Generation */
+#define TIM2_EGR_CC4G               (1 << 4)  /* Bit 4: Capture/Compare 4 Generation */
+#define TIM2_EGR_TG                 (1 << 6)  /* Bit 6: Trigger Generation */
+
+#define TIM16_EGR_UG                (1 << 0)  /* Bit 0: Update Generation */
+#define TIM16_EGR_CC1G              (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM16_EGR_COMG              (1 << 5)  /* Bit 5: Capture/Compare Control Update Generation */
+#define TIM16_EGR_BG                (1 << 7)  /* Bit 7: Break Generation */
+
+#define TIM17_EGR_UG                (1 << 0)  /* Bit 0: Update Generation */
+#define TIM17_EGR_CC1G              (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM17_EGR_COMG              (1 << 5)  /* Bit 5: Capture/Compare Control Update Generation */
+#define TIM17_EGR_BG                (1 << 7)  /* Bit 7: Break Generation */
+
+/* Capture/compare mode registers - capture/compare mode selection */
+
+#define TIM1_CCMR1_CC1S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM1_CCMR1_CC1S_MASK        (0x3 << TIM1_CCMR1_CC1S_SHIFT)
+#  define TIM1_CCMR1_CC1S_CCOUT     (0x0 << TIM1_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM1_CCMR1_CC1S_CCIN1     (0x1 << TIM1_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+#  define TIM1_CCMR1_CC1S_CCIN2     (0x2 << TIM1_CCMR1_CC1S_SHIFT) /* 10: CC1 channel input, IC1 is TI2 */
+#  define TIM1_CCMR1_CC1S_CCINTRC   (0x3 << TIM1_CCMR1_CC1S_SHIFT) /* 11: CC1 channel input, IC1 is TRC */
+
+#define TIM1_CCMR1_CC2S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 2 Selection */
+#define TIM1_CCMR1_CC2S_MASK        (0x3 << TIM1_CCMR1_CC2S_SHIFT)
+#  define TIM1_CCMR1_CC2S_CCOUT     (0x0 << TIM1_CCMR1_CC2S_SHIFT) /* 00: CC2 channel output */
+#  define TIM1_CCMR1_CC2S_CCIN1     (0x1 << TIM1_CCMR1_CC2S_SHIFT) /* 01: CC2 channel input, IC2 is TI2 */
+#  define TIM1_CCMR1_CC2S_CCIN2     (0x2 << TIM1_CCMR1_CC2S_SHIFT) /* 10: CC2 channel input, IC2 is TI1 */
+#  define TIM1_CCMR1_CC2S_CCINTRC   (0x3 << TIM1_CCMR1_CC2S_SHIFT) /* 11: CC2 channel input, IC2 is TRC */
+
+#define TIM1_CCMR2_CC3S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 3 Selection */
+#define TIM1_CCMR2_CC3S_MASK        (0x3 << TIM1_CCMR2_CC3S_SHIFT)
+#  define TIM1_CCMR2_CC3S_CCOUT     (0x0 << TIM1_CCMR2_CC3S_SHIFT) /* 00: CC3 channel output */
+#  define TIM1_CCMR2_CC3S_CCIN1     (0x1 << TIM1_CCMR2_CC3S_SHIFT) /* 01: CC3 channel input, IC3 is TI3 */
+#  define TIM1_CCMR2_CC3S_CCIN2     (0x2 << TIM1_CCMR2_CC3S_SHIFT) /* 10: CC3 channel input, IC3 is TI4 */
+#  define TIM1_CCMR2_CC3S_CCINTRC   (0x3 << TIM1_CCMR2_CC3S_SHIFT) /* 11: CC3 channel input, IC3 is TRC */
+
+#define TIM1_CCMR2_CC4S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 4 Selection */
+#define TIM1_CCMR2_CC4S_MASK        (0x3 << TIM1_CCMR2_CC4S_SHIFT)
+#  define TIM1_CCMR2_CC4S_CCOUT     (0x0 << TIM1_CCMR2_CC4S_SHIFT) /* 00: CC4 channel output */
+#  define TIM1_CCMR2_CC4S_CCIN1     (0x1 << TIM1_CCMR2_CC4S_SHIFT) /* 01: CC4 channel input, IC4 is TI4 */
+#  define TIM1_CCMR2_CC4S_CCIN2     (0x2 << TIM1_CCMR2_CC4S_SHIFT) /* 10: CC4 channel input, IC4 is TI3 */
+#  define TIM1_CCMR2_CC4S_CCINTRC   (0x3 << TIM1_CCMR2_CC4S_SHIFT) /* 11: CC4 channel input, IC4 is TRC */
+
+#define TIM2_CCMR1_CC1S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM2_CCMR1_CC1S_MASK        (0x3 << TIM2_CCMR1_CC1S_SHIFT)
+#  define TIM2_CCMR1_CC1S_CCOUT     (0x0 << TIM2_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM2_CCMR1_CC1S_CCIN1     (0x1 << TIM2_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+#  define TIM2_CCMR1_CC1S_CCIN2     (0x2 << TIM2_CCMR1_CC1S_SHIFT) /* 10: CC1 channel input, IC1 is TI2 */
+#  define TIM2_CCMR1_CC1S_CCINTRC   (0x3 << TIM2_CCMR1_CC1S_SHIFT) /* 11: CC1 channel input, IC1 is TRC */
+
+#define TIM2_CCMR1_CC2S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 2 Selection */
+#define TIM2_CCMR1_CC2S_MASK        (0x3 << TIM2_CCMR1_CC2S_SHIFT)
+#  define TIM2_CCMR1_CC2S_CCOUT     (0x0 << TIM2_CCMR1_CC2S_SHIFT) /* 00: CC2 channel output */
+#  define TIM2_CCMR1_CC2S_CCIN1     (0x1 << TIM2_CCMR1_CC2S_SHIFT) /* 01: CC2 channel input, IC2 is TI2 */
+#  define TIM2_CCMR1_CC2S_CCIN2     (0x2 << TIM2_CCMR1_CC2S_SHIFT) /* 10: CC2 channel input, IC2 is TI1 */
+#  define TIM2_CCMR1_CC2S_CCINTRC   (0x3 << TIM2_CCMR1_CC2S_SHIFT) /* 11: CC2 channel input, IC2 is TRC */
+
+#define TIM2_CCMR2_CC3S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 3 Selection */
+#define TIM2_CCMR2_CC3S_MASK        (0x3 << TIM2_CCMR2_CC3S_SHIFT)
+#  define TIM2_CCMR2_CC3S_CCOUT     (0x0 << TIM2_CCMR2_CC3S_SHIFT) /* 00: CC3 channel output */
+#  define TIM2_CCMR2_CC3S_CCIN1     (0x1 << TIM2_CCMR2_CC3S_SHIFT) /* 01: CC3 channel input, IC3 is TI3 */
+#  define TIM2_CCMR2_CC3S_CCIN2     (0x2 << TIM2_CCMR2_CC3S_SHIFT) /* 10: CC3 channel input, IC3 is TI4 */
+#  define TIM2_CCMR2_CC3S_CCINTRC   (0x3 << TIM2_CCMR2_CC3S_SHIFT) /* 11: CC3 channel input, IC3 is TRC */
+
+#define TIM2_CCMR2_CC4S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 4 Selection */
+#define TIM2_CCMR2_CC4S_MASK        (0x3 << TIM2_CCMR2_CC4S_SHIFT)
+#  define TIM2_CCMR2_CC4S_CCOUT     (0x0 << TIM2_CCMR2_CC4S_SHIFT) /* 00: CC4 channel output */
+#  define TIM2_CCMR2_CC4S_CCIN1     (0x1 << TIM2_CCMR2_CC4S_SHIFT) /* 01: CC4 channel input, IC4 is TI4 */
+#  define TIM2_CCMR2_CC4S_CCIN2     (0x2 << TIM2_CCMR2_CC4S_SHIFT) /* 10: CC4 channel input, IC4 is TI3 */
+#  define TIM2_CCMR2_CC4S_CCINTRC   (0x3 << TIM2_CCMR2_CC4S_SHIFT) /* 11: CC4 channel input, IC4 is TRC */
+
+#define TIM16_CCMR1_CC1S_SHIFT      (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM16_CCMR1_CC1S_MASK       (0x3 << TIM16_CCMR1_CC1S_SHIFT)
+#  define TIM16_CCMR1_CC1S_CCOUT    (0x0 << TIM16_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM16_CCMR1_CC1S_CCIN1    (0x1 << TIM16_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+
+#define TIM17_CCMR1_CC1S_SHIFT      (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM17_CCMR1_CC1S_MASK       (0x3 << TIM17_CCMR1_CC1S_SHIFT)
+#  define TIM17_CCMR1_CC1S_CCOUT    (0x0 << TIM17_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM17_CCMR1_CC1S_CCIN1    (0x1 << TIM17_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+
+/* Capture/compare mode registers - Output compare mode */
+
+#define TIM1_CCMR1_OC1FE            (1 << 2)  /* Bit 2: Output Compare 1 Fast enable */
+#define TIM1_CCMR1_OC1PE            (1 << 3)  /* Bit 3: Output Compare 1 Preload enable */
+#define TIM1_CCMR1_OC1M_LO_SHIFT    (4)       /* Bits 4-6: Output Compare 1 Mode, bits [2:0] */
+#define TIM1_CCMR1_OC1M_HI_SHIFT    (16)      /* Bit 16: Output Compare 1 Mode, bits [3] */
+#define TIM1_CCMR1_OC1M_BITS(h,l)   ((h << TIM1_CCMR1_OC1M_HI_SHIFT) | (l << TIM1_CCMR1_OC1M_LO_SHIFT))
+#define TIM1_CCMR1_OC1M_MASK        TIM1_CCMR1_OC1M_BITS(0x1, 0x7)
+#  define TIM1_CCMR1_OC1M_FRZN      TIM1_CCMR1_OC1M_BITS(0x0, 0x0)  /* 0,000: Frozen */
+#  define TIM1_CCMR1_OC1M_CHACT     TIM1_CCMR1_OC1M_BITS(0x0, 0x1)  /* 0,001: Channel 1 active on match */
+#  define TIM1_CCMR1_OC1M_CHINACT   TIM1_CCMR1_OC1M_BITS(0x0, 0x2)  /* 0,010: Channel 1 inactive on match */
+#  define TIM1_CCMR1_OC1M_OCREFTOG  TIM1_CCMR1_OC1M_BITS(0x0, 0x3)  /* 0,011: OC1REF toggle TIM_CNT=TIM_CCR1 */
+#  define TIM1_CCMR1_OC1M_OCREFLO   TIM1_CCMR1_OC1M_BITS(0x0, 0x4)  /* 0,100: OC1REF forced low */
+#  define TIM1_CCMR1_OC1M_OCREFHI   TIM1_CCMR1_OC1M_BITS(0x0, 0x5)  /* 0,101: OC1REF forced high */
+#  define TIM1_CCMR1_OC1M_PWM1      TIM1_CCMR1_OC1M_BITS(0x0, 0x6)  /* 0,110: PWM mode 1 */
+#  define TIM1_CCMR1_OC1M_PWM2      TIM1_CCMR1_OC1M_BITS(0x0, 0x7)  /* 0,111: PWM mode 2 */
+#  define TIM1_CCMR1_OC1M_OPM1      TIM1_CCMR1_OC1M_BITS(0x1, 0x0)  /* 1,000: OPM mode 1 */
+#  define TIM1_CCMR1_OC1M_OPM2      TIM1_CCMR1_OC1M_BITS(0x1, 0x1)  /* 1,001: OPM mode 2 */
+#  define TIM1_CCMR1_OC1M_COMBINED1 TIM1_CCMR1_OC1M_BITS(0x1, 0x4)  /* 1,100: Combined PWM mode 1 */
+#  define TIM1_CCMR1_OC1M_COMBINED2 TIM1_CCMR1_OC1M_BITS(0x1, 0x5)  /* 1,101: Combined PWM mode 2 */
+#  define TIM1_CCMR1_OC1M_ASYMM1    TIM1_CCMR1_OC1M_BITS(0x1, 0x6)  /* 1,110: Asymmetric PWM mode 1 */
+#  define TIM1_CCMR1_OC1M_ASYMM2    TIM1_CCMR1_OC1M_BITS(0x1, 0x7)  /* 1,111: Asymmetric PWM mode 2 */
+
+#define TIM1_CCMR1_OC1CE            (1 << 7)  /* Bit 7: Output Compare 1 Clear Enable */
+
+#define TIM1_CCMR1_OC2FE            (1 << 10) /* Bit 10: Output Compare 2 Fast enable */
+#define TIM1_CCMR1_OC2PE            (1 << 11) /* Bit 11: Output Compare 2 Preload enable */
+#define TIM1_CCMR1_OC2M_LO_SHIFT    (12)      /* Bits 12-14: Output Compare 2 Mode, bits [2:0] */
+#define TIM1_CCMR1_OC2M_HI_SHIFT    (24)      /* Bit 24: Output Compare 2 Mode, bits [3] */
+#define TIM1_CCMR1_OC2M_BITS(h,l)   ((h << TIM1_CCMR1_OC2M_HI_SHIFT) | (l << TIM1_CCMR1_OC2M_LO_SHIFT))
+#define TIM1_CCMR1_OC2M_MASK        TIM1_CCMR1_OC2M_BITS(0x1, 0x7)
+#  define TIM1_CCMR1_OC2M_FRZN      TIM1_CCMR1_OC2M_BITS(0x0, 0x0)  /* 0,000: Frozen */
+#  define TIM1_CCMR1_OC2M_CHACT     TIM1_CCMR1_OC2M_BITS(0x0, 0x1)  /* 0,001: Channel 2 active on match */
+#  define TIM1_CCMR1_OC2M_CHINACT   TIM1_CCMR1_OC2M_BITS(0x0, 0x2)  /* 0,010: Channel 2 inactive on match */
+#  define TIM1_CCMR1_OC2M_OCREFTOG  TIM1_CCMR1_OC2M_BITS(0x0, 0x3)  /* 0,011: OC2REF toggle TIM_CNT=TIM_CCR2 */
+#  define TIM1_CCMR1_OC2M_OCREFLO   TIM1_CCMR1_OC2M_BITS(0x0, 0x4)  /* 0,100: OC2REF forced low */
+#  define TIM1_CCMR1_OC2M_OCREFHI   TIM1_CCMR1_OC2M_BITS(0x0, 0x5)  /* 0,101: OC2REF forced high */
+#  define TIM1_CCMR1_OC2M_PWM1      TIM1_CCMR1_OC2M_BITS(0x0, 0x6)  /* 0,110: PWM mode 1 */
+#  define TIM1_CCMR1_OC2M_PWM2      TIM1_CCMR1_OC2M_BITS(0x0, 0x7)  /* 0,111: PWM mode 2 */
+#  define TIM1_CCMR1_OC2M_OPM1      TIM1_CCMR1_OC2M_BITS(0x1, 0x0)  /* 1,000: OPM mode 1 */
+#  define TIM1_CCMR1_OC2M_OPM2      TIM1_CCMR1_OC2M_BITS(0x1, 0x1)  /* 1,001: OPM mode 2 */
+#  define TIM1_CCMR1_OC2M_COMBINED1 TIM1_CCMR1_OC2M_BITS(0x1, 0x4)  /* 1,100: Combined PWM mode 1 */
+#  define TIM1_CCMR1_OC2M_COMBINED2 TIM1_CCMR1_OC2M_BITS(0x1, 0x5)  /* 1,101: Combined PWM mode 2 */
+#  define TIM1_CCMR1_OC2M_ASYMM1    TIM1_CCMR1_OC2M_BITS(0x1, 0x6)  /* 1,110: Asymmetric PWM mode 1 */
+#  define TIM1_CCMR1_OC2M_ASYMM2    TIM1_CCMR1_OC2M_BITS(0x1, 0x7)  /* 1,111: Asymmetric PWM mode 2 */
+
+#define TIM1_CCMR1_OC2CE            (1 << 15) /* Bit 15: Output Compare 2 Clear Enable */
+
+#define TIM1_CCMR2_OC3FE            (1 << 2)  /* Bit 2: Output Compare 3 Fast enable */
+#define TIM1_CCMR2_OC3PE            (1 << 3)  /* Bit 3: Output Compare 3 Preload enable */
+#define TIM1_CCMR2_OC3M_LO_SHIFT    (4)       /* Bits 4-6: Output Compare 3 Mode, bits [2:0] */
+#define TIM1_CCMR2_OC3M_HI_SHIFT    (16)      /* Bit 16: Output Compare 3 Mode, bits [3] */
+#define TIM1_CCMR2_OC3M_BITS(h,l)   ((h << TIM1_CCMR2_OC3M_HI_SHIFT) | (l << TIM1_CCMR2_OC3M_LO_SHIFT))

Review Comment:
   ditto



##########
arch/arm/include/stm32wb/stm32wb_irq.h:
##########
@@ -0,0 +1,220 @@
+/****************************************************************************
+ * arch/arm/include/stm32wb/stm32wb_irq.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* This file should never be included directly but, rather, only indirectly
+ * through arch/irq.h
+ */
+
+#ifndef __ARCH_ARM_INCLUDE_STM32WB_STM32WB55XX_IRQ_H
+#define __ARCH_ARM_INCLUDE_STM32WB_STM32WB55XX_IRQ_H

Review Comment:
   ```suggestion
   #ifndef __ARCH_ARM_INCLUDE_STM32WB_STM32WB_IRQ_H
   #define __ARCH_ARM_INCLUDE_STM32WB_STM32WB_IRQ_H
   ```



##########
arch/arm/src/stm32wb/stm32wb_irq.c:
##########
@@ -0,0 +1,545 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_irq.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <debug.h>
+#include <assert.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include "arm_internal.h"
+#include "nvic.h"
+#ifdef CONFIG_ARCH_RAMVECTORS
+#  include "ram_vectors.h"
+#endif
+#include "stm32wb_rtc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Get a 32-bit version of the default priority */
+
+#define DEFPRIORITY32 \
+  (NVIC_SYSH_PRIORITY_DEFAULT << 24 | \
+   NVIC_SYSH_PRIORITY_DEFAULT << 16 | \
+   NVIC_SYSH_PRIORITY_DEFAULT << 8  | \
+   NVIC_SYSH_PRIORITY_DEFAULT)

Review Comment:
   minor
   ```suggestion
     ((NVIC_SYSH_PRIORITY_DEFAULT << 24) | \
      (NVIC_SYSH_PRIORITY_DEFAULT << 16) | \
      (NVIC_SYSH_PRIORITY_DEFAULT << 8)  | \
      NVIC_SYSH_PRIORITY_DEFAULT)
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_syscfg.h:
##########
@@ -0,0 +1,227 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_syscfg.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_SYSCFG_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_SYSCFG_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "chip.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_SYSCFG_MEMRMP_OFFSET    0x0000 /* SYSCFG memory remap register */
+#define STM32WB_SYSCFG_CFGR1_OFFSET     0x0004 /* SYSCFG configuration register 1 */
+
+#define STM32WB_SYSCFG_EXTICR_OFFSET(p) (0x0008 + ((p) & 0x0c)) /* Pin p = 0..15 */
+
+#define STM32WB_SYSCFG_EXTICR1_OFFSET   0x0008 /* SYSCFG external interrupt configuration register 1 */
+#define STM32WB_SYSCFG_EXTICR2_OFFSET   0x000c /* SYSCFG external interrupt configuration register 2 */
+#define STM32WB_SYSCFG_EXTICR3_OFFSET   0x0010 /* SYSCFG external interrupt configuration register 3 */
+#define STM32WB_SYSCFG_EXTICR4_OFFSET   0x0014 /* SYSCFG external interrupt configuration register 4 */
+
+#define STM32WB_SYSCFG_SCSR_OFFSET      0x0018 /* SYSCFG SRAM2 control and status register */
+#define STM32WB_SYSCFG_CFGR2_OFFSET     0x001c /* SYSCFG configuration register 2 */
+#define STM32WB_SYSCFG_SWPR1_OFFSET     0x0020 /* SYSCFG SRAM2 write protection register 1 */
+#define STM32WB_SYSCFG_SKR_OFFSET       0x0024 /* SYSCFG SRAM2 key register */
+#define STM32WB_SYSCFG_SWPR2_OFFSET     0x0028 /* SYSCFG SRAM2 write protection register 2 */
+
+#define STM32WB_SYSCFG_IMR1_OFFSET      0x0100 /* SYSCFG Interrupt mask register 1 */
+#define STM32WB_SYSCFG_IMR2_OFFSET      0x0104 /* SYSCFG Interrupt mask register 2 */
+#define STM32WB_SYSCFG_C2IMR1_OFFSET    0x0108 /* SYSCFG CPU2 Interrupt mask register 1 */
+#define STM32WB_SYSCFG_C2IMR2_OFFSET    0x010c /* SYSCFG CPU2 Interrupt mask register 2 */
+#define STM32WB_SYSCFG_SIPCR_OFFSET     0x0110 /* SYSCFG Secure IP control register */
+
+/* Register Addresses *******************************************************/
+
+#define STM32WB_SYSCFG_MEMRMP           (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_MEMRMP_OFFSET)
+#define STM32WB_SYSCFG_CFGR1            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_CFGR1_OFFSET)
+#define STM32WB_SYSCFG_EXTICR(p)        (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR_OFFSET(p))
+#define STM32WB_SYSCFG_EXTICR1          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR1_OFFSET)
+#define STM32WB_SYSCFG_EXTICR2          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR2_OFFSET)
+#define STM32WB_SYSCFG_EXTICR3          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR3_OFFSET)
+#define STM32WB_SYSCFG_EXTICR4          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR4_OFFSET)
+#define STM32WB_SYSCFG_SCSR             (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SCSR_OFFSET)
+#define STM32WB_SYSCFG_CFGR2            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_CFGR2_OFFSET)
+#define STM32WB_SYSCFG_SWPR1            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SWPR1_OFFSET)
+#define STM32WB_SYSCFG_SKR              (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SKR_OFFSET)
+#define STM32WB_SYSCFG_SWPR2            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SWPR2_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* SYSCFG memory remap register */
+
+#define SYSCFG_MEMRMP_SHIFT           (0)       /* Bits 2-0: Memory mapping selection */
+#define SYSCFG_MEMRMP_MASK            (0x7 << SYSCFG_MEMRMP_SHIFT)
+#  define SYSCFG_MEMRMP_FLASH         (0x0 << SYSCFG_MEMRMP_SHIFT) /* 000: Main Flash memory mapped at 0x00000000 */
+#  define SYSCFG_MEMRMP_SYSTEM        (0x1 << SYSCFG_MEMRMP_SHIFT) /* 001: System Flash memory mapped at 0x00000000 */
+#  define SYSCFG_MEMRMP_SRAM          (0x3 << SYSCFG_MEMRMP_SHIFT) /* 011: SRAM1 mapped at 0x00000000 */
+#  define SYSCFG_MEMRMP_QSPI          (0x6 << SYSCFG_MEMRMP_SHIFT) /* 110: QSPI memory mapped at 0x00000000 */
+
+/* SYSCFG configuration register 1 */
+
+#define SYSCFG_CFGR1_BOOSTEN          (1 << 8)  /* Bit  8: I/O analog switch voltage booster enable */
+#define SYSCFG_CFGR1_I2C_PB6_FMP      (1 << 16) /* Bit 16: Fast-mode Plus (Fm+) driving capability activation on PB6 */
+#define SYSCFG_CFGR1_I2C_PB7_FMP      (1 << 17) /* Bit 17: Fast-mode Plus (Fm+) driving capability activation on PB7 */
+#define SYSCFG_CFGR1_I2C_PB8_FMP      (1 << 18) /* Bit 18: Fast-mode Plus (Fm+) driving capability activation on PB8 */
+#define SYSCFG_CFGR1_I2C_PB9_FMP      (1 << 19) /* Bit 19: Fast-mode Plus (Fm+) driving capability activation on PB9 */
+#define SYSCFG_CFGR1_I2C1_FMP         (1 << 20) /* Bit 20: I2C1 Fast-mode Plus (Fm+) driving capability activation */
+#define SYSCFG_CFGR1_I2C3_FMP         (1 << 22) /* Bit 22: I2C3 Fast-mode Plus (Fm+) driving capability activation */
+#define SYSCFG_CFGR1_FPU_IE0          (1 << 26) /* Bit 26: FPU Invalid operation interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE1          (1 << 27) /* Bit 27: FPU Divide-by-zero interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE2          (1 << 28) /* Bit 28: FPU Underflow interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE3          (1 << 29) /* Bit 29: FPU Overflow interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE4          (1 << 30) /* Bit 30: FPU Input denormal interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE5          (1 << 31) /* Bit 31: FPU Inexact interrupt enable */
+
+/* SYSCFG external interrupt configuration register 1-4 */
+
+#define SYSCFG_EXTICR_PORTA           (0x0)     /* 000: PA[x] pin */
+#define SYSCFG_EXTICR_PORTB           (0x1)     /* 001: PB[x] pin */
+#define SYSCFG_EXTICR_PORTC           (0x2)     /* 010: PC[x] pin */
+#define SYSCFG_EXTICR_PORTD           (0x3)     /* 011: PD[x] pin */
+#define SYSCFG_EXTICR_PORTE           (0x4)     /* 100: PE[x] pin */
+#define SYSCFG_EXTICR_PORTH           (0x7)     /* 111: PH[x] pin */
+
+#define SYSCFG_EXTICR_PORT_MASK       (0x7)
+#define SYSCFG_EXTICR_EXTI_SHIFT(g)   (((g) & 0x3) << 2)
+#define SYSCFG_EXTICR_EXTI_MASK(g)    (SYSCFG_EXTICR_PORT_MASK << (SYSCFG_EXTICR_EXTI_SHIFT(g)))
+
+#define SYSCFG_EXTICR1_EXTI0_SHIFT    (0)       /* Bits 0-2: EXTI 0 configuration */
+#define SYSCFG_EXTICR1_EXTI0_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI0_SHIFT)
+#define SYSCFG_EXTICR1_EXTI1_SHIFT    (4)       /* Bits 4-6: EXTI 1 configuration */
+#define SYSCFG_EXTICR1_EXTI1_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI1_SHIFT)
+#define SYSCFG_EXTICR1_EXTI2_SHIFT    (8)       /* Bits 8-10: EXTI 2 configuration */
+#define SYSCFG_EXTICR1_EXTI2_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI2_SHIFT)
+#define SYSCFG_EXTICR1_EXTI3_SHIFT    (12)      /* Bits 12-14: EXTI 3 configuration */
+#define SYSCFG_EXTICR1_EXTI3_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI3_SHIFT)
+
+#define SYSCFG_EXTICR2_EXTI4_SHIFT    (0)       /* Bits 0-2: EXTI 4 configuration */
+#define SYSCFG_EXTICR2_EXTI4_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI4_SHIFT)
+#define SYSCFG_EXTICR2_EXTI5_SHIFT    (4)       /* Bits 4-6: EXTI 5 configuration */
+#define SYSCFG_EXTICR2_EXTI5_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI5_SHIFT)
+#define SYSCFG_EXTICR2_EXTI6_SHIFT    (8)       /* Bits 8-10: EXTI 6 configuration */
+#define SYSCFG_EXTICR2_EXTI6_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI6_SHIFT)
+#define SYSCFG_EXTICR2_EXTI7_SHIFT    (12)      /* Bits 12-14: EXTI 7 configuration */
+#define SYSCFG_EXTICR2_EXTI7_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI7_SHIFT)
+
+#define SYSCFG_EXTICR3_EXTI8_SHIFT    (0)       /* Bits 0-2: EXTI 8 configuration */
+#define SYSCFG_EXTICR3_EXTI8_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI8_SHIFT)
+#define SYSCFG_EXTICR3_EXTI9_SHIFT    (4)       /* Bits 4-6: EXTI 9 configuration */
+#define SYSCFG_EXTICR3_EXTI9_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI9_SHIFT)
+#define SYSCFG_EXTICR3_EXTI10_SHIFT   (8)       /* Bits 8-10: EXTI 10 configuration */
+#define SYSCFG_EXTICR3_EXTI10_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI10_SHIFT)
+#define SYSCFG_EXTICR3_EXTI11_SHIFT   (12)      /* Bits 12-14: EXTI 11 configuration */
+#define SYSCFG_EXTICR3_EXTI11_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI11_SHIFT)
+
+#define SYSCFG_EXTICR4_EXTI12_SHIFT   (0)       /* Bits 0-2: EXTI 12 configuration */
+#define SYSCFG_EXTICR4_EXTI12_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI12_SHIFT)
+#define SYSCFG_EXTICR4_EXTI13_SHIFT   (4)       /* Bits 4-6: EXTI 13 configuration */
+#define SYSCFG_EXTICR4_EXTI13_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI13_SHIFT)
+#define SYSCFG_EXTICR4_EXTI14_SHIFT   (8)       /* Bits 8-10: EXTI 14 configuration */
+#define SYSCFG_EXTICR4_EXTI14_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI14_SHIFT)
+#define SYSCFG_EXTICR4_EXTI15_SHIFT   (12)      /* Bits 12-14: EXTI 15 configuration */
+#define SYSCFG_EXTICR4_EXTI15_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI15_SHIFT)
+
+/* SYSCFG SRAM2 control and status register */
+
+#define SYSCFG_SCSR_SRAM2ER           (1 << 0)  /* Bit  0: SRAM2 and PKA RAM Erase */
+#define SYSCFG_SCSR_SRAM2BSY          (1 << 1)  /* Bit  1: SRAM2 and PKA RAM busy by erase operation */
+#define SYSCFG_SCSR_C2RFD             (1 << 31) /* Bit 31: CPU2 SRAM fetch disable */
+
+/* SYSCFG configuration register 2 */
+
+#define SYSCFG_CFGR2_CLL              (1 << 0)  /* Bit 0: LOCKUP (Hardfault) output enable */
+#define SYSCFG_CFGR2_SPL              (1 << 1)  /* Bit 1: SRAM2 parity lock enable  */
+#define SYSCFG_CFGR2_PVDL             (1 << 2)  /* Bit 2: PVD lock enable */
+#define SYSCFG_CFGR2_ECCL             (1 << 3)  /* Bit 3: ECC lock enable */
+#define SYSCFG_CFGR2_SPF              (1 << 8)  /* Bit 8: SRAM2 parity error flag */
+
+/* SYSCFG SRAM2 write protection register 1 */
+
+#define SYSCFG_SWPR1_PWP(x)           (1 << (x)) /* Bits 0-31: SRAM2 1Kb page x = 0..31 write protection */
+
+/* SYSCFG SRAM2 key register */
+
+#define SYSCFG_SKR_SHIFT              (0)
+#define SYSCFG_SKR_MASK               (0xff << SYSCFG_SKR_SHIFT)
+#define SYSCFG_SKR_KEY1               0xca
+#define SYSCFG_SKR_KEY2               0x53
+
+/* SYSCFG SRAM2 write protection register 2 */
+
+#define SYSCFG_SWPR2_PWP(x)           (1 << ((x)-32)) /* Bits 0-31: SRAM2 1Kb page x = 32..63 write protection */
+
+/* SYSCFG Interrupt mask register 1 */
+
+#define SYSCFG_IMR1_EXTIIM(n)         (1 << ((n)+16)) /* EXTI[n] interrupt mask, n = 5..15 */
+
+#define SYSCFG_IMR1_TIM1IM            (1 << 13) /* Bit 13: TIM1 interrupt mask */
+#define SYSCFG_IMR1_TIM16IM           (1 << 14) /* Bit 14: TIM16 interrupt mask */
+#define SYSCFG_IMR1_TIM17IM           (1 << 15) /* Bit 15: TIM17 interrupt mask */
+
+/* SYSCFG Interrupt mask register 2 */
+
+#define SYSCFG_IMR2_PVM1IM            (1 << 16) /* Bit 16: PVM1 interrupt mask */
+#define SYSCFG_IMR2_PVM3IM            (1 << 18) /* Bit 18: PVM3 interrupt mask */
+#define SYSCFG_IMR2_PVDIM             (1 << 20) /* Bit 20: PVD interrupt mask */
+
+/* SYSCFG CPU2 Interrupt mask register 1 */
+
+#define SYSCFG_C2IMR1_RTCSTLSECSSIM   (1 << 0)  /* Bit 0: CPU2 RTC, STAMP, TAMP, LSECSS interrupt mask */
+#define SYSCFG_C2IMR1_RTCWKUPIM       (1 << 3)  /* Bit 3: CPU2 RTC Wakeup interrupt mask */
+#define SYSCFG_C2IMR1_RTCALRMIM       (1 << 4)  /* Bit 4: CPU2 RTC Alarm interrupt mask */
+#define SYSCFG_C2IMR1_RCCIM           (1 << 5)  /* Bit 5: CPU2 RCC interrupt mask */
+#define SYSCFG_C2IMR1_FLASHIM         (1 << 6)  /* Bit 6: CPU2 FLASH interrupt mask */
+#define SYSCFG_C2IMR1_PKAIM           (1 << 8)  /* Bit 8: CPU2 PKA interrupt mask */
+#define SYSCFG_C2IMR1_RNGIM           (1 << 9)  /* Bit 9: CPU2 RNG interrupt mask */
+#define SYSCFG_C2IMR1_AES1IM          (1 << 10) /* Bit 10: CPU2 AES1 interrupt mask */
+#define SYSCFG_C2IMR1_COMPIM          (1 << 11) /* Bit 11: CPU2 COMP interrupt mask */
+#define SYSCFG_C2IMR1_ADCIM           (1 << 12) /* Bit 12: CPU2 ADC interrupt mask */
+
+#define SYSCFG_C2IMR1_EXTIIM(n)       (1 << ((n)+16)) /* CPU2 EXTI[n] interrupt mask, n = 0..15 */

Review Comment:
   ```suggestion
   #define SYSCFG_C2IMR1_EXTIIM(n)       (1 << ((n) + 16)) /* CPU2 EXTI[n] interrupt mask, n = 0..15 */
   ```



##########
arch/arm/src/stm32wb/stm32wb_oneshot.c:
##########
@@ -0,0 +1,460 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_oneshot.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sched.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+
+#include "stm32wb_oneshot.h"
+
+#ifdef CONFIG_STM32WB_ONESHOT
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int stm32wb_oneshot_handler(int irq, void *context, void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct stm32wb_oneshot_s *g_oneshot[CONFIG_STM32WB_ONESHOT_MAXTIMERS];
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_handler
+ *
+ * Description:
+ *   Common timer interrupt callback.  When any oneshot timer interrupt
+ *   expires, this function will be called.  It will forward the call to
+ *   the next level up.
+ *
+ * Input Parameters:
+ *   oneshot - The state associated with the expired timer
+ *
+ * Returned Value:
+ *   Always returns OK
+ *
+ ****************************************************************************/
+
+static int stm32wb_oneshot_handler(int irq, void *context, void *arg)
+{
+  struct stm32wb_oneshot_s *oneshot = (struct stm32wb_oneshot_s *) arg;
+  oneshot_handler_t oneshot_handler;
+  void *oneshot_arg;
+
+  tmrinfo("Expired...\n");
+  DEBUGASSERT(oneshot != NULL && oneshot->handler);
+
+  /* The clock was stopped, but not disabled when the RC match occurred.
+   * Disable the TC now and disable any further interrupts.
+   */
+
+  STM32WB_TIM_SETISR(oneshot->tch, NULL, NULL, 0);
+  STM32WB_TIM_DISABLEINT(oneshot->tch, GTIM_DIER_UIE);
+  STM32WB_TIM_SETMODE(oneshot->tch, STM32WB_TIM_MODE_DISABLED);
+  STM32WB_TIM_ACKINT(oneshot->tch, GTIM_SR_UIF);
+
+  /* The timer is no longer running */
+
+  oneshot->running = false;
+
+  /* Forward the event, clearing out any vestiges */
+
+  oneshot_handler  = (oneshot_handler_t)oneshot->handler;
+  oneshot->handler = NULL;
+  oneshot_arg      = (void *)oneshot->arg;
+  oneshot->arg     = NULL;
+
+  oneshot_handler(oneshot_arg);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_allocate_handler
+ *
+ * Description:
+ *   Allocate a timer callback handler for the oneshot instance.
+ *
+ * Input Parameters:
+ *   oneshot - The state instance the new oneshot timer
+ *
+ * Returned Value:
+ *   Returns zero (OK) on success.  This can only fail if the number of
+ *   timers exceeds CONFIG_STM32WB_ONESHOT_MAXTIMERS.
+ *
+ ****************************************************************************/
+
+static inline int stm32wb_allocate_handler(struct stm32wb_oneshot_s *oneshot)
+{
+#if CONFIG_STM32WB_ONESHOT_MAXTIMERS > 1
+  int ret = -EBUSY;
+  int i;
+
+  /* Search for an unused handler */
+
+  sched_lock();
+  for (i = 0; i < CONFIG_STM32WB_ONESHOT_MAXTIMERS; i++)
+    {
+      /* Is this handler available? */
+
+      if (g_oneshot[i] == NULL)
+        {
+          /* Yes... assign it to this oneshot */
+
+          g_oneshot[i]   = oneshot;
+          oneshot->cbndx = i;
+          ret            = OK;
+          break;
+        }
+    }
+
+  sched_unlock();
+  return ret;
+
+#else
+  if (g_oneshot[0] == NULL)
+    {
+      g_oneshot[0] = oneshot;
+      return OK;
+    }
+
+  return -EBUSY;
+#endif
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_initialize
+ *
+ * Description:
+ *   Initialize the oneshot timer wrapper
+ *
+ * Input Parameters:
+ *   oneshot    Caller allocated instance of the oneshot state structure
+ *   chan       Timer counter channel to be used.
+ *   resolution The required resolution of the timer in units of
+ *              microseconds.  NOTE that the range is restricted to the
+ *              range of uint16_t (excluding zero).
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned
+ *   on failure.
+ *
+ ****************************************************************************/
+
+int stm32wb_oneshot_initialize(struct stm32wb_oneshot_s *oneshot,
+                               int chan, uint16_t resolution)
+{
+  uint32_t frequency;
+
+  tmrinfo("chan=%d resolution=%d usec\n", chan, resolution);
+  DEBUGASSERT(oneshot && resolution > 0);
+
+  /* Get the TC frequency the corresponds to the requested resolution */
+
+  frequency = USEC_PER_SEC / (uint32_t)resolution;
+  oneshot->frequency = frequency;
+
+  oneshot->tch = stm32wb_tim_init(chan);
+  if (!oneshot->tch)
+    {
+      tmrerr("ERROR: Failed to allocate TIM%d\n", chan);
+      return -EBUSY;
+    }
+
+  STM32WB_TIM_SETCLOCK(oneshot->tch, frequency);
+
+  /* Initialize the remaining fields in the state structure. */
+
+  oneshot->chan       = chan;
+  oneshot->running    = false;
+  oneshot->handler    = NULL;
+  oneshot->arg        = NULL;
+
+  /* Assign a callback handler to the oneshot */
+
+  return stm32wb_allocate_handler(oneshot);
+}
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_max_delay
+ *
+ * Description:
+ *   Determine the maximum delay of the one-shot timer (in microseconds)
+ *
+ ****************************************************************************/
+
+int stm32wb_oneshot_max_delay(struct stm32wb_oneshot_s *oneshot,
+                              uint64_t *usec)
+{
+  DEBUGASSERT(oneshot != NULL && usec != NULL);
+
+  *usec = (uint64_t)(UINT32_MAX / oneshot->frequency) *
+          (uint64_t)USEC_PER_SEC;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_start
+ *
+ * Description:
+ *   Start the oneshot timer
+ *
+ * Input Parameters:
+ *   oneshot Caller allocated instance of the oneshot state structure.  This
+ *           structure must have been previously initialized via a call to
+ *           stm32wb_oneshot_initialize();
+ *   handler The function to call when when the oneshot timer expires.
+ *   arg     An opaque argument that will accompany the callback.
+ *   ts      Provides the duration of the one shot timer.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned
+ *   on failure.
+ *
+ ****************************************************************************/
+
+int stm32wb_oneshot_start(struct stm32wb_oneshot_s *oneshot,
+                          oneshot_handler_t handler, void *arg,
+                          const struct timespec *ts)
+{
+  uint64_t usec;
+  uint64_t period;
+  irqstate_t flags;
+
+  tmrinfo("handler=%p arg=%p, ts=(%lu, %lu)\n",
+         handler, arg, (unsigned long)ts->tv_sec,
+         (unsigned long)ts->tv_nsec);
+  DEBUGASSERT(oneshot && handler && ts);
+  DEBUGASSERT(oneshot->tch);
+
+  /* Was the oneshot already running? */
+
+  flags = enter_critical_section();
+  if (oneshot->running)
+    {
+      /* Yes.. then cancel it */
+
+      tmrinfo("Already running... cancelling\n");
+      stm32wb_oneshot_cancel(oneshot, NULL);
+    }
+
+  /* Save the new handler and its argument */
+
+  oneshot->handler = handler;
+  oneshot->arg     = arg;
+
+  /* Express the delay in microseconds */
+
+  usec = (uint64_t)ts->tv_sec * USEC_PER_SEC +
+         (uint64_t)(ts->tv_nsec / NSEC_PER_USEC);
+
+  /* Get the timer counter frequency and determine the number of counts need
+   * to achieve the requested delay.
+   *
+   *   frequency = ticks / second
+   *   ticks     = seconds * frequency
+   *             = (usecs * frequency) / USEC_PER_SEC;
+   */
+
+  period = (usec * (uint64_t)oneshot->frequency) / USEC_PER_SEC;
+
+  tmrinfo("usec=%llu period=%08llx\n", usec, period);
+  DEBUGASSERT(period > 0 && period <= UINT32_MAX);
+
+  /* Set up to receive the callback when the interrupt occurs */
+
+  STM32WB_TIM_SETISR(oneshot->tch, stm32wb_oneshot_handler, oneshot, 0);
+
+  /* Set timer period */
+
+  oneshot->period = (uint32_t)period;
+  STM32WB_TIM_SETPERIOD(oneshot->tch, (uint32_t)period);
+
+  /* Start the counter */
+
+  STM32WB_TIM_SETMODE(oneshot->tch, STM32WB_TIM_MODE_PULSE);
+
+  STM32WB_TIM_ACKINT(oneshot->tch, GTIM_SR_UIF);
+  STM32WB_TIM_ENABLEINT(oneshot->tch, GTIM_DIER_UIE);
+
+  /* Enable interrupts.  We should get the callback when the interrupt
+   * occurs.
+   */
+
+  oneshot->running = true;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_cancel
+ *
+ * Description:
+ *   Cancel the oneshot timer and return the time remaining on the timer.
+ *
+ *   NOTE: This function may execute at a high rate with no timer running (as
+ *   when pre-emption is enabled and disabled).
+ *
+ * Input Parameters:
+ *   oneshot Caller allocated instance of the oneshot state structure.  This
+ *           structure must have been previously initialized via a call to
+ *           stm32wb_oneshot_initialize();
+ *   ts      The location in which to return the time remaining on the
+ *           oneshot timer.  A time of zero is returned if the timer is
+ *           not running.  ts may be zero in which case the time remaining
+ *           is not returned.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success.  A call to up_timer_cancel() when
+ *   the timer is not active should also return success; a negated errno
+ *   value is returned on any failure.
+ *
+ ****************************************************************************/
+
+int stm32wb_oneshot_cancel(struct stm32wb_oneshot_s *oneshot,
+                           struct timespec *ts)
+{
+  irqstate_t flags;
+  uint64_t usec;
+  uint64_t sec;
+  uint64_t nsec;
+  uint32_t count;
+  uint32_t period;
+
+  /* Was the timer running? */
+
+  flags = enter_critical_section();
+  if (!oneshot->running)
+    {
+      /* No.. Just return zero timer remaining and successful cancellation.
+       * This function may execute at a high rate with no timer running
+       * (as when pre-emption is enabled and disabled).
+       */
+
+      ts->tv_sec  = 0;
+      ts->tv_nsec = 0;
+      leave_critical_section(flags);
+      return OK;
+    }
+
+  /* Yes.. Get the timer counter and period registers and stop the counter.
+   * If the counter expires while we are doing this, the counter clock will
+   * be stopped, but the clock will not be disabled.
+   *
+   * The expected behavior is that the counter register will freezes at
+   * a value equal to the RC register when the timer expires.  The counter
+   * should have values between 0 and RC in all other cased.
+   *
+   * REVISIT:  This does not appear to be the case.
+   */
+
+  tmrinfo("Cancelling...\n");
+
+  count  = STM32WB_TIM_GETCOUNTER(oneshot->tch);
+  period = oneshot->period;
+
+  /* Now we can disable the interrupt and stop the timer. */
+
+  STM32WB_TIM_DISABLEINT(oneshot->tch, GTIM_DIER_UIE);
+  STM32WB_TIM_SETISR(oneshot->tch, NULL, NULL, 0);
+  STM32WB_TIM_SETMODE(oneshot->tch, STM32WB_TIM_MODE_DISABLED);
+
+  oneshot->running = false;
+  oneshot->handler = NULL;
+  oneshot->arg     = NULL;
+  leave_critical_section(flags);
+
+  /* Did the caller provide us with a location to return the time
+   * remaining?
+   */
+
+  if (ts)
+    {
+      /* Yes.. then calculate and return the time remaining on the
+       * oneshot timer.
+       */
+
+      tmrinfo("period=%lu count=%lu\n",
+             (unsigned long)period, (unsigned long)count);
+
+      /* REVISIT: I am not certain why the timer counter value sometimes
+       * exceeds RC.  Might be a bug, or perhaps the counter does not stop
+       * in all cases.
+       */
+
+      if (count >= period)
+        {
+          /* No time remaining (?) */
+
+          ts->tv_sec  = 0;
+          ts->tv_nsec = 0;
+        }
+      else
+        {
+          /* The total time remaining is the difference.  Convert that
+           * to units of microseconds.
+           *
+           *   frequency = ticks / second
+           *   seconds   = ticks * frequency
+           *   usecs     = (ticks * USEC_PER_SEC) / frequency;
+           */
+
+          usec        = (((uint64_t)(period - count)) * USEC_PER_SEC) /
+                        oneshot->frequency;
+
+          /* Return the time remaining in the correct form */
+
+          sec         = usec / USEC_PER_SEC;
+          nsec        = ((usec) - (sec * USEC_PER_SEC)) * NSEC_PER_USEC;
+
+          ts->tv_sec  = (time_t)sec;
+          ts->tv_nsec = (unsigned long)nsec;
+        }
+
+      tmrinfo("remaining (%lu, %lu)\n",
+             (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec);

Review Comment:
   ```suggestion
         tmrinfo("remaining (%lu, %lu)\n",
                 (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec);
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_syscfg.h:
##########
@@ -0,0 +1,227 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_syscfg.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_SYSCFG_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_SYSCFG_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "chip.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_SYSCFG_MEMRMP_OFFSET    0x0000 /* SYSCFG memory remap register */
+#define STM32WB_SYSCFG_CFGR1_OFFSET     0x0004 /* SYSCFG configuration register 1 */
+
+#define STM32WB_SYSCFG_EXTICR_OFFSET(p) (0x0008 + ((p) & 0x0c)) /* Pin p = 0..15 */
+
+#define STM32WB_SYSCFG_EXTICR1_OFFSET   0x0008 /* SYSCFG external interrupt configuration register 1 */
+#define STM32WB_SYSCFG_EXTICR2_OFFSET   0x000c /* SYSCFG external interrupt configuration register 2 */
+#define STM32WB_SYSCFG_EXTICR3_OFFSET   0x0010 /* SYSCFG external interrupt configuration register 3 */
+#define STM32WB_SYSCFG_EXTICR4_OFFSET   0x0014 /* SYSCFG external interrupt configuration register 4 */
+
+#define STM32WB_SYSCFG_SCSR_OFFSET      0x0018 /* SYSCFG SRAM2 control and status register */
+#define STM32WB_SYSCFG_CFGR2_OFFSET     0x001c /* SYSCFG configuration register 2 */
+#define STM32WB_SYSCFG_SWPR1_OFFSET     0x0020 /* SYSCFG SRAM2 write protection register 1 */
+#define STM32WB_SYSCFG_SKR_OFFSET       0x0024 /* SYSCFG SRAM2 key register */
+#define STM32WB_SYSCFG_SWPR2_OFFSET     0x0028 /* SYSCFG SRAM2 write protection register 2 */
+
+#define STM32WB_SYSCFG_IMR1_OFFSET      0x0100 /* SYSCFG Interrupt mask register 1 */
+#define STM32WB_SYSCFG_IMR2_OFFSET      0x0104 /* SYSCFG Interrupt mask register 2 */
+#define STM32WB_SYSCFG_C2IMR1_OFFSET    0x0108 /* SYSCFG CPU2 Interrupt mask register 1 */
+#define STM32WB_SYSCFG_C2IMR2_OFFSET    0x010c /* SYSCFG CPU2 Interrupt mask register 2 */
+#define STM32WB_SYSCFG_SIPCR_OFFSET     0x0110 /* SYSCFG Secure IP control register */
+
+/* Register Addresses *******************************************************/
+
+#define STM32WB_SYSCFG_MEMRMP           (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_MEMRMP_OFFSET)
+#define STM32WB_SYSCFG_CFGR1            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_CFGR1_OFFSET)
+#define STM32WB_SYSCFG_EXTICR(p)        (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR_OFFSET(p))
+#define STM32WB_SYSCFG_EXTICR1          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR1_OFFSET)
+#define STM32WB_SYSCFG_EXTICR2          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR2_OFFSET)
+#define STM32WB_SYSCFG_EXTICR3          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR3_OFFSET)
+#define STM32WB_SYSCFG_EXTICR4          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR4_OFFSET)
+#define STM32WB_SYSCFG_SCSR             (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SCSR_OFFSET)
+#define STM32WB_SYSCFG_CFGR2            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_CFGR2_OFFSET)
+#define STM32WB_SYSCFG_SWPR1            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SWPR1_OFFSET)
+#define STM32WB_SYSCFG_SKR              (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SKR_OFFSET)
+#define STM32WB_SYSCFG_SWPR2            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SWPR2_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* SYSCFG memory remap register */
+
+#define SYSCFG_MEMRMP_SHIFT           (0)       /* Bits 2-0: Memory mapping selection */
+#define SYSCFG_MEMRMP_MASK            (0x7 << SYSCFG_MEMRMP_SHIFT)
+#  define SYSCFG_MEMRMP_FLASH         (0x0 << SYSCFG_MEMRMP_SHIFT) /* 000: Main Flash memory mapped at 0x00000000 */
+#  define SYSCFG_MEMRMP_SYSTEM        (0x1 << SYSCFG_MEMRMP_SHIFT) /* 001: System Flash memory mapped at 0x00000000 */
+#  define SYSCFG_MEMRMP_SRAM          (0x3 << SYSCFG_MEMRMP_SHIFT) /* 011: SRAM1 mapped at 0x00000000 */
+#  define SYSCFG_MEMRMP_QSPI          (0x6 << SYSCFG_MEMRMP_SHIFT) /* 110: QSPI memory mapped at 0x00000000 */
+
+/* SYSCFG configuration register 1 */
+
+#define SYSCFG_CFGR1_BOOSTEN          (1 << 8)  /* Bit  8: I/O analog switch voltage booster enable */
+#define SYSCFG_CFGR1_I2C_PB6_FMP      (1 << 16) /* Bit 16: Fast-mode Plus (Fm+) driving capability activation on PB6 */
+#define SYSCFG_CFGR1_I2C_PB7_FMP      (1 << 17) /* Bit 17: Fast-mode Plus (Fm+) driving capability activation on PB7 */
+#define SYSCFG_CFGR1_I2C_PB8_FMP      (1 << 18) /* Bit 18: Fast-mode Plus (Fm+) driving capability activation on PB8 */
+#define SYSCFG_CFGR1_I2C_PB9_FMP      (1 << 19) /* Bit 19: Fast-mode Plus (Fm+) driving capability activation on PB9 */
+#define SYSCFG_CFGR1_I2C1_FMP         (1 << 20) /* Bit 20: I2C1 Fast-mode Plus (Fm+) driving capability activation */
+#define SYSCFG_CFGR1_I2C3_FMP         (1 << 22) /* Bit 22: I2C3 Fast-mode Plus (Fm+) driving capability activation */
+#define SYSCFG_CFGR1_FPU_IE0          (1 << 26) /* Bit 26: FPU Invalid operation interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE1          (1 << 27) /* Bit 27: FPU Divide-by-zero interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE2          (1 << 28) /* Bit 28: FPU Underflow interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE3          (1 << 29) /* Bit 29: FPU Overflow interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE4          (1 << 30) /* Bit 30: FPU Input denormal interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE5          (1 << 31) /* Bit 31: FPU Inexact interrupt enable */
+
+/* SYSCFG external interrupt configuration register 1-4 */
+
+#define SYSCFG_EXTICR_PORTA           (0x0)     /* 000: PA[x] pin */
+#define SYSCFG_EXTICR_PORTB           (0x1)     /* 001: PB[x] pin */
+#define SYSCFG_EXTICR_PORTC           (0x2)     /* 010: PC[x] pin */
+#define SYSCFG_EXTICR_PORTD           (0x3)     /* 011: PD[x] pin */
+#define SYSCFG_EXTICR_PORTE           (0x4)     /* 100: PE[x] pin */
+#define SYSCFG_EXTICR_PORTH           (0x7)     /* 111: PH[x] pin */
+
+#define SYSCFG_EXTICR_PORT_MASK       (0x7)
+#define SYSCFG_EXTICR_EXTI_SHIFT(g)   (((g) & 0x3) << 2)
+#define SYSCFG_EXTICR_EXTI_MASK(g)    (SYSCFG_EXTICR_PORT_MASK << (SYSCFG_EXTICR_EXTI_SHIFT(g)))
+
+#define SYSCFG_EXTICR1_EXTI0_SHIFT    (0)       /* Bits 0-2: EXTI 0 configuration */
+#define SYSCFG_EXTICR1_EXTI0_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI0_SHIFT)
+#define SYSCFG_EXTICR1_EXTI1_SHIFT    (4)       /* Bits 4-6: EXTI 1 configuration */
+#define SYSCFG_EXTICR1_EXTI1_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI1_SHIFT)
+#define SYSCFG_EXTICR1_EXTI2_SHIFT    (8)       /* Bits 8-10: EXTI 2 configuration */
+#define SYSCFG_EXTICR1_EXTI2_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI2_SHIFT)
+#define SYSCFG_EXTICR1_EXTI3_SHIFT    (12)      /* Bits 12-14: EXTI 3 configuration */
+#define SYSCFG_EXTICR1_EXTI3_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI3_SHIFT)
+
+#define SYSCFG_EXTICR2_EXTI4_SHIFT    (0)       /* Bits 0-2: EXTI 4 configuration */
+#define SYSCFG_EXTICR2_EXTI4_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI4_SHIFT)
+#define SYSCFG_EXTICR2_EXTI5_SHIFT    (4)       /* Bits 4-6: EXTI 5 configuration */
+#define SYSCFG_EXTICR2_EXTI5_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI5_SHIFT)
+#define SYSCFG_EXTICR2_EXTI6_SHIFT    (8)       /* Bits 8-10: EXTI 6 configuration */
+#define SYSCFG_EXTICR2_EXTI6_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI6_SHIFT)
+#define SYSCFG_EXTICR2_EXTI7_SHIFT    (12)      /* Bits 12-14: EXTI 7 configuration */
+#define SYSCFG_EXTICR2_EXTI7_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI7_SHIFT)
+
+#define SYSCFG_EXTICR3_EXTI8_SHIFT    (0)       /* Bits 0-2: EXTI 8 configuration */
+#define SYSCFG_EXTICR3_EXTI8_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI8_SHIFT)
+#define SYSCFG_EXTICR3_EXTI9_SHIFT    (4)       /* Bits 4-6: EXTI 9 configuration */
+#define SYSCFG_EXTICR3_EXTI9_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI9_SHIFT)
+#define SYSCFG_EXTICR3_EXTI10_SHIFT   (8)       /* Bits 8-10: EXTI 10 configuration */
+#define SYSCFG_EXTICR3_EXTI10_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI10_SHIFT)
+#define SYSCFG_EXTICR3_EXTI11_SHIFT   (12)      /* Bits 12-14: EXTI 11 configuration */
+#define SYSCFG_EXTICR3_EXTI11_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI11_SHIFT)
+
+#define SYSCFG_EXTICR4_EXTI12_SHIFT   (0)       /* Bits 0-2: EXTI 12 configuration */
+#define SYSCFG_EXTICR4_EXTI12_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI12_SHIFT)
+#define SYSCFG_EXTICR4_EXTI13_SHIFT   (4)       /* Bits 4-6: EXTI 13 configuration */
+#define SYSCFG_EXTICR4_EXTI13_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI13_SHIFT)
+#define SYSCFG_EXTICR4_EXTI14_SHIFT   (8)       /* Bits 8-10: EXTI 14 configuration */
+#define SYSCFG_EXTICR4_EXTI14_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI14_SHIFT)
+#define SYSCFG_EXTICR4_EXTI15_SHIFT   (12)      /* Bits 12-14: EXTI 15 configuration */
+#define SYSCFG_EXTICR4_EXTI15_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI15_SHIFT)
+
+/* SYSCFG SRAM2 control and status register */
+
+#define SYSCFG_SCSR_SRAM2ER           (1 << 0)  /* Bit  0: SRAM2 and PKA RAM Erase */
+#define SYSCFG_SCSR_SRAM2BSY          (1 << 1)  /* Bit  1: SRAM2 and PKA RAM busy by erase operation */
+#define SYSCFG_SCSR_C2RFD             (1 << 31) /* Bit 31: CPU2 SRAM fetch disable */
+
+/* SYSCFG configuration register 2 */
+
+#define SYSCFG_CFGR2_CLL              (1 << 0)  /* Bit 0: LOCKUP (Hardfault) output enable */
+#define SYSCFG_CFGR2_SPL              (1 << 1)  /* Bit 1: SRAM2 parity lock enable  */
+#define SYSCFG_CFGR2_PVDL             (1 << 2)  /* Bit 2: PVD lock enable */
+#define SYSCFG_CFGR2_ECCL             (1 << 3)  /* Bit 3: ECC lock enable */
+#define SYSCFG_CFGR2_SPF              (1 << 8)  /* Bit 8: SRAM2 parity error flag */
+
+/* SYSCFG SRAM2 write protection register 1 */
+
+#define SYSCFG_SWPR1_PWP(x)           (1 << (x)) /* Bits 0-31: SRAM2 1Kb page x = 0..31 write protection */
+
+/* SYSCFG SRAM2 key register */
+
+#define SYSCFG_SKR_SHIFT              (0)
+#define SYSCFG_SKR_MASK               (0xff << SYSCFG_SKR_SHIFT)
+#define SYSCFG_SKR_KEY1               0xca
+#define SYSCFG_SKR_KEY2               0x53
+
+/* SYSCFG SRAM2 write protection register 2 */
+
+#define SYSCFG_SWPR2_PWP(x)           (1 << ((x)-32)) /* Bits 0-31: SRAM2 1Kb page x = 32..63 write protection */
+
+/* SYSCFG Interrupt mask register 1 */
+
+#define SYSCFG_IMR1_EXTIIM(n)         (1 << ((n)+16)) /* EXTI[n] interrupt mask, n = 5..15 */

Review Comment:
   ```suggestion
   #define SYSCFG_IMR1_EXTIIM(n)         (1 << ((n) + 16)) /* EXTI[n] interrupt mask, n = 5..15 */
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_syscfg.h:
##########
@@ -0,0 +1,227 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_syscfg.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_SYSCFG_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_SYSCFG_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "chip.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_SYSCFG_MEMRMP_OFFSET    0x0000 /* SYSCFG memory remap register */
+#define STM32WB_SYSCFG_CFGR1_OFFSET     0x0004 /* SYSCFG configuration register 1 */
+
+#define STM32WB_SYSCFG_EXTICR_OFFSET(p) (0x0008 + ((p) & 0x0c)) /* Pin p = 0..15 */
+
+#define STM32WB_SYSCFG_EXTICR1_OFFSET   0x0008 /* SYSCFG external interrupt configuration register 1 */
+#define STM32WB_SYSCFG_EXTICR2_OFFSET   0x000c /* SYSCFG external interrupt configuration register 2 */
+#define STM32WB_SYSCFG_EXTICR3_OFFSET   0x0010 /* SYSCFG external interrupt configuration register 3 */
+#define STM32WB_SYSCFG_EXTICR4_OFFSET   0x0014 /* SYSCFG external interrupt configuration register 4 */
+
+#define STM32WB_SYSCFG_SCSR_OFFSET      0x0018 /* SYSCFG SRAM2 control and status register */
+#define STM32WB_SYSCFG_CFGR2_OFFSET     0x001c /* SYSCFG configuration register 2 */
+#define STM32WB_SYSCFG_SWPR1_OFFSET     0x0020 /* SYSCFG SRAM2 write protection register 1 */
+#define STM32WB_SYSCFG_SKR_OFFSET       0x0024 /* SYSCFG SRAM2 key register */
+#define STM32WB_SYSCFG_SWPR2_OFFSET     0x0028 /* SYSCFG SRAM2 write protection register 2 */
+
+#define STM32WB_SYSCFG_IMR1_OFFSET      0x0100 /* SYSCFG Interrupt mask register 1 */
+#define STM32WB_SYSCFG_IMR2_OFFSET      0x0104 /* SYSCFG Interrupt mask register 2 */
+#define STM32WB_SYSCFG_C2IMR1_OFFSET    0x0108 /* SYSCFG CPU2 Interrupt mask register 1 */
+#define STM32WB_SYSCFG_C2IMR2_OFFSET    0x010c /* SYSCFG CPU2 Interrupt mask register 2 */
+#define STM32WB_SYSCFG_SIPCR_OFFSET     0x0110 /* SYSCFG Secure IP control register */
+
+/* Register Addresses *******************************************************/
+
+#define STM32WB_SYSCFG_MEMRMP           (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_MEMRMP_OFFSET)
+#define STM32WB_SYSCFG_CFGR1            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_CFGR1_OFFSET)
+#define STM32WB_SYSCFG_EXTICR(p)        (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR_OFFSET(p))
+#define STM32WB_SYSCFG_EXTICR1          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR1_OFFSET)
+#define STM32WB_SYSCFG_EXTICR2          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR2_OFFSET)
+#define STM32WB_SYSCFG_EXTICR3          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR3_OFFSET)
+#define STM32WB_SYSCFG_EXTICR4          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR4_OFFSET)
+#define STM32WB_SYSCFG_SCSR             (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SCSR_OFFSET)
+#define STM32WB_SYSCFG_CFGR2            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_CFGR2_OFFSET)
+#define STM32WB_SYSCFG_SWPR1            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SWPR1_OFFSET)
+#define STM32WB_SYSCFG_SKR              (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SKR_OFFSET)
+#define STM32WB_SYSCFG_SWPR2            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SWPR2_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* SYSCFG memory remap register */
+
+#define SYSCFG_MEMRMP_SHIFT           (0)       /* Bits 2-0: Memory mapping selection */
+#define SYSCFG_MEMRMP_MASK            (0x7 << SYSCFG_MEMRMP_SHIFT)
+#  define SYSCFG_MEMRMP_FLASH         (0x0 << SYSCFG_MEMRMP_SHIFT) /* 000: Main Flash memory mapped at 0x00000000 */
+#  define SYSCFG_MEMRMP_SYSTEM        (0x1 << SYSCFG_MEMRMP_SHIFT) /* 001: System Flash memory mapped at 0x00000000 */
+#  define SYSCFG_MEMRMP_SRAM          (0x3 << SYSCFG_MEMRMP_SHIFT) /* 011: SRAM1 mapped at 0x00000000 */
+#  define SYSCFG_MEMRMP_QSPI          (0x6 << SYSCFG_MEMRMP_SHIFT) /* 110: QSPI memory mapped at 0x00000000 */
+
+/* SYSCFG configuration register 1 */
+
+#define SYSCFG_CFGR1_BOOSTEN          (1 << 8)  /* Bit  8: I/O analog switch voltage booster enable */
+#define SYSCFG_CFGR1_I2C_PB6_FMP      (1 << 16) /* Bit 16: Fast-mode Plus (Fm+) driving capability activation on PB6 */
+#define SYSCFG_CFGR1_I2C_PB7_FMP      (1 << 17) /* Bit 17: Fast-mode Plus (Fm+) driving capability activation on PB7 */
+#define SYSCFG_CFGR1_I2C_PB8_FMP      (1 << 18) /* Bit 18: Fast-mode Plus (Fm+) driving capability activation on PB8 */
+#define SYSCFG_CFGR1_I2C_PB9_FMP      (1 << 19) /* Bit 19: Fast-mode Plus (Fm+) driving capability activation on PB9 */
+#define SYSCFG_CFGR1_I2C1_FMP         (1 << 20) /* Bit 20: I2C1 Fast-mode Plus (Fm+) driving capability activation */
+#define SYSCFG_CFGR1_I2C3_FMP         (1 << 22) /* Bit 22: I2C3 Fast-mode Plus (Fm+) driving capability activation */
+#define SYSCFG_CFGR1_FPU_IE0          (1 << 26) /* Bit 26: FPU Invalid operation interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE1          (1 << 27) /* Bit 27: FPU Divide-by-zero interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE2          (1 << 28) /* Bit 28: FPU Underflow interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE3          (1 << 29) /* Bit 29: FPU Overflow interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE4          (1 << 30) /* Bit 30: FPU Input denormal interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE5          (1 << 31) /* Bit 31: FPU Inexact interrupt enable */
+
+/* SYSCFG external interrupt configuration register 1-4 */
+
+#define SYSCFG_EXTICR_PORTA           (0x0)     /* 000: PA[x] pin */
+#define SYSCFG_EXTICR_PORTB           (0x1)     /* 001: PB[x] pin */
+#define SYSCFG_EXTICR_PORTC           (0x2)     /* 010: PC[x] pin */
+#define SYSCFG_EXTICR_PORTD           (0x3)     /* 011: PD[x] pin */
+#define SYSCFG_EXTICR_PORTE           (0x4)     /* 100: PE[x] pin */
+#define SYSCFG_EXTICR_PORTH           (0x7)     /* 111: PH[x] pin */
+
+#define SYSCFG_EXTICR_PORT_MASK       (0x7)
+#define SYSCFG_EXTICR_EXTI_SHIFT(g)   (((g) & 0x3) << 2)
+#define SYSCFG_EXTICR_EXTI_MASK(g)    (SYSCFG_EXTICR_PORT_MASK << (SYSCFG_EXTICR_EXTI_SHIFT(g)))
+
+#define SYSCFG_EXTICR1_EXTI0_SHIFT    (0)       /* Bits 0-2: EXTI 0 configuration */
+#define SYSCFG_EXTICR1_EXTI0_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI0_SHIFT)
+#define SYSCFG_EXTICR1_EXTI1_SHIFT    (4)       /* Bits 4-6: EXTI 1 configuration */
+#define SYSCFG_EXTICR1_EXTI1_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI1_SHIFT)
+#define SYSCFG_EXTICR1_EXTI2_SHIFT    (8)       /* Bits 8-10: EXTI 2 configuration */
+#define SYSCFG_EXTICR1_EXTI2_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI2_SHIFT)
+#define SYSCFG_EXTICR1_EXTI3_SHIFT    (12)      /* Bits 12-14: EXTI 3 configuration */
+#define SYSCFG_EXTICR1_EXTI3_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI3_SHIFT)
+
+#define SYSCFG_EXTICR2_EXTI4_SHIFT    (0)       /* Bits 0-2: EXTI 4 configuration */
+#define SYSCFG_EXTICR2_EXTI4_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI4_SHIFT)
+#define SYSCFG_EXTICR2_EXTI5_SHIFT    (4)       /* Bits 4-6: EXTI 5 configuration */
+#define SYSCFG_EXTICR2_EXTI5_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI5_SHIFT)
+#define SYSCFG_EXTICR2_EXTI6_SHIFT    (8)       /* Bits 8-10: EXTI 6 configuration */
+#define SYSCFG_EXTICR2_EXTI6_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI6_SHIFT)
+#define SYSCFG_EXTICR2_EXTI7_SHIFT    (12)      /* Bits 12-14: EXTI 7 configuration */
+#define SYSCFG_EXTICR2_EXTI7_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI7_SHIFT)
+
+#define SYSCFG_EXTICR3_EXTI8_SHIFT    (0)       /* Bits 0-2: EXTI 8 configuration */
+#define SYSCFG_EXTICR3_EXTI8_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI8_SHIFT)
+#define SYSCFG_EXTICR3_EXTI9_SHIFT    (4)       /* Bits 4-6: EXTI 9 configuration */
+#define SYSCFG_EXTICR3_EXTI9_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI9_SHIFT)
+#define SYSCFG_EXTICR3_EXTI10_SHIFT   (8)       /* Bits 8-10: EXTI 10 configuration */
+#define SYSCFG_EXTICR3_EXTI10_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI10_SHIFT)
+#define SYSCFG_EXTICR3_EXTI11_SHIFT   (12)      /* Bits 12-14: EXTI 11 configuration */
+#define SYSCFG_EXTICR3_EXTI11_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI11_SHIFT)
+
+#define SYSCFG_EXTICR4_EXTI12_SHIFT   (0)       /* Bits 0-2: EXTI 12 configuration */
+#define SYSCFG_EXTICR4_EXTI12_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI12_SHIFT)
+#define SYSCFG_EXTICR4_EXTI13_SHIFT   (4)       /* Bits 4-6: EXTI 13 configuration */
+#define SYSCFG_EXTICR4_EXTI13_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI13_SHIFT)
+#define SYSCFG_EXTICR4_EXTI14_SHIFT   (8)       /* Bits 8-10: EXTI 14 configuration */
+#define SYSCFG_EXTICR4_EXTI14_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI14_SHIFT)
+#define SYSCFG_EXTICR4_EXTI15_SHIFT   (12)      /* Bits 12-14: EXTI 15 configuration */
+#define SYSCFG_EXTICR4_EXTI15_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI15_SHIFT)
+
+/* SYSCFG SRAM2 control and status register */
+
+#define SYSCFG_SCSR_SRAM2ER           (1 << 0)  /* Bit  0: SRAM2 and PKA RAM Erase */
+#define SYSCFG_SCSR_SRAM2BSY          (1 << 1)  /* Bit  1: SRAM2 and PKA RAM busy by erase operation */
+#define SYSCFG_SCSR_C2RFD             (1 << 31) /* Bit 31: CPU2 SRAM fetch disable */
+
+/* SYSCFG configuration register 2 */
+
+#define SYSCFG_CFGR2_CLL              (1 << 0)  /* Bit 0: LOCKUP (Hardfault) output enable */
+#define SYSCFG_CFGR2_SPL              (1 << 1)  /* Bit 1: SRAM2 parity lock enable  */
+#define SYSCFG_CFGR2_PVDL             (1 << 2)  /* Bit 2: PVD lock enable */
+#define SYSCFG_CFGR2_ECCL             (1 << 3)  /* Bit 3: ECC lock enable */
+#define SYSCFG_CFGR2_SPF              (1 << 8)  /* Bit 8: SRAM2 parity error flag */
+
+/* SYSCFG SRAM2 write protection register 1 */
+
+#define SYSCFG_SWPR1_PWP(x)           (1 << (x)) /* Bits 0-31: SRAM2 1Kb page x = 0..31 write protection */
+
+/* SYSCFG SRAM2 key register */
+
+#define SYSCFG_SKR_SHIFT              (0)
+#define SYSCFG_SKR_MASK               (0xff << SYSCFG_SKR_SHIFT)
+#define SYSCFG_SKR_KEY1               0xca
+#define SYSCFG_SKR_KEY2               0x53
+
+/* SYSCFG SRAM2 write protection register 2 */
+
+#define SYSCFG_SWPR2_PWP(x)           (1 << ((x)-32)) /* Bits 0-31: SRAM2 1Kb page x = 32..63 write protection */

Review Comment:
   ```suggestion
   #define SYSCFG_SWPR2_PWP(x)           (1 << ((x) - 32)) /* Bits 0-31: SRAM2 1Kb page x = 32..63 write protection */
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_rcc.h:
##########
@@ -0,0 +1,877 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_rcc.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_RCC_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_RCC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_RCC_CR_OFFSET           0x0000  /* Clock control register */
+#define STM32WB_RCC_ICSCR_OFFSET        0x0004  /* Internal clock sources calibration register */
+#define STM32WB_RCC_CFGR_OFFSET         0x0008  /* Clock configuration register */
+#define STM32WB_RCC_PLLCFG_OFFSET       0x000c  /* PLL configuration register */
+#define STM32WB_RCC_PLLSAI1CFG_OFFSET   0x0010  /* PLLSAI1 configuration register */
+#define STM32WB_RCC_CIER_OFFSET         0x0018  /* Clock interrupt enable register */
+#define STM32WB_RCC_CIFR_OFFSET         0x001c  /* Clock interrupt flag register */
+#define STM32WB_RCC_CICR_OFFSET         0x0020  /* Clock interrupt clear register */
+#define STM32WB_RCC_SMPSCR_OFFSET       0x0024  /* Step-down converter control register */
+#define STM32WB_RCC_AHB1RSTR_OFFSET     0x0028  /* AHB1 peripheral reset register */
+#define STM32WB_RCC_AHB2RSTR_OFFSET     0x002c  /* AHB2 peripheral reset register */
+#define STM32WB_RCC_AHB3RSTR_OFFSET     0x0030  /* AHB3 peripheral reset register */
+#define STM32WB_RCC_APB1RSTR1_OFFSET    0x0038  /* APB1 Peripheral reset register 1 */
+#define STM32WB_RCC_APB1RSTR2_OFFSET    0x003c  /* APB1 Peripheral reset register 2 */
+#define STM32WB_RCC_APB2RSTR_OFFSET     0x0040  /* APB2 Peripheral reset register */
+#define STM32WB_RCC_APB3RSTR_OFFSET     0x0044  /* APB3 Peripheral reset register */
+#define STM32WB_RCC_AHB1ENR_OFFSET      0x0048  /* AHB1 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB2ENR_OFFSET      0x004c  /* AHB2 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB3ENR_OFFSET      0x0050  /* AHB3 Peripheral Clock enable register */
+#define STM32WB_RCC_APB1ENR1_OFFSET     0x0058  /* APB1 Peripheral Clock enable register 1 */
+#define STM32WB_RCC_APB1ENR2_OFFSET     0x005c  /* APB1 Peripheral Clock enable register 2 */
+#define STM32WB_RCC_APB2ENR_OFFSET      0x0060  /* APB2 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB1SMENR_OFFSET    0x0068  /* AHB1 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_AHB2SMENR_OFFSET    0x006c  /* AHB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_AHB3SMENR_OFFSET    0x0070  /* AHB3 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_APB1SMENR1_OFFSET   0x0078  /* APB1 clock enable in sleep and stop modes register 1 */
+#define STM32WB_RCC_APB1SMENR2_OFFSET   0x007c  /* APB1 clock enable in sleep and stop modes register 2 */
+#define STM32WB_RCC_APB2SMENR_OFFSET    0x0080  /* APB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_CCIPR_OFFSET        0x0088  /* Peripherals independent clock configuration register */
+#define STM32WB_RCC_BDCR_OFFSET         0x0090  /* Backup domain control register */
+#define STM32WB_RCC_CSR_OFFSET          0x0094  /* Control/status register */
+#define STM32WB_RCC_CRRCR_OFFSET        0x0098  /* Clock recovery RC register */
+#define STM32WB_RCC_HSECR_OFFSET        0x009c  /* Clock HSE register */
+#define STM32WB_RCC_EXTCFGR_OFFSET      0x0108  /* Extended clock recovery register */
+#define STM32WB_RCC_C2AHB1ENR_OFFSET    0x0148  /* CPU2 AHB1 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB2ENR_OFFSET    0x014c  /* CPU2 AHB2 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB3ENR_OFFSET    0x0150  /* CPU2 AHB3 Peripheral Clock enable register */
+#define STM32WB_RCC_C2APB1ENR1_OFFSET   0x0158  /* CPU2 APB1 Peripheral Clock enable register 1 */
+#define STM32WB_RCC_C2APB1ENR2_OFFSET   0x015c  /* CPU2 APB1 Peripheral Clock enable register 2 */
+#define STM32WB_RCC_C2APB2ENR_OFFSET    0x0160  /* CPU2 APB2 Peripheral Clock enable register */
+#define STM32WB_RCC_C2APB3ENR_OFFSET    0x0164  /* CPU2 APB3 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB1SMENR_OFFSET  0x0168  /* CPU2 AHB1 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2AHB2SMENR_OFFSET  0x016c  /* CPU2 AHB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2AHB3SMENR_OFFSET  0x0170  /* CPU2 AHB3 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2APB1SMENR1_OFFSET 0x0178  /* CPU2 APB1 clock enable in sleep and stop modes register 1 */
+#define STM32WB_RCC_C2APB1SMENR2_OFFSET 0x017c  /* CPU2 APB1 clock enable in sleep and stop modes register 2 */
+#define STM32WB_RCC_C2APB2SMENR_OFFSET  0x0180  /* CPU2 APB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2APB3SMENR_OFFSET  0x0184  /* CPU2 APB3 clock enable in sleep and stop modes register */
+
+/* Register Addresses *******************************************************/
+
+#define STM32WB_RCC_CR                (STM32WB_RCC_BASE + STM32WB_RCC_CR_OFFSET)
+#define STM32WB_RCC_ICSCR             (STM32WB_RCC_BASE + STM32WB_RCC_ICSCR_OFFSET)
+#define STM32WB_RCC_CFGR              (STM32WB_RCC_BASE + STM32WB_RCC_CFGR_OFFSET)
+#define STM32WB_RCC_PLLCFG            (STM32WB_RCC_BASE + STM32WB_RCC_PLLCFG_OFFSET)
+#define STM32WB_RCC_PLLSAI1CFG        (STM32WB_RCC_BASE + STM32WB_RCC_PLLSAI1CFG_OFFSET)
+#define STM32WB_RCC_CIER              (STM32WB_RCC_BASE + STM32WB_RCC_CIER_OFFSET)
+#define STM32WB_RCC_CIFR              (STM32WB_RCC_BASE + STM32WB_RCC_CIFR_OFFSET)
+#define STM32WB_RCC_CICR              (STM32WB_RCC_BASE + STM32WB_RCC_CICR_OFFSET)
+#define STM32WB_RCC_SMPSCR            (STM32WB_RCC_BASE + STM32WB_RCC_SMPSCR_OFFSET)
+#define STM32WB_RCC_AHB1RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB1RSTR_OFFSET)
+#define STM32WB_RCC_AHB2RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB2RSTR_OFFSET)
+#define STM32WB_RCC_AHB3RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB3RSTR_OFFSET)
+#define STM32WB_RCC_APB1RSTR1         (STM32WB_RCC_BASE + STM32WB_RCC_APB1RSTR1_OFFSET)
+#define STM32WB_RCC_APB1RSTR2         (STM32WB_RCC_BASE + STM32WB_RCC_APB1RSTR2_OFFSET)
+#define STM32WB_RCC_APB2RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_APB2RSTR_OFFSET)
+#define STM32WB_RCC_APB3RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_APB3RSTR_OFFSET)
+#define STM32WB_RCC_AHB1ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB1ENR_OFFSET)
+#define STM32WB_RCC_AHB2ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB2ENR_OFFSET)
+#define STM32WB_RCC_AHB3ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB3ENR_OFFSET)
+#define STM32WB_RCC_APB1ENR1          (STM32WB_RCC_BASE + STM32WB_RCC_APB1ENR1_OFFSET)
+#define STM32WB_RCC_APB1ENR2          (STM32WB_RCC_BASE + STM32WB_RCC_APB1ENR2_OFFSET)
+#define STM32WB_RCC_APB2ENR           (STM32WB_RCC_BASE + STM32WB_RCC_APB2ENR_OFFSET)
+#define STM32WB_RCC_AHB1SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB1SMENR_OFFSET)
+#define STM32WB_RCC_AHB2SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB2SMENR_OFFSET)
+#define STM32WB_RCC_AHB3SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB3SMENR_OFFSET)
+#define STM32WB_RCC_APB1SMENR1        (STM32WB_RCC_BASE + STM32WB_RCC_APB1SMENR1_OFFSET)
+#define STM32WB_RCC_APB1SMENR2        (STM32WB_RCC_BASE + STM32WB_RCC_APB1SMENR2_OFFSET)
+#define STM32WB_RCC_APB2SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_APB2SMENR_OFFSET)
+#define STM32WB_RCC_CCIPR             (STM32WB_RCC_BASE + STM32WB_RCC_CCIPR_OFFSET)
+#define STM32WB_RCC_BDCR              (STM32WB_RCC_BASE + STM32WB_RCC_BDCR_OFFSET)
+#define STM32WB_RCC_CSR               (STM32WB_RCC_BASE + STM32WB_RCC_CSR_OFFSET)
+#define STM32WB_RCC_CRRCR             (STM32WB_RCC_BASE + STM32WB_RCC_CRRCR_OFFSET)
+#define STM32WB_RCC_HSECR             (STM32WB_RCC_BASE + STM32WB_RCC_HSECR_OFFSET)
+#define STM32WB_RCC_EXTCFGR           (STM32WB_RCC_BASE + STM32WB_RCC_EXTCFGR_OFFSET)
+#define STM32WB_RCC_C2AHB1ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB1ENR_OFFSET)
+#define STM32WB_RCC_C2AHB2ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB2ENR_OFFSET)
+#define STM32WB_RCC_C2AHB3ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB3ENR_OFFSET)
+#define STM32WB_RCC_C2APB1ENR1        (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1ENR1_OFFSET)
+#define STM32WB_RCC_C2APB1ENR2        (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1ENR2_OFFSET)
+#define STM32WB_RCC_C2APB2ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2APB2ENR_OFFSET)
+#define STM32WB_RCC_C2APB3ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2APB3ENR_OFFSET)
+#define STM32WB_RCC_C2AHB1SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB1SMENR_OFFSET)
+#define STM32WB_RCC_C2AHB2SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB2SMENR_OFFSET)
+#define STM32WB_RCC_C2AHB3SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB3SMENR_OFFSET)
+#define STM32WB_RCC_C2APB1SMENR1      (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1SMENR1_OFFSET)
+#define STM32WB_RCC_C2APB1SMENR2      (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1SMENR2_OFFSET)
+#define STM32WB_RCC_C2APB2SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2APB2SMENR_OFFSET)
+#define STM32WB_RCC_C2APB3SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2APB3SMENR_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Clock control register */
+
+#define RCC_CR_MSION                  (1 << 0)  /* Bit 0: Internal Multi Speed clock enable */
+#define RCC_CR_MSIRDY                 (1 << 1)  /* Bit 1: Internal Multi Speed clock ready flag */
+#define RCC_CR_MSIPLLEN               (1 << 2)  /* Bit 2: MSI clock PLL enable */
+#define RCC_CR_MSIRANGE_SHIFT         (4)       /* Bits 7-4: MSI clock range */
+#define RCC_CR_MSIRANGE_MASK          (0x0f << RCC_CR_MSIRANGE_SHIFT)
+#  define RCC_CR_MSIRANGE_100K        (0  << RCC_CR_MSIRANGE_SHIFT) /* 0000: around 100 kHz */
+#  define RCC_CR_MSIRANGE_200K        (1  << RCC_CR_MSIRANGE_SHIFT) /* 0001: around 200 kHz */
+#  define RCC_CR_MSIRANGE_400K        (2  << RCC_CR_MSIRANGE_SHIFT) /* 0010: around 400 kHz */
+#  define RCC_CR_MSIRANGE_800K        (3  << RCC_CR_MSIRANGE_SHIFT) /* 0011: around 800 kHz */
+#  define RCC_CR_MSIRANGE_1M          (4  << RCC_CR_MSIRANGE_SHIFT) /* 0100: around 1 MHz */
+#  define RCC_CR_MSIRANGE_2M          (5  << RCC_CR_MSIRANGE_SHIFT) /* 0101: around 2 MHz */
+#  define RCC_CR_MSIRANGE_4M          (6  << RCC_CR_MSIRANGE_SHIFT) /* 0110: around 4 MHz */
+#  define RCC_CR_MSIRANGE_8M          (7  << RCC_CR_MSIRANGE_SHIFT) /* 0111: around 8 MHz */
+#  define RCC_CR_MSIRANGE_16M         (8  << RCC_CR_MSIRANGE_SHIFT) /* 1000: around 16 MHz */
+#  define RCC_CR_MSIRANGE_24M         (9  << RCC_CR_MSIRANGE_SHIFT) /* 1001: around 24 MHz */
+#  define RCC_CR_MSIRANGE_32M         (10 << RCC_CR_MSIRANGE_SHIFT) /* 1010: around 32 MHz */
+#  define RCC_CR_MSIRANGE_48M         (11 << RCC_CR_MSIRANGE_SHIFT) /* 1011: around 48 MHz */
+
+#define RCC_CR_HSION                  (1 << 8)  /* Bit 8:  HSI16 clock enable */
+#define RCC_CR_HSIKERON               (1 << 9)  /* Bit 9:  HSI16 always enable for peripheral kernels */
+#define RCC_CR_HSIRDY                 (1 << 10) /* Bit 10: HSI16 clock ready flag */
+#define RCC_CR_HSIASFS                (1 << 11) /* Bit 11: HSI16 automatic start from stop */
+#define RCC_CR_HSEON                  (1 << 16) /* Bit 16: HSE clock enable */
+#define RCC_CR_HSERDY                 (1 << 17) /* Bit 17: HSE clock ready flag */
+#define RCC_CR_CSSON                  (1 << 19) /* Bit 19: Clock Security System enable */
+#define RCC_CR_HSEPRE                 (1 << 20) /* Bit 20: HSE sysclk and PLL M divider prescaler */
+#define RCC_CR_PLLON                  (1 << 24) /* Bit 24: PLL enable */
+#define RCC_CR_PLLRDY                 (1 << 25) /* Bit 25: PLL clock ready flag */
+#define RCC_CR_PLLSAI1ON              (1 << 26) /* Bit 26: PLLSAI1 enable */
+#define RCC_CR_PLLSAI1RDY             (1 << 27) /* Bit 27: PLLSAI1 clock ready flag */
+
+/* Internal Clock Sources Calibration */
+
+#define RCC_CR_HSITRIM_SHIFT          (24)      /* Bits 30-24: HSI16 clock trimming */
+#define RCC_CR_HSITRIM_MASK           (0x7f << RCC_CR_HSITRIM_SHIFT)
+#define RCC_CR_HSICAL_SHIFT           (16)      /* Bits 23-16: HSI16 clock Calibration */
+#define RCC_CR_HSICAL_MASK            (0xff << RCC_CR_HSICAL_SHIFT)
+#define RCC_CR_MSITRIM_SHIFT          (8)       /* Bits 15-8:  Internal Multi Speed clock trimming */
+#define RCC_CR_MSITRIM_MASK           (0xff << RCC_CR_MSITRIM_SHIFT)
+#define RCC_CR_MSICAL_SHIFT           (0)       /* Bits 7-0:   Internal Multi Speed clock Calibration */
+#define RCC_CR_MSICAL_MASK            (0xff << RCC_CR_MSICAL_SHIFT)
+
+/* Clock configuration register */
+
+#define RCC_CFGR_SW_SHIFT             (0)       /* Bits 0-1: System clock Switch */
+#define RCC_CFGR_SW_MASK              (0x3 << RCC_CFGR_SW_SHIFT)
+#  define RCC_CFGR_SW_MSI             (0x0 << RCC_CFGR_SW_SHIFT) /* 00: MSI selected as system clock */
+#  define RCC_CFGR_SW_HSI16           (0x1 << RCC_CFGR_SW_SHIFT) /* 00: HSI16 selected as system clock */
+#  define RCC_CFGR_SW_HSE             (0x2 << RCC_CFGR_SW_SHIFT) /* 01: HSE selected as system clock */
+#  define RCC_CFGR_SW_PLL             (0x3 << RCC_CFGR_SW_SHIFT) /* 10: PLL selected as system clock */
+
+#define RCC_CFGR_SWS_SHIFT            (2)       /* Bits 2-3: System Clock Switch Status */
+#define RCC_CFGR_SWS_MASK             (0x3 << RCC_CFGR_SWS_SHIFT)
+#  define RCC_CFGR_SWS_MSI            (0x0 << RCC_CFGR_SWS_SHIFT) /* 00: MSI oscillator used as system clock */
+#  define RCC_CFGR_SWS_HSI16          (0x1 << RCC_CFGR_SWS_SHIFT) /* 00: HSI16 oscillator used as system clock */
+#  define RCC_CFGR_SWS_HSE            (0x2 << RCC_CFGR_SWS_SHIFT) /* 01: HSE oscillator used as system clock */
+#  define RCC_CFGR_SWS_PLL            (0x3 << RCC_CFGR_SWS_SHIFT) /* 10: PLL used as system clock */
+
+#define RCC_CFGR_HPRE_SHIFT           (4)       /* Bits 4-7: HCLK1 prescaler (AHB1, AHB2, AHB3 and SRAM1) */
+#define RCC_CFGR_HPRE_MASK            (0x0f << RCC_CFGR_HPRE_SHIFT)
+#  define RCC_CFGR_HPRE_SYSCLK        (0x00 << RCC_CFGR_HPRE_SHIFT) /* 0000: SYSCLK not divided */
+#  define RCC_CFGR_HPRE_SYSCLKd3      (0x01 << RCC_CFGR_HPRE_SHIFT) /* 0001: SYSCLK divided by 3 */
+#  define RCC_CFGR_HPRE_SYSCLKd5      (0x02 << RCC_CFGR_HPRE_SHIFT) /* 0010: SYSCLK divided by 5 */
+#  define RCC_CFGR_HPRE_SYSCLKd6      (0x05 << RCC_CFGR_HPRE_SHIFT) /* 0101: SYSCLK divided by 6 */
+#  define RCC_CFGR_HPRE_SYSCLKd10     (0x06 << RCC_CFGR_HPRE_SHIFT) /* 0110: SYSCLK divided by 10 */
+#  define RCC_CFGR_HPRE_SYSCLKd32     (0x07 << RCC_CFGR_HPRE_SHIFT) /* 0111: SYSCLK divided by 32 */
+#  define RCC_CFGR_HPRE_SYSCLKd2      (0x08 << RCC_CFGR_HPRE_SHIFT) /* 1000: SYSCLK divided by 2 */
+#  define RCC_CFGR_HPRE_SYSCLKd4      (0x09 << RCC_CFGR_HPRE_SHIFT) /* 1001: SYSCLK divided by 4 */
+#  define RCC_CFGR_HPRE_SYSCLKd8      (0x10 << RCC_CFGR_HPRE_SHIFT) /* 1010: SYSCLK divided by 8 */
+#  define RCC_CFGR_HPRE_SYSCLKd16     (0x11 << RCC_CFGR_HPRE_SHIFT) /* 1011: SYSCLK divided by 16 */
+#  define RCC_CFGR_HPRE_SYSCLKd64     (0x12 << RCC_CFGR_HPRE_SHIFT) /* 1100: SYSCLK divided by 64 */
+#  define RCC_CFGR_HPRE_SYSCLKd128    (0x13 << RCC_CFGR_HPRE_SHIFT) /* 1101: SYSCLK divided by 128 */
+#  define RCC_CFGR_HPRE_SYSCLKd256    (0x14 << RCC_CFGR_HPRE_SHIFT) /* 1110: SYSCLK divided by 256 */
+#  define RCC_CFGR_HPRE_SYSCLKd512    (0x15 << RCC_CFGR_HPRE_SHIFT) /* 1111: SYSCLK divided by 512 */
+
+#define RCC_CFGR_PPRE1_SHIFT          (8)       /* Bits 8-10: PCLK1 Low speed prescaler (APB1) */
+#define RCC_CFGR_PPRE1_MASK           (0x7 << RCC_CFGR_PPRE1_SHIFT)
+#  define RCC_CFGR_PPRE1_HCLK1        (0x0 << RCC_CFGR_PPRE1_SHIFT) /* 0xx: HCLK1 not divided */
+#  define RCC_CFGR_PPRE1_HCLK1d2      (0x4 << RCC_CFGR_PPRE1_SHIFT) /* 100: HCLK1 divided by 2 */
+#  define RCC_CFGR_PPRE1_HCLK1d4      (0x5 << RCC_CFGR_PPRE1_SHIFT) /* 101: HCLK1 divided by 4 */
+#  define RCC_CFGR_PPRE1_HCLK1d8      (0x6 << RCC_CFGR_PPRE1_SHIFT) /* 110: HCLK1 divided by 8 */
+#  define RCC_CFGR_PPRE1_HCLK1d16     (0x7 << RCC_CFGR_PPRE1_SHIFT) /* 111: HCLK1 divided by 16 */
+
+#define RCC_CFGR_PPRE2_SHIFT          (11)      /* Bits 11-13: PCLK2 High speed prescaler (APB2) */
+#define RCC_CFGR_PPRE2_MASK           (0x7 << RCC_CFGR_PPRE2_SHIFT)
+#  define RCC_CFGR_PPRE2_HCLK1        (0x0 << RCC_CFGR_PPRE2_SHIFT) /* 0xx: HCLK1 not divided */
+#  define RCC_CFGR_PPRE2_HCLK1d2      (0x4 << RCC_CFGR_PPRE2_SHIFT) /* 100: HCLK1 divided by 2 */
+#  define RCC_CFGR_PPRE2_HCLK1d4      (0x5 << RCC_CFGR_PPRE2_SHIFT) /* 101: HCLK1 divided by 4 */
+#  define RCC_CFGR_PPRE2_HCLK1d8      (0x6 << RCC_CFGR_PPRE2_SHIFT) /* 110: HCLK1 divided by 8 */
+#  define RCC_CFGR_PPRE2_HCLK1d16     (0x7 << RCC_CFGR_PPRE2_SHIFT) /* 111: HCLK1 divided by 16 */
+
+#define RCC_CFGR_STOPWUCK             (1 << 15) /* Bit 15: Wakeup from Stop and CSS backup clock selection */
+#  define RCC_CFGR_STOPWUCK_MSI       (0 << 15) /* 0: MSI */
+#  define RCC_CFGR_STOPWUCK_HSI16     (1 << 15) /* 0: HSI16 */
+
+#define RCC_CFGR_HPREF                (1 << 16) /* Bit 16: HCLK1 prescaler flag (AHB1, AHB2, AHB3, SRAM1) */
+#define RCC_CFGR_PPRE1F               (1 << 17) /* Bit 17: PCLK1 prescaler flag (APB1) */
+#define RCC_CFGR_PPRE2F               (1 << 18) /* Bit 18: PCLK2 prescaler flag (APB2) */
+
+#define RCC_CFGR_MCOSEL_SHIFT         (24)      /* Bits 24-27: Microcontroller Clock Output */
+#define RCC_CFGR_MCOSEL_MASK          (0xf << RCC_CFGR_MCOSEL_SHIFT)
+#  define RCC_CFGR_MCOSEL_DISABLED    (0x0 << RCC_CFGR_MCOSEL_SHIFT) /* 0000: Output disabled, no clock on MCO */
+#  define RCC_CFGR_MCOSEL_SYSCLK      (0x1 << RCC_CFGR_MCOSEL_SHIFT) /* 0001: SYSCLK system clock selected */
+#  define RCC_CFGR_MCOSEL_MSI         (0x2 << RCC_CFGR_MCOSEL_SHIFT) /* 0010: MSI clock selected */
+#  define RCC_CFGR_MCOSEL_HSI16       (0x3 << RCC_CFGR_MCOSEL_SHIFT) /* 0011: HSI16 clock selected */
+#  define RCC_CFGR_MCOSEL_HSE_AFT     (0x4 << RCC_CFGR_MCOSEL_SHIFT) /* 0100: HSE clock after stabilization */
+#  define RCC_CFGR_MCOSEL_PLL         (0x5 << RCC_CFGR_MCOSEL_SHIFT) /* 0101: Main PLLRCLK selected  */
+#  define RCC_CFGR_MCOSEL_LSI1        (0x6 << RCC_CFGR_MCOSEL_SHIFT) /* 0110: LSI1 clock selected */
+#  define RCC_CFGR_MCOSEL_LSI2        (0x7 << RCC_CFGR_MCOSEL_SHIFT) /* 0111: LSI2 clock selected */
+#  define RCC_CFGR_MCOSEL_LSE         (0x8 << RCC_CFGR_MCOSEL_SHIFT) /* 1000: LSE clock selected */
+#  define RCC_CFGR_MCOSEL_HSI48       (0x9 << RCC_CFGR_MCOSEL_SHIFT) /* 1001: HSI48 clock selected */
+#  define RCC_CFGR_MCOSEL_HSE_BFR     (0xc << RCC_CFGR_MCOSEL_SHIFT) /* 1100: HSE clock before stabilization */
+
+#define RCC_CFGR_MCOPRE_SHIFT         (28)      /* Bits 28-30: MCO prescaler */
+#define RCC_CFGR_MCOPRE_MASK          (0x7 << RCC_CFGR_MCOPRE_SHIFT)
+#  define RCC_CFGR_MCOPRE_DIV1        (0x0 << RCC_CFGR_MCOPRE_SHIFT) /* 000: no division */
+#  define RCC_CFGR_MCOPRE_DIV2        (0x1 << RCC_CFGR_MCOPRE_SHIFT) /* 001: division by 2 */
+#  define RCC_CFGR_MCOPRE_DIV4        (0x2 << RCC_CFGR_MCOPRE_SHIFT) /* 010: division by 4 */
+#  define RCC_CFGR_MCOPRE_DIV8        (0x3 << RCC_CFGR_MCOPRE_SHIFT) /* 011: division by 8 */
+#  define RCC_CFGR_MCOPRE_DIV16       (0x4 << RCC_CFGR_MCOPRE_SHIFT) /* 100: division by 16 */
+
+#define RCC_CFGR_RESET_MASK           (0x00070000)
+
+/* PLL configuration register */
+
+#define RCC_PLLCFG_PLLSRC_SHIFT       (0)       /* Bits 0-1: Main PLL and audio PLLSAI1 entry clock source */
+#define RCC_PLLCFG_PLLSRC_MASK        (0x3 << RCC_PLLCFG_PLLSRC_SHIFT)
+#  define RCC_PLLCFG_PLLSRC_NONE      (0x0 << RCC_PLLCFG_PLLSRC_SHIFT) /* 00: No clock sent to PLLs */
+#  define RCC_PLLCFG_PLLSRC_MSI       (0x1 << RCC_PLLCFG_PLLSRC_SHIFT) /* 01: MSI selected as PLLs source */
+#  define RCC_PLLCFG_PLLSRC_HSI16     (0x2 << RCC_PLLCFG_PLLSRC_SHIFT) /* 10: HSI16 selected as PLLs source */
+#  define RCC_PLLCFG_PLLSRC_HSE       (0x3 << RCC_PLLCFG_PLLSRC_SHIFT) /* 11: HSE selected as PLLs source */
+
+#define RCC_PLLCFG_PLLM_SHIFT         (4)       /* Bits 4-6: Main PLL and audio PLLSAI1 divider */
+#define RCC_PLLCFG_PLLM_MASK          (0x07 << RCC_PLLCFG_PLLM_SHIFT)
+#  define RCC_PLLCFG_PLLM(n)          ((n-1) << RCC_PLLCFG_PLLM_SHIFT) /* n = 1..8 */
+
+#define RCC_PLLCFG_PLLN_SHIFT         (8)       /* Bits 8-14: Main PLL (PLL) VCO multiplier */
+#define RCC_PLLCFG_PLLN_MASK          (0x7f << RCC_PLLCFG_PLLN_SHIFT)
+#  define RCC_PLLCFG_PLLN(n)          ((n) << RCC_PLLCFG_PLLN_SHIFT) /* n = 6..127 */
+
+#define RCC_PLLCFG_PLLPEN             (1 << 16) /* Bit 16: Main PLL PLLPCLK output enable */
+
+#define RCC_PLLCFG_PLLP_SHIFT         (17)      /* Bits 17-21: Main PLL div factor for PLLPCLK */
+#define RCC_PLLCFG_PLLP_MASK          (0x1f << RCC_PLLCFG_PLLP_SHIFT)
+#  define RCC_PLLCFG_PLLP(n)          ((n-1) << RCC_PLLCFG_PLLP_SHIFT) /* n = 2..32 */
+
+#define RCC_PLLCFG_PLLQEN             (1 << 24) /* Bit 24: Main PLL PLLQCLK output enable */
+
+#define RCC_PLLCFG_PLLQ_SHIFT         (25)      /* Bits 25-27: Main PLL division factor for PLLQCLK */
+#define RCC_PLLCFG_PLLQ_MASK          (0x7 << RCC_PLLCFG_PLLQ_SHIFT)
+#  define RCC_PLLCFG_PLLQ(n)          ((n-1) << RCC_PLLCFG_PLLQ_SHIFT) /* n = 2..8 */
+
+#define RCC_PLLCFG_PLLREN             (1 << 28) /* Bit 28: Main PLL PLLRCLK output enable */
+
+#define RCC_PLLCFG_PLLR_SHIFT         (29)      /* Bits 29-31: Main PLL division factor for PLLRCLK */
+#define RCC_PLLCFG_PLLR_MASK          (0x7 << RCC_PLLCFG_PLLR_SHIFT)
+#  define RCC_PLLCFG_PLLR(n)          ((n-1) << RCC_PLLCFG_PLLR_SHIFT) /* n = 2..8 */

Review Comment:
   ```suggestion
   #  define RCC_PLLCFG_PLLR(n)          (((n) - 1) << RCC_PLLCFG_PLLR_SHIFT) /* n = 2..8 */
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_gpio.h:
##########
@@ -0,0 +1,317 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_gpio.h
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.  The
+ *  ASF licenses this file to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance with the
+ *  License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ *  License for the specific language governing permissions and limitations
+ *  under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_GPIO_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_GPIO_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_GPIO_MODER_OFFSET        0x0000         /* GPIO port mode register */
+#define STM32WB_GPIO_OTYPER_OFFSET       0x0004         /* GPIO port output type register */
+#define STM32WB_GPIO_OSPEED_OFFSET       0x0008         /* GPIO port output speed register */
+#define STM32WB_GPIO_PUPDR_OFFSET        0x000c         /* GPIO port pull-up/pull-down register */
+#define STM32WB_GPIO_IDR_OFFSET          0x0010         /* GPIO port input data register */
+#define STM32WB_GPIO_ODR_OFFSET          0x0014         /* GPIO port output data register */
+#define STM32WB_GPIO_BSRR_OFFSET         0x0018         /* GPIO port bit set/reset register */
+#define STM32WB_GPIO_LCKR_OFFSET         0x001c         /* GPIO port configuration lock register */
+#define STM32WB_GPIO_AFRL_OFFSET         0x0020         /* GPIO alternate function low register */
+#define STM32WB_GPIO_AFRH_OFFSET         0x0024         /* GPIO alternate function high register */
+#define STM32WB_GPIO_BRR_OFFSET          0x0028         /* GPIO port bit reset register */
+
+/* Register Addresses *******************************************************/
+
+#define STM32WB_GPIOA_MODER              (STM32WB_GPIOA_BASE + STM32WB_GPIO_MODER_OFFSET)
+#define STM32WB_GPIOA_OTYPER             (STM32WB_GPIOA_BASE + STM32WB_GPIO_OTYPER_OFFSET)
+#define STM32WB_GPIOA_OSPEED             (STM32WB_GPIOA_BASE + STM32WB_GPIO_OSPEED_OFFSET)
+#define STM32WB_GPIOA_PUPDR              (STM32WB_GPIOA_BASE + STM32WB_GPIO_PUPDR_OFFSET)
+#define STM32WB_GPIOA_IDR                (STM32WB_GPIOA_BASE + STM32WB_GPIO_IDR_OFFSET)
+#define STM32WB_GPIOA_ODR                (STM32WB_GPIOA_BASE + STM32WB_GPIO_ODR_OFFSET)
+#define STM32WB_GPIOA_BSRR               (STM32WB_GPIOA_BASE + STM32WB_GPIO_BSRR_OFFSET)
+#define STM32WB_GPIOA_LCKR               (STM32WB_GPIOA_BASE + STM32WB_GPIO_LCKR_OFFSET)
+#define STM32WB_GPIOA_AFRL               (STM32WB_GPIOA_BASE + STM32WB_GPIO_AFRL_OFFSET)
+#define STM32WB_GPIOA_AFRH               (STM32WB_GPIOA_BASE + STM32WB_GPIO_AFRH_OFFSET)
+#define STM32WB_GPIOA_BRR                (STM32WB_GPIOA_BASE + STM32WB_GPIO_BRR_OFFSET)
+
+#define STM32WB_GPIOB_MODER              (STM32WB_GPIOB_BASE + STM32WB_GPIO_MODER_OFFSET)
+#define STM32WB_GPIOB_OTYPER             (STM32WB_GPIOB_BASE + STM32WB_GPIO_OTYPER_OFFSET)
+#define STM32WB_GPIOB_OSPEED             (STM32WB_GPIOB_BASE + STM32WB_GPIO_OSPEED_OFFSET)
+#define STM32WB_GPIOB_PUPDR              (STM32WB_GPIOB_BASE + STM32WB_GPIO_PUPDR_OFFSET)
+#define STM32WB_GPIOB_IDR                (STM32WB_GPIOB_BASE + STM32WB_GPIO_IDR_OFFSET)
+#define STM32WB_GPIOB_ODR                (STM32WB_GPIOB_BASE + STM32WB_GPIO_ODR_OFFSET)
+#define STM32WB_GPIOB_BSRR               (STM32WB_GPIOB_BASE + STM32WB_GPIO_BSRR_OFFSET)
+#define STM32WB_GPIOB_LCKR               (STM32WB_GPIOB_BASE + STM32WB_GPIO_LCKR_OFFSET)
+#define STM32WB_GPIOB_AFRL               (STM32WB_GPIOB_BASE + STM32WB_GPIO_AFRL_OFFSET)
+#define STM32WB_GPIOB_AFRH               (STM32WB_GPIOB_BASE + STM32WB_GPIO_AFRH_OFFSET)
+#define STM32WB_GPIOB_BRR                (STM32WB_GPIOB_BASE + STM32WB_GPIO_BRR_OFFSET)
+
+#define STM32WB_GPIOC_MODER              (STM32WB_GPIOC_BASE + STM32WB_GPIO_MODER_OFFSET)
+#define STM32WB_GPIOC_OTYPER             (STM32WB_GPIOC_BASE + STM32WB_GPIO_OTYPER_OFFSET)
+#define STM32WB_GPIOC_OSPEED             (STM32WB_GPIOC_BASE + STM32WB_GPIO_OSPEED_OFFSET)
+#define STM32WB_GPIOC_PUPDR              (STM32WB_GPIOC_BASE + STM32WB_GPIO_PUPDR_OFFSET)
+#define STM32WB_GPIOC_IDR                (STM32WB_GPIOC_BASE + STM32WB_GPIO_IDR_OFFSET)
+#define STM32WB_GPIOC_ODR                (STM32WB_GPIOC_BASE + STM32WB_GPIO_ODR_OFFSET)
+#define STM32WB_GPIOC_BSRR               (STM32WB_GPIOC_BASE + STM32WB_GPIO_BSRR_OFFSET)
+#define STM32WB_GPIOC_LCKR               (STM32WB_GPIOC_BASE + STM32WB_GPIO_LCKR_OFFSET)
+#define STM32WB_GPIOC_AFRL               (STM32WB_GPIOC_BASE + STM32WB_GPIO_AFRL_OFFSET)
+#define STM32WB_GPIOC_AFRH               (STM32WB_GPIOC_BASE + STM32WB_GPIO_AFRH_OFFSET)
+#define STM32WB_GPIOC_BRR                (STM32WB_GPIOC_BASE + STM32WB_GPIO_BRR_OFFSET)
+
+#if defined(CONFIG_STM32WB_GPIO_HAVE_PORTD)
+#  define STM32WB_GPIOD_MODER            (STM32WB_GPIOD_BASE + STM32WB_GPIO_MODER_OFFSET)
+#  define STM32WB_GPIOD_OTYPER           (STM32WB_GPIOD_BASE + STM32WB_GPIO_OTYPER_OFFSET)
+#  define STM32WB_GPIOD_OSPEED           (STM32WB_GPIOD_BASE + STM32WB_GPIO_OSPEED_OFFSET)
+#  define STM32WB_GPIOD_PUPDR            (STM32WB_GPIOD_BASE + STM32WB_GPIO_PUPDR_OFFSET)
+#  define STM32WB_GPIOD_IDR              (STM32WB_GPIOD_BASE + STM32WB_GPIO_IDR_OFFSET)
+#  define STM32WB_GPIOD_ODR              (STM32WB_GPIOD_BASE + STM32WB_GPIO_ODR_OFFSET)
+#  define STM32WB_GPIOD_BSRR             (STM32WB_GPIOD_BASE + STM32WB_GPIO_BSRR_OFFSET)
+#  define STM32WB_GPIOD_LCKR             (STM32WB_GPIOD_BASE + STM32WB_GPIO_LCKR_OFFSET)
+#  define STM32WB_GPIOD_AFRL             (STM32WB_GPIOD_BASE + STM32WB_GPIO_AFRL_OFFSET)
+#  define STM32WB_GPIOD_AFRH             (STM32WB_GPIOD_BASE + STM32WB_GPIO_AFRH_OFFSET)
+#  define STM32WB_GPIOD_BRR              (STM32WB_GPIOD_BASE + STM32WB_GPIO_BRR_OFFSET)
+#endif
+
+#define STM32WB_GPIOE_MODER              (STM32WB_GPIOE_BASE + STM32WB_GPIO_MODER_OFFSET)
+#define STM32WB_GPIOE_OTYPER             (STM32WB_GPIOE_BASE + STM32WB_GPIO_OTYPER_OFFSET)
+#define STM32WB_GPIOE_OSPEED             (STM32WB_GPIOE_BASE + STM32WB_GPIO_OSPEED_OFFSET)
+#define STM32WB_GPIOE_PUPDR              (STM32WB_GPIOE_BASE + STM32WB_GPIO_PUPDR_OFFSET)
+#define STM32WB_GPIOE_IDR                (STM32WB_GPIOE_BASE + STM32WB_GPIO_IDR_OFFSET)
+#define STM32WB_GPIOE_ODR                (STM32WB_GPIOE_BASE + STM32WB_GPIO_ODR_OFFSET)
+#define STM32WB_GPIOE_BSRR               (STM32WB_GPIOE_BASE + STM32WB_GPIO_BSRR_OFFSET)
+#define STM32WB_GPIOE_LCKR               (STM32WB_GPIOE_BASE + STM32WB_GPIO_LCKR_OFFSET)
+#define STM32WB_GPIOE_AFRL               (STM32WB_GPIOE_BASE + STM32WB_GPIO_AFRL_OFFSET)
+#define STM32WB_GPIOE_BRR                (STM32WB_GPIOE_BASE + STM32WB_GPIO_BRR_OFFSET)
+
+#define STM32WB_GPIOH_MODER              (STM32WB_GPIOH_BASE + STM32WB_GPIO_MODER_OFFSET)
+#define STM32WB_GPIOH_OTYPER             (STM32WB_GPIOH_BASE + STM32WB_GPIO_OTYPER_OFFSET)
+#define STM32WB_GPIOH_OSPEED             (STM32WB_GPIOH_BASE + STM32WB_GPIO_OSPEED_OFFSET)
+#define STM32WB_GPIOH_PUPDR              (STM32WB_GPIOH_BASE + STM32WB_GPIO_PUPDR_OFFSET)
+#define STM32WB_GPIOH_IDR                (STM32WB_GPIOH_BASE + STM32WB_GPIO_IDR_OFFSET)
+#define STM32WB_GPIOH_ODR                (STM32WB_GPIOH_BASE + STM32WB_GPIO_ODR_OFFSET)
+#define STM32WB_GPIOH_BSRR               (STM32WB_GPIOH_BASE + STM32WB_GPIO_BSRR_OFFSET)
+#define STM32WB_GPIOH_LCKR               (STM32WB_GPIOH_BASE + STM32WB_GPIO_LCKR_OFFSET)
+#define STM32WB_GPIOH_AFRL               (STM32WB_GPIOH_BASE + STM32WB_GPIO_AFRL_OFFSET)
+#define STM32WB_GPIOH_BRR                (STM32WB_GPIOH_BASE + STM32WB_GPIO_BRR_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* GPIO port mode register */
+
+#define GPIO_MODER_INPUT               (0x0)          /* Input mode */
+#define GPIO_MODER_OUTPUT              (0x1)          /* General purpose output mode */
+#define GPIO_MODER_ALT                 (0x2)          /* Alternate mode */
+#define GPIO_MODER_ANALOG              (0x3)          /* Analog mode (reset state) */
+
+#define GPIO_MODER_SHIFT(n)            ((n) << 1)
+#define GPIO_MODER_MASK(n)             (0x3 << GPIO_MODER_SHIFT(n))
+
+#define GPIO_MODER0_SHIFT              (0)
+#define GPIO_MODER0_MASK               (0x3 << GPIO_MODER0_SHIFT)
+#define GPIO_MODER1_SHIFT              (2)
+#define GPIO_MODER1_MASK               (0x3 << GPIO_MODER1_SHIFT)
+#define GPIO_MODER2_SHIFT              (4)
+#define GPIO_MODER2_MASK               (0x3 << GPIO_MODER2_SHIFT)
+#define GPIO_MODER3_SHIFT              (6)
+#define GPIO_MODER3_MASK               (0x3 << GPIO_MODER3_SHIFT)
+#define GPIO_MODER4_SHIFT              (8)
+#define GPIO_MODER4_MASK               (0x3 << GPIO_MODER4_SHIFT)
+#define GPIO_MODER5_SHIFT              (10)
+#define GPIO_MODER5_MASK               (0x3 << GPIO_MODER5_SHIFT)
+#define GPIO_MODER6_SHIFT              (12)
+#define GPIO_MODER6_MASK               (0x3 << GPIO_MODER6_SHIFT)
+#define GPIO_MODER7_SHIFT              (14)
+#define GPIO_MODER7_MASK               (0x3 << GPIO_MODER7_SHIFT)
+#define GPIO_MODER8_SHIFT              (16)
+#define GPIO_MODER8_MASK               (0x3 << GPIO_MODER8_SHIFT)
+#define GPIO_MODER9_SHIFT              (18)
+#define GPIO_MODER9_MASK               (0x3 << GPIO_MODER9_SHIFT)
+#define GPIO_MODER10_SHIFT             (20)
+#define GPIO_MODER10_MASK              (0x3 << GPIO_MODER10_SHIFT)
+#define GPIO_MODER11_SHIFT             (22)
+#define GPIO_MODER11_MASK              (0x3 << GPIO_MODER11_SHIFT)
+#define GPIO_MODER12_SHIFT             (24)
+#define GPIO_MODER12_MASK              (0x3 << GPIO_MODER12_SHIFT)
+#define GPIO_MODER13_SHIFT             (26)
+#define GPIO_MODER13_MASK              (0x3 << GPIO_MODER13_SHIFT)
+#define GPIO_MODER14_SHIFT             (28)
+#define GPIO_MODER14_MASK              (0x3 << GPIO_MODER14_SHIFT)
+#define GPIO_MODER15_SHIFT             (30)
+#define GPIO_MODER15_MASK              (0x3 << GPIO_MODER15_SHIFT)
+
+/* GPIO port output type register */
+
+#define GPIO_OTYPER_PP(n)              (0)            /* 0=Output push-pull (reset state) */
+#define GPIO_OTYPER_OD(n)              (1 << (n))     /* 1=Output open-drain */
+
+/* GPIO port output speed register */
+
+#define GPIO_OSPEED_5MHz               (0x0)          /* 5 MHz Low speed output */
+#define GPIO_OSPEED_25MHz              (0x1)          /* 25 MHz Medium speed output */
+#define GPIO_OSPEED_50MHz              (0x2)          /* 50 MHz Fast speed output */
+#define GPIO_OSPEED_120MHz             (0x3)          /* 120 MHz High speed output */
+
+#define GPIO_OSPEED_SHIFT(n)           ((n) << 1)
+#define GPIO_OSPEED_MASK(n)            (0x3 << GPIO_OSPEED_SHIFT(n))
+
+#define GPIO_OSPEED0_SHIFT             (0)
+#define GPIO_OSPEED0_MASK              (0x3 << GPIO_OSPEED0_SHIFT)
+#define GPIO_OSPEED1_SHIFT             (2)
+#define GPIO_OSPEED1_MASK              (0x3 << GPIO_OSPEED1_SHIFT)
+#define GPIO_OSPEED2_SHIFT             (4)
+#define GPIO_OSPEED2_MASK              (0x3 << GPIO_OSPEED2_SHIFT)
+#define GPIO_OSPEED3_SHIFT             (6)
+#define GPIO_OSPEED3_MASK              (0x3 << GPIO_OSPEED3_SHIFT)
+#define GPIO_OSPEED4_SHIFT             (8)
+#define GPIO_OSPEED4_MASK              (0x3 << GPIO_OSPEED4_SHIFT)
+#define GPIO_OSPEED5_SHIFT             (10)
+#define GPIO_OSPEED5_MASK              (0x3 << GPIO_OSPEED5_SHIFT)
+#define GPIO_OSPEED6_SHIFT             (12)
+#define GPIO_OSPEED6_MASK              (0x3 << GPIO_OSPEED6_SHIFT)
+#define GPIO_OSPEED7_SHIFT             (14)
+#define GPIO_OSPEED7_MASK              (0x3 << GPIO_OSPEED7_SHIFT)
+#define GPIO_OSPEED8_SHIFT             (16)
+#define GPIO_OSPEED8_MASK              (0x3 << GPIO_OSPEED8_SHIFT)
+#define GPIO_OSPEED9_SHIFT             (18)
+#define GPIO_OSPEED9_MASK              (0x3 << GPIO_OSPEED9_SHIFT)
+#define GPIO_OSPEED10_SHIFT            (20)
+#define GPIO_OSPEED10_MASK             (0x3 << GPIO_OSPEED10_SHIFT)
+#define GPIO_OSPEED11_SHIFT            (22)
+#define GPIO_OSPEED11_MASK             (0x3 << GPIO_OSPEED11_SHIFT)
+#define GPIO_OSPEED12_SHIFT            (24)
+#define GPIO_OSPEED12_MASK             (0x3 << GPIO_OSPEED12_SHIFT)
+#define GPIO_OSPEED13_SHIFT            (26)
+#define GPIO_OSPEED13_MASK             (0x3 << GPIO_OSPEED13_SHIFT)
+#define GPIO_OSPEED14_SHIFT            (28)
+#define GPIO_OSPEED14_MASK             (0x3 << GPIO_OSPEED14_SHIFT)
+#define GPIO_OSPEED15_SHIFT            (30)
+#define GPIO_OSPEED15_MASK             (0x3 << GPIO_OSPEED15_SHIFT)
+
+/* GPIO port pull-up/pull-down register */
+
+#define GPIO_PUPDR_NONE                (0x0)          /* No pull-up, pull-down */
+#define GPIO_PUPDR_PULLUP              (0x1)          /* Pull-up */
+#define GPIO_PUPDR_PULLDOWN            (0x2)          /* Pull-down */
+
+#define GPIO_PUPDR_SHIFT(n)            ((n) << 1)
+#define GPIO_PUPDR_MASK(n)             (0x3 << GPIO_PUPDR_SHIFT(n))
+
+#define GPIO_PUPDR0_SHIFT              (0)
+#define GPIO_PUPDR0_MASK               (0x3 << GPIO_PUPDR0_SHIFT)
+#define GPIO_PUPDR1_SHIFT              (2)
+#define GPIO_PUPDR1_MASK               (0x3 << GPIO_PUPDR1_SHIFT)
+#define GPIO_PUPDR2_SHIFT              (4)
+#define GPIO_PUPDR2_MASK               (0x3 << GPIO_PUPDR2_SHIFT)
+#define GPIO_PUPDR3_SHIFT              (6)
+#define GPIO_PUPDR3_MASK               (0x3 << GPIO_PUPDR3_SHIFT)
+#define GPIO_PUPDR4_SHIFT              (8)
+#define GPIO_PUPDR4_MASK               (0x3 << GPIO_PUPDR4_SHIFT)
+#define GPIO_PUPDR5_SHIFT              (10)
+#define GPIO_PUPDR5_MASK               (0x3 << GPIO_PUPDR5_SHIFT)
+#define GPIO_PUPDR6_SHIFT              (12)
+#define GPIO_PUPDR6_MASK               (0x3 << GPIO_PUPDR6_SHIFT)
+#define GPIO_PUPDR7_SHIFT              (14)
+#define GPIO_PUPDR7_MASK               (0x3 << GPIO_PUPDR7_SHIFT)
+#define GPIO_PUPDR8_SHIFT              (16)
+#define GPIO_PUPDR8_MASK               (0x3 << GPIO_PUPDR8_SHIFT)
+#define GPIO_PUPDR9_SHIFT              (18)
+#define GPIO_PUPDR9_MASK               (0x3 << GPIO_PUPDR9_SHIFT)
+#define GPIO_PUPDR10_SHIFT             (20)
+#define GPIO_PUPDR10_MASK              (0x3 << GPIO_PUPDR10_SHIFT)
+#define GPIO_PUPDR11_SHIFT             (22)
+#define GPIO_PUPDR11_MASK              (0x3 << GPIO_PUPDR11_SHIFT)
+#define GPIO_PUPDR12_SHIFT             (24)
+#define GPIO_PUPDR12_MASK              (0x3 << GPIO_PUPDR12_SHIFT)
+#define GPIO_PUPDR13_SHIFT             (26)
+#define GPIO_PUPDR13_MASK              (0x3 << GPIO_PUPDR13_SHIFT)
+#define GPIO_PUPDR14_SHIFT             (28)
+#define GPIO_PUPDR14_MASK              (0x3 << GPIO_PUPDR14_SHIFT)
+#define GPIO_PUPDR15_SHIFT             (30)
+#define GPIO_PUPDR15_MASK              (0x3 << GPIO_PUPDR15_SHIFT)
+
+/* GPIO port input data register */
+
+#define GPIO_IDR(n)                    (1 << (n))
+
+/* GPIO port output data register */
+
+#define GPIO_ODR(n)                    (1 << (n))
+
+/* GPIO port bit set/reset register */
+
+#define GPIO_BSRR_SET(n)               (1 << (n))
+#define GPIO_BSRR_RESET(n)             (1 << ((n) + 16))
+
+/* GPIO port configuration lock register */
+
+#define GPIO_LCKR(n)                   (1 << (n))
+#define GPIO_LCKK                      (1 << 16)      /* Bit 16: Lock key */
+
+/* GPIO alternate function low register */
+
+#define GPIO_AFRL_AFSEL_SHIFT(n)        ((n) << 2)    /* Alt function selection for pins 0 to 7 */
+#define GPIO_AFRL_AFSEL_MASK(n)         (0xf << GPIO_AFRL_AFSEL_SHIFT(n))
+
+#define GPIO_AFRL_AFSEL0_SHIFT          (0)
+#define GPIO_AFRL_AFSEL0_MASK           (0xf << GPIO_AFRL_AFSEL0_SHIFT)
+#define GPIO_AFRL_AFSEL1_SHIFT          (4)
+#define GPIO_AFRL_AFSEL1_MASK           (0xf << GPIO_AFRL_AFSEL1_SHIFT)
+#define GPIO_AFRL_AFSEL2_SHIFT          (8)
+#define GPIO_AFRL_AFSEL2_MASK           (0xf << GPIO_AFRL_AFSEL2_SHIFT)
+#define GPIO_AFRL_AFSEL3_SHIFT          (12)
+#define GPIO_AFRL_AFSEL3_MASK           (0xf << GPIO_AFRL_AFSEL3_SHIFT)
+#define GPIO_AFRL_AFSEL4_SHIFT          (16)
+#define GPIO_AFRL_AFSEL4_MASK           (0xf << GPIO_AFRL_AFSEL4_SHIFT)
+#define GPIO_AFRL_AFSEL5_SHIFT          (20)
+#define GPIO_AFRL_AFSEL5_MASK           (0xf << GPIO_AFRL_AFSEL5_SHIFT)
+#define GPIO_AFRL_AFSEL6_SHIFT          (24)
+#define GPIO_AFRL_AFSEL6_MASK           (0xf << GPIO_AFRL_AFSEL6_SHIFT)
+#define GPIO_AFRL_AFSEL7_SHIFT          (28)
+#define GPIO_AFRL_AFSEL7_MASK           (0xf << GPIO_AFRL_AFSEL7_SHIFT)
+
+/* GPIO alternate function high register */
+
+#define GPIO_AFRH_AFSEL_SHIFT(n)        ((n-8) << 2)  /* Alt function selection for pins 8 to 15 */
+#define GPIO_AFRH_AFSEL_MASK(n)         (0xf << GPIO_AFRH_AFSEL_SHIFT(n))
+
+#define GPIO_AFRH_AFSEL8_SHIFT          (0)
+#define GPIO_AFRH_AFSEL8_MASK           (0xf << GPIO_AFRH_AFSEL8_SHIFT)
+#define GPIO_AFRH_AFSEL9_SHIFT          (4)
+#define GPIO_AFRH_AFSEL9_MASK           (0xf << GPIO_AFRH_AFSEL9_SHIFT)
+#define GPIO_AFRH_AFSEL10_SHIFT         (8)
+#define GPIO_AFRH_AFSEL10_MASK          (0xf << GPIO_AFRH_AFSEL10_SHIFT)
+#define GPIO_AFRH_AFSEL11_SHIFT         (12)
+#define GPIO_AFRH_AFSEL11_MASK          (0xf << GPIO_AFRH_AFSEL11_SHIFT)
+#define GPIO_AFRH_AFSEL12_SHIFT         (16)
+#define GPIO_AFRH_AFSEL12_MASK          (0xf << GPIO_AFRH_AFSEL12_SHIFT)
+#define GPIO_AFRH_AFSEL13_SHIFT         (20)
+#define GPIO_AFRH_AFSEL13_MASK          (0xf << GPIO_AFRH_AFSEL13_SHIFT)
+#define GPIO_AFRH_AFSEL14_SHIFT         (24)
+#define GPIO_AFRH_AFSEL14_MASK          (0xf << GPIO_AFRH_AFSEL14_SHIFT)
+#define GPIO_AFRH_AFSEL15_SHIFT         (28)
+#define GPIO_AFRH_AFSEL15_MASK          (0xf << GPIO_AFRH_AFSEL15_SHIFT)
+
+/* GPIO port bit reset register */
+
+#define GPIO_BRR_RESET(n)              (1 << (n))

Review Comment:
   ```suggestion
   #define GPIO_BRR_RESET(n)               (1 << (n))
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_rcc.h:
##########
@@ -0,0 +1,877 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_rcc.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_RCC_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_RCC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_RCC_CR_OFFSET           0x0000  /* Clock control register */
+#define STM32WB_RCC_ICSCR_OFFSET        0x0004  /* Internal clock sources calibration register */
+#define STM32WB_RCC_CFGR_OFFSET         0x0008  /* Clock configuration register */
+#define STM32WB_RCC_PLLCFG_OFFSET       0x000c  /* PLL configuration register */
+#define STM32WB_RCC_PLLSAI1CFG_OFFSET   0x0010  /* PLLSAI1 configuration register */
+#define STM32WB_RCC_CIER_OFFSET         0x0018  /* Clock interrupt enable register */
+#define STM32WB_RCC_CIFR_OFFSET         0x001c  /* Clock interrupt flag register */
+#define STM32WB_RCC_CICR_OFFSET         0x0020  /* Clock interrupt clear register */
+#define STM32WB_RCC_SMPSCR_OFFSET       0x0024  /* Step-down converter control register */
+#define STM32WB_RCC_AHB1RSTR_OFFSET     0x0028  /* AHB1 peripheral reset register */
+#define STM32WB_RCC_AHB2RSTR_OFFSET     0x002c  /* AHB2 peripheral reset register */
+#define STM32WB_RCC_AHB3RSTR_OFFSET     0x0030  /* AHB3 peripheral reset register */
+#define STM32WB_RCC_APB1RSTR1_OFFSET    0x0038  /* APB1 Peripheral reset register 1 */
+#define STM32WB_RCC_APB1RSTR2_OFFSET    0x003c  /* APB1 Peripheral reset register 2 */
+#define STM32WB_RCC_APB2RSTR_OFFSET     0x0040  /* APB2 Peripheral reset register */
+#define STM32WB_RCC_APB3RSTR_OFFSET     0x0044  /* APB3 Peripheral reset register */
+#define STM32WB_RCC_AHB1ENR_OFFSET      0x0048  /* AHB1 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB2ENR_OFFSET      0x004c  /* AHB2 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB3ENR_OFFSET      0x0050  /* AHB3 Peripheral Clock enable register */
+#define STM32WB_RCC_APB1ENR1_OFFSET     0x0058  /* APB1 Peripheral Clock enable register 1 */
+#define STM32WB_RCC_APB1ENR2_OFFSET     0x005c  /* APB1 Peripheral Clock enable register 2 */
+#define STM32WB_RCC_APB2ENR_OFFSET      0x0060  /* APB2 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB1SMENR_OFFSET    0x0068  /* AHB1 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_AHB2SMENR_OFFSET    0x006c  /* AHB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_AHB3SMENR_OFFSET    0x0070  /* AHB3 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_APB1SMENR1_OFFSET   0x0078  /* APB1 clock enable in sleep and stop modes register 1 */
+#define STM32WB_RCC_APB1SMENR2_OFFSET   0x007c  /* APB1 clock enable in sleep and stop modes register 2 */
+#define STM32WB_RCC_APB2SMENR_OFFSET    0x0080  /* APB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_CCIPR_OFFSET        0x0088  /* Peripherals independent clock configuration register */
+#define STM32WB_RCC_BDCR_OFFSET         0x0090  /* Backup domain control register */
+#define STM32WB_RCC_CSR_OFFSET          0x0094  /* Control/status register */
+#define STM32WB_RCC_CRRCR_OFFSET        0x0098  /* Clock recovery RC register */
+#define STM32WB_RCC_HSECR_OFFSET        0x009c  /* Clock HSE register */
+#define STM32WB_RCC_EXTCFGR_OFFSET      0x0108  /* Extended clock recovery register */
+#define STM32WB_RCC_C2AHB1ENR_OFFSET    0x0148  /* CPU2 AHB1 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB2ENR_OFFSET    0x014c  /* CPU2 AHB2 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB3ENR_OFFSET    0x0150  /* CPU2 AHB3 Peripheral Clock enable register */
+#define STM32WB_RCC_C2APB1ENR1_OFFSET   0x0158  /* CPU2 APB1 Peripheral Clock enable register 1 */
+#define STM32WB_RCC_C2APB1ENR2_OFFSET   0x015c  /* CPU2 APB1 Peripheral Clock enable register 2 */
+#define STM32WB_RCC_C2APB2ENR_OFFSET    0x0160  /* CPU2 APB2 Peripheral Clock enable register */
+#define STM32WB_RCC_C2APB3ENR_OFFSET    0x0164  /* CPU2 APB3 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB1SMENR_OFFSET  0x0168  /* CPU2 AHB1 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2AHB2SMENR_OFFSET  0x016c  /* CPU2 AHB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2AHB3SMENR_OFFSET  0x0170  /* CPU2 AHB3 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2APB1SMENR1_OFFSET 0x0178  /* CPU2 APB1 clock enable in sleep and stop modes register 1 */
+#define STM32WB_RCC_C2APB1SMENR2_OFFSET 0x017c  /* CPU2 APB1 clock enable in sleep and stop modes register 2 */
+#define STM32WB_RCC_C2APB2SMENR_OFFSET  0x0180  /* CPU2 APB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2APB3SMENR_OFFSET  0x0184  /* CPU2 APB3 clock enable in sleep and stop modes register */
+
+/* Register Addresses *******************************************************/
+
+#define STM32WB_RCC_CR                (STM32WB_RCC_BASE + STM32WB_RCC_CR_OFFSET)
+#define STM32WB_RCC_ICSCR             (STM32WB_RCC_BASE + STM32WB_RCC_ICSCR_OFFSET)
+#define STM32WB_RCC_CFGR              (STM32WB_RCC_BASE + STM32WB_RCC_CFGR_OFFSET)
+#define STM32WB_RCC_PLLCFG            (STM32WB_RCC_BASE + STM32WB_RCC_PLLCFG_OFFSET)
+#define STM32WB_RCC_PLLSAI1CFG        (STM32WB_RCC_BASE + STM32WB_RCC_PLLSAI1CFG_OFFSET)
+#define STM32WB_RCC_CIER              (STM32WB_RCC_BASE + STM32WB_RCC_CIER_OFFSET)
+#define STM32WB_RCC_CIFR              (STM32WB_RCC_BASE + STM32WB_RCC_CIFR_OFFSET)
+#define STM32WB_RCC_CICR              (STM32WB_RCC_BASE + STM32WB_RCC_CICR_OFFSET)
+#define STM32WB_RCC_SMPSCR            (STM32WB_RCC_BASE + STM32WB_RCC_SMPSCR_OFFSET)
+#define STM32WB_RCC_AHB1RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB1RSTR_OFFSET)
+#define STM32WB_RCC_AHB2RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB2RSTR_OFFSET)
+#define STM32WB_RCC_AHB3RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB3RSTR_OFFSET)
+#define STM32WB_RCC_APB1RSTR1         (STM32WB_RCC_BASE + STM32WB_RCC_APB1RSTR1_OFFSET)
+#define STM32WB_RCC_APB1RSTR2         (STM32WB_RCC_BASE + STM32WB_RCC_APB1RSTR2_OFFSET)
+#define STM32WB_RCC_APB2RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_APB2RSTR_OFFSET)
+#define STM32WB_RCC_APB3RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_APB3RSTR_OFFSET)
+#define STM32WB_RCC_AHB1ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB1ENR_OFFSET)
+#define STM32WB_RCC_AHB2ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB2ENR_OFFSET)
+#define STM32WB_RCC_AHB3ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB3ENR_OFFSET)
+#define STM32WB_RCC_APB1ENR1          (STM32WB_RCC_BASE + STM32WB_RCC_APB1ENR1_OFFSET)
+#define STM32WB_RCC_APB1ENR2          (STM32WB_RCC_BASE + STM32WB_RCC_APB1ENR2_OFFSET)
+#define STM32WB_RCC_APB2ENR           (STM32WB_RCC_BASE + STM32WB_RCC_APB2ENR_OFFSET)
+#define STM32WB_RCC_AHB1SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB1SMENR_OFFSET)
+#define STM32WB_RCC_AHB2SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB2SMENR_OFFSET)
+#define STM32WB_RCC_AHB3SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB3SMENR_OFFSET)
+#define STM32WB_RCC_APB1SMENR1        (STM32WB_RCC_BASE + STM32WB_RCC_APB1SMENR1_OFFSET)
+#define STM32WB_RCC_APB1SMENR2        (STM32WB_RCC_BASE + STM32WB_RCC_APB1SMENR2_OFFSET)
+#define STM32WB_RCC_APB2SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_APB2SMENR_OFFSET)
+#define STM32WB_RCC_CCIPR             (STM32WB_RCC_BASE + STM32WB_RCC_CCIPR_OFFSET)
+#define STM32WB_RCC_BDCR              (STM32WB_RCC_BASE + STM32WB_RCC_BDCR_OFFSET)
+#define STM32WB_RCC_CSR               (STM32WB_RCC_BASE + STM32WB_RCC_CSR_OFFSET)
+#define STM32WB_RCC_CRRCR             (STM32WB_RCC_BASE + STM32WB_RCC_CRRCR_OFFSET)
+#define STM32WB_RCC_HSECR             (STM32WB_RCC_BASE + STM32WB_RCC_HSECR_OFFSET)
+#define STM32WB_RCC_EXTCFGR           (STM32WB_RCC_BASE + STM32WB_RCC_EXTCFGR_OFFSET)
+#define STM32WB_RCC_C2AHB1ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB1ENR_OFFSET)
+#define STM32WB_RCC_C2AHB2ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB2ENR_OFFSET)
+#define STM32WB_RCC_C2AHB3ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB3ENR_OFFSET)
+#define STM32WB_RCC_C2APB1ENR1        (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1ENR1_OFFSET)
+#define STM32WB_RCC_C2APB1ENR2        (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1ENR2_OFFSET)
+#define STM32WB_RCC_C2APB2ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2APB2ENR_OFFSET)
+#define STM32WB_RCC_C2APB3ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2APB3ENR_OFFSET)
+#define STM32WB_RCC_C2AHB1SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB1SMENR_OFFSET)
+#define STM32WB_RCC_C2AHB2SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB2SMENR_OFFSET)
+#define STM32WB_RCC_C2AHB3SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB3SMENR_OFFSET)
+#define STM32WB_RCC_C2APB1SMENR1      (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1SMENR1_OFFSET)
+#define STM32WB_RCC_C2APB1SMENR2      (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1SMENR2_OFFSET)
+#define STM32WB_RCC_C2APB2SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2APB2SMENR_OFFSET)
+#define STM32WB_RCC_C2APB3SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2APB3SMENR_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Clock control register */
+
+#define RCC_CR_MSION                  (1 << 0)  /* Bit 0: Internal Multi Speed clock enable */
+#define RCC_CR_MSIRDY                 (1 << 1)  /* Bit 1: Internal Multi Speed clock ready flag */
+#define RCC_CR_MSIPLLEN               (1 << 2)  /* Bit 2: MSI clock PLL enable */
+#define RCC_CR_MSIRANGE_SHIFT         (4)       /* Bits 7-4: MSI clock range */
+#define RCC_CR_MSIRANGE_MASK          (0x0f << RCC_CR_MSIRANGE_SHIFT)
+#  define RCC_CR_MSIRANGE_100K        (0  << RCC_CR_MSIRANGE_SHIFT) /* 0000: around 100 kHz */
+#  define RCC_CR_MSIRANGE_200K        (1  << RCC_CR_MSIRANGE_SHIFT) /* 0001: around 200 kHz */
+#  define RCC_CR_MSIRANGE_400K        (2  << RCC_CR_MSIRANGE_SHIFT) /* 0010: around 400 kHz */
+#  define RCC_CR_MSIRANGE_800K        (3  << RCC_CR_MSIRANGE_SHIFT) /* 0011: around 800 kHz */
+#  define RCC_CR_MSIRANGE_1M          (4  << RCC_CR_MSIRANGE_SHIFT) /* 0100: around 1 MHz */
+#  define RCC_CR_MSIRANGE_2M          (5  << RCC_CR_MSIRANGE_SHIFT) /* 0101: around 2 MHz */
+#  define RCC_CR_MSIRANGE_4M          (6  << RCC_CR_MSIRANGE_SHIFT) /* 0110: around 4 MHz */
+#  define RCC_CR_MSIRANGE_8M          (7  << RCC_CR_MSIRANGE_SHIFT) /* 0111: around 8 MHz */
+#  define RCC_CR_MSIRANGE_16M         (8  << RCC_CR_MSIRANGE_SHIFT) /* 1000: around 16 MHz */
+#  define RCC_CR_MSIRANGE_24M         (9  << RCC_CR_MSIRANGE_SHIFT) /* 1001: around 24 MHz */
+#  define RCC_CR_MSIRANGE_32M         (10 << RCC_CR_MSIRANGE_SHIFT) /* 1010: around 32 MHz */
+#  define RCC_CR_MSIRANGE_48M         (11 << RCC_CR_MSIRANGE_SHIFT) /* 1011: around 48 MHz */
+
+#define RCC_CR_HSION                  (1 << 8)  /* Bit 8:  HSI16 clock enable */
+#define RCC_CR_HSIKERON               (1 << 9)  /* Bit 9:  HSI16 always enable for peripheral kernels */
+#define RCC_CR_HSIRDY                 (1 << 10) /* Bit 10: HSI16 clock ready flag */
+#define RCC_CR_HSIASFS                (1 << 11) /* Bit 11: HSI16 automatic start from stop */
+#define RCC_CR_HSEON                  (1 << 16) /* Bit 16: HSE clock enable */
+#define RCC_CR_HSERDY                 (1 << 17) /* Bit 17: HSE clock ready flag */
+#define RCC_CR_CSSON                  (1 << 19) /* Bit 19: Clock Security System enable */
+#define RCC_CR_HSEPRE                 (1 << 20) /* Bit 20: HSE sysclk and PLL M divider prescaler */
+#define RCC_CR_PLLON                  (1 << 24) /* Bit 24: PLL enable */
+#define RCC_CR_PLLRDY                 (1 << 25) /* Bit 25: PLL clock ready flag */
+#define RCC_CR_PLLSAI1ON              (1 << 26) /* Bit 26: PLLSAI1 enable */
+#define RCC_CR_PLLSAI1RDY             (1 << 27) /* Bit 27: PLLSAI1 clock ready flag */
+
+/* Internal Clock Sources Calibration */
+
+#define RCC_CR_HSITRIM_SHIFT          (24)      /* Bits 30-24: HSI16 clock trimming */
+#define RCC_CR_HSITRIM_MASK           (0x7f << RCC_CR_HSITRIM_SHIFT)
+#define RCC_CR_HSICAL_SHIFT           (16)      /* Bits 23-16: HSI16 clock Calibration */
+#define RCC_CR_HSICAL_MASK            (0xff << RCC_CR_HSICAL_SHIFT)
+#define RCC_CR_MSITRIM_SHIFT          (8)       /* Bits 15-8:  Internal Multi Speed clock trimming */
+#define RCC_CR_MSITRIM_MASK           (0xff << RCC_CR_MSITRIM_SHIFT)
+#define RCC_CR_MSICAL_SHIFT           (0)       /* Bits 7-0:   Internal Multi Speed clock Calibration */
+#define RCC_CR_MSICAL_MASK            (0xff << RCC_CR_MSICAL_SHIFT)
+
+/* Clock configuration register */
+
+#define RCC_CFGR_SW_SHIFT             (0)       /* Bits 0-1: System clock Switch */
+#define RCC_CFGR_SW_MASK              (0x3 << RCC_CFGR_SW_SHIFT)
+#  define RCC_CFGR_SW_MSI             (0x0 << RCC_CFGR_SW_SHIFT) /* 00: MSI selected as system clock */
+#  define RCC_CFGR_SW_HSI16           (0x1 << RCC_CFGR_SW_SHIFT) /* 00: HSI16 selected as system clock */
+#  define RCC_CFGR_SW_HSE             (0x2 << RCC_CFGR_SW_SHIFT) /* 01: HSE selected as system clock */
+#  define RCC_CFGR_SW_PLL             (0x3 << RCC_CFGR_SW_SHIFT) /* 10: PLL selected as system clock */
+
+#define RCC_CFGR_SWS_SHIFT            (2)       /* Bits 2-3: System Clock Switch Status */
+#define RCC_CFGR_SWS_MASK             (0x3 << RCC_CFGR_SWS_SHIFT)
+#  define RCC_CFGR_SWS_MSI            (0x0 << RCC_CFGR_SWS_SHIFT) /* 00: MSI oscillator used as system clock */
+#  define RCC_CFGR_SWS_HSI16          (0x1 << RCC_CFGR_SWS_SHIFT) /* 00: HSI16 oscillator used as system clock */
+#  define RCC_CFGR_SWS_HSE            (0x2 << RCC_CFGR_SWS_SHIFT) /* 01: HSE oscillator used as system clock */
+#  define RCC_CFGR_SWS_PLL            (0x3 << RCC_CFGR_SWS_SHIFT) /* 10: PLL used as system clock */
+
+#define RCC_CFGR_HPRE_SHIFT           (4)       /* Bits 4-7: HCLK1 prescaler (AHB1, AHB2, AHB3 and SRAM1) */
+#define RCC_CFGR_HPRE_MASK            (0x0f << RCC_CFGR_HPRE_SHIFT)
+#  define RCC_CFGR_HPRE_SYSCLK        (0x00 << RCC_CFGR_HPRE_SHIFT) /* 0000: SYSCLK not divided */
+#  define RCC_CFGR_HPRE_SYSCLKd3      (0x01 << RCC_CFGR_HPRE_SHIFT) /* 0001: SYSCLK divided by 3 */
+#  define RCC_CFGR_HPRE_SYSCLKd5      (0x02 << RCC_CFGR_HPRE_SHIFT) /* 0010: SYSCLK divided by 5 */
+#  define RCC_CFGR_HPRE_SYSCLKd6      (0x05 << RCC_CFGR_HPRE_SHIFT) /* 0101: SYSCLK divided by 6 */
+#  define RCC_CFGR_HPRE_SYSCLKd10     (0x06 << RCC_CFGR_HPRE_SHIFT) /* 0110: SYSCLK divided by 10 */
+#  define RCC_CFGR_HPRE_SYSCLKd32     (0x07 << RCC_CFGR_HPRE_SHIFT) /* 0111: SYSCLK divided by 32 */
+#  define RCC_CFGR_HPRE_SYSCLKd2      (0x08 << RCC_CFGR_HPRE_SHIFT) /* 1000: SYSCLK divided by 2 */
+#  define RCC_CFGR_HPRE_SYSCLKd4      (0x09 << RCC_CFGR_HPRE_SHIFT) /* 1001: SYSCLK divided by 4 */
+#  define RCC_CFGR_HPRE_SYSCLKd8      (0x10 << RCC_CFGR_HPRE_SHIFT) /* 1010: SYSCLK divided by 8 */
+#  define RCC_CFGR_HPRE_SYSCLKd16     (0x11 << RCC_CFGR_HPRE_SHIFT) /* 1011: SYSCLK divided by 16 */
+#  define RCC_CFGR_HPRE_SYSCLKd64     (0x12 << RCC_CFGR_HPRE_SHIFT) /* 1100: SYSCLK divided by 64 */
+#  define RCC_CFGR_HPRE_SYSCLKd128    (0x13 << RCC_CFGR_HPRE_SHIFT) /* 1101: SYSCLK divided by 128 */
+#  define RCC_CFGR_HPRE_SYSCLKd256    (0x14 << RCC_CFGR_HPRE_SHIFT) /* 1110: SYSCLK divided by 256 */
+#  define RCC_CFGR_HPRE_SYSCLKd512    (0x15 << RCC_CFGR_HPRE_SHIFT) /* 1111: SYSCLK divided by 512 */
+
+#define RCC_CFGR_PPRE1_SHIFT          (8)       /* Bits 8-10: PCLK1 Low speed prescaler (APB1) */
+#define RCC_CFGR_PPRE1_MASK           (0x7 << RCC_CFGR_PPRE1_SHIFT)
+#  define RCC_CFGR_PPRE1_HCLK1        (0x0 << RCC_CFGR_PPRE1_SHIFT) /* 0xx: HCLK1 not divided */
+#  define RCC_CFGR_PPRE1_HCLK1d2      (0x4 << RCC_CFGR_PPRE1_SHIFT) /* 100: HCLK1 divided by 2 */
+#  define RCC_CFGR_PPRE1_HCLK1d4      (0x5 << RCC_CFGR_PPRE1_SHIFT) /* 101: HCLK1 divided by 4 */
+#  define RCC_CFGR_PPRE1_HCLK1d8      (0x6 << RCC_CFGR_PPRE1_SHIFT) /* 110: HCLK1 divided by 8 */
+#  define RCC_CFGR_PPRE1_HCLK1d16     (0x7 << RCC_CFGR_PPRE1_SHIFT) /* 111: HCLK1 divided by 16 */
+
+#define RCC_CFGR_PPRE2_SHIFT          (11)      /* Bits 11-13: PCLK2 High speed prescaler (APB2) */
+#define RCC_CFGR_PPRE2_MASK           (0x7 << RCC_CFGR_PPRE2_SHIFT)
+#  define RCC_CFGR_PPRE2_HCLK1        (0x0 << RCC_CFGR_PPRE2_SHIFT) /* 0xx: HCLK1 not divided */
+#  define RCC_CFGR_PPRE2_HCLK1d2      (0x4 << RCC_CFGR_PPRE2_SHIFT) /* 100: HCLK1 divided by 2 */
+#  define RCC_CFGR_PPRE2_HCLK1d4      (0x5 << RCC_CFGR_PPRE2_SHIFT) /* 101: HCLK1 divided by 4 */
+#  define RCC_CFGR_PPRE2_HCLK1d8      (0x6 << RCC_CFGR_PPRE2_SHIFT) /* 110: HCLK1 divided by 8 */
+#  define RCC_CFGR_PPRE2_HCLK1d16     (0x7 << RCC_CFGR_PPRE2_SHIFT) /* 111: HCLK1 divided by 16 */
+
+#define RCC_CFGR_STOPWUCK             (1 << 15) /* Bit 15: Wakeup from Stop and CSS backup clock selection */
+#  define RCC_CFGR_STOPWUCK_MSI       (0 << 15) /* 0: MSI */
+#  define RCC_CFGR_STOPWUCK_HSI16     (1 << 15) /* 0: HSI16 */
+
+#define RCC_CFGR_HPREF                (1 << 16) /* Bit 16: HCLK1 prescaler flag (AHB1, AHB2, AHB3, SRAM1) */
+#define RCC_CFGR_PPRE1F               (1 << 17) /* Bit 17: PCLK1 prescaler flag (APB1) */
+#define RCC_CFGR_PPRE2F               (1 << 18) /* Bit 18: PCLK2 prescaler flag (APB2) */
+
+#define RCC_CFGR_MCOSEL_SHIFT         (24)      /* Bits 24-27: Microcontroller Clock Output */
+#define RCC_CFGR_MCOSEL_MASK          (0xf << RCC_CFGR_MCOSEL_SHIFT)
+#  define RCC_CFGR_MCOSEL_DISABLED    (0x0 << RCC_CFGR_MCOSEL_SHIFT) /* 0000: Output disabled, no clock on MCO */
+#  define RCC_CFGR_MCOSEL_SYSCLK      (0x1 << RCC_CFGR_MCOSEL_SHIFT) /* 0001: SYSCLK system clock selected */
+#  define RCC_CFGR_MCOSEL_MSI         (0x2 << RCC_CFGR_MCOSEL_SHIFT) /* 0010: MSI clock selected */
+#  define RCC_CFGR_MCOSEL_HSI16       (0x3 << RCC_CFGR_MCOSEL_SHIFT) /* 0011: HSI16 clock selected */
+#  define RCC_CFGR_MCOSEL_HSE_AFT     (0x4 << RCC_CFGR_MCOSEL_SHIFT) /* 0100: HSE clock after stabilization */
+#  define RCC_CFGR_MCOSEL_PLL         (0x5 << RCC_CFGR_MCOSEL_SHIFT) /* 0101: Main PLLRCLK selected  */
+#  define RCC_CFGR_MCOSEL_LSI1        (0x6 << RCC_CFGR_MCOSEL_SHIFT) /* 0110: LSI1 clock selected */
+#  define RCC_CFGR_MCOSEL_LSI2        (0x7 << RCC_CFGR_MCOSEL_SHIFT) /* 0111: LSI2 clock selected */
+#  define RCC_CFGR_MCOSEL_LSE         (0x8 << RCC_CFGR_MCOSEL_SHIFT) /* 1000: LSE clock selected */
+#  define RCC_CFGR_MCOSEL_HSI48       (0x9 << RCC_CFGR_MCOSEL_SHIFT) /* 1001: HSI48 clock selected */
+#  define RCC_CFGR_MCOSEL_HSE_BFR     (0xc << RCC_CFGR_MCOSEL_SHIFT) /* 1100: HSE clock before stabilization */
+
+#define RCC_CFGR_MCOPRE_SHIFT         (28)      /* Bits 28-30: MCO prescaler */
+#define RCC_CFGR_MCOPRE_MASK          (0x7 << RCC_CFGR_MCOPRE_SHIFT)
+#  define RCC_CFGR_MCOPRE_DIV1        (0x0 << RCC_CFGR_MCOPRE_SHIFT) /* 000: no division */
+#  define RCC_CFGR_MCOPRE_DIV2        (0x1 << RCC_CFGR_MCOPRE_SHIFT) /* 001: division by 2 */
+#  define RCC_CFGR_MCOPRE_DIV4        (0x2 << RCC_CFGR_MCOPRE_SHIFT) /* 010: division by 4 */
+#  define RCC_CFGR_MCOPRE_DIV8        (0x3 << RCC_CFGR_MCOPRE_SHIFT) /* 011: division by 8 */
+#  define RCC_CFGR_MCOPRE_DIV16       (0x4 << RCC_CFGR_MCOPRE_SHIFT) /* 100: division by 16 */
+
+#define RCC_CFGR_RESET_MASK           (0x00070000)
+
+/* PLL configuration register */
+
+#define RCC_PLLCFG_PLLSRC_SHIFT       (0)       /* Bits 0-1: Main PLL and audio PLLSAI1 entry clock source */
+#define RCC_PLLCFG_PLLSRC_MASK        (0x3 << RCC_PLLCFG_PLLSRC_SHIFT)
+#  define RCC_PLLCFG_PLLSRC_NONE      (0x0 << RCC_PLLCFG_PLLSRC_SHIFT) /* 00: No clock sent to PLLs */
+#  define RCC_PLLCFG_PLLSRC_MSI       (0x1 << RCC_PLLCFG_PLLSRC_SHIFT) /* 01: MSI selected as PLLs source */
+#  define RCC_PLLCFG_PLLSRC_HSI16     (0x2 << RCC_PLLCFG_PLLSRC_SHIFT) /* 10: HSI16 selected as PLLs source */
+#  define RCC_PLLCFG_PLLSRC_HSE       (0x3 << RCC_PLLCFG_PLLSRC_SHIFT) /* 11: HSE selected as PLLs source */
+
+#define RCC_PLLCFG_PLLM_SHIFT         (4)       /* Bits 4-6: Main PLL and audio PLLSAI1 divider */
+#define RCC_PLLCFG_PLLM_MASK          (0x07 << RCC_PLLCFG_PLLM_SHIFT)
+#  define RCC_PLLCFG_PLLM(n)          ((n-1) << RCC_PLLCFG_PLLM_SHIFT) /* n = 1..8 */
+
+#define RCC_PLLCFG_PLLN_SHIFT         (8)       /* Bits 8-14: Main PLL (PLL) VCO multiplier */
+#define RCC_PLLCFG_PLLN_MASK          (0x7f << RCC_PLLCFG_PLLN_SHIFT)
+#  define RCC_PLLCFG_PLLN(n)          ((n) << RCC_PLLCFG_PLLN_SHIFT) /* n = 6..127 */
+
+#define RCC_PLLCFG_PLLPEN             (1 << 16) /* Bit 16: Main PLL PLLPCLK output enable */
+
+#define RCC_PLLCFG_PLLP_SHIFT         (17)      /* Bits 17-21: Main PLL div factor for PLLPCLK */
+#define RCC_PLLCFG_PLLP_MASK          (0x1f << RCC_PLLCFG_PLLP_SHIFT)
+#  define RCC_PLLCFG_PLLP(n)          ((n-1) << RCC_PLLCFG_PLLP_SHIFT) /* n = 2..32 */
+
+#define RCC_PLLCFG_PLLQEN             (1 << 24) /* Bit 24: Main PLL PLLQCLK output enable */
+
+#define RCC_PLLCFG_PLLQ_SHIFT         (25)      /* Bits 25-27: Main PLL division factor for PLLQCLK */
+#define RCC_PLLCFG_PLLQ_MASK          (0x7 << RCC_PLLCFG_PLLQ_SHIFT)
+#  define RCC_PLLCFG_PLLQ(n)          ((n-1) << RCC_PLLCFG_PLLQ_SHIFT) /* n = 2..8 */
+
+#define RCC_PLLCFG_PLLREN             (1 << 28) /* Bit 28: Main PLL PLLRCLK output enable */
+
+#define RCC_PLLCFG_PLLR_SHIFT         (29)      /* Bits 29-31: Main PLL division factor for PLLRCLK */
+#define RCC_PLLCFG_PLLR_MASK          (0x7 << RCC_PLLCFG_PLLR_SHIFT)
+#  define RCC_PLLCFG_PLLR(n)          ((n-1) << RCC_PLLCFG_PLLR_SHIFT) /* n = 2..8 */
+
+#define RCC_PLLCFG_RESET              (0x22040100) /* PLLCFG reset value */
+
+/* PLLSAI1 Configuration register */
+
+#define RCC_PLLSAI1CFG_PLLN_SHIFT     (8)       /* Bits 8-14: SAI1 PLL (PLLSAI1) VCO multiplier */
+#define RCC_PLLSAI1CFG_PLLN_MASK      (0x7f << RCC_PLLSAI1CFG_PLLN_SHIFT)
+#  define RCC_PLLSAI1CFG_PLLN(n)      ((n) << RCC_PLLSAI1CFG_PLLN_SHIFT) /* n = 4..86 */
+
+#define RCC_PLLSAI1CFG_PLLPEN         (1 << 16) /* Bit 16: SAI1 PLL PLLSAI1CLK output enable */
+
+#define RCC_PLLSAI1CFG_PLLP_SHIFT     (17)      /* Bit 17-21: Main PLL div factor for PLLSAI1CLK */
+#define RCC_PLLSAI1CFG_PLLP_MASK      (0x1f << RCC_PLLSAI1CFG_PLLP_SHIFT)
+#  define RCC_PLLSAI1CFG_PLLP(n)      ((n-1) << RCC_PLLSAI1CFG_PLLP_SHIFT) /* n = 2..32 */
+
+#define RCC_PLLSAI1CFG_PLLQEN         (1 << 24) /* Bit 24: PLLSAI1QCLK output enable */
+
+#define RCC_PLLSAI1CFG_PLLQ_SHIFT     (25)      /* Bits 25-27: PLLSAI1 division factor for PLLSAI1QCLK */
+#define RCC_PLLSAI1CFG_PLLQ_MASK      (0x3 << RCC_PLLSAI1CFG_PLLQ_SHIFT)
+#  define RCC_PLLSAI1CFG_PLLQ(n)      ((n-1) << RCC_PLLSAI1CFG_PLLQ_SHIFT) /* n = 2..8 */
+
+#define RCC_PLLSAI1CFG_PLLREN         (1 << 28) /* Bit 28: SAI1 PLL PLLSAI1RCLK output enable */
+
+#define RCC_PLLSAI1CFG_PLLR_SHIFT     (29)      /* Bits 29-31: PLLSAI1 division factor for PLLSAI1RCLK */
+#define RCC_PLLSAI1CFG_PLLR_MASK      (0x3 << RCC_PLLSAI1CFG_PLLR_SHIFT)
+#  define RCC_PLLSAI1CFG_PLLR(n)      ((n-1) << RCC_PLLSAI1CFG_PLLR_SHIFT) /* n = 2..8 */

Review Comment:
   ```suggestion
   #  define RCC_PLLSAI1CFG_PLLR(n)      (((n) - 1) << RCC_PLLSAI1CFG_PLLR_SHIFT) /* n = 2..8 */
   ```



##########
arch/arm/src/stm32wb/stm32wb_rcc.h:
##########
@@ -0,0 +1,279 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_rcc.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_STM32WB_RCC_H
+#define __ARCH_ARM_SRC_STM32WB_STM32WB_RCC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "chip.h"
+#include "hardware/stm32wb_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/* This symbol references the Cortex-M4 vector table (as positioned by the
+ * linker script, ld.script or ld.script.dfu.  The standard location for the
+ * vector table is at the beginning of FLASH at address 0x0800:0000.  If we
+ * are using the STMicro DFU bootloader, then the vector table will be offset
+ * to a different location in FLASH and we will need to set the NVIC vector
+ * location to this alternative location.
+ */
+
+#if defined(__ICCARM__)
+/* _vectors replaced on __vector_table for IAR C-SPY Simulator */
+
+extern uint32_t __vector_table[];
+#else
+extern uint32_t _vectors[];

Review Comment:
   ```suggestion
   EXTERN uint32_t _vectors[];
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_syscfg.h:
##########
@@ -0,0 +1,227 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_syscfg.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_SYSCFG_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_SYSCFG_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "chip.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_SYSCFG_MEMRMP_OFFSET    0x0000 /* SYSCFG memory remap register */
+#define STM32WB_SYSCFG_CFGR1_OFFSET     0x0004 /* SYSCFG configuration register 1 */
+
+#define STM32WB_SYSCFG_EXTICR_OFFSET(p) (0x0008 + ((p) & 0x0c)) /* Pin p = 0..15 */
+
+#define STM32WB_SYSCFG_EXTICR1_OFFSET   0x0008 /* SYSCFG external interrupt configuration register 1 */
+#define STM32WB_SYSCFG_EXTICR2_OFFSET   0x000c /* SYSCFG external interrupt configuration register 2 */
+#define STM32WB_SYSCFG_EXTICR3_OFFSET   0x0010 /* SYSCFG external interrupt configuration register 3 */
+#define STM32WB_SYSCFG_EXTICR4_OFFSET   0x0014 /* SYSCFG external interrupt configuration register 4 */
+
+#define STM32WB_SYSCFG_SCSR_OFFSET      0x0018 /* SYSCFG SRAM2 control and status register */
+#define STM32WB_SYSCFG_CFGR2_OFFSET     0x001c /* SYSCFG configuration register 2 */
+#define STM32WB_SYSCFG_SWPR1_OFFSET     0x0020 /* SYSCFG SRAM2 write protection register 1 */
+#define STM32WB_SYSCFG_SKR_OFFSET       0x0024 /* SYSCFG SRAM2 key register */
+#define STM32WB_SYSCFG_SWPR2_OFFSET     0x0028 /* SYSCFG SRAM2 write protection register 2 */
+
+#define STM32WB_SYSCFG_IMR1_OFFSET      0x0100 /* SYSCFG Interrupt mask register 1 */
+#define STM32WB_SYSCFG_IMR2_OFFSET      0x0104 /* SYSCFG Interrupt mask register 2 */
+#define STM32WB_SYSCFG_C2IMR1_OFFSET    0x0108 /* SYSCFG CPU2 Interrupt mask register 1 */
+#define STM32WB_SYSCFG_C2IMR2_OFFSET    0x010c /* SYSCFG CPU2 Interrupt mask register 2 */
+#define STM32WB_SYSCFG_SIPCR_OFFSET     0x0110 /* SYSCFG Secure IP control register */
+
+/* Register Addresses *******************************************************/
+
+#define STM32WB_SYSCFG_MEMRMP           (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_MEMRMP_OFFSET)
+#define STM32WB_SYSCFG_CFGR1            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_CFGR1_OFFSET)
+#define STM32WB_SYSCFG_EXTICR(p)        (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR_OFFSET(p))
+#define STM32WB_SYSCFG_EXTICR1          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR1_OFFSET)
+#define STM32WB_SYSCFG_EXTICR2          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR2_OFFSET)
+#define STM32WB_SYSCFG_EXTICR3          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR3_OFFSET)
+#define STM32WB_SYSCFG_EXTICR4          (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_EXTICR4_OFFSET)
+#define STM32WB_SYSCFG_SCSR             (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SCSR_OFFSET)
+#define STM32WB_SYSCFG_CFGR2            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_CFGR2_OFFSET)
+#define STM32WB_SYSCFG_SWPR1            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SWPR1_OFFSET)
+#define STM32WB_SYSCFG_SKR              (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SKR_OFFSET)
+#define STM32WB_SYSCFG_SWPR2            (STM32WB_SYSCFG_BASE + STM32WB_SYSCFG_SWPR2_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* SYSCFG memory remap register */
+
+#define SYSCFG_MEMRMP_SHIFT           (0)       /* Bits 2-0: Memory mapping selection */
+#define SYSCFG_MEMRMP_MASK            (0x7 << SYSCFG_MEMRMP_SHIFT)
+#  define SYSCFG_MEMRMP_FLASH         (0x0 << SYSCFG_MEMRMP_SHIFT) /* 000: Main Flash memory mapped at 0x00000000 */
+#  define SYSCFG_MEMRMP_SYSTEM        (0x1 << SYSCFG_MEMRMP_SHIFT) /* 001: System Flash memory mapped at 0x00000000 */
+#  define SYSCFG_MEMRMP_SRAM          (0x3 << SYSCFG_MEMRMP_SHIFT) /* 011: SRAM1 mapped at 0x00000000 */
+#  define SYSCFG_MEMRMP_QSPI          (0x6 << SYSCFG_MEMRMP_SHIFT) /* 110: QSPI memory mapped at 0x00000000 */
+
+/* SYSCFG configuration register 1 */
+
+#define SYSCFG_CFGR1_BOOSTEN          (1 << 8)  /* Bit  8: I/O analog switch voltage booster enable */
+#define SYSCFG_CFGR1_I2C_PB6_FMP      (1 << 16) /* Bit 16: Fast-mode Plus (Fm+) driving capability activation on PB6 */
+#define SYSCFG_CFGR1_I2C_PB7_FMP      (1 << 17) /* Bit 17: Fast-mode Plus (Fm+) driving capability activation on PB7 */
+#define SYSCFG_CFGR1_I2C_PB8_FMP      (1 << 18) /* Bit 18: Fast-mode Plus (Fm+) driving capability activation on PB8 */
+#define SYSCFG_CFGR1_I2C_PB9_FMP      (1 << 19) /* Bit 19: Fast-mode Plus (Fm+) driving capability activation on PB9 */
+#define SYSCFG_CFGR1_I2C1_FMP         (1 << 20) /* Bit 20: I2C1 Fast-mode Plus (Fm+) driving capability activation */
+#define SYSCFG_CFGR1_I2C3_FMP         (1 << 22) /* Bit 22: I2C3 Fast-mode Plus (Fm+) driving capability activation */
+#define SYSCFG_CFGR1_FPU_IE0          (1 << 26) /* Bit 26: FPU Invalid operation interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE1          (1 << 27) /* Bit 27: FPU Divide-by-zero interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE2          (1 << 28) /* Bit 28: FPU Underflow interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE3          (1 << 29) /* Bit 29: FPU Overflow interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE4          (1 << 30) /* Bit 30: FPU Input denormal interrupt enable */
+#define SYSCFG_CFGR1_FPU_IE5          (1 << 31) /* Bit 31: FPU Inexact interrupt enable */
+
+/* SYSCFG external interrupt configuration register 1-4 */
+
+#define SYSCFG_EXTICR_PORTA           (0x0)     /* 000: PA[x] pin */
+#define SYSCFG_EXTICR_PORTB           (0x1)     /* 001: PB[x] pin */
+#define SYSCFG_EXTICR_PORTC           (0x2)     /* 010: PC[x] pin */
+#define SYSCFG_EXTICR_PORTD           (0x3)     /* 011: PD[x] pin */
+#define SYSCFG_EXTICR_PORTE           (0x4)     /* 100: PE[x] pin */
+#define SYSCFG_EXTICR_PORTH           (0x7)     /* 111: PH[x] pin */
+
+#define SYSCFG_EXTICR_PORT_MASK       (0x7)
+#define SYSCFG_EXTICR_EXTI_SHIFT(g)   (((g) & 0x3) << 2)
+#define SYSCFG_EXTICR_EXTI_MASK(g)    (SYSCFG_EXTICR_PORT_MASK << (SYSCFG_EXTICR_EXTI_SHIFT(g)))
+
+#define SYSCFG_EXTICR1_EXTI0_SHIFT    (0)       /* Bits 0-2: EXTI 0 configuration */
+#define SYSCFG_EXTICR1_EXTI0_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI0_SHIFT)
+#define SYSCFG_EXTICR1_EXTI1_SHIFT    (4)       /* Bits 4-6: EXTI 1 configuration */
+#define SYSCFG_EXTICR1_EXTI1_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI1_SHIFT)
+#define SYSCFG_EXTICR1_EXTI2_SHIFT    (8)       /* Bits 8-10: EXTI 2 configuration */
+#define SYSCFG_EXTICR1_EXTI2_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI2_SHIFT)
+#define SYSCFG_EXTICR1_EXTI3_SHIFT    (12)      /* Bits 12-14: EXTI 3 configuration */
+#define SYSCFG_EXTICR1_EXTI3_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR1_EXTI3_SHIFT)
+
+#define SYSCFG_EXTICR2_EXTI4_SHIFT    (0)       /* Bits 0-2: EXTI 4 configuration */
+#define SYSCFG_EXTICR2_EXTI4_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI4_SHIFT)
+#define SYSCFG_EXTICR2_EXTI5_SHIFT    (4)       /* Bits 4-6: EXTI 5 configuration */
+#define SYSCFG_EXTICR2_EXTI5_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI5_SHIFT)
+#define SYSCFG_EXTICR2_EXTI6_SHIFT    (8)       /* Bits 8-10: EXTI 6 configuration */
+#define SYSCFG_EXTICR2_EXTI6_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI6_SHIFT)
+#define SYSCFG_EXTICR2_EXTI7_SHIFT    (12)      /* Bits 12-14: EXTI 7 configuration */
+#define SYSCFG_EXTICR2_EXTI7_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR2_EXTI7_SHIFT)
+
+#define SYSCFG_EXTICR3_EXTI8_SHIFT    (0)       /* Bits 0-2: EXTI 8 configuration */
+#define SYSCFG_EXTICR3_EXTI8_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI8_SHIFT)
+#define SYSCFG_EXTICR3_EXTI9_SHIFT    (4)       /* Bits 4-6: EXTI 9 configuration */
+#define SYSCFG_EXTICR3_EXTI9_MASK     (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI9_SHIFT)
+#define SYSCFG_EXTICR3_EXTI10_SHIFT   (8)       /* Bits 8-10: EXTI 10 configuration */
+#define SYSCFG_EXTICR3_EXTI10_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI10_SHIFT)
+#define SYSCFG_EXTICR3_EXTI11_SHIFT   (12)      /* Bits 12-14: EXTI 11 configuration */
+#define SYSCFG_EXTICR3_EXTI11_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR3_EXTI11_SHIFT)
+
+#define SYSCFG_EXTICR4_EXTI12_SHIFT   (0)       /* Bits 0-2: EXTI 12 configuration */
+#define SYSCFG_EXTICR4_EXTI12_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI12_SHIFT)
+#define SYSCFG_EXTICR4_EXTI13_SHIFT   (4)       /* Bits 4-6: EXTI 13 configuration */
+#define SYSCFG_EXTICR4_EXTI13_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI13_SHIFT)
+#define SYSCFG_EXTICR4_EXTI14_SHIFT   (8)       /* Bits 8-10: EXTI 14 configuration */
+#define SYSCFG_EXTICR4_EXTI14_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI14_SHIFT)
+#define SYSCFG_EXTICR4_EXTI15_SHIFT   (12)      /* Bits 12-14: EXTI 15 configuration */
+#define SYSCFG_EXTICR4_EXTI15_MASK    (SYSCFG_EXTICR_PORT_MASK << SYSCFG_EXTICR4_EXTI15_SHIFT)
+
+/* SYSCFG SRAM2 control and status register */
+
+#define SYSCFG_SCSR_SRAM2ER           (1 << 0)  /* Bit  0: SRAM2 and PKA RAM Erase */
+#define SYSCFG_SCSR_SRAM2BSY          (1 << 1)  /* Bit  1: SRAM2 and PKA RAM busy by erase operation */
+#define SYSCFG_SCSR_C2RFD             (1 << 31) /* Bit 31: CPU2 SRAM fetch disable */
+
+/* SYSCFG configuration register 2 */
+
+#define SYSCFG_CFGR2_CLL              (1 << 0)  /* Bit 0: LOCKUP (Hardfault) output enable */
+#define SYSCFG_CFGR2_SPL              (1 << 1)  /* Bit 1: SRAM2 parity lock enable  */
+#define SYSCFG_CFGR2_PVDL             (1 << 2)  /* Bit 2: PVD lock enable */
+#define SYSCFG_CFGR2_ECCL             (1 << 3)  /* Bit 3: ECC lock enable */
+#define SYSCFG_CFGR2_SPF              (1 << 8)  /* Bit 8: SRAM2 parity error flag */
+
+/* SYSCFG SRAM2 write protection register 1 */
+
+#define SYSCFG_SWPR1_PWP(x)           (1 << (x)) /* Bits 0-31: SRAM2 1Kb page x = 0..31 write protection */
+
+/* SYSCFG SRAM2 key register */
+
+#define SYSCFG_SKR_SHIFT              (0)
+#define SYSCFG_SKR_MASK               (0xff << SYSCFG_SKR_SHIFT)
+#define SYSCFG_SKR_KEY1               0xca
+#define SYSCFG_SKR_KEY2               0x53
+
+/* SYSCFG SRAM2 write protection register 2 */
+
+#define SYSCFG_SWPR2_PWP(x)           (1 << ((x)-32)) /* Bits 0-31: SRAM2 1Kb page x = 32..63 write protection */
+
+/* SYSCFG Interrupt mask register 1 */
+
+#define SYSCFG_IMR1_EXTIIM(n)         (1 << ((n)+16)) /* EXTI[n] interrupt mask, n = 5..15 */
+
+#define SYSCFG_IMR1_TIM1IM            (1 << 13) /* Bit 13: TIM1 interrupt mask */
+#define SYSCFG_IMR1_TIM16IM           (1 << 14) /* Bit 14: TIM16 interrupt mask */
+#define SYSCFG_IMR1_TIM17IM           (1 << 15) /* Bit 15: TIM17 interrupt mask */
+
+/* SYSCFG Interrupt mask register 2 */
+
+#define SYSCFG_IMR2_PVM1IM            (1 << 16) /* Bit 16: PVM1 interrupt mask */
+#define SYSCFG_IMR2_PVM3IM            (1 << 18) /* Bit 18: PVM3 interrupt mask */
+#define SYSCFG_IMR2_PVDIM             (1 << 20) /* Bit 20: PVD interrupt mask */
+
+/* SYSCFG CPU2 Interrupt mask register 1 */
+
+#define SYSCFG_C2IMR1_RTCSTLSECSSIM   (1 << 0)  /* Bit 0: CPU2 RTC, STAMP, TAMP, LSECSS interrupt mask */
+#define SYSCFG_C2IMR1_RTCWKUPIM       (1 << 3)  /* Bit 3: CPU2 RTC Wakeup interrupt mask */
+#define SYSCFG_C2IMR1_RTCALRMIM       (1 << 4)  /* Bit 4: CPU2 RTC Alarm interrupt mask */
+#define SYSCFG_C2IMR1_RCCIM           (1 << 5)  /* Bit 5: CPU2 RCC interrupt mask */
+#define SYSCFG_C2IMR1_FLASHIM         (1 << 6)  /* Bit 6: CPU2 FLASH interrupt mask */
+#define SYSCFG_C2IMR1_PKAIM           (1 << 8)  /* Bit 8: CPU2 PKA interrupt mask */
+#define SYSCFG_C2IMR1_RNGIM           (1 << 9)  /* Bit 9: CPU2 RNG interrupt mask */
+#define SYSCFG_C2IMR1_AES1IM          (1 << 10) /* Bit 10: CPU2 AES1 interrupt mask */
+#define SYSCFG_C2IMR1_COMPIM          (1 << 11) /* Bit 11: CPU2 COMP interrupt mask */
+#define SYSCFG_C2IMR1_ADCIM           (1 << 12) /* Bit 12: CPU2 ADC interrupt mask */
+
+#define SYSCFG_C2IMR1_EXTIIM(n)       (1 << ((n)+16)) /* CPU2 EXTI[n] interrupt mask, n = 0..15 */
+
+/* SYSCFG CPU2 Interrupt mask register 2 */
+
+#define SYSCFG_C2IMR2_DMA1IM(n)       (1 << ((n)-1)) /* CPU2 DMA1[n] interrupt mask, n = 1..7 */

Review Comment:
   ```suggestion
   #define SYSCFG_C2IMR2_DMA1IM(n)       (1 << ((n) - 1)) /* CPU2 DMA1[n] interrupt mask, n = 1..7 */
   ```



##########
arch/arm/src/stm32wb/stm32wb_pmsleep.c:
##########
@@ -0,0 +1,84 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_pmsleep.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+
+#include "nvic.h"
+#include "stm32wb_pwr.h"
+#include "stm32wb_pm.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_pmsleep
+ *
+ * Description:
+ *   Enter SLEEP mode.
+ *
+ * Input Parameters:
+ *   sleeponexit - true:  SLEEPONEXIT bit is set when the WFI instruction is
+ *                        executed, the MCU enters Sleep mode as soon as it
+ *                        exits the lowest priority ISR.
+ *               - false: SLEEPONEXIT bit is cleared, the MCU enters Sleep
+ *                        mode as soon as WFI or WFE instruction is executed.
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void stm32wb_pmsleep(bool sleeponexit)
+{
+  uint32_t regval;
+
+  /* Clear SLEEPDEEP bit of Cortex System Control Register */
+
+  regval  = getreg32(NVIC_SYSCON);
+  regval &= ~NVIC_SYSCON_SLEEPDEEP;
+  if (sleeponexit)
+    {
+      regval |= NVIC_SYSCON_SLEEPONEXIT;
+    }
+  else
+    {
+      regval &= ~NVIC_SYSCON_SLEEPONEXIT;
+    }
+
+  putreg32(regval, NVIC_SYSCON);
+
+  /* Sleep until the wakeup interrupt or event occurs */
+
+#ifdef CONFIG_PM_WFE
+  /* Mode: SLEEP + Entry with WFE */
+
+  asm("wfe");
+#else
+  /* Mode: SLEEP + Entry with WFI */
+
+  asm("wfi");

Review Comment:
   Do we need `__asm__ volatile` as in other files?



##########
arch/arm/src/stm32wb/stm32wb_rcc.h:
##########
@@ -0,0 +1,279 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_rcc.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_STM32WB_RCC_H
+#define __ARCH_ARM_SRC_STM32WB_STM32WB_RCC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "chip.h"
+#include "hardware/stm32wb_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/* This symbol references the Cortex-M4 vector table (as positioned by the
+ * linker script, ld.script or ld.script.dfu.  The standard location for the
+ * vector table is at the beginning of FLASH at address 0x0800:0000.  If we
+ * are using the STMicro DFU bootloader, then the vector table will be offset
+ * to a different location in FLASH and we will need to set the NVIC vector
+ * location to this alternative location.
+ */
+
+#if defined(__ICCARM__)
+/* _vectors replaced on __vector_table for IAR C-SPY Simulator */
+
+extern uint32_t __vector_table[];

Review Comment:
   ```suggestion
   EXTERN uint32_t __vector_table[];
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_rcc.h:
##########
@@ -0,0 +1,877 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_rcc.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_RCC_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_RCC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_RCC_CR_OFFSET           0x0000  /* Clock control register */
+#define STM32WB_RCC_ICSCR_OFFSET        0x0004  /* Internal clock sources calibration register */
+#define STM32WB_RCC_CFGR_OFFSET         0x0008  /* Clock configuration register */
+#define STM32WB_RCC_PLLCFG_OFFSET       0x000c  /* PLL configuration register */
+#define STM32WB_RCC_PLLSAI1CFG_OFFSET   0x0010  /* PLLSAI1 configuration register */
+#define STM32WB_RCC_CIER_OFFSET         0x0018  /* Clock interrupt enable register */
+#define STM32WB_RCC_CIFR_OFFSET         0x001c  /* Clock interrupt flag register */
+#define STM32WB_RCC_CICR_OFFSET         0x0020  /* Clock interrupt clear register */
+#define STM32WB_RCC_SMPSCR_OFFSET       0x0024  /* Step-down converter control register */
+#define STM32WB_RCC_AHB1RSTR_OFFSET     0x0028  /* AHB1 peripheral reset register */
+#define STM32WB_RCC_AHB2RSTR_OFFSET     0x002c  /* AHB2 peripheral reset register */
+#define STM32WB_RCC_AHB3RSTR_OFFSET     0x0030  /* AHB3 peripheral reset register */
+#define STM32WB_RCC_APB1RSTR1_OFFSET    0x0038  /* APB1 Peripheral reset register 1 */
+#define STM32WB_RCC_APB1RSTR2_OFFSET    0x003c  /* APB1 Peripheral reset register 2 */
+#define STM32WB_RCC_APB2RSTR_OFFSET     0x0040  /* APB2 Peripheral reset register */
+#define STM32WB_RCC_APB3RSTR_OFFSET     0x0044  /* APB3 Peripheral reset register */
+#define STM32WB_RCC_AHB1ENR_OFFSET      0x0048  /* AHB1 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB2ENR_OFFSET      0x004c  /* AHB2 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB3ENR_OFFSET      0x0050  /* AHB3 Peripheral Clock enable register */
+#define STM32WB_RCC_APB1ENR1_OFFSET     0x0058  /* APB1 Peripheral Clock enable register 1 */
+#define STM32WB_RCC_APB1ENR2_OFFSET     0x005c  /* APB1 Peripheral Clock enable register 2 */
+#define STM32WB_RCC_APB2ENR_OFFSET      0x0060  /* APB2 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB1SMENR_OFFSET    0x0068  /* AHB1 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_AHB2SMENR_OFFSET    0x006c  /* AHB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_AHB3SMENR_OFFSET    0x0070  /* AHB3 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_APB1SMENR1_OFFSET   0x0078  /* APB1 clock enable in sleep and stop modes register 1 */
+#define STM32WB_RCC_APB1SMENR2_OFFSET   0x007c  /* APB1 clock enable in sleep and stop modes register 2 */
+#define STM32WB_RCC_APB2SMENR_OFFSET    0x0080  /* APB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_CCIPR_OFFSET        0x0088  /* Peripherals independent clock configuration register */
+#define STM32WB_RCC_BDCR_OFFSET         0x0090  /* Backup domain control register */
+#define STM32WB_RCC_CSR_OFFSET          0x0094  /* Control/status register */
+#define STM32WB_RCC_CRRCR_OFFSET        0x0098  /* Clock recovery RC register */
+#define STM32WB_RCC_HSECR_OFFSET        0x009c  /* Clock HSE register */
+#define STM32WB_RCC_EXTCFGR_OFFSET      0x0108  /* Extended clock recovery register */
+#define STM32WB_RCC_C2AHB1ENR_OFFSET    0x0148  /* CPU2 AHB1 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB2ENR_OFFSET    0x014c  /* CPU2 AHB2 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB3ENR_OFFSET    0x0150  /* CPU2 AHB3 Peripheral Clock enable register */
+#define STM32WB_RCC_C2APB1ENR1_OFFSET   0x0158  /* CPU2 APB1 Peripheral Clock enable register 1 */
+#define STM32WB_RCC_C2APB1ENR2_OFFSET   0x015c  /* CPU2 APB1 Peripheral Clock enable register 2 */
+#define STM32WB_RCC_C2APB2ENR_OFFSET    0x0160  /* CPU2 APB2 Peripheral Clock enable register */
+#define STM32WB_RCC_C2APB3ENR_OFFSET    0x0164  /* CPU2 APB3 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB1SMENR_OFFSET  0x0168  /* CPU2 AHB1 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2AHB2SMENR_OFFSET  0x016c  /* CPU2 AHB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2AHB3SMENR_OFFSET  0x0170  /* CPU2 AHB3 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2APB1SMENR1_OFFSET 0x0178  /* CPU2 APB1 clock enable in sleep and stop modes register 1 */
+#define STM32WB_RCC_C2APB1SMENR2_OFFSET 0x017c  /* CPU2 APB1 clock enable in sleep and stop modes register 2 */
+#define STM32WB_RCC_C2APB2SMENR_OFFSET  0x0180  /* CPU2 APB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2APB3SMENR_OFFSET  0x0184  /* CPU2 APB3 clock enable in sleep and stop modes register */
+
+/* Register Addresses *******************************************************/
+
+#define STM32WB_RCC_CR                (STM32WB_RCC_BASE + STM32WB_RCC_CR_OFFSET)
+#define STM32WB_RCC_ICSCR             (STM32WB_RCC_BASE + STM32WB_RCC_ICSCR_OFFSET)
+#define STM32WB_RCC_CFGR              (STM32WB_RCC_BASE + STM32WB_RCC_CFGR_OFFSET)
+#define STM32WB_RCC_PLLCFG            (STM32WB_RCC_BASE + STM32WB_RCC_PLLCFG_OFFSET)
+#define STM32WB_RCC_PLLSAI1CFG        (STM32WB_RCC_BASE + STM32WB_RCC_PLLSAI1CFG_OFFSET)
+#define STM32WB_RCC_CIER              (STM32WB_RCC_BASE + STM32WB_RCC_CIER_OFFSET)
+#define STM32WB_RCC_CIFR              (STM32WB_RCC_BASE + STM32WB_RCC_CIFR_OFFSET)
+#define STM32WB_RCC_CICR              (STM32WB_RCC_BASE + STM32WB_RCC_CICR_OFFSET)
+#define STM32WB_RCC_SMPSCR            (STM32WB_RCC_BASE + STM32WB_RCC_SMPSCR_OFFSET)
+#define STM32WB_RCC_AHB1RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB1RSTR_OFFSET)
+#define STM32WB_RCC_AHB2RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB2RSTR_OFFSET)
+#define STM32WB_RCC_AHB3RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB3RSTR_OFFSET)
+#define STM32WB_RCC_APB1RSTR1         (STM32WB_RCC_BASE + STM32WB_RCC_APB1RSTR1_OFFSET)
+#define STM32WB_RCC_APB1RSTR2         (STM32WB_RCC_BASE + STM32WB_RCC_APB1RSTR2_OFFSET)
+#define STM32WB_RCC_APB2RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_APB2RSTR_OFFSET)
+#define STM32WB_RCC_APB3RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_APB3RSTR_OFFSET)
+#define STM32WB_RCC_AHB1ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB1ENR_OFFSET)
+#define STM32WB_RCC_AHB2ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB2ENR_OFFSET)
+#define STM32WB_RCC_AHB3ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB3ENR_OFFSET)
+#define STM32WB_RCC_APB1ENR1          (STM32WB_RCC_BASE + STM32WB_RCC_APB1ENR1_OFFSET)
+#define STM32WB_RCC_APB1ENR2          (STM32WB_RCC_BASE + STM32WB_RCC_APB1ENR2_OFFSET)
+#define STM32WB_RCC_APB2ENR           (STM32WB_RCC_BASE + STM32WB_RCC_APB2ENR_OFFSET)
+#define STM32WB_RCC_AHB1SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB1SMENR_OFFSET)
+#define STM32WB_RCC_AHB2SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB2SMENR_OFFSET)
+#define STM32WB_RCC_AHB3SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB3SMENR_OFFSET)
+#define STM32WB_RCC_APB1SMENR1        (STM32WB_RCC_BASE + STM32WB_RCC_APB1SMENR1_OFFSET)
+#define STM32WB_RCC_APB1SMENR2        (STM32WB_RCC_BASE + STM32WB_RCC_APB1SMENR2_OFFSET)
+#define STM32WB_RCC_APB2SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_APB2SMENR_OFFSET)
+#define STM32WB_RCC_CCIPR             (STM32WB_RCC_BASE + STM32WB_RCC_CCIPR_OFFSET)
+#define STM32WB_RCC_BDCR              (STM32WB_RCC_BASE + STM32WB_RCC_BDCR_OFFSET)
+#define STM32WB_RCC_CSR               (STM32WB_RCC_BASE + STM32WB_RCC_CSR_OFFSET)
+#define STM32WB_RCC_CRRCR             (STM32WB_RCC_BASE + STM32WB_RCC_CRRCR_OFFSET)
+#define STM32WB_RCC_HSECR             (STM32WB_RCC_BASE + STM32WB_RCC_HSECR_OFFSET)
+#define STM32WB_RCC_EXTCFGR           (STM32WB_RCC_BASE + STM32WB_RCC_EXTCFGR_OFFSET)
+#define STM32WB_RCC_C2AHB1ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB1ENR_OFFSET)
+#define STM32WB_RCC_C2AHB2ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB2ENR_OFFSET)
+#define STM32WB_RCC_C2AHB3ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB3ENR_OFFSET)
+#define STM32WB_RCC_C2APB1ENR1        (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1ENR1_OFFSET)
+#define STM32WB_RCC_C2APB1ENR2        (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1ENR2_OFFSET)
+#define STM32WB_RCC_C2APB2ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2APB2ENR_OFFSET)
+#define STM32WB_RCC_C2APB3ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2APB3ENR_OFFSET)
+#define STM32WB_RCC_C2AHB1SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB1SMENR_OFFSET)
+#define STM32WB_RCC_C2AHB2SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB2SMENR_OFFSET)
+#define STM32WB_RCC_C2AHB3SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB3SMENR_OFFSET)
+#define STM32WB_RCC_C2APB1SMENR1      (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1SMENR1_OFFSET)
+#define STM32WB_RCC_C2APB1SMENR2      (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1SMENR2_OFFSET)
+#define STM32WB_RCC_C2APB2SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2APB2SMENR_OFFSET)
+#define STM32WB_RCC_C2APB3SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2APB3SMENR_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Clock control register */
+
+#define RCC_CR_MSION                  (1 << 0)  /* Bit 0: Internal Multi Speed clock enable */
+#define RCC_CR_MSIRDY                 (1 << 1)  /* Bit 1: Internal Multi Speed clock ready flag */
+#define RCC_CR_MSIPLLEN               (1 << 2)  /* Bit 2: MSI clock PLL enable */
+#define RCC_CR_MSIRANGE_SHIFT         (4)       /* Bits 7-4: MSI clock range */
+#define RCC_CR_MSIRANGE_MASK          (0x0f << RCC_CR_MSIRANGE_SHIFT)
+#  define RCC_CR_MSIRANGE_100K        (0  << RCC_CR_MSIRANGE_SHIFT) /* 0000: around 100 kHz */
+#  define RCC_CR_MSIRANGE_200K        (1  << RCC_CR_MSIRANGE_SHIFT) /* 0001: around 200 kHz */
+#  define RCC_CR_MSIRANGE_400K        (2  << RCC_CR_MSIRANGE_SHIFT) /* 0010: around 400 kHz */
+#  define RCC_CR_MSIRANGE_800K        (3  << RCC_CR_MSIRANGE_SHIFT) /* 0011: around 800 kHz */
+#  define RCC_CR_MSIRANGE_1M          (4  << RCC_CR_MSIRANGE_SHIFT) /* 0100: around 1 MHz */
+#  define RCC_CR_MSIRANGE_2M          (5  << RCC_CR_MSIRANGE_SHIFT) /* 0101: around 2 MHz */
+#  define RCC_CR_MSIRANGE_4M          (6  << RCC_CR_MSIRANGE_SHIFT) /* 0110: around 4 MHz */
+#  define RCC_CR_MSIRANGE_8M          (7  << RCC_CR_MSIRANGE_SHIFT) /* 0111: around 8 MHz */
+#  define RCC_CR_MSIRANGE_16M         (8  << RCC_CR_MSIRANGE_SHIFT) /* 1000: around 16 MHz */
+#  define RCC_CR_MSIRANGE_24M         (9  << RCC_CR_MSIRANGE_SHIFT) /* 1001: around 24 MHz */
+#  define RCC_CR_MSIRANGE_32M         (10 << RCC_CR_MSIRANGE_SHIFT) /* 1010: around 32 MHz */
+#  define RCC_CR_MSIRANGE_48M         (11 << RCC_CR_MSIRANGE_SHIFT) /* 1011: around 48 MHz */
+
+#define RCC_CR_HSION                  (1 << 8)  /* Bit 8:  HSI16 clock enable */
+#define RCC_CR_HSIKERON               (1 << 9)  /* Bit 9:  HSI16 always enable for peripheral kernels */
+#define RCC_CR_HSIRDY                 (1 << 10) /* Bit 10: HSI16 clock ready flag */
+#define RCC_CR_HSIASFS                (1 << 11) /* Bit 11: HSI16 automatic start from stop */
+#define RCC_CR_HSEON                  (1 << 16) /* Bit 16: HSE clock enable */
+#define RCC_CR_HSERDY                 (1 << 17) /* Bit 17: HSE clock ready flag */
+#define RCC_CR_CSSON                  (1 << 19) /* Bit 19: Clock Security System enable */
+#define RCC_CR_HSEPRE                 (1 << 20) /* Bit 20: HSE sysclk and PLL M divider prescaler */
+#define RCC_CR_PLLON                  (1 << 24) /* Bit 24: PLL enable */
+#define RCC_CR_PLLRDY                 (1 << 25) /* Bit 25: PLL clock ready flag */
+#define RCC_CR_PLLSAI1ON              (1 << 26) /* Bit 26: PLLSAI1 enable */
+#define RCC_CR_PLLSAI1RDY             (1 << 27) /* Bit 27: PLLSAI1 clock ready flag */
+
+/* Internal Clock Sources Calibration */
+
+#define RCC_CR_HSITRIM_SHIFT          (24)      /* Bits 30-24: HSI16 clock trimming */
+#define RCC_CR_HSITRIM_MASK           (0x7f << RCC_CR_HSITRIM_SHIFT)
+#define RCC_CR_HSICAL_SHIFT           (16)      /* Bits 23-16: HSI16 clock Calibration */
+#define RCC_CR_HSICAL_MASK            (0xff << RCC_CR_HSICAL_SHIFT)
+#define RCC_CR_MSITRIM_SHIFT          (8)       /* Bits 15-8:  Internal Multi Speed clock trimming */
+#define RCC_CR_MSITRIM_MASK           (0xff << RCC_CR_MSITRIM_SHIFT)
+#define RCC_CR_MSICAL_SHIFT           (0)       /* Bits 7-0:   Internal Multi Speed clock Calibration */
+#define RCC_CR_MSICAL_MASK            (0xff << RCC_CR_MSICAL_SHIFT)
+
+/* Clock configuration register */
+
+#define RCC_CFGR_SW_SHIFT             (0)       /* Bits 0-1: System clock Switch */
+#define RCC_CFGR_SW_MASK              (0x3 << RCC_CFGR_SW_SHIFT)
+#  define RCC_CFGR_SW_MSI             (0x0 << RCC_CFGR_SW_SHIFT) /* 00: MSI selected as system clock */
+#  define RCC_CFGR_SW_HSI16           (0x1 << RCC_CFGR_SW_SHIFT) /* 00: HSI16 selected as system clock */
+#  define RCC_CFGR_SW_HSE             (0x2 << RCC_CFGR_SW_SHIFT) /* 01: HSE selected as system clock */
+#  define RCC_CFGR_SW_PLL             (0x3 << RCC_CFGR_SW_SHIFT) /* 10: PLL selected as system clock */
+
+#define RCC_CFGR_SWS_SHIFT            (2)       /* Bits 2-3: System Clock Switch Status */
+#define RCC_CFGR_SWS_MASK             (0x3 << RCC_CFGR_SWS_SHIFT)
+#  define RCC_CFGR_SWS_MSI            (0x0 << RCC_CFGR_SWS_SHIFT) /* 00: MSI oscillator used as system clock */
+#  define RCC_CFGR_SWS_HSI16          (0x1 << RCC_CFGR_SWS_SHIFT) /* 00: HSI16 oscillator used as system clock */
+#  define RCC_CFGR_SWS_HSE            (0x2 << RCC_CFGR_SWS_SHIFT) /* 01: HSE oscillator used as system clock */
+#  define RCC_CFGR_SWS_PLL            (0x3 << RCC_CFGR_SWS_SHIFT) /* 10: PLL used as system clock */
+
+#define RCC_CFGR_HPRE_SHIFT           (4)       /* Bits 4-7: HCLK1 prescaler (AHB1, AHB2, AHB3 and SRAM1) */
+#define RCC_CFGR_HPRE_MASK            (0x0f << RCC_CFGR_HPRE_SHIFT)
+#  define RCC_CFGR_HPRE_SYSCLK        (0x00 << RCC_CFGR_HPRE_SHIFT) /* 0000: SYSCLK not divided */
+#  define RCC_CFGR_HPRE_SYSCLKd3      (0x01 << RCC_CFGR_HPRE_SHIFT) /* 0001: SYSCLK divided by 3 */
+#  define RCC_CFGR_HPRE_SYSCLKd5      (0x02 << RCC_CFGR_HPRE_SHIFT) /* 0010: SYSCLK divided by 5 */
+#  define RCC_CFGR_HPRE_SYSCLKd6      (0x05 << RCC_CFGR_HPRE_SHIFT) /* 0101: SYSCLK divided by 6 */
+#  define RCC_CFGR_HPRE_SYSCLKd10     (0x06 << RCC_CFGR_HPRE_SHIFT) /* 0110: SYSCLK divided by 10 */
+#  define RCC_CFGR_HPRE_SYSCLKd32     (0x07 << RCC_CFGR_HPRE_SHIFT) /* 0111: SYSCLK divided by 32 */
+#  define RCC_CFGR_HPRE_SYSCLKd2      (0x08 << RCC_CFGR_HPRE_SHIFT) /* 1000: SYSCLK divided by 2 */
+#  define RCC_CFGR_HPRE_SYSCLKd4      (0x09 << RCC_CFGR_HPRE_SHIFT) /* 1001: SYSCLK divided by 4 */
+#  define RCC_CFGR_HPRE_SYSCLKd8      (0x10 << RCC_CFGR_HPRE_SHIFT) /* 1010: SYSCLK divided by 8 */
+#  define RCC_CFGR_HPRE_SYSCLKd16     (0x11 << RCC_CFGR_HPRE_SHIFT) /* 1011: SYSCLK divided by 16 */
+#  define RCC_CFGR_HPRE_SYSCLKd64     (0x12 << RCC_CFGR_HPRE_SHIFT) /* 1100: SYSCLK divided by 64 */
+#  define RCC_CFGR_HPRE_SYSCLKd128    (0x13 << RCC_CFGR_HPRE_SHIFT) /* 1101: SYSCLK divided by 128 */
+#  define RCC_CFGR_HPRE_SYSCLKd256    (0x14 << RCC_CFGR_HPRE_SHIFT) /* 1110: SYSCLK divided by 256 */
+#  define RCC_CFGR_HPRE_SYSCLKd512    (0x15 << RCC_CFGR_HPRE_SHIFT) /* 1111: SYSCLK divided by 512 */
+
+#define RCC_CFGR_PPRE1_SHIFT          (8)       /* Bits 8-10: PCLK1 Low speed prescaler (APB1) */
+#define RCC_CFGR_PPRE1_MASK           (0x7 << RCC_CFGR_PPRE1_SHIFT)
+#  define RCC_CFGR_PPRE1_HCLK1        (0x0 << RCC_CFGR_PPRE1_SHIFT) /* 0xx: HCLK1 not divided */
+#  define RCC_CFGR_PPRE1_HCLK1d2      (0x4 << RCC_CFGR_PPRE1_SHIFT) /* 100: HCLK1 divided by 2 */
+#  define RCC_CFGR_PPRE1_HCLK1d4      (0x5 << RCC_CFGR_PPRE1_SHIFT) /* 101: HCLK1 divided by 4 */
+#  define RCC_CFGR_PPRE1_HCLK1d8      (0x6 << RCC_CFGR_PPRE1_SHIFT) /* 110: HCLK1 divided by 8 */
+#  define RCC_CFGR_PPRE1_HCLK1d16     (0x7 << RCC_CFGR_PPRE1_SHIFT) /* 111: HCLK1 divided by 16 */
+
+#define RCC_CFGR_PPRE2_SHIFT          (11)      /* Bits 11-13: PCLK2 High speed prescaler (APB2) */
+#define RCC_CFGR_PPRE2_MASK           (0x7 << RCC_CFGR_PPRE2_SHIFT)
+#  define RCC_CFGR_PPRE2_HCLK1        (0x0 << RCC_CFGR_PPRE2_SHIFT) /* 0xx: HCLK1 not divided */
+#  define RCC_CFGR_PPRE2_HCLK1d2      (0x4 << RCC_CFGR_PPRE2_SHIFT) /* 100: HCLK1 divided by 2 */
+#  define RCC_CFGR_PPRE2_HCLK1d4      (0x5 << RCC_CFGR_PPRE2_SHIFT) /* 101: HCLK1 divided by 4 */
+#  define RCC_CFGR_PPRE2_HCLK1d8      (0x6 << RCC_CFGR_PPRE2_SHIFT) /* 110: HCLK1 divided by 8 */
+#  define RCC_CFGR_PPRE2_HCLK1d16     (0x7 << RCC_CFGR_PPRE2_SHIFT) /* 111: HCLK1 divided by 16 */
+
+#define RCC_CFGR_STOPWUCK             (1 << 15) /* Bit 15: Wakeup from Stop and CSS backup clock selection */
+#  define RCC_CFGR_STOPWUCK_MSI       (0 << 15) /* 0: MSI */
+#  define RCC_CFGR_STOPWUCK_HSI16     (1 << 15) /* 0: HSI16 */
+
+#define RCC_CFGR_HPREF                (1 << 16) /* Bit 16: HCLK1 prescaler flag (AHB1, AHB2, AHB3, SRAM1) */
+#define RCC_CFGR_PPRE1F               (1 << 17) /* Bit 17: PCLK1 prescaler flag (APB1) */
+#define RCC_CFGR_PPRE2F               (1 << 18) /* Bit 18: PCLK2 prescaler flag (APB2) */
+
+#define RCC_CFGR_MCOSEL_SHIFT         (24)      /* Bits 24-27: Microcontroller Clock Output */
+#define RCC_CFGR_MCOSEL_MASK          (0xf << RCC_CFGR_MCOSEL_SHIFT)
+#  define RCC_CFGR_MCOSEL_DISABLED    (0x0 << RCC_CFGR_MCOSEL_SHIFT) /* 0000: Output disabled, no clock on MCO */
+#  define RCC_CFGR_MCOSEL_SYSCLK      (0x1 << RCC_CFGR_MCOSEL_SHIFT) /* 0001: SYSCLK system clock selected */
+#  define RCC_CFGR_MCOSEL_MSI         (0x2 << RCC_CFGR_MCOSEL_SHIFT) /* 0010: MSI clock selected */
+#  define RCC_CFGR_MCOSEL_HSI16       (0x3 << RCC_CFGR_MCOSEL_SHIFT) /* 0011: HSI16 clock selected */
+#  define RCC_CFGR_MCOSEL_HSE_AFT     (0x4 << RCC_CFGR_MCOSEL_SHIFT) /* 0100: HSE clock after stabilization */
+#  define RCC_CFGR_MCOSEL_PLL         (0x5 << RCC_CFGR_MCOSEL_SHIFT) /* 0101: Main PLLRCLK selected  */
+#  define RCC_CFGR_MCOSEL_LSI1        (0x6 << RCC_CFGR_MCOSEL_SHIFT) /* 0110: LSI1 clock selected */
+#  define RCC_CFGR_MCOSEL_LSI2        (0x7 << RCC_CFGR_MCOSEL_SHIFT) /* 0111: LSI2 clock selected */
+#  define RCC_CFGR_MCOSEL_LSE         (0x8 << RCC_CFGR_MCOSEL_SHIFT) /* 1000: LSE clock selected */
+#  define RCC_CFGR_MCOSEL_HSI48       (0x9 << RCC_CFGR_MCOSEL_SHIFT) /* 1001: HSI48 clock selected */
+#  define RCC_CFGR_MCOSEL_HSE_BFR     (0xc << RCC_CFGR_MCOSEL_SHIFT) /* 1100: HSE clock before stabilization */
+
+#define RCC_CFGR_MCOPRE_SHIFT         (28)      /* Bits 28-30: MCO prescaler */
+#define RCC_CFGR_MCOPRE_MASK          (0x7 << RCC_CFGR_MCOPRE_SHIFT)
+#  define RCC_CFGR_MCOPRE_DIV1        (0x0 << RCC_CFGR_MCOPRE_SHIFT) /* 000: no division */
+#  define RCC_CFGR_MCOPRE_DIV2        (0x1 << RCC_CFGR_MCOPRE_SHIFT) /* 001: division by 2 */
+#  define RCC_CFGR_MCOPRE_DIV4        (0x2 << RCC_CFGR_MCOPRE_SHIFT) /* 010: division by 4 */
+#  define RCC_CFGR_MCOPRE_DIV8        (0x3 << RCC_CFGR_MCOPRE_SHIFT) /* 011: division by 8 */
+#  define RCC_CFGR_MCOPRE_DIV16       (0x4 << RCC_CFGR_MCOPRE_SHIFT) /* 100: division by 16 */
+
+#define RCC_CFGR_RESET_MASK           (0x00070000)
+
+/* PLL configuration register */
+
+#define RCC_PLLCFG_PLLSRC_SHIFT       (0)       /* Bits 0-1: Main PLL and audio PLLSAI1 entry clock source */
+#define RCC_PLLCFG_PLLSRC_MASK        (0x3 << RCC_PLLCFG_PLLSRC_SHIFT)
+#  define RCC_PLLCFG_PLLSRC_NONE      (0x0 << RCC_PLLCFG_PLLSRC_SHIFT) /* 00: No clock sent to PLLs */
+#  define RCC_PLLCFG_PLLSRC_MSI       (0x1 << RCC_PLLCFG_PLLSRC_SHIFT) /* 01: MSI selected as PLLs source */
+#  define RCC_PLLCFG_PLLSRC_HSI16     (0x2 << RCC_PLLCFG_PLLSRC_SHIFT) /* 10: HSI16 selected as PLLs source */
+#  define RCC_PLLCFG_PLLSRC_HSE       (0x3 << RCC_PLLCFG_PLLSRC_SHIFT) /* 11: HSE selected as PLLs source */
+
+#define RCC_PLLCFG_PLLM_SHIFT         (4)       /* Bits 4-6: Main PLL and audio PLLSAI1 divider */
+#define RCC_PLLCFG_PLLM_MASK          (0x07 << RCC_PLLCFG_PLLM_SHIFT)
+#  define RCC_PLLCFG_PLLM(n)          ((n-1) << RCC_PLLCFG_PLLM_SHIFT) /* n = 1..8 */
+
+#define RCC_PLLCFG_PLLN_SHIFT         (8)       /* Bits 8-14: Main PLL (PLL) VCO multiplier */
+#define RCC_PLLCFG_PLLN_MASK          (0x7f << RCC_PLLCFG_PLLN_SHIFT)
+#  define RCC_PLLCFG_PLLN(n)          ((n) << RCC_PLLCFG_PLLN_SHIFT) /* n = 6..127 */
+
+#define RCC_PLLCFG_PLLPEN             (1 << 16) /* Bit 16: Main PLL PLLPCLK output enable */
+
+#define RCC_PLLCFG_PLLP_SHIFT         (17)      /* Bits 17-21: Main PLL div factor for PLLPCLK */
+#define RCC_PLLCFG_PLLP_MASK          (0x1f << RCC_PLLCFG_PLLP_SHIFT)
+#  define RCC_PLLCFG_PLLP(n)          ((n-1) << RCC_PLLCFG_PLLP_SHIFT) /* n = 2..32 */
+
+#define RCC_PLLCFG_PLLQEN             (1 << 24) /* Bit 24: Main PLL PLLQCLK output enable */
+
+#define RCC_PLLCFG_PLLQ_SHIFT         (25)      /* Bits 25-27: Main PLL division factor for PLLQCLK */
+#define RCC_PLLCFG_PLLQ_MASK          (0x7 << RCC_PLLCFG_PLLQ_SHIFT)
+#  define RCC_PLLCFG_PLLQ(n)          ((n-1) << RCC_PLLCFG_PLLQ_SHIFT) /* n = 2..8 */

Review Comment:
   ```suggestion
   #  define RCC_PLLCFG_PLLQ(n)          (((n) - 1) << RCC_PLLCFG_PLLQ_SHIFT) /* n = 2..8 */
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_tim.h:
##########
@@ -0,0 +1,1385 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_tim.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_TIM_CR1_OFFSET      0x0000  /* Control register 1 */
+#define STM32WB_TIM_CR2_OFFSET      0x0004  /* Control register 2 */
+#define STM32WB_TIM_SMCR_OFFSET     0x0008  /* Slave mode control register (TIM1, TIM2) */
+#define STM32WB_TIM_DIER_OFFSET     0x000c  /* DMA / Interrupt enable register */
+#define STM32WB_TIM_SR_OFFSET       0x0010  /* Status register */
+#define STM32WB_TIM_EGR_OFFSET      0x0014  /* Event generation register */
+#define STM32WB_TIM_CCMR1_OFFSET    0x0018  /* Capture/compare mode register 1 */
+#define STM32WB_TIM_CCMR2_OFFSET    0x001c  /* Capture/compare mode register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCER_OFFSET     0x0020  /* Capture/compare enable register */
+#define STM32WB_TIM_CNT_OFFSET      0x0024  /* Counter */
+#define STM32WB_TIM_PSC_OFFSET      0x0028  /* Prescaler */
+#define STM32WB_TIM_ARR_OFFSET      0x002c  /* Auto-reload register */
+#define STM32WB_TIM_RCR_OFFSET      0x0030  /* Repetition counter register (TIM1, TIM16/TIM17) */
+#define STM32WB_TIM_CCR1_OFFSET     0x0034  /* Capture/compare register 1 */
+#define STM32WB_TIM_CCR2_OFFSET     0x0038  /* Capture/compare register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR3_OFFSET     0x003c  /* Capture/compare register 3 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR4_OFFSET     0x0040  /* Capture/compare register 4 (TIM1, TIM2) */
+#define STM32WB_TIM_BDTR_OFFSET     0x0044  /* Break and dead-time register (TIM1, TIM16/17) */
+#define STM32WB_TIM_DCR_OFFSET      0x0048  /* DMA control register */
+#define STM32WB_TIM_DMAR_OFFSET     0x004c  /* DMA address for burst mode */
+#define STM32WB_TIM_OR1_OFFSET      0x0050  /* Option register 1 */
+#define STM32WB_TIM_CCMR3_OFFSET    0x0054  /* Capture/compare mode register 3 (TIM1) */
+#define STM32WB_TIM_CCR5_OFFSET     0x0058  /* Capture/compare register 5 (TIM1) */
+#define STM32WB_TIM_CCR6_OFFSET     0x005C  /* Capture/compare register 6 (TIM1) */
+#define STM32WB_TIM_AF1_OFFSET      0x0060  /* Alternate function register 1 */
+#define STM32WB_TIM_AF2_OFFSET      0x0064  /* Alternate function register 2 (TIM1) */
+#define STM32WB_TIM_TISEL_OFFSET    0x0068  /* Input selector register */
+
+/* Register Addresses *******************************************************/
+
+/* Advanced Timer TIM1 */
+
+#define STM32WB_TIM1_CR1            (STM32WB_TIM1_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM1_CR2            (STM32WB_TIM1_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM1_SMCR           (STM32WB_TIM1_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM1_DIER           (STM32WB_TIM1_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM1_SR             (STM32WB_TIM1_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM1_EGR            (STM32WB_TIM1_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM1_CCMR1          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM1_CCMR2          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM1_CCER           (STM32WB_TIM1_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM1_CNT            (STM32WB_TIM1_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM1_PSC            (STM32WB_TIM1_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM1_ARR            (STM32WB_TIM1_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM1_RCR            (STM32WB_TIM1_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM1_CCR1           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM1_CCR2           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM1_CCR3           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM1_CCR4           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM1_BDTR           (STM32WB_TIM1_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM1_DCR            (STM32WB_TIM1_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM1_DMAR           (STM32WB_TIM1_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM1_OR1            (STM32WB_TIM1_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM1_CCMR3          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR3_OFFSET)
+#define STM32WB_TIM1_CCR5           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR5_OFFSET)
+#define STM32WB_TIM1_CCR6           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR6_OFFSET)
+#define STM32WB_TIM1_AF1            (STM32WB_TIM1_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM1_AF2            (STM32WB_TIM1_BASE + STM32WB_TIM_AF2_OFFSET)
+#define STM32WB_TIM1_TISEL          (STM32WB_TIM1_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General 32-bit Timer TIM2 */
+
+#define STM32WB_TIM2_CR1            (STM32WB_TIM2_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM2_CR2            (STM32WB_TIM2_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM2_SMCR           (STM32WB_TIM2_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM2_DIER           (STM32WB_TIM2_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM2_SR             (STM32WB_TIM2_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM2_EGR            (STM32WB_TIM2_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM2_CCMR1          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM2_CCMR2          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM2_CCER           (STM32WB_TIM2_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM2_CNT            (STM32WB_TIM2_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM2_PSC            (STM32WB_TIM2_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM2_ARR            (STM32WB_TIM2_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM2_CCR1           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM2_CCR2           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM2_CCR3           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM2_CCR4           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM2_DCR            (STM32WB_TIM2_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM2_DMAR           (STM32WB_TIM2_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM2_OR1            (STM32WB_TIM2_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM2_AF1            (STM32WB_TIM2_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM2_TISEL          (STM32WB_TIM2_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General Timers TIM16/TIM17 */
+
+#define STM32WB_TIM16_CR1           (STM32WB_TIM16_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM16_CR2           (STM32WB_TIM16_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM16_DIER          (STM32WB_TIM16_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM16_SR            (STM32WB_TIM16_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM16_EGR           (STM32WB_TIM16_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM16_CCMR1         (STM32WB_TIM16_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM16_CCER          (STM32WB_TIM16_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM16_CNT           (STM32WB_TIM16_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM16_PSC           (STM32WB_TIM16_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM16_ARR           (STM32WB_TIM16_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM16_RCR           (STM32WB_TIM16_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM16_CCR1          (STM32WB_TIM16_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM16_BDTR          (STM32WB_TIM16_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM16_DCR           (STM32WB_TIM16_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM16_DMAR          (STM32WB_TIM16_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM16_OR1           (STM32WB_TIM16_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM16_AF1           (STM32WB_TIM16_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM16_TISEL         (STM32WB_TIM16_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+#define STM32WB_TIM17_CR1           (STM32WB_TIM17_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM17_CR2           (STM32WB_TIM17_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM17_DIER          (STM32WB_TIM17_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM17_SR            (STM32WB_TIM17_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM17_EGR           (STM32WB_TIM17_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM17_CCMR1         (STM32WB_TIM17_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM17_CCER          (STM32WB_TIM17_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM17_CNT           (STM32WB_TIM17_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM17_PSC           (STM32WB_TIM17_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM17_ARR           (STM32WB_TIM17_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM17_RCR           (STM32WB_TIM17_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM17_CCR1          (STM32WB_TIM17_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM17_BDTR          (STM32WB_TIM17_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM17_DCR           (STM32WB_TIM17_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM17_DMAR          (STM32WB_TIM17_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM17_OR1           (STM32WB_TIM17_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM17_AF1           (STM32WB_TIM17_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM17_TISEL         (STM32WB_TIM17_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* Register Value Constants *************************************************/
+
+/* Digital Filter options */
+
+#define STM32WB_DF_NOFILT           (0x0) /* 0000: No filter */
+#define STM32WB_DF_FCKINTn2         (0x1) /* 0001: fSAMPLING = fCK_INT, N=2 */
+#define STM32WB_DF_FCKINTn4         (0x2) /* 0010: fSAMPLING = fCK_INT, N=4 */
+#define STM32WB_DF_FCKINTn8         (0x3) /* 0011: fSAMPLING = fCK_INT, N=8 */
+#define STM32WB_DF_FDTSd2n6         (0x4) /* 0100: fSAMPLING = fDTS/2, N=6 */
+#define STM32WB_DF_FDTSd2n8         (0x5) /* 0101: fSAMPLING = fDTS/2, N=8 */
+#define STM32WB_DF_FDTSd4n6         (0x6) /* 0110: fSAMPLING = fDTS/4, N=6 */
+#define STM32WB_DF_FDTSd4n8         (0x7) /* 0111: fSAMPLING = fDTS/4, N=8 */
+#define STM32WB_DF_FDTSd8n6         (0x8) /* 1000: fSAMPLING = fDTS/8, N=6 */
+#define STM32WB_DF_FDTSd8n8         (0x9) /* 1001: fSAMPLING = fDTS/8, N=8 */
+#define STM32WB_DF_FDTSd16n5        (0xa) /* 1010: fSAMPLING = fDTS/16, N=5 */
+#define STM32WB_DF_FDTSd16n6        (0xb) /* 1011: fSAMPLING = fDTS/16, N=6 */
+#define STM32WB_DF_FDTSd16n8        (0xc) /* 1100: fSAMPLING = fDTS/16, N=8 */
+#define STM32WB_DF_FDTSd32n5        (0xd) /* 1101: fSAMPLING = fDTS/32, N=5 */
+#define STM32WB_DF_FDTSd32n6        (0xe) /* 1110: fSAMPLING = fDTS/32, N=6 */
+#define STM32WB_DF_FDTSd32n8        (0xf) /* 1111: fSAMPLING = fDTS/32, N=8 */
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Control register 1 */
+
+#define TIM1_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM1_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM1_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM1_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM1_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM1_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM1_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM1_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM1_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM1_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM1_CR1_CMS_MASK           (0x3 << TIM1_CR1_CMS_SHIFT)
+#  define TIM1_CR1_CMS_EDGE         (0x0 << TIM1_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM1_CR1_CMS_CNTR1        (0x1 << TIM1_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM1_CR1_CMS_CNTR2        (0x2 << TIM1_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM1_CR1_CMS_CNTR3        (0x3 << TIM1_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM1_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM1_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM1_CR1_CKD_MASK           (0x3 << TIM1_CR1_CKD_SHIFT)
+#  define TIM1_CR1_CKD_TCKINT       (0x0 << TIM1_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM1_CR1_CKD_2TCKINT      (0x1 << TIM1_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM1_CR1_CKD_4TCKINT      (0x2 << TIM1_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM1_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM2_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM2_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM2_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM2_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM2_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM2_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM2_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM2_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM2_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM2_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM2_CR1_CMS_MASK           (0x3 << TIM2_CR1_CMS_SHIFT)
+#  define TIM2_CR1_CMS_EDGE         (0x0 << TIM2_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM2_CR1_CMS_CNTR1        (0x1 << TIM2_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM2_CR1_CMS_CNTR2        (0x2 << TIM2_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM2_CR1_CMS_CNTR3        (0x3 << TIM2_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM2_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM2_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM2_CR1_CKD_MASK           (0x3 << TIM2_CR1_CKD_SHIFT)
+#  define TIM2_CR1_CKD_TCKINT       (0x0 << TIM2_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM2_CR1_CKD_2TCKINT      (0x1 << TIM2_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM2_CR1_CKD_4TCKINT      (0x2 << TIM2_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM2_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM16_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM16_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM16_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM16_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM16_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM16_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM16_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM16_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM16_CR1_CKD_MASK          (0x3 << TIM16_CR1_CKD_SHIFT)
+#  define TIM16_CR1_CKD_TCKINT      (0x0 << TIM16_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM16_CR1_CKD_2TCKINT     (0x1 << TIM16_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM16_CR1_CKD_4TCKINT     (0x2 << TIM16_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM16_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM17_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM17_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM17_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM17_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM17_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM17_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM17_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM17_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM17_CR1_CKD_MASK          (0x3 << TIM17_CR1_CKD_SHIFT)
+#  define TIM17_CR1_CKD_TCKINT      (0x0 << TIM17_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM17_CR1_CKD_2TCKINT     (0x1 << TIM17_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM17_CR1_CKD_4TCKINT     (0x2 << TIM17_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM17_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+/* Control register 2 */
+
+#define TIM1_CR2_CCPC               (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM1_CR2_CCUS               (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM1_CR2_CCUS_COMG        (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM1_CR2_CCUS_COMG_TRGI   (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM1_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM1_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM1_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM1_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM1_CR2_MMS_MASK           (0x7 << TIM1_CR2_MMS_SHIFT)
+#  define TIM1_CR2_MMS_RESET        (0x0 << TIM1_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM1_CR2_MMS_ENABLE       (0x1 << TIM1_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM1_CR2_MMS_UPDATE       (0x2 << TIM1_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM1_CR2_MMS_COMPP        (0x3 << TIM1_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS_OC1REF       (0x4 << TIM1_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM1_CR2_MMS_OC2REF       (0x5 << TIM1_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM1_CR2_MMS_OC3REF       (0x6 << TIM1_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM1_CR2_MMS_OC4REF       (0x7 << TIM1_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM1_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM1_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM1_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM1_CR2_OIS1               (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM1_CR2_OIS1N              (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+#define TIM1_CR2_OIS2               (1 << 10) /* Bit 10: Output Idle state 2 (OC2 output) */
+#define TIM1_CR2_OIS2N              (1 << 11) /* Bit 11: Output Idle state 2 (OC2N output) */
+#define TIM1_CR2_OIS3               (1 << 12) /* Bit 12: Output Idle state 3 (OC3 output) */
+#define TIM1_CR2_OIS3N              (1 << 13) /* Bit 13: Output Idle state 3 (OC3N output) */
+#define TIM1_CR2_OIS4               (1 << 14) /* Bit 14: Output Idle state 4 (OC4 output) */
+#define TIM1_CR2_OIS5               (1 << 16) /* Bit 16: Output Idle state 5 (OC5 output) */
+#define TIM1_CR2_OIS6               (1 << 18) /* Bit 18: Output Idle state 6 (OC6 output) */
+#define TIM1_CR2_MMS2_SHIFT         (20)      /* Bits 20-23: Master Mode Selection 2 */
+#define TIM1_CR2_MMS2_MASK          (0xf << TIM1_CR2_MMS2_SHIFT)
+#  define TIM1_CR2_MMS2_RESET       (0x0 << TIM1_CR2_MMS2_SHIFT) /* 0000: Reset - TIMx_EGR UG bit is TRG9 */
+#  define TIM1_CR2_MMS2_ENABLE      (0x1 << TIM1_CR2_MMS2_SHIFT) /* 0001: Enable - CNT_EN is TRGO2 */
+#  define TIM1_CR2_MMS2_UPDATE      (0x2 << TIM1_CR2_MMS2_SHIFT) /* 0010: Update event is TRGO2 */
+#  define TIM1_CR2_MMS2_COMPP       (0x3 << TIM1_CR2_MMS2_SHIFT) /* 0011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS2_OC1REF      (0x4 << TIM1_CR2_MMS2_SHIFT) /* 0100: Compare OC1REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC2REF      (0x5 << TIM1_CR2_MMS2_SHIFT) /* 0101: Compare OC2REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC3REF      (0x6 << TIM1_CR2_MMS2_SHIFT) /* 0110: Compare OC3REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC4REF      (0x7 << TIM1_CR2_MMS2_SHIFT) /* 0111: Compare OC4REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC5REF      (0x8 << TIM1_CR2_MMS2_SHIFT) /* 1000: Compare OC5REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC6REF      (0x9 << TIM1_CR2_MMS2_SHIFT) /* 1001: Compare OC6REF is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4      (0xa << TIM1_CR2_MMS2_SHIFT) /* 1010: Compare pulse - OC4REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC6      (0xb << TIM1_CR2_MMS2_SHIFT) /* 1011: Compare pulse - OC6REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4R6R   (0xc << TIM1_CR2_MMS2_SHIFT) /* 1100: Compare pulse - OC4REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC4R6F   (0xd << TIM1_CR2_MMS2_SHIFT) /* 1101: Compare pulse - OC4REF rising/OC6REF falling */
+#  define TIM1_CR2_MMS2_CMPOC5R6R   (0xe << TIM1_CR2_MMS2_SHIFT) /* 1110: Compare pulse - OC5REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC5R6F   (0xf << TIM1_CR2_MMS2_SHIFT) /* 1111: Compare pulse - OC5REF rising/OC6REF falling */
+
+#define TIM2_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM2_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM2_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM2_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM2_CR2_MMS_MASK           (0x7 << TIM2_CR2_MMS_SHIFT)
+#  define TIM2_CR2_MMS_RESET        (0x0 << TIM2_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM2_CR2_MMS_ENABLE       (0x1 << TIM2_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM2_CR2_MMS_UPDATE       (0x2 << TIM2_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM2_CR2_MMS_COMPP        (0x3 << TIM2_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM2_CR2_MMS_OC1REF       (0x4 << TIM2_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM2_CR2_MMS_OC2REF       (0x5 << TIM2_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM2_CR2_MMS_OC3REF       (0x6 << TIM2_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM2_CR2_MMS_OC4REF       (0x7 << TIM2_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM2_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM2_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM2_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM16_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM16_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM16_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM16_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM16_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM16_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM16_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM16_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM16_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+#define TIM17_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM17_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM17_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM17_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM17_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM17_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM17_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM17_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM17_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+/* Slave mode control register */
+
+#define TIM1_SMCR_SMS_LO_SHIFT      (0)       /* Bits 0-2: Slave mode selection, bits [2:0] */
+#define TIM1_SMCR_SMS_HI_SHIFT      (16)      /* Bit 16: Slave mode selection, bits [3] */
+#define TIM1_SMCR_SMS_BITS(h,l)     ((h << TIM1_SMCR_SMS_HI_SHIFT) | (l << TIM1_SMCR_SMS_LO_SHIFT))
+#define TIM1_SMCR_SMS_MASK          TIM1_SMCR_SMS_BITS(0x1, 0x7)
+#  define TIM1_SMCR_DISAB           TIM1_SMCR_SMS_BITS(0x0, 0x0) /* 0,000: Slave mode disabled */
+#  define TIM1_SMCR_ENCMD1          TIM1_SMCR_SMS_BITS(0x0, 0x1) /* 0,001: Encoder mode 1 */
+#  define TIM1_SMCR_ENCMD2          TIM1_SMCR_SMS_BITS(0x0, 0x2) /* 0,010: Encoder mode 2 */
+#  define TIM1_SMCR_ENCMD3          TIM1_SMCR_SMS_BITS(0x0, 0x3) /* 0,011: Encoder mode 3 */
+#  define TIM1_SMCR_RESET           TIM1_SMCR_SMS_BITS(0x0, 0x4) /* 0,100: Reset Mode */
+#  define TIM1_SMCR_GATED           TIM1_SMCR_SMS_BITS(0x0, 0x5) /* 0,101: Gated Mode */
+#  define TIM1_SMCR_TRIGGER         TIM1_SMCR_SMS_BITS(0x0, 0x6) /* 0,110: Trigger Mode */
+#  define TIM1_SMCR_EXTCLK1         TIM1_SMCR_SMS_BITS(0x0, 0x7) /* 0,111: External Clock Mode 1 */
+#  define TIM1_SMCR_SMS_COMBINED    TIM1_SMCR_SMS_BITS(0x1, 0x0) /* 1,000: Combined Reset and Trigger mode */
+
+#define TIM1_SMCR_OCCS              (1 << 3)  /* Bit 3: OCREF clear selection */
+#  define TIM1_SMCR_OCCS_CLR        (0 << 3)  /* 0: OCREF clear triggered by CLR input */
+#  define TIM1_SMCR_OCCS_ETRF       (1 << 3)  /* 1: OCREF clear triggered by ETRF */
+
+#define TIM1_SMCR_TS_LO_SHIFT       (4)       /* Bits 4-6: Trigger selection, bits [2:0] */
+#define TIM1_SMCR_TS_HI_SHIFT       (20)      /* Bits 20-21: Trigger selection, bits [1:0] */
+#define TIM1_SMCR_TS_BITS(h,l)      ((h << TIM1_SMCR_TS_HI_SHIFT) | (l << TIM1_SMCR_TS_LO_SHIFT))
+#define TIM1_SMCR_TS_MASK           TIM1_SMCR_TS_BITS(0x3, 0x7)
+#  define TIM1_SMCR_ITR0            TIM1_SMCR_TS_BITS(0x0, 0x0) /* 00,000: Internal trigger 0 (ITR0) */
+#  define TIM1_SMCR_ITR1            TIM1_SMCR_TS_BITS(0x0, 0x1) /* 00,001: Internal trigger 1 (ITR1) */
+#  define TIM1_SMCR_ITR2            TIM1_SMCR_TS_BITS(0x0, 0x2) /* 00,010: Internal trigger 2 (ITR2) */
+#  define TIM1_SMCR_ITR3            TIM1_SMCR_TS_BITS(0x0, 0x3) /* 00,011: Internal trigger 3 (ITR3) */
+#  define TIM1_SMCR_T1FED           TIM1_SMCR_TS_BITS(0x0, 0x4) /* 00,100: TI1 Edge detector (TI1F_ED) */
+#  define TIM1_SMCR_TI1FP1          TIM1_SMCR_TS_BITS(0x0, 0x5) /* 00,101: Filtered timer input 1 (TI1FP1) */
+#  define TIM1_SMCR_T12FP2          TIM1_SMCR_TS_BITS(0x0, 0x6) /* 00,110: Filtered timer input 2 (TI2FP2) */
+#  define TIM1_SMCR_ETRF            TIM1_SMCR_TS_BITS(0x0, 0x7) /* 00,111: External trigger input (ETRF) */
+
+#define TIM1_SMCR_MSM               (1 << 7)  /* Bit 7: Master/slave mode */
+#define TIM1_SMCR_ETF_SHIFT         (8)       /* Bits 8-11: External trigger filter */
+#define TIM1_SMCR_ETF_MASK          (0xf << TIM1_SMCR_ETF_SHIFT)
+#  define TIM1_SMCR_ETF(f)          ((f) << TIM1_SMCR_ETF_SHIFT) /* f = STM32WB_DF_[digital filter option] */
+
+#define TIM1_SMCR_ETPS_SHIFT        (12)      /* Bits 12-13: External trigger prescaler */
+#define TIM1_SMCR_ETPS_MASK         (0x3 << TIM1_SMCR_ETPS_SHIFT)
+#  define TIM1_SMCR_PSCOFF          (0x0 << TIM1_SMCR_ETPS_SHIFT) /* 00: Prescaler OFF */
+#  define TIM1_SMCR_ETRPd2          (0x1 << TIM1_SMCR_ETPS_SHIFT) /* 01: ETRP frequency divided by 2 */
+#  define TIM1_SMCR_ETRPd4          (0x2 << TIM1_SMCR_ETPS_SHIFT) /* 10: ETRP frequency divided by 4 */
+#  define TIM1_SMCR_ETRPd8          (0x3 << TIM1_SMCR_ETPS_SHIFT) /* 11: ETRP frequency divided by 8 */
+
+#define TIM1_SMCR_ECE               (1 << 14) /* Bit 14: External clock enable */
+#define TIM1_SMCR_ETP               (1 << 15) /* Bit 15: External trigger polarity */
+#  define TIM1_SMCR_ETP_HIGH        (0 << 15) /* 0: ETR is non-inverted, active at high level or rising edge */
+#  define TIM1_SMCR_ETP_LOW         (1 << 15) /* 1: ETR is inverted, active at low level or falling edge */
+
+#define TIM2_SMCR_SMS_LO_SHIFT      (0)       /* Bits 0-2: Slave mode selection, bits [2:0] */
+#define TIM2_SMCR_SMS_HI_SHIFT      (16)      /* Bit 16: Slave mode selection, bits [3] */
+#define TIM2_SMCR_SMS_BITS(h,l)     ((h << TIM2_SMCR_SMS_HI_SHIFT) | (l << TIM2_SMCR_SMS_LO_SHIFT))
+#define TIM2_SMCR_SMS_MASK          TIM2_SMCR_SMS_BITS(0x1, 0x7)
+#  define TIM2_SMCR_DISAB           TIM2_SMCR_SMS_BITS(0x0, 0x0) /* 0,000: Slave mode disabled */
+#  define TIM2_SMCR_ENCMD1          TIM2_SMCR_SMS_BITS(0x0, 0x1) /* 0,001: Encoder mode 1 */
+#  define TIM2_SMCR_ENCMD2          TIM2_SMCR_SMS_BITS(0x0, 0x2) /* 0,010: Encoder mode 2 */
+#  define TIM2_SMCR_ENCMD3          TIM2_SMCR_SMS_BITS(0x0, 0x3) /* 0,011: Encoder mode 3 */
+#  define TIM2_SMCR_RESET           TIM2_SMCR_SMS_BITS(0x0, 0x4) /* 0,100: Reset Mode */
+#  define TIM2_SMCR_GATED           TIM2_SMCR_SMS_BITS(0x0, 0x5) /* 0,101: Gated Mode */
+#  define TIM2_SMCR_TRIGGER         TIM2_SMCR_SMS_BITS(0x0, 0x6) /* 0,110: Trigger Mode */
+#  define TIM2_SMCR_EXTCLK1         TIM2_SMCR_SMS_BITS(0x0, 0x7) /* 0,111: External Clock Mode 1 */
+#  define TIM2_SMCR_SMS_COMBINED    TIM2_SMCR_SMS_BITS(0x1, 0x0) /* 1,000: Combined Reset and Trigger mode */
+
+#define TIM2_SMCR_OCCS              (1 << 3)  /* Bit 3: OCREF clear selection */
+#  define TIM2_SMCR_OCCS_CLR        (0 << 3)  /* 0: OCREF clear triggered by CLR input */
+#  define TIM2_SMCR_OCCS_ETRF       (1 << 3)  /* 1: OCREF clear triggered by ETRF */
+
+#define TIM2_SMCR_TS_LO_SHIFT       (4)       /* Bits 4-6: Trigger selection, bits [2:0] */
+#define TIM2_SMCR_TS_HI_SHIFT       (20)      /* Bits 20-21: Trigger selection, bits [1:0] */
+#define TIM2_SMCR_TS_BITS(h,l)      ((h << TIM2_SMCR_TS_HI_SHIFT) | (l << TIM2_SMCR_TS_LO_SHIFT))

Review Comment:
   ditto



##########
arch/arm/src/stm32wb/hardware/stm32wb_tim.h:
##########
@@ -0,0 +1,1385 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_tim.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_TIM_CR1_OFFSET      0x0000  /* Control register 1 */
+#define STM32WB_TIM_CR2_OFFSET      0x0004  /* Control register 2 */
+#define STM32WB_TIM_SMCR_OFFSET     0x0008  /* Slave mode control register (TIM1, TIM2) */
+#define STM32WB_TIM_DIER_OFFSET     0x000c  /* DMA / Interrupt enable register */
+#define STM32WB_TIM_SR_OFFSET       0x0010  /* Status register */
+#define STM32WB_TIM_EGR_OFFSET      0x0014  /* Event generation register */
+#define STM32WB_TIM_CCMR1_OFFSET    0x0018  /* Capture/compare mode register 1 */
+#define STM32WB_TIM_CCMR2_OFFSET    0x001c  /* Capture/compare mode register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCER_OFFSET     0x0020  /* Capture/compare enable register */
+#define STM32WB_TIM_CNT_OFFSET      0x0024  /* Counter */
+#define STM32WB_TIM_PSC_OFFSET      0x0028  /* Prescaler */
+#define STM32WB_TIM_ARR_OFFSET      0x002c  /* Auto-reload register */
+#define STM32WB_TIM_RCR_OFFSET      0x0030  /* Repetition counter register (TIM1, TIM16/TIM17) */
+#define STM32WB_TIM_CCR1_OFFSET     0x0034  /* Capture/compare register 1 */
+#define STM32WB_TIM_CCR2_OFFSET     0x0038  /* Capture/compare register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR3_OFFSET     0x003c  /* Capture/compare register 3 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR4_OFFSET     0x0040  /* Capture/compare register 4 (TIM1, TIM2) */
+#define STM32WB_TIM_BDTR_OFFSET     0x0044  /* Break and dead-time register (TIM1, TIM16/17) */
+#define STM32WB_TIM_DCR_OFFSET      0x0048  /* DMA control register */
+#define STM32WB_TIM_DMAR_OFFSET     0x004c  /* DMA address for burst mode */
+#define STM32WB_TIM_OR1_OFFSET      0x0050  /* Option register 1 */
+#define STM32WB_TIM_CCMR3_OFFSET    0x0054  /* Capture/compare mode register 3 (TIM1) */
+#define STM32WB_TIM_CCR5_OFFSET     0x0058  /* Capture/compare register 5 (TIM1) */
+#define STM32WB_TIM_CCR6_OFFSET     0x005C  /* Capture/compare register 6 (TIM1) */
+#define STM32WB_TIM_AF1_OFFSET      0x0060  /* Alternate function register 1 */
+#define STM32WB_TIM_AF2_OFFSET      0x0064  /* Alternate function register 2 (TIM1) */
+#define STM32WB_TIM_TISEL_OFFSET    0x0068  /* Input selector register */
+
+/* Register Addresses *******************************************************/
+
+/* Advanced Timer TIM1 */
+
+#define STM32WB_TIM1_CR1            (STM32WB_TIM1_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM1_CR2            (STM32WB_TIM1_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM1_SMCR           (STM32WB_TIM1_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM1_DIER           (STM32WB_TIM1_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM1_SR             (STM32WB_TIM1_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM1_EGR            (STM32WB_TIM1_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM1_CCMR1          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM1_CCMR2          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM1_CCER           (STM32WB_TIM1_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM1_CNT            (STM32WB_TIM1_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM1_PSC            (STM32WB_TIM1_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM1_ARR            (STM32WB_TIM1_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM1_RCR            (STM32WB_TIM1_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM1_CCR1           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM1_CCR2           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM1_CCR3           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM1_CCR4           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM1_BDTR           (STM32WB_TIM1_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM1_DCR            (STM32WB_TIM1_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM1_DMAR           (STM32WB_TIM1_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM1_OR1            (STM32WB_TIM1_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM1_CCMR3          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR3_OFFSET)
+#define STM32WB_TIM1_CCR5           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR5_OFFSET)
+#define STM32WB_TIM1_CCR6           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR6_OFFSET)
+#define STM32WB_TIM1_AF1            (STM32WB_TIM1_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM1_AF2            (STM32WB_TIM1_BASE + STM32WB_TIM_AF2_OFFSET)
+#define STM32WB_TIM1_TISEL          (STM32WB_TIM1_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General 32-bit Timer TIM2 */
+
+#define STM32WB_TIM2_CR1            (STM32WB_TIM2_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM2_CR2            (STM32WB_TIM2_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM2_SMCR           (STM32WB_TIM2_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM2_DIER           (STM32WB_TIM2_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM2_SR             (STM32WB_TIM2_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM2_EGR            (STM32WB_TIM2_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM2_CCMR1          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM2_CCMR2          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM2_CCER           (STM32WB_TIM2_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM2_CNT            (STM32WB_TIM2_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM2_PSC            (STM32WB_TIM2_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM2_ARR            (STM32WB_TIM2_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM2_CCR1           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM2_CCR2           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM2_CCR3           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM2_CCR4           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM2_DCR            (STM32WB_TIM2_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM2_DMAR           (STM32WB_TIM2_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM2_OR1            (STM32WB_TIM2_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM2_AF1            (STM32WB_TIM2_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM2_TISEL          (STM32WB_TIM2_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General Timers TIM16/TIM17 */
+
+#define STM32WB_TIM16_CR1           (STM32WB_TIM16_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM16_CR2           (STM32WB_TIM16_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM16_DIER          (STM32WB_TIM16_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM16_SR            (STM32WB_TIM16_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM16_EGR           (STM32WB_TIM16_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM16_CCMR1         (STM32WB_TIM16_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM16_CCER          (STM32WB_TIM16_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM16_CNT           (STM32WB_TIM16_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM16_PSC           (STM32WB_TIM16_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM16_ARR           (STM32WB_TIM16_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM16_RCR           (STM32WB_TIM16_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM16_CCR1          (STM32WB_TIM16_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM16_BDTR          (STM32WB_TIM16_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM16_DCR           (STM32WB_TIM16_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM16_DMAR          (STM32WB_TIM16_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM16_OR1           (STM32WB_TIM16_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM16_AF1           (STM32WB_TIM16_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM16_TISEL         (STM32WB_TIM16_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+#define STM32WB_TIM17_CR1           (STM32WB_TIM17_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM17_CR2           (STM32WB_TIM17_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM17_DIER          (STM32WB_TIM17_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM17_SR            (STM32WB_TIM17_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM17_EGR           (STM32WB_TIM17_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM17_CCMR1         (STM32WB_TIM17_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM17_CCER          (STM32WB_TIM17_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM17_CNT           (STM32WB_TIM17_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM17_PSC           (STM32WB_TIM17_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM17_ARR           (STM32WB_TIM17_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM17_RCR           (STM32WB_TIM17_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM17_CCR1          (STM32WB_TIM17_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM17_BDTR          (STM32WB_TIM17_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM17_DCR           (STM32WB_TIM17_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM17_DMAR          (STM32WB_TIM17_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM17_OR1           (STM32WB_TIM17_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM17_AF1           (STM32WB_TIM17_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM17_TISEL         (STM32WB_TIM17_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* Register Value Constants *************************************************/
+
+/* Digital Filter options */
+
+#define STM32WB_DF_NOFILT           (0x0) /* 0000: No filter */
+#define STM32WB_DF_FCKINTn2         (0x1) /* 0001: fSAMPLING = fCK_INT, N=2 */
+#define STM32WB_DF_FCKINTn4         (0x2) /* 0010: fSAMPLING = fCK_INT, N=4 */
+#define STM32WB_DF_FCKINTn8         (0x3) /* 0011: fSAMPLING = fCK_INT, N=8 */
+#define STM32WB_DF_FDTSd2n6         (0x4) /* 0100: fSAMPLING = fDTS/2, N=6 */
+#define STM32WB_DF_FDTSd2n8         (0x5) /* 0101: fSAMPLING = fDTS/2, N=8 */
+#define STM32WB_DF_FDTSd4n6         (0x6) /* 0110: fSAMPLING = fDTS/4, N=6 */
+#define STM32WB_DF_FDTSd4n8         (0x7) /* 0111: fSAMPLING = fDTS/4, N=8 */
+#define STM32WB_DF_FDTSd8n6         (0x8) /* 1000: fSAMPLING = fDTS/8, N=6 */
+#define STM32WB_DF_FDTSd8n8         (0x9) /* 1001: fSAMPLING = fDTS/8, N=8 */
+#define STM32WB_DF_FDTSd16n5        (0xa) /* 1010: fSAMPLING = fDTS/16, N=5 */
+#define STM32WB_DF_FDTSd16n6        (0xb) /* 1011: fSAMPLING = fDTS/16, N=6 */
+#define STM32WB_DF_FDTSd16n8        (0xc) /* 1100: fSAMPLING = fDTS/16, N=8 */
+#define STM32WB_DF_FDTSd32n5        (0xd) /* 1101: fSAMPLING = fDTS/32, N=5 */
+#define STM32WB_DF_FDTSd32n6        (0xe) /* 1110: fSAMPLING = fDTS/32, N=6 */
+#define STM32WB_DF_FDTSd32n8        (0xf) /* 1111: fSAMPLING = fDTS/32, N=8 */
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Control register 1 */
+
+#define TIM1_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM1_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM1_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM1_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM1_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM1_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM1_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM1_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM1_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM1_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM1_CR1_CMS_MASK           (0x3 << TIM1_CR1_CMS_SHIFT)
+#  define TIM1_CR1_CMS_EDGE         (0x0 << TIM1_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM1_CR1_CMS_CNTR1        (0x1 << TIM1_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM1_CR1_CMS_CNTR2        (0x2 << TIM1_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM1_CR1_CMS_CNTR3        (0x3 << TIM1_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM1_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM1_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM1_CR1_CKD_MASK           (0x3 << TIM1_CR1_CKD_SHIFT)
+#  define TIM1_CR1_CKD_TCKINT       (0x0 << TIM1_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM1_CR1_CKD_2TCKINT      (0x1 << TIM1_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM1_CR1_CKD_4TCKINT      (0x2 << TIM1_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM1_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM2_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM2_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM2_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM2_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM2_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM2_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM2_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM2_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM2_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM2_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM2_CR1_CMS_MASK           (0x3 << TIM2_CR1_CMS_SHIFT)
+#  define TIM2_CR1_CMS_EDGE         (0x0 << TIM2_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM2_CR1_CMS_CNTR1        (0x1 << TIM2_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM2_CR1_CMS_CNTR2        (0x2 << TIM2_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM2_CR1_CMS_CNTR3        (0x3 << TIM2_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM2_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM2_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM2_CR1_CKD_MASK           (0x3 << TIM2_CR1_CKD_SHIFT)
+#  define TIM2_CR1_CKD_TCKINT       (0x0 << TIM2_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM2_CR1_CKD_2TCKINT      (0x1 << TIM2_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM2_CR1_CKD_4TCKINT      (0x2 << TIM2_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM2_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM16_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM16_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM16_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM16_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM16_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM16_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM16_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM16_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM16_CR1_CKD_MASK          (0x3 << TIM16_CR1_CKD_SHIFT)
+#  define TIM16_CR1_CKD_TCKINT      (0x0 << TIM16_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM16_CR1_CKD_2TCKINT     (0x1 << TIM16_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM16_CR1_CKD_4TCKINT     (0x2 << TIM16_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM16_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM17_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM17_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM17_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM17_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM17_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM17_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM17_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM17_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM17_CR1_CKD_MASK          (0x3 << TIM17_CR1_CKD_SHIFT)
+#  define TIM17_CR1_CKD_TCKINT      (0x0 << TIM17_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM17_CR1_CKD_2TCKINT     (0x1 << TIM17_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM17_CR1_CKD_4TCKINT     (0x2 << TIM17_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM17_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+/* Control register 2 */
+
+#define TIM1_CR2_CCPC               (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM1_CR2_CCUS               (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM1_CR2_CCUS_COMG        (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM1_CR2_CCUS_COMG_TRGI   (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM1_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM1_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM1_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM1_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM1_CR2_MMS_MASK           (0x7 << TIM1_CR2_MMS_SHIFT)
+#  define TIM1_CR2_MMS_RESET        (0x0 << TIM1_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM1_CR2_MMS_ENABLE       (0x1 << TIM1_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM1_CR2_MMS_UPDATE       (0x2 << TIM1_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM1_CR2_MMS_COMPP        (0x3 << TIM1_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS_OC1REF       (0x4 << TIM1_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM1_CR2_MMS_OC2REF       (0x5 << TIM1_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM1_CR2_MMS_OC3REF       (0x6 << TIM1_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM1_CR2_MMS_OC4REF       (0x7 << TIM1_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM1_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM1_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM1_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM1_CR2_OIS1               (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM1_CR2_OIS1N              (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+#define TIM1_CR2_OIS2               (1 << 10) /* Bit 10: Output Idle state 2 (OC2 output) */
+#define TIM1_CR2_OIS2N              (1 << 11) /* Bit 11: Output Idle state 2 (OC2N output) */
+#define TIM1_CR2_OIS3               (1 << 12) /* Bit 12: Output Idle state 3 (OC3 output) */
+#define TIM1_CR2_OIS3N              (1 << 13) /* Bit 13: Output Idle state 3 (OC3N output) */
+#define TIM1_CR2_OIS4               (1 << 14) /* Bit 14: Output Idle state 4 (OC4 output) */
+#define TIM1_CR2_OIS5               (1 << 16) /* Bit 16: Output Idle state 5 (OC5 output) */
+#define TIM1_CR2_OIS6               (1 << 18) /* Bit 18: Output Idle state 6 (OC6 output) */
+#define TIM1_CR2_MMS2_SHIFT         (20)      /* Bits 20-23: Master Mode Selection 2 */
+#define TIM1_CR2_MMS2_MASK          (0xf << TIM1_CR2_MMS2_SHIFT)
+#  define TIM1_CR2_MMS2_RESET       (0x0 << TIM1_CR2_MMS2_SHIFT) /* 0000: Reset - TIMx_EGR UG bit is TRG9 */
+#  define TIM1_CR2_MMS2_ENABLE      (0x1 << TIM1_CR2_MMS2_SHIFT) /* 0001: Enable - CNT_EN is TRGO2 */
+#  define TIM1_CR2_MMS2_UPDATE      (0x2 << TIM1_CR2_MMS2_SHIFT) /* 0010: Update event is TRGO2 */
+#  define TIM1_CR2_MMS2_COMPP       (0x3 << TIM1_CR2_MMS2_SHIFT) /* 0011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS2_OC1REF      (0x4 << TIM1_CR2_MMS2_SHIFT) /* 0100: Compare OC1REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC2REF      (0x5 << TIM1_CR2_MMS2_SHIFT) /* 0101: Compare OC2REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC3REF      (0x6 << TIM1_CR2_MMS2_SHIFT) /* 0110: Compare OC3REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC4REF      (0x7 << TIM1_CR2_MMS2_SHIFT) /* 0111: Compare OC4REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC5REF      (0x8 << TIM1_CR2_MMS2_SHIFT) /* 1000: Compare OC5REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC6REF      (0x9 << TIM1_CR2_MMS2_SHIFT) /* 1001: Compare OC6REF is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4      (0xa << TIM1_CR2_MMS2_SHIFT) /* 1010: Compare pulse - OC4REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC6      (0xb << TIM1_CR2_MMS2_SHIFT) /* 1011: Compare pulse - OC6REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4R6R   (0xc << TIM1_CR2_MMS2_SHIFT) /* 1100: Compare pulse - OC4REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC4R6F   (0xd << TIM1_CR2_MMS2_SHIFT) /* 1101: Compare pulse - OC4REF rising/OC6REF falling */
+#  define TIM1_CR2_MMS2_CMPOC5R6R   (0xe << TIM1_CR2_MMS2_SHIFT) /* 1110: Compare pulse - OC5REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC5R6F   (0xf << TIM1_CR2_MMS2_SHIFT) /* 1111: Compare pulse - OC5REF rising/OC6REF falling */
+
+#define TIM2_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM2_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM2_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM2_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM2_CR2_MMS_MASK           (0x7 << TIM2_CR2_MMS_SHIFT)
+#  define TIM2_CR2_MMS_RESET        (0x0 << TIM2_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM2_CR2_MMS_ENABLE       (0x1 << TIM2_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM2_CR2_MMS_UPDATE       (0x2 << TIM2_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM2_CR2_MMS_COMPP        (0x3 << TIM2_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM2_CR2_MMS_OC1REF       (0x4 << TIM2_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM2_CR2_MMS_OC2REF       (0x5 << TIM2_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM2_CR2_MMS_OC3REF       (0x6 << TIM2_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM2_CR2_MMS_OC4REF       (0x7 << TIM2_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM2_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM2_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM2_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM16_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM16_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM16_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM16_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM16_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM16_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM16_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM16_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM16_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+#define TIM17_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM17_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM17_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM17_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM17_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM17_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM17_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM17_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM17_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+/* Slave mode control register */
+
+#define TIM1_SMCR_SMS_LO_SHIFT      (0)       /* Bits 0-2: Slave mode selection, bits [2:0] */
+#define TIM1_SMCR_SMS_HI_SHIFT      (16)      /* Bit 16: Slave mode selection, bits [3] */
+#define TIM1_SMCR_SMS_BITS(h,l)     ((h << TIM1_SMCR_SMS_HI_SHIFT) | (l << TIM1_SMCR_SMS_LO_SHIFT))
+#define TIM1_SMCR_SMS_MASK          TIM1_SMCR_SMS_BITS(0x1, 0x7)
+#  define TIM1_SMCR_DISAB           TIM1_SMCR_SMS_BITS(0x0, 0x0) /* 0,000: Slave mode disabled */
+#  define TIM1_SMCR_ENCMD1          TIM1_SMCR_SMS_BITS(0x0, 0x1) /* 0,001: Encoder mode 1 */
+#  define TIM1_SMCR_ENCMD2          TIM1_SMCR_SMS_BITS(0x0, 0x2) /* 0,010: Encoder mode 2 */
+#  define TIM1_SMCR_ENCMD3          TIM1_SMCR_SMS_BITS(0x0, 0x3) /* 0,011: Encoder mode 3 */
+#  define TIM1_SMCR_RESET           TIM1_SMCR_SMS_BITS(0x0, 0x4) /* 0,100: Reset Mode */
+#  define TIM1_SMCR_GATED           TIM1_SMCR_SMS_BITS(0x0, 0x5) /* 0,101: Gated Mode */
+#  define TIM1_SMCR_TRIGGER         TIM1_SMCR_SMS_BITS(0x0, 0x6) /* 0,110: Trigger Mode */
+#  define TIM1_SMCR_EXTCLK1         TIM1_SMCR_SMS_BITS(0x0, 0x7) /* 0,111: External Clock Mode 1 */
+#  define TIM1_SMCR_SMS_COMBINED    TIM1_SMCR_SMS_BITS(0x1, 0x0) /* 1,000: Combined Reset and Trigger mode */
+
+#define TIM1_SMCR_OCCS              (1 << 3)  /* Bit 3: OCREF clear selection */
+#  define TIM1_SMCR_OCCS_CLR        (0 << 3)  /* 0: OCREF clear triggered by CLR input */
+#  define TIM1_SMCR_OCCS_ETRF       (1 << 3)  /* 1: OCREF clear triggered by ETRF */
+
+#define TIM1_SMCR_TS_LO_SHIFT       (4)       /* Bits 4-6: Trigger selection, bits [2:0] */
+#define TIM1_SMCR_TS_HI_SHIFT       (20)      /* Bits 20-21: Trigger selection, bits [1:0] */
+#define TIM1_SMCR_TS_BITS(h,l)      ((h << TIM1_SMCR_TS_HI_SHIFT) | (l << TIM1_SMCR_TS_LO_SHIFT))
+#define TIM1_SMCR_TS_MASK           TIM1_SMCR_TS_BITS(0x3, 0x7)
+#  define TIM1_SMCR_ITR0            TIM1_SMCR_TS_BITS(0x0, 0x0) /* 00,000: Internal trigger 0 (ITR0) */
+#  define TIM1_SMCR_ITR1            TIM1_SMCR_TS_BITS(0x0, 0x1) /* 00,001: Internal trigger 1 (ITR1) */
+#  define TIM1_SMCR_ITR2            TIM1_SMCR_TS_BITS(0x0, 0x2) /* 00,010: Internal trigger 2 (ITR2) */
+#  define TIM1_SMCR_ITR3            TIM1_SMCR_TS_BITS(0x0, 0x3) /* 00,011: Internal trigger 3 (ITR3) */
+#  define TIM1_SMCR_T1FED           TIM1_SMCR_TS_BITS(0x0, 0x4) /* 00,100: TI1 Edge detector (TI1F_ED) */
+#  define TIM1_SMCR_TI1FP1          TIM1_SMCR_TS_BITS(0x0, 0x5) /* 00,101: Filtered timer input 1 (TI1FP1) */
+#  define TIM1_SMCR_T12FP2          TIM1_SMCR_TS_BITS(0x0, 0x6) /* 00,110: Filtered timer input 2 (TI2FP2) */
+#  define TIM1_SMCR_ETRF            TIM1_SMCR_TS_BITS(0x0, 0x7) /* 00,111: External trigger input (ETRF) */
+
+#define TIM1_SMCR_MSM               (1 << 7)  /* Bit 7: Master/slave mode */
+#define TIM1_SMCR_ETF_SHIFT         (8)       /* Bits 8-11: External trigger filter */
+#define TIM1_SMCR_ETF_MASK          (0xf << TIM1_SMCR_ETF_SHIFT)
+#  define TIM1_SMCR_ETF(f)          ((f) << TIM1_SMCR_ETF_SHIFT) /* f = STM32WB_DF_[digital filter option] */
+
+#define TIM1_SMCR_ETPS_SHIFT        (12)      /* Bits 12-13: External trigger prescaler */
+#define TIM1_SMCR_ETPS_MASK         (0x3 << TIM1_SMCR_ETPS_SHIFT)
+#  define TIM1_SMCR_PSCOFF          (0x0 << TIM1_SMCR_ETPS_SHIFT) /* 00: Prescaler OFF */
+#  define TIM1_SMCR_ETRPd2          (0x1 << TIM1_SMCR_ETPS_SHIFT) /* 01: ETRP frequency divided by 2 */
+#  define TIM1_SMCR_ETRPd4          (0x2 << TIM1_SMCR_ETPS_SHIFT) /* 10: ETRP frequency divided by 4 */
+#  define TIM1_SMCR_ETRPd8          (0x3 << TIM1_SMCR_ETPS_SHIFT) /* 11: ETRP frequency divided by 8 */
+
+#define TIM1_SMCR_ECE               (1 << 14) /* Bit 14: External clock enable */
+#define TIM1_SMCR_ETP               (1 << 15) /* Bit 15: External trigger polarity */
+#  define TIM1_SMCR_ETP_HIGH        (0 << 15) /* 0: ETR is non-inverted, active at high level or rising edge */
+#  define TIM1_SMCR_ETP_LOW         (1 << 15) /* 1: ETR is inverted, active at low level or falling edge */
+
+#define TIM2_SMCR_SMS_LO_SHIFT      (0)       /* Bits 0-2: Slave mode selection, bits [2:0] */
+#define TIM2_SMCR_SMS_HI_SHIFT      (16)      /* Bit 16: Slave mode selection, bits [3] */
+#define TIM2_SMCR_SMS_BITS(h,l)     ((h << TIM2_SMCR_SMS_HI_SHIFT) | (l << TIM2_SMCR_SMS_LO_SHIFT))

Review Comment:
   ditto



##########
arch/arm/src/stm32wb/stm32wb_rcc.c:
##########
@@ -0,0 +1,1008 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_rcc.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb_rcc.h"
+#include "stm32wb_rtc.h"
+#include "stm32wb_pwr.h"
+#include "hardware/stm32wb_flash.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Allow up to 100 milliseconds for the high speed clock to become ready.
+ * that is a very long delay, but if the clock does not become ready we are
+ * hosed anyway.
+ */
+
+#define HSERDY_TIMEOUT (100 * CONFIG_BOARD_LOOPSPERMSEC)
+
+/* Same for HSI and MSI */
+
+#define HSIRDY_TIMEOUT HSERDY_TIMEOUT
+#define MSIRDY_TIMEOUT HSERDY_TIMEOUT
+
+/* Determine if board wants to use HSI48 as 48 MHz oscillator. */
+
+#if defined(CONFIG_STM32WB_HAVE_HSI48) && defined(STM32WB_USE_CLK48)
+#  if STM32WB_CLK48_SEL == RCC_CCIPR_CLK48SEL_HSI48
+#    define STM32WB_USE_HSI48
+#  endif
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rcc_reset
+ *
+ * Description:
+ *   Reset the RCC clock configuration to the default reset state
+ *
+ ****************************************************************************/
+
+static inline void rcc_reset(void)
+{
+  uint32_t regval;
+
+  /* Enable the Multi-Speed Internal clock (MSI) @ 4MHz */
+
+  regval  = getreg32(STM32WB_RCC_CR);
+  regval &= ~RCC_CR_MSIRANGE_MASK;
+  regval |= RCC_CR_MSIRANGE_4M | RCC_CR_MSION;
+  putreg32(regval, STM32WB_RCC_CR);
+
+  /* Reset CFGR register */
+
+  regval  = getreg32(STM32WB_RCC_CFGR);
+  regval &= RCC_CFGR_RESET_MASK;
+  putreg32(regval, STM32WB_RCC_CFGR);
+
+  /* Reset PLLSAI1ON, PLLON, HSECSSON, HSEON, HSION, and MSIPLLON bits */
+
+  regval  = getreg32(STM32WB_RCC_CR);
+  regval &= ~(RCC_CR_PLLON | RCC_CR_PLLSAI1ON | RCC_CR_CSSON |
+              RCC_CR_HSEON | RCC_CR_HSION | RCC_CR_MSIPLLEN);
+  putreg32(regval, STM32WB_RCC_CR);
+
+  /* Reset LSI1 and LSI2 bits */
+
+  regval  = getreg32(STM32WB_RCC_CSR);
+  regval &= ~(RCC_CSR_LSI1ON | RCC_CSR_LSI2ON);
+  putreg32(regval, STM32WB_RCC_CSR);
+
+  /* Reset HSI48ON bit */
+
+  regval  = getreg32(STM32WB_RCC_CRRCR);
+  regval &= ~(RCC_CRRCR_HSI48ON);
+  putreg32(regval, STM32WB_RCC_CRRCR);
+
+  /* Reset PLLCFGR register */
+
+  putreg32(RCC_PLLCFG_RESET, STM32WB_RCC_PLLCFG);
+
+  /* Reset PLLSAI1CFG register */
+
+  putreg32(RCC_PLLSAI1CFG_RESET, STM32WB_RCC_PLLSAI1CFG);
+
+  /* Disable all interrupts */
+
+  putreg32(0x00000000, STM32WB_RCC_CIER);
+}
+
+/****************************************************************************
+ * Name: rcc_enableahb1
+ *
+ * Description:
+ *   Enable selected AHB1 peripherals
+ *
+ ****************************************************************************/
+
+static inline void rcc_enableahb1(void)
+{
+  uint32_t regval;
+
+  /* Set the appropriate bits in the AHB1ENR register to enable the
+   * selected AHB1 peripherals.
+   */
+
+  regval = getreg32(STM32WB_RCC_AHB1ENR);
+
+#ifdef CONFIG_STM32WB_DMA1
+  /* DMA 1 clock enable */
+
+  regval |= RCC_AHB1ENR_DMA1EN;
+#endif
+
+#ifdef CONFIG_STM32WB_DMA2
+  /* DMA 2 clock enable */
+
+  regval |= RCC_AHB1ENR_DMA2EN;
+#endif
+
+#ifdef CONFIG_STM32WB_DMAMUX
+  /* DMAMUX 1 clock enable */
+
+  regval |= RCC_AHB1ENR_DMAMUX1EN;
+#endif
+
+#ifdef CONFIG_STM32WB_CRC
+  /* CRC clock enable */
+
+  regval |= RCC_AHB1ENR_CRCEN;
+#endif
+
+#ifdef CONFIG_STM32WB_TSC
+  /* TSC clock enable */
+
+  regval |= RCC_AHB1ENR_TSCEN;
+#endif
+
+  putreg32(regval, STM32WB_RCC_AHB1ENR);   /* Enable peripherals */
+}
+
+/****************************************************************************
+ * Name: rcc_enableahb2
+ *
+ * Description:
+ *   Enable selected AHB2 peripherals
+ *
+ ****************************************************************************/
+
+static inline void rcc_enableahb2(void)
+{
+  uint32_t regval;
+
+  /* Set the appropriate bits in the AHB2ENR register to enabled the
+   * selected AHB2 peripherals.
+   */
+
+  regval = getreg32(STM32WB_RCC_AHB2ENR);
+
+  /* Enable GPIO ports A-E, H */
+
+  regval |= (RCC_AHB2ENR_GPIOAEN
+             | RCC_AHB2ENR_GPIOBEN
+             | RCC_AHB2ENR_GPIOCEN
+#if defined(CONFIG_STM32WB_GPIO_HAVE_PORTD)
+             | RCC_AHB2ENR_GPIODEN
+#endif
+             | RCC_AHB2ENR_GPIOEEN
+             | RCC_AHB2ENR_GPIOHEN
+             );
+
+#if defined(CONFIG_STM32WB_ADC1)
+  /* ADC clock enable */
+
+  regval |= RCC_AHB2ENR_ADCEN;
+#endif
+
+#ifdef CONFIG_STM32WB_AES1
+  /* AES1 cryptographic accelerator clock enable */
+
+  regval |= RCC_AHB2ENR_AES1EN;
+#endif
+
+  putreg32(regval, STM32WB_RCC_AHB2ENR);   /* Enable peripherals */
+}
+
+/****************************************************************************
+ * Name: rcc_enableahb3
+ *
+ * Description:
+ *   Enable selected AHB3 peripherals
+ *
+ ****************************************************************************/
+
+static inline void rcc_enableahb3(void)
+{
+  uint32_t regval;
+
+  /* Set the appropriate bits in the AHB3ENR register to enabled the
+   * selected AHB3 peripherals.
+   */
+
+  regval = getreg32(STM32WB_RCC_AHB3ENR);
+
+#ifdef CONFIG_STM32WB_QSPI
+  /* QuadSPI module clock enable */
+
+  regval |= RCC_AHB3ENR_QSPIEN;
+#endif
+
+#ifdef CONFIG_STM32WB_PKA
+  /* Public key accelerator clock enable */
+
+  regval |= RCC_AHB3ENR_PKAEN;
+#endif
+
+#ifdef CONFIG_STM32WB_AES2
+  /* AES2 cryptographic accelerator clock enable */
+
+  regval |= RCC_AHB3ENR_AES2EN;
+#endif
+
+#ifdef CONFIG_STM32WB_RNG
+  /* Random number generator clock enable */
+
+  regval |= RCC_AHB3ENR_RNGEN;
+#endif
+
+#ifdef CONFIG_STM32WB_HSEM
+  /* Hardware semaphore clock enable */
+
+  regval |= RCC_AHB3ENR_HSEMEN;
+#endif
+
+#ifdef CONFIG_STM32WB_IPCC
+  /* Inter-processor communication controller clock enable */
+
+  regval |= RCC_AHB3ENR_IPCCEN;
+#endif
+
+#ifdef CONFIG_STM32WB_FLASH
+  /* Flash memory interface clock enable */
+
+  regval |= RCC_AHB3ENR_FLASHEN;
+#endif
+
+  putreg32(regval, STM32WB_RCC_AHB3ENR);   /* Enable peripherals */
+}
+
+/****************************************************************************
+ * Name: rcc_enableapb1
+ *
+ * Description:
+ *   Enable selected APB1 peripherals
+ *
+ ****************************************************************************/
+
+static inline void rcc_enableapb1(void)
+{
+  uint32_t regval;
+
+  /* Set the appropriate bits in the APB1ENR register to enabled the
+   * selected APB1 peripherals.
+   */
+
+  regval = getreg32(STM32WB_RCC_APB1ENR1);
+
+#ifdef CONFIG_STM32WB_TIM2
+  /* TIM2 clock enable */
+
+  regval |= RCC_APB1ENR1_TIM2EN;
+#endif
+
+#ifdef CONFIG_STM32WB_LCD
+  /* LCD clock enable */
+
+  regval |= RCC_APB1ENR1_LCDEN;
+#endif
+
+#if defined(CONFIG_STM32WB_RTC)
+  /* RTC APB clock enable */
+
+  regval |= RCC_APB1ENR1_RTCAPBEN;
+#endif
+
+#if defined(CONFIG_STM32WB_WWDG)
+  /* Window watchdog clock enable */
+
+  regval |= RCC_APB1ENR1_WWDGEN;
+#endif
+
+#ifdef CONFIG_STM32WB_SPI2
+  /* SPI2 clock enable */
+
+  regval |= RCC_APB1ENR1_SPI2EN;
+#endif
+
+#ifdef CONFIG_STM32WB_I2C1
+  /* I2C1 clock enable */
+
+  regval |= RCC_APB1ENR1_I2C1EN;
+#endif
+
+#ifdef CONFIG_STM32WB_I2C3
+  /* I2C3 clock enable */
+
+  regval |= RCC_APB1ENR1_I2C3EN;
+#endif
+
+#ifdef STM32WB_USE_HSI48
+  if (STM32WB_HSI48_SYNCSRC != SYNCSRC_NONE)
+    {
+      /* Clock Recovery System clock enable */
+
+      regval |= RCC_APB1ENR1_CRSEN;
+    }
+#endif
+
+#if defined(CONFIG_STM32WB_USB)
+  /* USB clock enable */
+
+  regval |= RCC_APB1ENR1_USBEN;
+#endif
+
+#ifdef CONFIG_STM32WB_LPTIM1
+  /* Low power timer 1 clock enable */
+
+  regval |= RCC_APB1ENR1_LPTIM1EN;
+#endif
+
+  putreg32(regval, STM32WB_RCC_APB1ENR1);   /* Enable peripherals */
+
+  /* Second APB1 register */
+
+  regval = getreg32(STM32WB_RCC_APB1ENR2);
+
+#ifdef CONFIG_STM32WB_LPUART1
+  /* Low power uart clock enable */
+
+  regval |= RCC_APB1ENR2_LPUART1EN;
+#endif
+
+#ifdef CONFIG_STM32WB_LPTIM2
+  /* Low power timer 2 clock enable */
+
+  regval |= RCC_APB1ENR2_LPTIM2EN;
+#endif
+
+  putreg32(regval, STM32WB_RCC_APB1ENR2);   /* Enable peripherals */
+}
+
+/****************************************************************************
+ * Name: rcc_enableapb2
+ *
+ * Description:
+ *   Enable selected APB2 peripherals
+ *
+ ****************************************************************************/
+
+static inline void rcc_enableapb2(void)
+{
+  uint32_t regval;
+
+  /* Set the appropriate bits in the APB2ENR register to enabled the
+   * selected APB2 peripherals.
+   */
+
+  regval = getreg32(STM32WB_RCC_APB2ENR);
+
+#ifdef CONFIG_STM32WB_TIM1
+  /* TIM1 clock enable */
+
+  regval |= RCC_APB2ENR_TIM1EN;
+#endif
+
+#ifdef CONFIG_STM32WB_SPI1
+  /* SPI1 clock enable */
+
+  regval |= RCC_APB2ENR_SPI1EN;
+#endif
+
+#ifdef CONFIG_STM32WB_USART1
+  /* USART1 clock enable */
+
+  regval |= RCC_APB2ENR_USART1EN;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM16
+  /* TIM16 clock enable */
+
+  regval |= RCC_APB2ENR_TIM16EN;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM17
+  /* TIM17 clock enable */
+
+  regval |= RCC_APB2ENR_TIM17EN;
+#endif
+
+#ifdef CONFIG_STM32WB_SAI1
+  /* SAI1 clock enable */
+
+  regval |= RCC_APB2ENR_SAI1EN;
+#endif
+
+  putreg32(regval, STM32WB_RCC_APB2ENR);   /* Enable peripherals */
+}
+
+/****************************************************************************
+ * Name: rcc_enableccip
+ *
+ * Description:
+ *   Set peripherals independent clock configuration.
+ *
+ ****************************************************************************/
+
+static inline void rcc_enableccip(void)
+{
+  uint32_t regval;
+
+  /* Certain peripherals have no clock selected even when their enable bit is
+   * set. Set some defaults in the CCIPR register so those peripherals
+   * will at least have a clock.
+   */
+
+  regval = getreg32(STM32WB_RCC_CCIPR);
+
+#if defined(STM32WB_I2C_USE_HSI16)
+#ifdef CONFIG_STM32WB_I2C1
+  /* Select HSI16 as I2C1 clock source. */
+
+  regval &= ~RCC_CCIPR_I2C1SEL_MASK;
+  regval |= RCC_CCIPR_I2C1SEL_HSI16;
+#endif
+#ifdef CONFIG_STM32WB_I2C3
+  /* Select HSI16 as I2C3 clock source. */
+
+  regval &= ~RCC_CCIPR_I2C3SEL_MASK;
+  regval |= RCC_CCIPR_I2C3SEL_HSI16;
+#endif
+#endif /* STM32WB_I2C_USE_HSI16 */
+
+#if defined(STM32WB_USE_CLK48)
+  /* XXX sanity if usb or rng, then we need to set the clk48 source
+   * and then we can also do away with STM32WB_USE_CLK48, and give better
+   * warning messages.
+   */
+
+  regval &= ~RCC_CCIPR_CLK48SEL_MASK;
+  regval |= RCC_CCIPR_CLK48SEL_HSI48;
+#endif
+
+#if defined(CONFIG_STM32WB_ADC1)
+  /* Select SYSCLK as ADC clock source */
+
+  regval &= ~RCC_CCIPR_ADCSEL_MASK;
+  regval |= RCC_CCIPR_ADCSEL_SYSCLK;
+#endif
+
+  putreg32(regval, STM32WB_RCC_CCIPR);
+}
+
+/****************************************************************************
+ * Name: stm32wb_stdclockconfig
+ *
+ * Description:
+ *   Called to change to new clock based on settings in board.h
+ *
+ *   NOTE:  This logic would need to be extended if you need to select low-
+ *   power clocking modes!
+ ****************************************************************************/
+
+#ifndef CONFIG_ARCH_BOARD_STM32WB_CUSTOM_CLOCKCONFIG
+static void stm32wb_stdclockconfig(void)
+{
+  uint32_t regval;
+  volatile int32_t timeout;
+
+#if defined(STM32WB_BOARD_USEHSI) || defined(STM32WB_I2C_USE_HSI16)
+  /* Enable Internal High-Speed Clock (HSI) */
+
+  regval  = getreg32(STM32WB_RCC_CR);
+  regval |= RCC_CR_HSION;           /* Enable HSI */
+  putreg32(regval, STM32WB_RCC_CR);
+
+  /* Wait until the HSI is ready (or until a timeout elapsed) */
+
+  for (timeout = HSIRDY_TIMEOUT; timeout > 0; timeout--)
+    {
+      /* Check if the HSIRDY flag is the set in the CR */
+
+      if ((getreg32(STM32WB_RCC_CR) & RCC_CR_HSIRDY) != 0)
+        {
+          /* If so, then break-out with timeout > 0 */
+
+          break;
+        }
+    }
+#endif
+
+#if defined(STM32WB_BOARD_USEHSI)
+  /* Already set above */
+
+#elif defined(STM32WB_BOARD_USEMSI)
+  /* Enable Internal Multi-Speed Clock (MSI) */
+
+  /* Wait until the MSI is either off or ready (or until a timeout elapsed) */
+
+  for (timeout = MSIRDY_TIMEOUT; timeout > 0; timeout--)
+    {
+      regval = getreg32(STM32WB_RCC_CR);
+
+      if ((regval & RCC_CR_MSIRDY) || ~(regval & RCC_CR_MSION))
+        {
+          /* If so, then break-out with timeout > 0 */
+
+          break;
+        }
+    }
+
+  /* Setting MSIRANGE */
+
+  regval  = getreg32(STM32WB_RCC_CR);
+  regval &= ~RCC_CR_MSIRANGE_MASK;
+  regval |= (STM32WB_BOARD_MSIRANGE | RCC_CR_MSION);    /* Enable MSI and frequency */
+  putreg32(regval, STM32WB_RCC_CR);
+
+  /* Wait until the MSI is ready (or until a timeout elapsed) */
+
+  for (timeout = MSIRDY_TIMEOUT; timeout > 0; timeout--)
+    {
+      /* Check if the MSIRDY flag is the set in the CR */
+
+      if ((getreg32(STM32WB_RCC_CR) & RCC_CR_MSIRDY) != 0)
+        {
+          /* If so, then break-out with timeout > 0 */
+
+          break;
+        }
+    }
+
+#elif defined(STM32WB_BOARD_USEHSE)
+  /* Enable External High-Speed Clock (HSE) */
+
+  regval  = getreg32(STM32WB_RCC_CR);
+  regval |= RCC_CR_HSEON;           /* Enable HSE */
+  putreg32(regval, STM32WB_RCC_CR);
+
+  /* Wait until the HSE is ready (or until a timeout elapsed) */
+
+  for (timeout = HSERDY_TIMEOUT; timeout > 0; timeout--)
+    {
+      /* Check if the HSERDY flag is the set in the CR */
+
+      if ((getreg32(STM32WB_RCC_CR) & RCC_CR_HSERDY) != 0)
+        {
+          /* If so, then break-out with timeout > 0 */
+
+          break;
+        }
+    }
+#else
+
+#  error stm32wb_stdclockconfig(), must have one of STM32WB_BOARD_USEHSI, STM32WB_BOARD_USEMSI, STM32WB_BOARD_USEHSE defined
+
+#endif
+
+  /* Check for a timeout.  If this timeout occurs, then we are hosed.  We
+   * have no real back-up plan, although the following logic makes it look
+   * as though we do.
+   */
+
+  if (timeout > 0)
+    {
+      /* Setup regulator voltage according to clock frequency */
+
+      regval  = getreg32(STM32WB_PWR_CR1);
+      regval &= ~PWR_CR1_VOS_MASK;
+#if STM32WB_SYSCLK_FREQUENCY > 16000000 || \
+    (defined(BOARD_MAX_PLL_FREQUENCY) && BOARD_MAX_PLL_FREQUENCY > 16000000)
+      regval |= PWR_CR1_VOS_RANGE1;
+#else
+      regval |= PWR_CR1_VOS_RANGE2;
+#endif
+      putreg32(regval, STM32WB_PWR_CR1);
+
+      /* Set the HCLK source/divider */
+
+      regval  = getreg32(STM32WB_RCC_CFGR);
+      regval &= ~RCC_CFGR_HPRE_MASK;
+      regval |= STM32WB_RCC_CFGR_HPRE;
+      putreg32(regval, STM32WB_RCC_CFGR);
+
+      /* Set the CPU2 HCLK2 source/divider */
+
+      regval  = getreg32(STM32WB_RCC_EXTCFGR);
+      regval &= ~RCC_EXTCFGR_C2HPRE_MASK;
+      regval |= STM32WB_RCC_EXTCFGR_C2HPRE;
+      putreg32(regval, STM32WB_RCC_EXTCFGR);
+
+      /* Set the HCLK4 source/divider */
+
+      regval  = getreg32(STM32WB_RCC_EXTCFGR);
+      regval &= ~RCC_EXTCFGR_SHDHPRE_MASK;
+      regval |= STM32WB_RCC_EXTCFGR_SHDHPRE;
+      putreg32(regval, STM32WB_RCC_EXTCFGR);
+
+      /* Set the PCLK1 divider */
+
+      regval  = getreg32(STM32WB_RCC_CFGR);
+      regval &= ~RCC_CFGR_PPRE1_MASK;
+      regval |= STM32WB_RCC_CFGR_PPRE1;
+      putreg32(regval, STM32WB_RCC_CFGR);
+
+      /* Set the PCLK2 divider */
+
+      regval  = getreg32(STM32WB_RCC_CFGR);
+      regval &= ~RCC_CFGR_PPRE2_MASK;
+      regval |= STM32WB_RCC_CFGR_PPRE2;
+      putreg32(regval, STM32WB_RCC_CFGR);
+
+      /* Configure Main PLL */
+
+      regval  = getreg32(STM32WB_RCC_PLLCFG);
+      regval &= ~(RCC_PLLCFG_PLLM_MASK | RCC_PLLCFG_PLLN_MASK);
+      regval |= (STM32WB_PLLCFG_PLLM | STM32WB_PLLCFG_PLLN);
+
+      /* Set the PLL dividers and multipliers to configure the main PLL */
+
+      regval &= ~(RCC_PLLCFG_PLLPEN | RCC_PLLCFG_PLLQEN | RCC_PLLCFG_PLLREN);
+#ifdef STM32WB_PLLCFG_PLLP_ENABLED
+      regval &= ~RCC_PLLCFG_PLLP_MASK;
+      regval |= (RCC_PLLCFG_PLLPEN | STM32WB_PLLCFG_PLLP);
+#endif
+#ifdef STM32WB_PLLCFG_PLLQ_ENABLED
+      regval &= ~RCC_PLLCFG_PLLQ_MASK;
+      regval |= (RCC_PLLCFG_PLLQEN | STM32WB_PLLCFG_PLLQ);
+#endif
+#ifdef STM32WB_PLLCFG_PLLR_ENABLED
+      regval &= ~RCC_PLLCFG_PLLR_MASK;
+      regval |= (RCC_PLLCFG_PLLREN | STM32WB_PLLCFG_PLLR);
+#endif
+
+      /* XXX The choice of clock source to PLL (all three) is independent
+       * of the sys clock source choice, review the STM32WB_BOARD_USEHSI
+       * name; probably split it into two, one for PLL source and one
+       * for sys clock source.
+       */
+
+      regval &= ~RCC_PLLCFG_PLLSRC_MASK;
+#ifdef STM32WB_BOARD_USEHSI
+      regval |= RCC_PLLCFG_PLLSRC_HSI16;
+#elif defined(STM32WB_BOARD_USEMSI)
+      regval |= RCC_PLLCFG_PLLSRC_MSI;
+#else /* if STM32WB_BOARD_USEHSE */
+      regval |= RCC_PLLCFG_PLLSRC_HSE;
+#endif
+
+      putreg32(regval, STM32WB_RCC_PLLCFG);
+
+      /* Enable the main PLL */
+
+      regval  = getreg32(STM32WB_RCC_CR);
+      regval |= RCC_CR_PLLON;
+      putreg32(regval, STM32WB_RCC_CR);
+
+      /* Wait until the PLL is ready */
+
+      while ((getreg32(STM32WB_RCC_CR) & RCC_CR_PLLRDY) == 0)
+        {
+        }
+
+#ifdef CONFIG_STM32WB_SAI1PLL
+      /* Configure SAI1 PLL */
+
+      regval  = getreg32(STM32WB_RCC_PLLSAI1CFG);
+      regval &= ~RCC_PLLSAI1CFG_PLLN_MASK;
+      regval |= STM32WB_PLLSAI1CFG_PLLN;
+
+      /* Set the PLL dividers and multipliers to configure the SAI1 PLL */
+
+      regval &= ~(RCC_PLLSAI1CFG_PLLPEN | RCC_PLLSAI1CFG_PLLQEN |
+                  RCC_PLLSAI1CFG_PLLREN);
+#ifdef STM32WB_PLLSAI1CFG_PLLP_ENABLED
+      regval &= ~RCC_PLLSAI1CFG_PLLP_MASK;
+      regval |= (RCC_PLLSAI1CFG_PLLPEN | STM32WB_PLLSAI1CFG_PLLP);
+#endif
+#ifdef STM32WB_PLLSAI1CFG_PLLQ_ENABLED
+      regval &= ~RCC_PLLSAI1CFG_PLLQ_MASK;
+      regval |= (RCC_PLLSAI1CFG_PLLQEN | STM32WB_PLLSAI1CFG_PLLQ);
+#endif
+#ifdef STM32WB_PLLSAI1CFG_PLLR_ENABLED
+      regval &= ~RCC_PLLSAI1CFG_PLLR_MASK;
+      regval |= (RCC_PLLSAI1CFG_PLLREN | STM32WB_PLLSAI1CFG_PLLR);
+#endif
+
+      putreg32(regval, STM32WB_RCC_PLLSAI1CFG);
+
+      /* Enable the SAI1 PLL */
+
+      regval  = getreg32(STM32WB_RCC_CR);
+      regval |= RCC_CR_PLLSAI1ON;
+      putreg32(regval, STM32WB_RCC_CR);
+
+      /* Wait until the PLL is ready */
+
+      while ((getreg32(STM32WB_RCC_CR) & RCC_CR_PLLSAI1RDY) == 0)
+        {
+        }
+#endif
+
+      /* Configure FLASH wait states */
+
+      regval  = getreg32(STM32WB_FLASH_ACR);
+      regval &= ~FLASH_ACR_LATENCY_MASK;
+#ifdef BOARD_FLASH_WAITSTATES
+      regval |= FLASH_ACR_LATENCY(BOARD_FLASH_WAITSTATES);
+#else
+      regval |= FLASH_ACR_LATENCY_3;
+#endif
+
+      /* Enable FLASH prefetch, instruction cache and data cache */
+
+#ifdef CONFIG_STM32WB_FLASH_PREFETCH
+      regval |= FLASH_ACR_PRFTEN;
+#else
+      regval &= ~FLASH_ACR_PRFTEN;
+#endif
+      regval |= (FLASH_ACR_ICEN | FLASH_ACR_DCEN);
+      putreg32(regval, STM32WB_FLASH_ACR);
+
+      /* Select the main PLL as system clock source */
+
+      regval  = getreg32(STM32WB_RCC_CFGR);
+      regval &= ~RCC_CFGR_SW_MASK;
+      regval |= RCC_CFGR_SW_PLL;
+      putreg32(regval, STM32WB_RCC_CFGR);
+
+      /* Wait until the PLL source is used as the system clock source */
+
+      while ((getreg32(STM32WB_RCC_CFGR) & RCC_CFGR_SWS_MASK) !=
+                       RCC_CFGR_SWS_PLL)
+        {
+        }
+
+#if defined(CONFIG_STM32WB_IWDG) || defined(CONFIG_STM32WB_RTC_LSICLOCK)
+      /* Low speed internal clock source LSI */
+
+      stm32wb_rcc_enable_lsi();
+#endif
+
+#if defined(STM32WB_USE_LSE)
+      /* Low speed external clock source LSE
+       *
+       * TODO: There is another case where the LSE needs to
+       * be enabled: if the MCO1 pin selects LSE as source.
+       * XXX and other cases, like automatic trimming of MSI for USB use
+       */
+
+      /* Turn on the LSE oscillator
+       * XXX this will almost surely get moved since we also want to use
+       * this for automatically trimming MSI, etc.
+       */
+
+      stm32wb_rcc_enable_lse();
+
+#  if defined(STM32WB_BOARD_USEMSI)
+      /* Now that LSE is up, auto trim the MSI */
+
+      regval  = getreg32(STM32WB_RCC_CR);
+      regval |= RCC_CR_MSIPLLEN;
+      putreg32(regval, STM32WB_RCC_CR);
+#  endif
+#endif /* STM32WB_USE_LSE */
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: rcc_enableperipherals
+ ****************************************************************************/
+
+static inline void rcc_enableperipherals(void)
+{
+  rcc_enableccip();
+  rcc_enableahb1();
+  rcc_enableahb2();
+  rcc_enableahb3();
+  rcc_enableapb1();
+  rcc_enableapb2();
+
+#ifdef STM32WB_USE_HSI48
+  /* Enable HSI48 clocking to support USB transfers or RNG */
+
+  stm32wb_enable_hsi48(STM32WB_HSI48_SYNCSRC);
+#endif
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rcc_resetbkp
+ *
+ * Description:
+ *   The RTC needs to reset the Backup Domain to change RTCSEL and resetting
+ *   the Backup Domain renders to disabling the LSE as consequence.   In
+ *   order to avoid resetting the Backup Domain when we already configured
+ *   LSE we will reset the Backup Domain early (here).
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_STM32WB_PWR) && defined(CONFIG_STM32WB_RTC)
+static inline void rcc_resetbkp(void)
+{
+  bool init_stat;
+
+  /* Check if the RTC is already configured */
+
+  init_stat = stm32wb_rtc_is_initialized();
+  if (!init_stat)
+    {
+      uint32_t bkregs[STM32WB_RTC_BKCOUNT];
+      int i;
+
+      /* Backup backup-registers before RTC reset. */
+
+      for (i = 0; i < STM32WB_RTC_BKCOUNT; i++)
+        {
+          bkregs[i] = getreg32(STM32WB_RTC_BKPR(i));
+        }
+
+      /* Enable write access to the backup domain (RTC registers, RTC
+       * backup data registers and backup SRAM).
+       */
+
+      stm32wb_pwr_enablebkp(true);
+
+      /* We might be changing RTCSEL - to ensure such changes work, we must
+       * reset the backup domain (having backed up the RTC_MAGIC token)
+       */
+
+      modifyreg32(STM32WB_RCC_BDCR, 0, RCC_BDCR_BDRST);
+      modifyreg32(STM32WB_RCC_BDCR, RCC_BDCR_BDRST, 0);
+
+      /* Restore backup-registers, except RTC related. */
+
+      for (i = 0; i < STM32WB_RTC_BKCOUNT; i++)
+        {
+          if (RTC_MAGIC_REG == STM32WB_RTC_BKPR(i))
+            {
+              continue;
+            }
+
+           putreg32(bkregs[i], STM32WB_RTC_BKPR(i));

Review Comment:
   ```suggestion
             if (RTC_MAGIC_REG != STM32WB_RTC_BKPR(i))
               {
                 putreg32(bkregs[i], STM32WB_RTC_BKPR(i));
               }
   ```



##########
arch/arm/src/stm32wb/stm32wb_rtc.c:
##########
@@ -0,0 +1,1835 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_rtc.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <errno.h>
+#include <debug.h>
+#include <assert.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/time.h>
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb_rcc.h"
+#include "stm32wb_pwr.h"
+#include "stm32wb_rtc.h"
+#include "stm32wb_exti.h"
+
+#ifdef CONFIG_STM32WB_RTC
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* This RTC implementation supports
+ *  - date/time RTC hardware
+ *  - extended functions Alarm A and B
+ * */
+
+#ifndef CONFIG_RTC_DATETIME
+#  error "CONFIG_RTC_DATETIME must be set to use this driver"
+#endif
+
+#ifdef CONFIG_RTC_HIRES
+#  error "CONFIG_RTC_HIRES must NOT be set with this driver"
+#endif
+
+#ifndef CONFIG_STM32WB_PWR
+#  error "CONFIG_STM32WB_PWR must selected to use this driver"
+#endif
+
+/* Constants ****************************************************************/
+
+#define SYNCHRO_TIMEOUT               (0x00020000)
+#define INITMODE_TIMEOUT              (0x00010000)
+
+#define RTC_ALRMR_ENABLE              (0x00000000)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+typedef unsigned int rtc_alarmreg_t;
+
+struct alm_cbinfo_s
+{
+  volatile alm_callback_t ac_cb; /* Client callback function */
+  volatile void *ac_arg;         /* Argument to pass with the callback function */
+};
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+/* Callback to use when an EXTI is activated  */
+
+static struct alm_cbinfo_s g_alarmcb[RTC_ALARM_LAST];
+static bool g_alarm_enabled;  /* True: Alarm interrupts are enabled */
+#endif
+
+#ifdef CONFIG_RTC_PERIODIC
+static wakeupcb_t g_wakeupcb;
+static bool g_wakeup_enabled;  /* True: Wakeup interrupts are enabled */
+#endif
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/* g_rtc_enabled is set true after the RTC has successfully initialized */
+
+volatile bool g_rtc_enabled = false;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int rtchw_check_alrawf(void);
+static int rtchw_set_alrmar(rtc_alarmreg_t alarmreg);
+#if CONFIG_RTC_NALARMS > 1
+static int rtchw_check_alrbwf(void);
+static int rtchw_set_alrmbr(rtc_alarmreg_t alarmreg);
+#endif
+static inline void rtc_enable_alarm(void);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rtc_dumpregs
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_RTC_INFO
+static void rtc_dumpregs(const char *msg)
+{
+  rtcinfo("%s:\n", msg);
+  rtcinfo("      TR: %08x\n", getreg32(STM32WB_RTC_TR));
+  rtcinfo("      DR: %08x\n", getreg32(STM32WB_RTC_DR));
+  rtcinfo("      CR: %08x\n", getreg32(STM32WB_RTC_CR));
+  rtcinfo("     ISR: %08x\n", getreg32(STM32WB_RTC_ISR));
+  rtcinfo("    PRER: %08x\n", getreg32(STM32WB_RTC_PRER));
+  rtcinfo("    WUTR: %08x\n", getreg32(STM32WB_RTC_WUTR));
+  rtcinfo("  ALRMAR: %08x\n", getreg32(STM32WB_RTC_ALRMAR));
+  rtcinfo("  ALRMBR: %08x\n", getreg32(STM32WB_RTC_ALRMBR));
+  rtcinfo("  SHIFTR: %08x\n", getreg32(STM32WB_RTC_SHIFTR));
+  rtcinfo("    TSTR: %08x\n", getreg32(STM32WB_RTC_TSTR));
+  rtcinfo("    TSDR: %08x\n", getreg32(STM32WB_RTC_TSDR));
+  rtcinfo("   TSSSR: %08x\n", getreg32(STM32WB_RTC_TSSSR));
+  rtcinfo("    CALR: %08x\n", getreg32(STM32WB_RTC_CALR));
+  rtcinfo("  TAMPCR: %08x\n", getreg32(STM32WB_RTC_TAMPCR));
+  rtcinfo("ALRMASSR: %08x\n", getreg32(STM32WB_RTC_ALRMASSR));
+  rtcinfo("ALRMBSSR: %08x\n", getreg32(STM32WB_RTC_ALRMBSSR));
+  rtcinfo("      OR: %08x\n", getreg32(STM32WB_RTC_OR));
+  rtcinfo("MAGICREG: %08x\n", getreg32(RTC_MAGIC_REG));
+}
+#else
+#  define rtc_dumpregs(msg)
+#endif
+
+/****************************************************************************
+ * Name: rtc_dumptime
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_RTC_INFO
+static void rtc_dumptime(const struct tm *tp, const char *msg)
+{
+  rtcinfo("%s:\n", msg);
+  rtcinfo("  tm_sec: %08x\n", tp->tm_sec);
+  rtcinfo("  tm_min: %08x\n", tp->tm_min);
+  rtcinfo(" tm_hour: %08x\n", tp->tm_hour);
+  rtcinfo(" tm_mday: %08x\n", tp->tm_mday);
+  rtcinfo("  tm_mon: %08x\n", tp->tm_mon);
+  rtcinfo(" tm_year: %08x\n", tp->tm_year);
+}
+#else
+#  define rtc_dumptime(tp, msg)
+#endif
+
+/****************************************************************************
+ * Name: rtc_wprunlock
+ *
+ * Description:
+ *    Disable RTC write protection
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void rtc_wprunlock(void)
+{
+  /* Enable write access to the backup domain. */
+
+  stm32wb_pwr_enablebkp(true);
+
+  /* The following steps are required to unlock the write protection on
+   * all the RTC registers (except for RTC_ISR[13:8], RTC_TAFCR, and
+   * RTC_BKPxR).
+   *
+   * 1. Write 0xca into the RTC_WPR register.
+   * 2. Write 0x53 into the RTC_WPR register.
+   *
+   * Writing a wrong key re-activates the write protection.
+   */
+
+  putreg32(RTC_WPR_KEY1, STM32WB_RTC_WPR);
+  putreg32(RTC_WPR_KEY2, STM32WB_RTC_WPR);
+}
+
+/****************************************************************************
+ * Name: rtc_wprlock
+ *
+ * Description:
+ *    Enable RTC write protection
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void rtc_wprlock(void)
+{
+  /* Writing any wrong key re-activates the write protection. */
+
+  putreg32(0xff, STM32WB_RTC_WPR);
+
+  /* Disable write access to the backup domain. */
+
+  stm32wb_pwr_enablebkp(false);
+}
+
+/****************************************************************************
+ * Name: rtc_synchwait
+ *
+ * Description:
+ *   Waits until the RTC Time and Date registers (RTC_TR and RTC_DR) are
+ *   synchronized with RTC APB clock.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int rtc_synchwait(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret;
+
+  /* Clear Registers synchronization flag (RSF) */
+
+  regval  = getreg32(STM32WB_RTC_ISR);
+  regval &= ~RTC_ISR_RSF;
+  putreg32(regval, STM32WB_RTC_ISR);
+
+  /* Now wait the registers to become synchronised */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < SYNCHRO_TIMEOUT; timeout++)
+    {
+      regval = getreg32(STM32WB_RTC_ISR);
+      if ((regval & RTC_ISR_RSF) != 0)
+        {
+          /* Synchronized */
+
+          ret = OK;
+          break;
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: rtc_enterinit
+ *
+ * Description:
+ *   Enter RTC initialization mode.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int rtc_enterinit(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret;
+
+  /* Check if the Initialization mode is already set */
+
+  regval = getreg32(STM32WB_RTC_ISR);
+
+  ret = OK;
+  if ((regval & RTC_ISR_INITF) == 0)
+    {
+      /* Set the Initialization mode */
+
+      putreg32(RTC_ISR_INIT, STM32WB_RTC_ISR);
+
+      /* Wait until the RTC is in the INIT state (or a timeout occurs) */
+
+      ret = -ETIMEDOUT;
+      for (timeout = 0; timeout < INITMODE_TIMEOUT; timeout++)
+        {
+          regval = getreg32(STM32WB_RTC_ISR);
+          if ((regval & RTC_ISR_INITF) != 0)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: rtc_exitinit
+ *
+ * Description:
+ *   Exit RTC initialization mode.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static void rtc_exitinit(void)
+{
+  uint32_t regval;
+
+  regval = getreg32(STM32WB_RTC_ISR);
+  regval &= ~(RTC_ISR_INIT);
+  putreg32(regval, STM32WB_RTC_ISR);
+}
+
+/****************************************************************************
+ * Name: rtc_bin2bcd
+ *
+ * Description:
+ *   Converts a 2 digit binary to BCD format
+ *
+ * Input Parameters:
+ *   value - The byte to be converted.
+ *
+ * Returned Value:
+ *   The value in BCD representation
+ *
+ ****************************************************************************/
+
+static uint32_t rtc_bin2bcd(int value)
+{
+  uint32_t msbcd = 0;
+
+  while (value >= 10)
+    {
+      msbcd++;
+      value -= 10;
+    }
+
+  return (msbcd << 4) | value;
+}
+
+/****************************************************************************
+ * Name: rtc_bin2bcd
+ *
+ * Description:
+ *   Convert from 2 digit BCD to binary.
+ *
+ * Input Parameters:
+ *   value - The BCD value to be converted.
+ *
+ * Returned Value:
+ *   The value in binary representation
+ *
+ ****************************************************************************/
+
+static int rtc_bcd2bin(uint32_t value)
+{
+  uint32_t tens = (value >> 4) * 10;
+  return (int)(tens + (value & 0x0f));
+}
+
+/****************************************************************************
+ * Name: rtc_resume
+ *
+ * Description:
+ *   Called when the RTC was already initialized on a previous power cycle.
+ *   This just brings the RTC back into full operation.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static void rtc_resume(void)
+{
+#ifdef CONFIG_RTC_ALARM
+  uint32_t regval;
+
+  /* Clear the RTC alarm flags */
+
+  regval  = getreg32(STM32WB_RTC_ISR);
+  regval &= ~(RTC_ISR_ALRAF | RTC_ISR_ALRBF);
+  putreg32(regval, STM32WB_RTC_ISR);
+
+  /* Clear the EXTI Line 17 Pending bit (Connected internally to RTC Alarm) */
+
+  putreg32(EXTI_PR1_PIF(EXTI_EVT_RTCALARM), STM32WB_EXTI_PR1);
+#endif
+}
+
+/****************************************************************************
+ * Name: stm32wb_rtc_alarm_handler
+ *
+ * Description:
+ *   RTC ALARM interrupt service routine through the EXTI line
+ *
+ * Input Parameters:
+ *   irq - The IRQ number that generated the interrupt
+ *   context - Architecture specific register save information.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; A negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int stm32wb_rtc_alarm_handler(int irq, void *context,
+                                     void *rtc_handler_arg)
+{
+  struct alm_cbinfo_s *cbinfo;
+  alm_callback_t cb;
+  void *arg;
+  uint32_t isr;
+  uint32_t cr;
+  int ret = OK;
+
+  /* Enable write access to the backup domain (RTC registers, RTC
+   * backup data registers and backup SRAM).
+   */
+
+  stm32wb_pwr_enablebkp(true);
+
+  /* Check for EXTI from Alarm A or B and handle according */
+
+  cr  = getreg32(STM32WB_RTC_CR);
+  if ((cr & RTC_CR_ALRAIE) != 0)
+    {
+      isr  = getreg32(STM32WB_RTC_ISR);
+      if ((isr & RTC_ISR_ALRAF) != 0)
+        {
+          cbinfo = &g_alarmcb[RTC_ALARMA];
+          if (cbinfo->ac_cb != NULL)
+            {
+              /* Alarm A callback */
+
+              cb  = cbinfo->ac_cb;
+              arg = (void *)cbinfo->ac_arg;
+
+              cbinfo->ac_cb  = NULL;
+              cbinfo->ac_arg = NULL;
+
+              cb(arg, RTC_ALARMA);
+            }
+
+          /* note, bits 8-13 do /not/ require the write enable procedure */
+
+          isr  = getreg32(STM32WB_RTC_ISR);
+          isr &= ~RTC_ISR_ALRAF;
+          putreg32(isr, STM32WB_RTC_ISR);
+        }
+    }
+
+#if CONFIG_RTC_NALARMS > 1
+  cr  = getreg32(STM32WB_RTC_CR);
+  if ((cr & RTC_CR_ALRBIE) != 0)
+    {
+      isr  = getreg32(STM32WB_RTC_ISR);
+      if ((isr & RTC_ISR_ALRBF) != 0)
+        {
+          cbinfo = &g_alarmcb[RTC_ALARMB];
+          if (cbinfo->ac_cb != NULL)
+            {
+              /* Alarm B callback */
+
+              cb  = cbinfo->ac_cb;
+              arg = (void *)cbinfo->ac_arg;
+
+              cbinfo->ac_cb  = NULL;
+              cbinfo->ac_arg = NULL;
+
+              cb(arg, RTC_ALARMB);
+            }
+
+          /* note, bits 8-13 do /not/ require the write enable procedure */
+
+          isr  = getreg32(STM32WB_RTC_ISR);
+          isr &= ~RTC_ISR_ALRBF;
+          putreg32(isr, STM32WB_RTC_ISR);
+        }
+    }
+#endif
+
+  /* Disable write access to the backup domain (RTC registers, RTC backup
+   * data registers and backup SRAM).
+   */
+
+  stm32wb_pwr_enablebkp(false);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: rtchw_check_alrXwf X= a or B
+ *
+ * Description:
+ *   Check registers
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int rtchw_check_alrawf(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret = -ETIMEDOUT;
+
+  /* Check RTC_ISR ALRAWF for access to alarm register,
+   * Can take 2 RTCCLK cycles or timeout
+   * CubeMX use GetTick.
+   */
+
+  for (timeout = 0; timeout < INITMODE_TIMEOUT; timeout++)
+    {
+      regval = getreg32(STM32WB_RTC_ISR);
+      if ((regval & RTC_ISR_ALRAWF) != 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  return ret;
+}
+#endif
+
+#if defined(CONFIG_RTC_ALARM) && CONFIG_RTC_NALARMS > 1
+static int rtchw_check_alrbwf(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret = -ETIMEDOUT;
+
+  /* Check RTC_ISR ALRBWF for access to alarm register,
+   * can take 2 RTCCLK cycles or timeout
+   * CubeMX use GetTick.
+   */
+
+  for (timeout = 0; timeout < INITMODE_TIMEOUT; timeout++)
+    {
+      regval = getreg32(STM32WB_RTC_ISR);
+      if ((regval & RTC_ISR_ALRBWF) != 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_rtchw_set_alrmXr X is a or b
+ *
+ * Description:
+ *   Set the alarm (A or B) hardware registers, using the required hardware
+ *   access protocol
+ *
+ * Input Parameters:
+ *   alarmreg - the register
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int rtchw_set_alrmar(rtc_alarmreg_t alarmreg)
+{
+  int isr;
+  int ret = -EBUSY;
+
+  /* Disable the write protection for RTC registers */
+
+  rtc_wprunlock();
+
+  /* Disable RTC alarm A & Interrupt A */
+
+  modifyreg32(STM32WB_RTC_CR, (RTC_CR_ALRAE | RTC_CR_ALRAIE), 0);
+
+  /* Ensure Alarm A flag reset; this is edge triggered */
+
+  isr  = getreg32(STM32WB_RTC_ISR) & ~RTC_ISR_ALRAF;
+  putreg32(isr, STM32WB_RTC_ISR);
+
+  /* Wait for Alarm A to be writable */
+
+  ret = rtchw_check_alrawf();
+  if (ret != OK)
+    {
+      goto errout_with_wprunlock;
+    }
+
+  /* Set the RTC Alarm A register */
+
+  putreg32(alarmreg, STM32WB_RTC_ALRMAR);
+  putreg32(0, STM32WB_RTC_ALRMASSR);
+  rtcinfo("  ALRMAR: %08" PRIx32 "\n", getreg32(STM32WB_RTC_ALRMAR));
+
+  /* Enable RTC alarm A */
+
+  modifyreg32(STM32WB_RTC_CR, 0, (RTC_CR_ALRAE | RTC_CR_ALRAIE));
+
+errout_with_wprunlock:
+  rtc_wprlock();
+  return ret;
+}
+#endif
+
+#if defined(CONFIG_RTC_ALARM) && CONFIG_RTC_NALARMS > 1
+static int rtchw_set_alrmbr(rtc_alarmreg_t alarmreg)
+{
+  int isr;
+  int ret = -EBUSY;
+
+  /* Disable the write protection for RTC registers */
+
+  rtc_wprunlock();
+
+  /* Disable RTC alarm B & Interrupt B */
+
+  modifyreg32(STM32WB_RTC_CR, (RTC_CR_ALRBE | RTC_CR_ALRBIE), 0);
+
+  /* Ensure Alarm B flag reset; this is edge triggered */
+
+  isr  = getreg32(STM32WB_RTC_ISR) & ~RTC_ISR_ALRBF;
+  putreg32(isr, STM32WB_RTC_ISR);
+
+  /* Wait for Alarm B to be writable */
+
+  ret = rtchw_check_alrbwf();
+  if (ret != OK)
+    {
+      goto rtchw_set_alrmbr_exit;
+    }
+
+  /* Set the RTC Alarm B register */
+
+  putreg32(alarmreg, STM32WB_RTC_ALRMBR);
+  putreg32(0, STM32WB_RTC_ALRMBSSR);
+  rtcinfo("  ALRMBR: %08" PRIx32 "\n", getreg32(STM32WB_RTC_ALRMBR));
+
+  /* Enable RTC alarm B */
+
+  modifyreg32(STM32WB_RTC_CR, 0, (RTC_CR_ALRBE | RTC_CR_ALRBIE));
+
+rtchw_set_alrmbr_exit:
+  rtc_wprlock();
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: rtc_enable_alarm
+ *
+ * Description:
+ *   Enable ALARM interrupts
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static inline void rtc_enable_alarm(void)
+{
+  /* Is the alarm already enabled? */
+
+  if (!g_alarm_enabled)
+    {
+      /* Configure RTC interrupt to catch alarm interrupts. All RTC
+       * interrupts are connected to the EXTI controller.  To enable the
+       * RTC Alarm interrupt, the following sequence is required:
+       *
+       * 1. Configure and enable the EXTI line 17 (RTC ALARM) in interrupt
+       *    mode and select the rising edge sensitivity.
+       *    EXTI line 18 RTC Tamper or Timestamp or CSS_LSE
+       *    EXTI line 19 RTC Wakeup
+       * 2. Configure and enable the RTC_Alarm IRQ channel in the NVIC.
+       * 3. Configure the RTC to generate RTC alarms (Alarm A or Alarm B).
+       */
+
+      stm32wb_exti_alarm(true, false, true, stm32wb_rtc_alarm_handler, NULL);
+      g_alarm_enabled = true;
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_rtc_getalarmdatetime
+ *
+ * Description:
+ *   Get the current date and time for a RTC alarm.
+ *
+ * Input Parameters:
+ *   reg - RTC alarm register
+ *   tp - The location to return the high resolution time value.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int stm32wb_rtc_getalarmdatetime(rtc_alarmreg_t reg,
+                                        struct tm *tp)
+{
+  uint32_t data;
+  uint32_t tmp;
+
+  DEBUGASSERT(tp != NULL);
+
+  /* Sample the data time register. */
+
+  data = getreg32(reg);
+
+  /* Convert the RTC time to fields in struct tm format.  All of the STM32WB
+   * ranges of values correspond between struct tm and the time register.
+   */
+
+  tmp = (data & (RTC_ALRMR_SU_MASK | RTC_ALRMR_ST_MASK)) >>
+        RTC_ALRMR_SU_SHIFT;
+  tp->tm_sec = rtc_bcd2bin(tmp);
+
+  tmp = (data & (RTC_ALRMR_MNU_MASK | RTC_ALRMR_MNT_MASK)) >>
+        RTC_ALRMR_MNU_SHIFT;
+  tp->tm_min = rtc_bcd2bin(tmp);
+
+  tmp = (data & (RTC_ALRMR_HU_MASK | RTC_ALRMR_HT_MASK)) >>
+        RTC_ALRMR_HU_SHIFT;
+  tp->tm_hour = rtc_bcd2bin(tmp);
+
+  tmp = (data & (RTC_ALRMR_DU_MASK | RTC_ALRMR_DT_MASK)) >>
+        RTC_ALRMR_DU_SHIFT;
+  tp->tm_mday = rtc_bcd2bin(tmp);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_rtc_is_initialized
+ *
+ * Description:
+ *    Returns 'true' if the RTC has been initialized
+ *    Returns 'false' if the RTC has never been initialized since first
+ *    time power up, and the counters are stopped until it is first
+ *    initialized.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Returns true if RTC has been initialized.
+ *
+ ****************************************************************************/
+
+bool stm32wb_rtc_is_initialized(void)
+{
+  uint32_t regval;
+
+  regval = getreg32(RTC_MAGIC_REG);
+
+  return regval == RTC_MAGIC || regval == RTC_MAGIC_TIME_SET;
+}
+
+/****************************************************************************
+ * Name: up_rtc_initialize
+ *
+ * Description:
+ *   Initialize the hardware RTC per the selected configuration.  This
+ *   function is called once during the OS initialization sequence
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int up_rtc_initialize(void)
+{
+  bool init_stat;
+  uint32_t regval;
+
+  rtc_dumpregs("Before Initialization");
+
+  /* See if the clock has already been initialized; since it is battery
+   * backed, we don't need or want to re-initialize on each reset.
+   */
+
+  init_stat = stm32wb_rtc_is_initialized();
+  if (!init_stat)
+    {
+      /* Enable write access to the backup domain (RTC registers, RTC
+       * backup data registers and backup SRAM).
+       */
+
+      stm32wb_pwr_enablebkp(true);
+
+#if defined(CONFIG_STM32WB_RTC_HSECLOCK)
+      modifyreg32(STM32WB_RCC_BDCR, RCC_BDCR_RTCSEL_MASK,
+                  RCC_BDCR_RTCSEL_HSE);
+#elif defined(CONFIG_STM32WB_RTC_LSICLOCK)
+      modifyreg32(STM32WB_RCC_BDCR, RCC_BDCR_RTCSEL_MASK,
+                  RCC_BDCR_RTCSEL_LSI);
+#elif defined(CONFIG_STM32WB_RTC_LSECLOCK)
+      modifyreg32(STM32WB_RCC_BDCR, RCC_BDCR_RTCSEL_MASK,
+                  RCC_BDCR_RTCSEL_LSE);
+#else
+#  error "No clock for RTC!"
+#endif
+
+      /* Enable the RTC Clock by setting the RTCEN bit in the RCC register */
+
+      modifyreg32(STM32WB_RCC_BDCR, 0, RCC_BDCR_RTCEN);
+
+      /* Disable the write protection for RTC registers */
+
+      rtc_wprunlock();
+
+      /* Set Initialization mode */
+
+      if (OK != rtc_enterinit())

Review Comment:
   I need to check if style guide has anything about "constant left" compare, but most of the code do not use it.



##########
arch/arm/src/stm32wb/stm32wb_idle.c:
##########
@@ -0,0 +1,192 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_idle.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/board.h>
+#include <nuttx/power/pm.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb_pm.h"
+#include "stm32wb_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Does the board support an IDLE LED to indicate that the board is in the
+ * IDLE state?
+ */
+
+#if defined(CONFIG_ARCH_LEDS) && defined(LED_IDLE)
+#  define BEGIN_IDLE() board_autoled_on(LED_IDLE)
+#  define END_IDLE()   board_autoled_off(LED_IDLE)
+#else
+#  define BEGIN_IDLE()
+#  define END_IDLE()
+#endif
+
+#define PM_IDLE_DOMAIN 0 /* Revisit */
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_idlepm
+ *
+ * Description:
+ *   Perform IDLE state power management.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void up_idlepm(void)
+{
+  static enum pm_state_e oldstate = PM_NORMAL;
+  enum pm_state_e newstate;
+  irqstate_t flags;
+  int ret;
+
+  /* Decide, which power saving level can be obtained */
+
+  newstate = pm_checkstate(PM_IDLE_DOMAIN);
+
+  /* Check for state changes */
+
+  if (newstate != oldstate)
+    {
+      flags = enter_critical_section();
+
+      /* Perform board-specific, state-dependent logic here */
+
+      _info("newstate= %d oldstate=%d\n", newstate, oldstate);
+
+      /* Then force the global state change */
+
+      ret = pm_changestate(PM_IDLE_DOMAIN, newstate);
+      if (ret < 0)
+        {
+          /* The new state change failed, revert to the preceding state */
+
+          pm_changestate(PM_IDLE_DOMAIN, oldstate);
+        }
+      else
+        {
+          /* Save the new state */
+
+          oldstate = newstate;
+        }
+
+      /* MCU-specific power management logic */
+
+      switch (newstate)
+        {
+        case PM_NORMAL:
+          break;
+
+        case PM_IDLE:
+          break;
+
+        case PM_STANDBY:
+
+          /* Enter STOP mode */
+
+          BEGIN_IDLE();
+          stm32wb_pmstop(true);
+          END_IDLE();
+
+          /* Set correct clock again after returning from STOP */
+
+          stm32wb_clockenable();
+
+          /* Inform of all drivers of the new state */
+
+          ret = pm_changestate(PM_IDLE_DOMAIN, PM_NORMAL);
+          if (ret >= 0)
+            {
+              oldstate = PM_NORMAL;
+            }
+
+          break;
+
+        case PM_SLEEP:
+          stm32wb_pmstandby();
+          break;
+
+        default:
+          break;
+        }
+
+      leave_critical_section(flags);
+    }
+}
+#else
+#  define up_idlepm()
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_idle
+ *
+ * Description:
+ *   up_idle() is the logic that will be executed when there is no other
+ *   ready-to-run task.  This is processor idle time and will continue until
+ *   some interrupt occurs to cause a context switch from the idle task.
+ *
+ *   Processing in this state may be processor-specific. e.g., this is where
+ *   power management operations might be performed.
+ *
+ ****************************************************************************/
+
+void up_idle(void)
+{
+#if defined(CONFIG_SUPPRESS_INTERRUPTS) || defined(CONFIG_SUPPRESS_TIMER_INTS)
+  /* If the system is idle and there are no timer interrupts, then process
+   * "fake" timer interrupts. Hopefully, something will wake up.
+   */
+
+  nxsched_process_timer();
+#else
+
+  /* Perform IDLE mode power management */
+
+  up_idlepm();
+
+  /* Sleep until an interrupt occurs to save power. */
+
+#if !(defined(CONFIG_DEBUG_SYMBOLS) && defined(CONFIG_STM32WB_DISABLE_IDLE_SLEEP_DURING_DEBUG))
+  BEGIN_IDLE();
+  asm("WFI");

Review Comment:
   Do we need `__asm__ volatile` here? Just from style point of view



##########
arch/arm/src/stm32wb/stm32wb_gpio.h:
##########
@@ -0,0 +1,357 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_gpio.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_STM32WB_GPIO_H
+#define __ARCH_ARM_SRC_STM32WB_STM32WB_GPIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#ifndef __ASSEMBLY__
+#  include <stdint.h>
+#  include <stdbool.h>
+#endif
+
+#include <nuttx/irq.h>
+#include <arch/stm32wb/chip.h>
+
+#include "chip.h"
+#include "hardware/stm32wb_gpio.h"
+
+/****************************************************************************
+ * Pre-Processor Declarations
+ ****************************************************************************/
+
+/* Bit-encoded input to stm32wb_configgpio() */
+
+/* Each port bit of the general-purpose I/O (GPIO) ports can be individually
+ * configured by software in several modes:
+ *
+ *  - Input floating
+ *  - Input pull-up
+ *  - Input-pull-down
+ *  - Output open-drain with pull-up or pull-down capability
+ *  - Output push-pull with pull-up or pull-down capability
+ *  - Alternate function push-pull with pull-up or pull-down capability
+ *  - Alternate function open-drain with pull-up or pull-down capability
+ *  - Analog
+ *
+ * 20-bit Encoding:       1111 1111 1100 0000 0000
+ *                        9876 5432 1098 7654 3210
+ *                        ---- ---- ---- ---- ----
+ * Inputs:                MMUU .... ...X PPPP BBBB
+ * Outputs:               MMUU .... FFOV PPPP BBBB
+ * Alternate Functions:   MMUU AAAA FFO. PPPP BBBB
+ * Analog:                MM.. .... .... PPPP BBBB
+ */
+
+/* Mode:
+ *
+ * 1111 1111 1100 0000 0000
+ * 9876 5432 1098 7654 3210
+ * ---- ---- ---- ---- ----
+ * MM.. .... .... .... ....
+ */
+
+#define GPIO_MODE_SHIFT               (18)                       /* Bits 18-19: GPIO port mode */
+#define GPIO_MODE_MASK                (3 << GPIO_MODE_SHIFT)
+#  define GPIO_INPUT                  (0 << GPIO_MODE_SHIFT)     /* Input mode */
+#  define GPIO_OUTPUT                 (1 << GPIO_MODE_SHIFT)     /* General purpose output mode */
+#  define GPIO_ALT                    (2 << GPIO_MODE_SHIFT)     /* Alternate function mode */
+#  define GPIO_ANALOG                 (3 << GPIO_MODE_SHIFT)     /* Analog mode */
+
+/* Input/output pull-ups/downs (not used with analog):
+ *
+ * 1111 1111 1100 0000 0000
+ * 9876 5432 1098 7654 3210
+ * ---- ---- ---- ---- ----
+ * ..UU .... .... .... ....
+ */
+
+#define GPIO_PUPD_SHIFT               (16)                       /* Bits 16-17: Pull-up/pull down */
+#define GPIO_PUPD_MASK                (3 << GPIO_PUPD_SHIFT)
+#  define GPIO_FLOAT                  (0 << GPIO_PUPD_SHIFT)     /* No pull-up, pull-down */
+#  define GPIO_PULLUP                 (1 << GPIO_PUPD_SHIFT)     /* Pull-up */
+#  define GPIO_PULLDOWN               (2 << GPIO_PUPD_SHIFT)     /* Pull-down */
+
+/* Alternate Functions:
+ *
+ * 1111 1111 1100 0000 0000
+ * 9876 5432 1098 7654 3210
+ * ---- ---- ---- ---- ----
+ * .... AAAA .... .... ....
+ */
+
+#define GPIO_AF_SHIFT                 (12)                       /* Bits 12-15: Alternate function */
+#define GPIO_AF_MASK                  (15 << GPIO_AF_SHIFT)
+#  define GPIO_AF(n)                  ((n) << GPIO_AF_SHIFT)
+#  define GPIO_AF0                    (0 << GPIO_AF_SHIFT)
+#  define GPIO_AF1                    (1 << GPIO_AF_SHIFT)
+#  define GPIO_AF2                    (2 << GPIO_AF_SHIFT)
+#  define GPIO_AF3                    (3 << GPIO_AF_SHIFT)
+#  define GPIO_AF4                    (4 << GPIO_AF_SHIFT)
+#  define GPIO_AF5                    (5 << GPIO_AF_SHIFT)
+#  define GPIO_AF6                    (6 << GPIO_AF_SHIFT)
+#  define GPIO_AF7                    (7 << GPIO_AF_SHIFT)
+#  define GPIO_AF8                    (8 << GPIO_AF_SHIFT)
+#  define GPIO_AF9                    (9 << GPIO_AF_SHIFT)
+#  define GPIO_AF10                   (10 << GPIO_AF_SHIFT)
+#  define GPIO_AF11                   (11 << GPIO_AF_SHIFT)
+#  define GPIO_AF12                   (12 << GPIO_AF_SHIFT)
+#  define GPIO_AF13                   (13 << GPIO_AF_SHIFT)
+#  define GPIO_AF14                   (14 << GPIO_AF_SHIFT)
+#  define GPIO_AF15                   (15 << GPIO_AF_SHIFT)
+
+/* Output/Alt function frequency selection:
+ *
+ * 1111 1111 1100 0000 0000
+ * 9876 5432 1098 7654 3210
+ * ---- ---- ---- ---- ----
+ * .... .... FF.. .... ....
+ */
+
+#define GPIO_SPEED_SHIFT              (10)                       /* Bits 10-11: GPIO frequency selection */
+#define GPIO_SPEED_MASK               (3 << GPIO_SPEED_SHIFT)
+#  define GPIO_SPEED_5MHz             (0 << GPIO_SPEED_SHIFT)    /* 5 MHz Low speed output */
+#  define GPIO_SPEED_25MHz            (1 << GPIO_SPEED_SHIFT)    /* 25 MHz Medium speed output */
+#  define GPIO_SPEED_50MHz            (2 << GPIO_SPEED_SHIFT)    /* 50 MHz Fast speed output */
+#  define GPIO_SPEED_120MHz           (3 << GPIO_SPEED_SHIFT)    /* 120 MHz High speed output */
+
+/* Output/Alt function type selection:
+ *
+ * 1111 1111 1100 0000 0000
+ * 9876 5432 1098 7654 3210
+ * ---- ---- ---- ---- ----
+ * .... .... ..O. .... ....
+ */
+
+#define GPIO_OPENDRAIN                (1 << 9)                   /* Bit9: 1=Open-drain output */
+#define GPIO_PUSHPULL                 (0)                        /* Bit9: 0=Push-pull output */
+
+/* If the pin is a GPIO digital output, then this identifies the initial
+ * output value.
+ * If the pin is an input, this bit is overloaded to provide the
+ * qualifier to distinguish input pull-up and -down:
+ *
+ * 1111 1111 1100 0000 0000
+ * 9876 5432 1098 7654 3210
+ * ---- ---- ---- ---- ----
+ * .... .... ...V .... ....
+ */
+
+#define GPIO_OUTPUT_SET               (1 << 8)                   /* Bit 8: If output, initial value of output */
+#define GPIO_OUTPUT_CLEAR             (0)
+
+/* External interrupt selection (GPIO inputs only):
+ *
+ * 1111 1111 1100 0000 0000
+ * 9876 5432 1098 7654 3210
+ * ---- ---- ---- ---- ----
+ * .... .... ...X .... ....
+ */
+
+#define GPIO_EXTI                     (1 << 8)                    /* Bit 8: Configure as EXTI interrupt */
+
+/* This identifies the GPIO port:
+ *
+ * 1111 1111 1100 0000 0000
+ * 9876 5432 1098 7654 3210
+ * ---- ---- ---- ---- ----
+ * .... .... .... PPPP ....
+ */
+
+#define GPIO_PORT_SHIFT               (4)                        /* Bit 4-7:  Port number */
+#define GPIO_PORT_MASK                (15 << GPIO_PORT_SHIFT)
+#  define GPIO_PORTA                  (0 << GPIO_PORT_SHIFT)     /*   GPIOA */
+#  define GPIO_PORTB                  (1 << GPIO_PORT_SHIFT)     /*   GPIOB */
+#  define GPIO_PORTC                  (2 << GPIO_PORT_SHIFT)     /*   GPIOC */
+#  define GPIO_PORTD                  (3 << GPIO_PORT_SHIFT)     /*   GPIOD */
+#  define GPIO_PORTE                  (4 << GPIO_PORT_SHIFT)     /*   GPIOE */
+#  define GPIO_PORTH                  (7 << GPIO_PORT_SHIFT)     /*   GPIOH */
+
+/* This identifies the bit in the port:
+ *
+ * 1111 1111 1100 0000 0000
+ * 9876 5432 1098 7654 3210
+ * ---- ---- ---- ---- ----
+ * .... .... .... .... BBBB
+ */
+
+#define GPIO_PIN_SHIFT                (0)                        /* Bits 0-3: GPIO number: 0-15 */
+#define GPIO_PIN_MASK                 (15 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN0                   (0 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN1                   (1 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN2                   (2 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN3                   (3 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN4                   (4 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN5                   (5 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN6                   (6 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN7                   (7 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN8                   (8 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN9                   (9 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN10                  (10 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN11                  (11 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN12                  (12 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN13                  (13 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN14                  (14 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN15                  (15 << GPIO_PIN_SHIFT)
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/* Base addresses for each GPIO block */
+#define STM32WB_NPORTS 6

Review Comment:
   Please move to `Pre-Processor Declarations` section



##########
arch/arm/src/stm32wb/hardware/stm32wb_tim.h:
##########
@@ -0,0 +1,1385 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_tim.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_TIM_CR1_OFFSET      0x0000  /* Control register 1 */
+#define STM32WB_TIM_CR2_OFFSET      0x0004  /* Control register 2 */
+#define STM32WB_TIM_SMCR_OFFSET     0x0008  /* Slave mode control register (TIM1, TIM2) */
+#define STM32WB_TIM_DIER_OFFSET     0x000c  /* DMA / Interrupt enable register */
+#define STM32WB_TIM_SR_OFFSET       0x0010  /* Status register */
+#define STM32WB_TIM_EGR_OFFSET      0x0014  /* Event generation register */
+#define STM32WB_TIM_CCMR1_OFFSET    0x0018  /* Capture/compare mode register 1 */
+#define STM32WB_TIM_CCMR2_OFFSET    0x001c  /* Capture/compare mode register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCER_OFFSET     0x0020  /* Capture/compare enable register */
+#define STM32WB_TIM_CNT_OFFSET      0x0024  /* Counter */
+#define STM32WB_TIM_PSC_OFFSET      0x0028  /* Prescaler */
+#define STM32WB_TIM_ARR_OFFSET      0x002c  /* Auto-reload register */
+#define STM32WB_TIM_RCR_OFFSET      0x0030  /* Repetition counter register (TIM1, TIM16/TIM17) */
+#define STM32WB_TIM_CCR1_OFFSET     0x0034  /* Capture/compare register 1 */
+#define STM32WB_TIM_CCR2_OFFSET     0x0038  /* Capture/compare register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR3_OFFSET     0x003c  /* Capture/compare register 3 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR4_OFFSET     0x0040  /* Capture/compare register 4 (TIM1, TIM2) */
+#define STM32WB_TIM_BDTR_OFFSET     0x0044  /* Break and dead-time register (TIM1, TIM16/17) */
+#define STM32WB_TIM_DCR_OFFSET      0x0048  /* DMA control register */
+#define STM32WB_TIM_DMAR_OFFSET     0x004c  /* DMA address for burst mode */
+#define STM32WB_TIM_OR1_OFFSET      0x0050  /* Option register 1 */
+#define STM32WB_TIM_CCMR3_OFFSET    0x0054  /* Capture/compare mode register 3 (TIM1) */
+#define STM32WB_TIM_CCR5_OFFSET     0x0058  /* Capture/compare register 5 (TIM1) */
+#define STM32WB_TIM_CCR6_OFFSET     0x005C  /* Capture/compare register 6 (TIM1) */
+#define STM32WB_TIM_AF1_OFFSET      0x0060  /* Alternate function register 1 */
+#define STM32WB_TIM_AF2_OFFSET      0x0064  /* Alternate function register 2 (TIM1) */
+#define STM32WB_TIM_TISEL_OFFSET    0x0068  /* Input selector register */
+
+/* Register Addresses *******************************************************/
+
+/* Advanced Timer TIM1 */
+
+#define STM32WB_TIM1_CR1            (STM32WB_TIM1_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM1_CR2            (STM32WB_TIM1_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM1_SMCR           (STM32WB_TIM1_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM1_DIER           (STM32WB_TIM1_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM1_SR             (STM32WB_TIM1_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM1_EGR            (STM32WB_TIM1_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM1_CCMR1          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM1_CCMR2          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM1_CCER           (STM32WB_TIM1_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM1_CNT            (STM32WB_TIM1_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM1_PSC            (STM32WB_TIM1_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM1_ARR            (STM32WB_TIM1_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM1_RCR            (STM32WB_TIM1_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM1_CCR1           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM1_CCR2           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM1_CCR3           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM1_CCR4           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM1_BDTR           (STM32WB_TIM1_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM1_DCR            (STM32WB_TIM1_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM1_DMAR           (STM32WB_TIM1_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM1_OR1            (STM32WB_TIM1_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM1_CCMR3          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR3_OFFSET)
+#define STM32WB_TIM1_CCR5           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR5_OFFSET)
+#define STM32WB_TIM1_CCR6           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR6_OFFSET)
+#define STM32WB_TIM1_AF1            (STM32WB_TIM1_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM1_AF2            (STM32WB_TIM1_BASE + STM32WB_TIM_AF2_OFFSET)
+#define STM32WB_TIM1_TISEL          (STM32WB_TIM1_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General 32-bit Timer TIM2 */
+
+#define STM32WB_TIM2_CR1            (STM32WB_TIM2_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM2_CR2            (STM32WB_TIM2_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM2_SMCR           (STM32WB_TIM2_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM2_DIER           (STM32WB_TIM2_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM2_SR             (STM32WB_TIM2_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM2_EGR            (STM32WB_TIM2_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM2_CCMR1          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM2_CCMR2          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM2_CCER           (STM32WB_TIM2_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM2_CNT            (STM32WB_TIM2_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM2_PSC            (STM32WB_TIM2_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM2_ARR            (STM32WB_TIM2_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM2_CCR1           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM2_CCR2           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM2_CCR3           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM2_CCR4           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM2_DCR            (STM32WB_TIM2_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM2_DMAR           (STM32WB_TIM2_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM2_OR1            (STM32WB_TIM2_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM2_AF1            (STM32WB_TIM2_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM2_TISEL          (STM32WB_TIM2_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General Timers TIM16/TIM17 */
+
+#define STM32WB_TIM16_CR1           (STM32WB_TIM16_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM16_CR2           (STM32WB_TIM16_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM16_DIER          (STM32WB_TIM16_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM16_SR            (STM32WB_TIM16_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM16_EGR           (STM32WB_TIM16_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM16_CCMR1         (STM32WB_TIM16_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM16_CCER          (STM32WB_TIM16_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM16_CNT           (STM32WB_TIM16_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM16_PSC           (STM32WB_TIM16_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM16_ARR           (STM32WB_TIM16_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM16_RCR           (STM32WB_TIM16_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM16_CCR1          (STM32WB_TIM16_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM16_BDTR          (STM32WB_TIM16_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM16_DCR           (STM32WB_TIM16_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM16_DMAR          (STM32WB_TIM16_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM16_OR1           (STM32WB_TIM16_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM16_AF1           (STM32WB_TIM16_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM16_TISEL         (STM32WB_TIM16_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+#define STM32WB_TIM17_CR1           (STM32WB_TIM17_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM17_CR2           (STM32WB_TIM17_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM17_DIER          (STM32WB_TIM17_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM17_SR            (STM32WB_TIM17_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM17_EGR           (STM32WB_TIM17_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM17_CCMR1         (STM32WB_TIM17_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM17_CCER          (STM32WB_TIM17_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM17_CNT           (STM32WB_TIM17_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM17_PSC           (STM32WB_TIM17_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM17_ARR           (STM32WB_TIM17_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM17_RCR           (STM32WB_TIM17_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM17_CCR1          (STM32WB_TIM17_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM17_BDTR          (STM32WB_TIM17_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM17_DCR           (STM32WB_TIM17_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM17_DMAR          (STM32WB_TIM17_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM17_OR1           (STM32WB_TIM17_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM17_AF1           (STM32WB_TIM17_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM17_TISEL         (STM32WB_TIM17_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* Register Value Constants *************************************************/
+
+/* Digital Filter options */
+
+#define STM32WB_DF_NOFILT           (0x0) /* 0000: No filter */
+#define STM32WB_DF_FCKINTn2         (0x1) /* 0001: fSAMPLING = fCK_INT, N=2 */
+#define STM32WB_DF_FCKINTn4         (0x2) /* 0010: fSAMPLING = fCK_INT, N=4 */
+#define STM32WB_DF_FCKINTn8         (0x3) /* 0011: fSAMPLING = fCK_INT, N=8 */
+#define STM32WB_DF_FDTSd2n6         (0x4) /* 0100: fSAMPLING = fDTS/2, N=6 */
+#define STM32WB_DF_FDTSd2n8         (0x5) /* 0101: fSAMPLING = fDTS/2, N=8 */
+#define STM32WB_DF_FDTSd4n6         (0x6) /* 0110: fSAMPLING = fDTS/4, N=6 */
+#define STM32WB_DF_FDTSd4n8         (0x7) /* 0111: fSAMPLING = fDTS/4, N=8 */
+#define STM32WB_DF_FDTSd8n6         (0x8) /* 1000: fSAMPLING = fDTS/8, N=6 */
+#define STM32WB_DF_FDTSd8n8         (0x9) /* 1001: fSAMPLING = fDTS/8, N=8 */
+#define STM32WB_DF_FDTSd16n5        (0xa) /* 1010: fSAMPLING = fDTS/16, N=5 */
+#define STM32WB_DF_FDTSd16n6        (0xb) /* 1011: fSAMPLING = fDTS/16, N=6 */
+#define STM32WB_DF_FDTSd16n8        (0xc) /* 1100: fSAMPLING = fDTS/16, N=8 */
+#define STM32WB_DF_FDTSd32n5        (0xd) /* 1101: fSAMPLING = fDTS/32, N=5 */
+#define STM32WB_DF_FDTSd32n6        (0xe) /* 1110: fSAMPLING = fDTS/32, N=6 */
+#define STM32WB_DF_FDTSd32n8        (0xf) /* 1111: fSAMPLING = fDTS/32, N=8 */
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Control register 1 */
+
+#define TIM1_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM1_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM1_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM1_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM1_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM1_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM1_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM1_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM1_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM1_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM1_CR1_CMS_MASK           (0x3 << TIM1_CR1_CMS_SHIFT)
+#  define TIM1_CR1_CMS_EDGE         (0x0 << TIM1_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM1_CR1_CMS_CNTR1        (0x1 << TIM1_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM1_CR1_CMS_CNTR2        (0x2 << TIM1_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM1_CR1_CMS_CNTR3        (0x3 << TIM1_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM1_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM1_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM1_CR1_CKD_MASK           (0x3 << TIM1_CR1_CKD_SHIFT)
+#  define TIM1_CR1_CKD_TCKINT       (0x0 << TIM1_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM1_CR1_CKD_2TCKINT      (0x1 << TIM1_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM1_CR1_CKD_4TCKINT      (0x2 << TIM1_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM1_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM2_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM2_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM2_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM2_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM2_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM2_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM2_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM2_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM2_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM2_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM2_CR1_CMS_MASK           (0x3 << TIM2_CR1_CMS_SHIFT)
+#  define TIM2_CR1_CMS_EDGE         (0x0 << TIM2_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM2_CR1_CMS_CNTR1        (0x1 << TIM2_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM2_CR1_CMS_CNTR2        (0x2 << TIM2_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM2_CR1_CMS_CNTR3        (0x3 << TIM2_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM2_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM2_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM2_CR1_CKD_MASK           (0x3 << TIM2_CR1_CKD_SHIFT)
+#  define TIM2_CR1_CKD_TCKINT       (0x0 << TIM2_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM2_CR1_CKD_2TCKINT      (0x1 << TIM2_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM2_CR1_CKD_4TCKINT      (0x2 << TIM2_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM2_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM16_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM16_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM16_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM16_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM16_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM16_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM16_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM16_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM16_CR1_CKD_MASK          (0x3 << TIM16_CR1_CKD_SHIFT)
+#  define TIM16_CR1_CKD_TCKINT      (0x0 << TIM16_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM16_CR1_CKD_2TCKINT     (0x1 << TIM16_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM16_CR1_CKD_4TCKINT     (0x2 << TIM16_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM16_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM17_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM17_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM17_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM17_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM17_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM17_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM17_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM17_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM17_CR1_CKD_MASK          (0x3 << TIM17_CR1_CKD_SHIFT)
+#  define TIM17_CR1_CKD_TCKINT      (0x0 << TIM17_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM17_CR1_CKD_2TCKINT     (0x1 << TIM17_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM17_CR1_CKD_4TCKINT     (0x2 << TIM17_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM17_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+/* Control register 2 */
+
+#define TIM1_CR2_CCPC               (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM1_CR2_CCUS               (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM1_CR2_CCUS_COMG        (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM1_CR2_CCUS_COMG_TRGI   (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM1_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM1_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM1_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM1_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM1_CR2_MMS_MASK           (0x7 << TIM1_CR2_MMS_SHIFT)
+#  define TIM1_CR2_MMS_RESET        (0x0 << TIM1_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM1_CR2_MMS_ENABLE       (0x1 << TIM1_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM1_CR2_MMS_UPDATE       (0x2 << TIM1_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM1_CR2_MMS_COMPP        (0x3 << TIM1_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS_OC1REF       (0x4 << TIM1_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM1_CR2_MMS_OC2REF       (0x5 << TIM1_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM1_CR2_MMS_OC3REF       (0x6 << TIM1_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM1_CR2_MMS_OC4REF       (0x7 << TIM1_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM1_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM1_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM1_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM1_CR2_OIS1               (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM1_CR2_OIS1N              (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+#define TIM1_CR2_OIS2               (1 << 10) /* Bit 10: Output Idle state 2 (OC2 output) */
+#define TIM1_CR2_OIS2N              (1 << 11) /* Bit 11: Output Idle state 2 (OC2N output) */
+#define TIM1_CR2_OIS3               (1 << 12) /* Bit 12: Output Idle state 3 (OC3 output) */
+#define TIM1_CR2_OIS3N              (1 << 13) /* Bit 13: Output Idle state 3 (OC3N output) */
+#define TIM1_CR2_OIS4               (1 << 14) /* Bit 14: Output Idle state 4 (OC4 output) */
+#define TIM1_CR2_OIS5               (1 << 16) /* Bit 16: Output Idle state 5 (OC5 output) */
+#define TIM1_CR2_OIS6               (1 << 18) /* Bit 18: Output Idle state 6 (OC6 output) */
+#define TIM1_CR2_MMS2_SHIFT         (20)      /* Bits 20-23: Master Mode Selection 2 */
+#define TIM1_CR2_MMS2_MASK          (0xf << TIM1_CR2_MMS2_SHIFT)
+#  define TIM1_CR2_MMS2_RESET       (0x0 << TIM1_CR2_MMS2_SHIFT) /* 0000: Reset - TIMx_EGR UG bit is TRG9 */
+#  define TIM1_CR2_MMS2_ENABLE      (0x1 << TIM1_CR2_MMS2_SHIFT) /* 0001: Enable - CNT_EN is TRGO2 */
+#  define TIM1_CR2_MMS2_UPDATE      (0x2 << TIM1_CR2_MMS2_SHIFT) /* 0010: Update event is TRGO2 */
+#  define TIM1_CR2_MMS2_COMPP       (0x3 << TIM1_CR2_MMS2_SHIFT) /* 0011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS2_OC1REF      (0x4 << TIM1_CR2_MMS2_SHIFT) /* 0100: Compare OC1REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC2REF      (0x5 << TIM1_CR2_MMS2_SHIFT) /* 0101: Compare OC2REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC3REF      (0x6 << TIM1_CR2_MMS2_SHIFT) /* 0110: Compare OC3REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC4REF      (0x7 << TIM1_CR2_MMS2_SHIFT) /* 0111: Compare OC4REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC5REF      (0x8 << TIM1_CR2_MMS2_SHIFT) /* 1000: Compare OC5REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC6REF      (0x9 << TIM1_CR2_MMS2_SHIFT) /* 1001: Compare OC6REF is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4      (0xa << TIM1_CR2_MMS2_SHIFT) /* 1010: Compare pulse - OC4REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC6      (0xb << TIM1_CR2_MMS2_SHIFT) /* 1011: Compare pulse - OC6REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4R6R   (0xc << TIM1_CR2_MMS2_SHIFT) /* 1100: Compare pulse - OC4REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC4R6F   (0xd << TIM1_CR2_MMS2_SHIFT) /* 1101: Compare pulse - OC4REF rising/OC6REF falling */
+#  define TIM1_CR2_MMS2_CMPOC5R6R   (0xe << TIM1_CR2_MMS2_SHIFT) /* 1110: Compare pulse - OC5REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC5R6F   (0xf << TIM1_CR2_MMS2_SHIFT) /* 1111: Compare pulse - OC5REF rising/OC6REF falling */
+
+#define TIM2_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM2_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM2_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM2_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM2_CR2_MMS_MASK           (0x7 << TIM2_CR2_MMS_SHIFT)
+#  define TIM2_CR2_MMS_RESET        (0x0 << TIM2_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM2_CR2_MMS_ENABLE       (0x1 << TIM2_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM2_CR2_MMS_UPDATE       (0x2 << TIM2_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM2_CR2_MMS_COMPP        (0x3 << TIM2_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM2_CR2_MMS_OC1REF       (0x4 << TIM2_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM2_CR2_MMS_OC2REF       (0x5 << TIM2_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM2_CR2_MMS_OC3REF       (0x6 << TIM2_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM2_CR2_MMS_OC4REF       (0x7 << TIM2_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM2_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM2_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM2_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM16_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM16_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM16_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM16_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM16_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM16_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM16_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM16_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM16_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+#define TIM17_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM17_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM17_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM17_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM17_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM17_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM17_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM17_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM17_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+/* Slave mode control register */
+
+#define TIM1_SMCR_SMS_LO_SHIFT      (0)       /* Bits 0-2: Slave mode selection, bits [2:0] */
+#define TIM1_SMCR_SMS_HI_SHIFT      (16)      /* Bit 16: Slave mode selection, bits [3] */
+#define TIM1_SMCR_SMS_BITS(h,l)     ((h << TIM1_SMCR_SMS_HI_SHIFT) | (l << TIM1_SMCR_SMS_LO_SHIFT))
+#define TIM1_SMCR_SMS_MASK          TIM1_SMCR_SMS_BITS(0x1, 0x7)
+#  define TIM1_SMCR_DISAB           TIM1_SMCR_SMS_BITS(0x0, 0x0) /* 0,000: Slave mode disabled */
+#  define TIM1_SMCR_ENCMD1          TIM1_SMCR_SMS_BITS(0x0, 0x1) /* 0,001: Encoder mode 1 */
+#  define TIM1_SMCR_ENCMD2          TIM1_SMCR_SMS_BITS(0x0, 0x2) /* 0,010: Encoder mode 2 */
+#  define TIM1_SMCR_ENCMD3          TIM1_SMCR_SMS_BITS(0x0, 0x3) /* 0,011: Encoder mode 3 */
+#  define TIM1_SMCR_RESET           TIM1_SMCR_SMS_BITS(0x0, 0x4) /* 0,100: Reset Mode */
+#  define TIM1_SMCR_GATED           TIM1_SMCR_SMS_BITS(0x0, 0x5) /* 0,101: Gated Mode */
+#  define TIM1_SMCR_TRIGGER         TIM1_SMCR_SMS_BITS(0x0, 0x6) /* 0,110: Trigger Mode */
+#  define TIM1_SMCR_EXTCLK1         TIM1_SMCR_SMS_BITS(0x0, 0x7) /* 0,111: External Clock Mode 1 */
+#  define TIM1_SMCR_SMS_COMBINED    TIM1_SMCR_SMS_BITS(0x1, 0x0) /* 1,000: Combined Reset and Trigger mode */
+
+#define TIM1_SMCR_OCCS              (1 << 3)  /* Bit 3: OCREF clear selection */
+#  define TIM1_SMCR_OCCS_CLR        (0 << 3)  /* 0: OCREF clear triggered by CLR input */
+#  define TIM1_SMCR_OCCS_ETRF       (1 << 3)  /* 1: OCREF clear triggered by ETRF */
+
+#define TIM1_SMCR_TS_LO_SHIFT       (4)       /* Bits 4-6: Trigger selection, bits [2:0] */
+#define TIM1_SMCR_TS_HI_SHIFT       (20)      /* Bits 20-21: Trigger selection, bits [1:0] */
+#define TIM1_SMCR_TS_BITS(h,l)      ((h << TIM1_SMCR_TS_HI_SHIFT) | (l << TIM1_SMCR_TS_LO_SHIFT))
+#define TIM1_SMCR_TS_MASK           TIM1_SMCR_TS_BITS(0x3, 0x7)
+#  define TIM1_SMCR_ITR0            TIM1_SMCR_TS_BITS(0x0, 0x0) /* 00,000: Internal trigger 0 (ITR0) */
+#  define TIM1_SMCR_ITR1            TIM1_SMCR_TS_BITS(0x0, 0x1) /* 00,001: Internal trigger 1 (ITR1) */
+#  define TIM1_SMCR_ITR2            TIM1_SMCR_TS_BITS(0x0, 0x2) /* 00,010: Internal trigger 2 (ITR2) */
+#  define TIM1_SMCR_ITR3            TIM1_SMCR_TS_BITS(0x0, 0x3) /* 00,011: Internal trigger 3 (ITR3) */
+#  define TIM1_SMCR_T1FED           TIM1_SMCR_TS_BITS(0x0, 0x4) /* 00,100: TI1 Edge detector (TI1F_ED) */
+#  define TIM1_SMCR_TI1FP1          TIM1_SMCR_TS_BITS(0x0, 0x5) /* 00,101: Filtered timer input 1 (TI1FP1) */
+#  define TIM1_SMCR_T12FP2          TIM1_SMCR_TS_BITS(0x0, 0x6) /* 00,110: Filtered timer input 2 (TI2FP2) */
+#  define TIM1_SMCR_ETRF            TIM1_SMCR_TS_BITS(0x0, 0x7) /* 00,111: External trigger input (ETRF) */
+
+#define TIM1_SMCR_MSM               (1 << 7)  /* Bit 7: Master/slave mode */
+#define TIM1_SMCR_ETF_SHIFT         (8)       /* Bits 8-11: External trigger filter */
+#define TIM1_SMCR_ETF_MASK          (0xf << TIM1_SMCR_ETF_SHIFT)
+#  define TIM1_SMCR_ETF(f)          ((f) << TIM1_SMCR_ETF_SHIFT) /* f = STM32WB_DF_[digital filter option] */
+
+#define TIM1_SMCR_ETPS_SHIFT        (12)      /* Bits 12-13: External trigger prescaler */
+#define TIM1_SMCR_ETPS_MASK         (0x3 << TIM1_SMCR_ETPS_SHIFT)
+#  define TIM1_SMCR_PSCOFF          (0x0 << TIM1_SMCR_ETPS_SHIFT) /* 00: Prescaler OFF */
+#  define TIM1_SMCR_ETRPd2          (0x1 << TIM1_SMCR_ETPS_SHIFT) /* 01: ETRP frequency divided by 2 */
+#  define TIM1_SMCR_ETRPd4          (0x2 << TIM1_SMCR_ETPS_SHIFT) /* 10: ETRP frequency divided by 4 */
+#  define TIM1_SMCR_ETRPd8          (0x3 << TIM1_SMCR_ETPS_SHIFT) /* 11: ETRP frequency divided by 8 */
+
+#define TIM1_SMCR_ECE               (1 << 14) /* Bit 14: External clock enable */
+#define TIM1_SMCR_ETP               (1 << 15) /* Bit 15: External trigger polarity */
+#  define TIM1_SMCR_ETP_HIGH        (0 << 15) /* 0: ETR is non-inverted, active at high level or rising edge */
+#  define TIM1_SMCR_ETP_LOW         (1 << 15) /* 1: ETR is inverted, active at low level or falling edge */
+
+#define TIM2_SMCR_SMS_LO_SHIFT      (0)       /* Bits 0-2: Slave mode selection, bits [2:0] */
+#define TIM2_SMCR_SMS_HI_SHIFT      (16)      /* Bit 16: Slave mode selection, bits [3] */
+#define TIM2_SMCR_SMS_BITS(h,l)     ((h << TIM2_SMCR_SMS_HI_SHIFT) | (l << TIM2_SMCR_SMS_LO_SHIFT))
+#define TIM2_SMCR_SMS_MASK          TIM2_SMCR_SMS_BITS(0x1, 0x7)
+#  define TIM2_SMCR_DISAB           TIM2_SMCR_SMS_BITS(0x0, 0x0) /* 0,000: Slave mode disabled */
+#  define TIM2_SMCR_ENCMD1          TIM2_SMCR_SMS_BITS(0x0, 0x1) /* 0,001: Encoder mode 1 */
+#  define TIM2_SMCR_ENCMD2          TIM2_SMCR_SMS_BITS(0x0, 0x2) /* 0,010: Encoder mode 2 */
+#  define TIM2_SMCR_ENCMD3          TIM2_SMCR_SMS_BITS(0x0, 0x3) /* 0,011: Encoder mode 3 */
+#  define TIM2_SMCR_RESET           TIM2_SMCR_SMS_BITS(0x0, 0x4) /* 0,100: Reset Mode */
+#  define TIM2_SMCR_GATED           TIM2_SMCR_SMS_BITS(0x0, 0x5) /* 0,101: Gated Mode */
+#  define TIM2_SMCR_TRIGGER         TIM2_SMCR_SMS_BITS(0x0, 0x6) /* 0,110: Trigger Mode */
+#  define TIM2_SMCR_EXTCLK1         TIM2_SMCR_SMS_BITS(0x0, 0x7) /* 0,111: External Clock Mode 1 */
+#  define TIM2_SMCR_SMS_COMBINED    TIM2_SMCR_SMS_BITS(0x1, 0x0) /* 1,000: Combined Reset and Trigger mode */
+
+#define TIM2_SMCR_OCCS              (1 << 3)  /* Bit 3: OCREF clear selection */
+#  define TIM2_SMCR_OCCS_CLR        (0 << 3)  /* 0: OCREF clear triggered by CLR input */
+#  define TIM2_SMCR_OCCS_ETRF       (1 << 3)  /* 1: OCREF clear triggered by ETRF */
+
+#define TIM2_SMCR_TS_LO_SHIFT       (4)       /* Bits 4-6: Trigger selection, bits [2:0] */
+#define TIM2_SMCR_TS_HI_SHIFT       (20)      /* Bits 20-21: Trigger selection, bits [1:0] */
+#define TIM2_SMCR_TS_BITS(h,l)      ((h << TIM2_SMCR_TS_HI_SHIFT) | (l << TIM2_SMCR_TS_LO_SHIFT))
+#define TIM2_SMCR_TS_MASK           TIM2_SMCR_TS_BITS(0x3, 0x7)
+#  define TIM2_SMCR_ITR0            TIM2_SMCR_TS_BITS(0x0, 0x0) /* 00,000: Internal trigger 0 (ITR0) */
+#  define TIM2_SMCR_ITR1            TIM2_SMCR_TS_BITS(0x0, 0x1) /* 00,001: Internal trigger 1 (ITR1) */
+#  define TIM2_SMCR_ITR2            TIM2_SMCR_TS_BITS(0x0, 0x2) /* 00,010: Internal trigger 2 (ITR2) */
+#  define TIM2_SMCR_ITR3            TIM2_SMCR_TS_BITS(0x0, 0x3) /* 00,011: Internal trigger 3 (ITR3) */
+#  define TIM2_SMCR_T1FED           TIM2_SMCR_TS_BITS(0x0, 0x4) /* 00,100: TI1 Edge detector (TI1F_ED) */
+#  define TIM2_SMCR_TI1FP1          TIM2_SMCR_TS_BITS(0x0, 0x5) /* 00,101: Filtered timer Input 1 (TI1FP1) */
+#  define TIM2_SMCR_T12FP2          TIM2_SMCR_TS_BITS(0x0, 0x6) /* 00,110: Filtered timer Input 2 (TI2FP2) */
+#  define TIM2_SMCR_ETRF            TIM2_SMCR_TS_BITS(0x0, 0x7) /* 00,111: External trigger input (ETRF) */
+#  define TIM2_SMCR_ITR4            TIM2_SMCR_TS_BITS(0x1, 0x0) /* 01,000: Internal trigger 4 (ITR4) */
+#  define TIM2_SMCR_ITR5            TIM2_SMCR_TS_BITS(0x1, 0x1) /* 01,001: Internal trigger 5 (ITR5) */
+#  define TIM2_SMCR_ITR6            TIM2_SMCR_TS_BITS(0x1, 0x2) /* 01,010: Internal trigger 6 (ITR6) */
+#  define TIM2_SMCR_ITR7            TIM2_SMCR_TS_BITS(0x1, 0x3) /* 01,011: Internal trigger 7 (ITR7) */
+#  define TIM2_SMCR_ITR8            TIM2_SMCR_TS_BITS(0x1, 0x4) /* 01,100: Internal trigger 8 (ITR8) */
+
+#define TIM2_SMCR_MSM               (1 << 7)  /* Bit 7: Master/slave mode */
+#define TIM2_SMCR_ETF_SHIFT         (8)       /* Bits 8-11: External trigger filter */
+#define TIM2_SMCR_ETF_MASK          (0xf << TIM2_SMCR_ETF_SHIFT)
+#  define TIM2_SMCR_ETF(f)          ((f) << TIM2_SMCR_ETF_SHIFT) /* f = STM32WB_DF_[digital filter option] */
+
+#define TIM2_SMCR_ETPS_SHIFT        (12)      /* Bits 12-13: External trigger prescaler */
+#define TIM2_SMCR_ETPS_MASK         (0x3 << TIM2_SMCR_ETPS_SHIFT)
+#  define TIM2_SMCR_PSCOFF          (0x0 << TIM2_SMCR_ETPS_SHIFT) /* 00: Prescaler OFF */
+#  define TIM2_SMCR_ETRPd2          (0x1 << TIM2_SMCR_ETPS_SHIFT) /* 01: ETRP frequency divided by 2 */
+#  define TIM2_SMCR_ETRPd4          (0x2 << TIM2_SMCR_ETPS_SHIFT) /* 10: ETRP frequency divided by 4 */
+#  define TIM2_SMCR_ETRPd8          (0x3 << TIM2_SMCR_ETPS_SHIFT) /* 11: ETRP frequency divided by 8 */
+
+#define TIM2_SMCR_ECE               (1 << 14) /* Bit 14: External clock enable */
+#define TIM2_SMCR_ETP               (1 << 15) /* Bit 15: External trigger polarity */
+#  define TIM2_SMCR_ETP_HIGH        (0 << 15) /* 0: ETR is non-inverted, active at high level or rising edge */
+#  define TIM2_SMCR_ETP_LOW         (1 << 15) /* 1: ETR is inverted, active at low level or falling edge */
+
+/* Timer DMA / Interrupt enable register */
+
+#define TIM1_DIER_UIE               (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM1_DIER_CC1IE             (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM1_DIER_CC2IE             (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt enable */
+#define TIM1_DIER_CC3IE             (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt enable */
+#define TIM1_DIER_CC4IE             (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt enable */
+#define TIM1_DIER_COMIE             (1 << 5)  /* Bit 5: COM interrupt enable */
+#define TIM1_DIER_TIE               (1 << 6)  /* Bit 6: Trigger interrupt enable */
+#define TIM1_DIER_BIE               (1 << 7)  /* Bit 7: Break interrupt enable */
+#define TIM1_DIER_UDE               (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM1_DIER_CC1DE             (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+#define TIM1_DIER_CC2DE             (1 << 10) /* Bit 10: Capture/Compare 2 DMA request enable */
+#define TIM1_DIER_CC3DE             (1 << 11) /* Bit 11: Capture/Compare 3 DMA request enable */
+#define TIM1_DIER_CC4DE             (1 << 12) /* Bit 12: Capture/Compare 4 DMA request enable */
+#define TIM1_DIER_COMDE             (1 << 13) /* Bit 13: COM DMA request enable */
+#define TIM1_DIER_TDE               (1 << 14) /* Bit 14: Trigger DMA request enable */
+
+#define TIM2_DIER_UIE               (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM2_DIER_CC1IE             (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM2_DIER_CC2IE             (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt enable */
+#define TIM2_DIER_CC3IE             (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt enable */
+#define TIM2_DIER_CC4IE             (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt enable */
+#define TIM2_DIER_TIE               (1 << 6)  /* Bit 6: Trigger interrupt enable */
+#define TIM2_DIER_UDE               (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM2_DIER_CC1DE             (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+#define TIM2_DIER_CC2DE             (1 << 10) /* Bit 10: Capture/Compare 2 DMA request enable */
+#define TIM2_DIER_CC3DE             (1 << 11) /* Bit 11: Capture/Compare 3 DMA request enable */
+#define TIM2_DIER_CC4DE             (1 << 12) /* Bit 12: Capture/Compare 4 DMA request enable */
+
+#define TIM16_DIER_UIE              (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM16_DIER_CC1IE            (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM16_DIER_COMIE            (1 << 5)  /* Bit 5: COM interrupt enable */
+#define TIM16_DIER_BIE              (1 << 7)  /* Bit 7: Break interrupt enable */
+#define TIM16_DIER_UDE              (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM16_DIER_CC1DE            (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+
+#define TIM17_DIER_UIE              (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM17_DIER_CC1IE            (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM17_DIER_COMIE            (1 << 5)  /* Bit 5: COM interrupt enable */
+#define TIM17_DIER_BIE              (1 << 7)  /* Bit 7: Break interrupt enable */
+#define TIM17_DIER_UDE              (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM17_DIER_CC1DE            (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+
+/* Status register */
+
+#define TIM1_SR_UIF                 (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM1_SR_CC1IF               (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM1_SR_CC2IF               (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt Flag */
+#define TIM1_SR_CC3IF               (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt Flag */
+#define TIM1_SR_CC4IF               (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt Flag */
+#define TIM1_SR_COMIF               (1 << 5)  /* Bit 5: COM interrupt Flag */
+#define TIM1_SR_TIF                 (1 << 6)  /* Bit 6: Trigger interrupt Flag */
+#define TIM1_SR_BIF                 (1 << 7)  /* Bit 7: Break interrupt Flag */
+#define TIM1_SR_B2IF                (1 << 8)  /* Bit 8: Break 2 interrupt Flag */
+#define TIM1_SR_CC1OF               (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+#define TIM1_SR_CC2OF               (1 << 10) /* Bit 10: Capture/Compare 2 Overcapture Flag */
+#define TIM1_SR_CC3OF               (1 << 11) /* Bit 11: Capture/Compare 3 Overcapture Flag */
+#define TIM1_SR_CC4OF               (1 << 12) /* Bit 12: Capture/Compare 4 Overcapture Flag */
+#define TIM1_SR_SBIF                (1 << 13) /* Bit 13: System break interrupt Flag */
+#define TIM1_SR_CC5IF               (1 << 16) /* Bit 16: Compare 5 interrupt flag */
+#define TIM1_SR_CC6IF               (1 << 17) /* Bit 17: Compare 6 interrupt flag */
+
+#define TIM2_SR_UIF                 (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM2_SR_CC1IF               (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM2_SR_CC2IF               (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt Flag */
+#define TIM2_SR_CC3IF               (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt Flag */
+#define TIM2_SR_CC4IF               (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt Flag */
+#define TIM2_SR_TIF                 (1 << 6)  /* Bit 6: Trigger interrupt Flag */
+#define TIM2_SR_CC1OF               (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+#define TIM2_SR_CC2OF               (1 << 10) /* Bit 10: Capture/Compare 2 Overcapture Flag */
+#define TIM2_SR_CC3OF               (1 << 11) /* Bit 11: Capture/Compare 3 Overcapture Flag */
+#define TIM2_SR_CC4OF               (1 << 12) /* Bit 12: Capture/Compare 4 Overcapture Flag */
+
+#define TIM16_SR_UIF                (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM16_SR_CC1IF              (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM16_SR_COMIF              (1 << 5)  /* Bit 5: COM interrupt Flag */
+#define TIM16_SR_BIF                (1 << 7)  /* Bit 7: Break interrupt Flag */
+#define TIM16_SR_CC1OF              (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+
+#define TIM17_SR_UIF                (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM17_SR_CC1IF              (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM17_SR_COMIF              (1 << 5)  /* Bit 5: COM interrupt Flag */
+#define TIM17_SR_BIF                (1 << 7)  /* Bit 7: Break interrupt Flag */
+#define TIM17_SR_CC1OF              (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+
+/* Event generation register */
+
+#define TIM1_EGR_UG                 (1 << 0)  /* Bit 0: Update Generation */
+#define TIM1_EGR_CC1G               (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM1_EGR_CC2G               (1 << 2)  /* Bit 2: Capture/Compare 2 Generation */
+#define TIM1_EGR_CC3G               (1 << 3)  /* Bit 3: Capture/Compare 3 Generation */
+#define TIM1_EGR_CC4G               (1 << 4)  /* Bit 4: Capture/Compare 4 Generation */
+#define TIM1_EGR_COMG               (1 << 5)  /* Bit 5: Capture/Compare Control Update Generation */
+#define TIM1_EGR_TG                 (1 << 6)  /* Bit 6: Trigger Generation */
+#define TIM1_EGR_BG                 (1 << 7)  /* Bit 7: Break Generation */
+#define TIM1_EGR_B2G                (1 << 8)  /* Bit 8: Break 2 Generation */
+
+#define TIM2_EGR_UG                 (1 << 0)  /* Bit 0: Update Generation */
+#define TIM2_EGR_CC1G               (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM2_EGR_CC2G               (1 << 2)  /* Bit 2: Capture/Compare 2 Generation */
+#define TIM2_EGR_CC3G               (1 << 3)  /* Bit 3: Capture/Compare 3 Generation */
+#define TIM2_EGR_CC4G               (1 << 4)  /* Bit 4: Capture/Compare 4 Generation */
+#define TIM2_EGR_TG                 (1 << 6)  /* Bit 6: Trigger Generation */
+
+#define TIM16_EGR_UG                (1 << 0)  /* Bit 0: Update Generation */
+#define TIM16_EGR_CC1G              (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM16_EGR_COMG              (1 << 5)  /* Bit 5: Capture/Compare Control Update Generation */
+#define TIM16_EGR_BG                (1 << 7)  /* Bit 7: Break Generation */
+
+#define TIM17_EGR_UG                (1 << 0)  /* Bit 0: Update Generation */
+#define TIM17_EGR_CC1G              (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM17_EGR_COMG              (1 << 5)  /* Bit 5: Capture/Compare Control Update Generation */
+#define TIM17_EGR_BG                (1 << 7)  /* Bit 7: Break Generation */
+
+/* Capture/compare mode registers - capture/compare mode selection */
+
+#define TIM1_CCMR1_CC1S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM1_CCMR1_CC1S_MASK        (0x3 << TIM1_CCMR1_CC1S_SHIFT)
+#  define TIM1_CCMR1_CC1S_CCOUT     (0x0 << TIM1_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM1_CCMR1_CC1S_CCIN1     (0x1 << TIM1_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+#  define TIM1_CCMR1_CC1S_CCIN2     (0x2 << TIM1_CCMR1_CC1S_SHIFT) /* 10: CC1 channel input, IC1 is TI2 */
+#  define TIM1_CCMR1_CC1S_CCINTRC   (0x3 << TIM1_CCMR1_CC1S_SHIFT) /* 11: CC1 channel input, IC1 is TRC */
+
+#define TIM1_CCMR1_CC2S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 2 Selection */
+#define TIM1_CCMR1_CC2S_MASK        (0x3 << TIM1_CCMR1_CC2S_SHIFT)
+#  define TIM1_CCMR1_CC2S_CCOUT     (0x0 << TIM1_CCMR1_CC2S_SHIFT) /* 00: CC2 channel output */
+#  define TIM1_CCMR1_CC2S_CCIN1     (0x1 << TIM1_CCMR1_CC2S_SHIFT) /* 01: CC2 channel input, IC2 is TI2 */
+#  define TIM1_CCMR1_CC2S_CCIN2     (0x2 << TIM1_CCMR1_CC2S_SHIFT) /* 10: CC2 channel input, IC2 is TI1 */
+#  define TIM1_CCMR1_CC2S_CCINTRC   (0x3 << TIM1_CCMR1_CC2S_SHIFT) /* 11: CC2 channel input, IC2 is TRC */
+
+#define TIM1_CCMR2_CC3S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 3 Selection */
+#define TIM1_CCMR2_CC3S_MASK        (0x3 << TIM1_CCMR2_CC3S_SHIFT)
+#  define TIM1_CCMR2_CC3S_CCOUT     (0x0 << TIM1_CCMR2_CC3S_SHIFT) /* 00: CC3 channel output */
+#  define TIM1_CCMR2_CC3S_CCIN1     (0x1 << TIM1_CCMR2_CC3S_SHIFT) /* 01: CC3 channel input, IC3 is TI3 */
+#  define TIM1_CCMR2_CC3S_CCIN2     (0x2 << TIM1_CCMR2_CC3S_SHIFT) /* 10: CC3 channel input, IC3 is TI4 */
+#  define TIM1_CCMR2_CC3S_CCINTRC   (0x3 << TIM1_CCMR2_CC3S_SHIFT) /* 11: CC3 channel input, IC3 is TRC */
+
+#define TIM1_CCMR2_CC4S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 4 Selection */
+#define TIM1_CCMR2_CC4S_MASK        (0x3 << TIM1_CCMR2_CC4S_SHIFT)
+#  define TIM1_CCMR2_CC4S_CCOUT     (0x0 << TIM1_CCMR2_CC4S_SHIFT) /* 00: CC4 channel output */
+#  define TIM1_CCMR2_CC4S_CCIN1     (0x1 << TIM1_CCMR2_CC4S_SHIFT) /* 01: CC4 channel input, IC4 is TI4 */
+#  define TIM1_CCMR2_CC4S_CCIN2     (0x2 << TIM1_CCMR2_CC4S_SHIFT) /* 10: CC4 channel input, IC4 is TI3 */
+#  define TIM1_CCMR2_CC4S_CCINTRC   (0x3 << TIM1_CCMR2_CC4S_SHIFT) /* 11: CC4 channel input, IC4 is TRC */
+
+#define TIM2_CCMR1_CC1S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM2_CCMR1_CC1S_MASK        (0x3 << TIM2_CCMR1_CC1S_SHIFT)
+#  define TIM2_CCMR1_CC1S_CCOUT     (0x0 << TIM2_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM2_CCMR1_CC1S_CCIN1     (0x1 << TIM2_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+#  define TIM2_CCMR1_CC1S_CCIN2     (0x2 << TIM2_CCMR1_CC1S_SHIFT) /* 10: CC1 channel input, IC1 is TI2 */
+#  define TIM2_CCMR1_CC1S_CCINTRC   (0x3 << TIM2_CCMR1_CC1S_SHIFT) /* 11: CC1 channel input, IC1 is TRC */
+
+#define TIM2_CCMR1_CC2S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 2 Selection */
+#define TIM2_CCMR1_CC2S_MASK        (0x3 << TIM2_CCMR1_CC2S_SHIFT)
+#  define TIM2_CCMR1_CC2S_CCOUT     (0x0 << TIM2_CCMR1_CC2S_SHIFT) /* 00: CC2 channel output */
+#  define TIM2_CCMR1_CC2S_CCIN1     (0x1 << TIM2_CCMR1_CC2S_SHIFT) /* 01: CC2 channel input, IC2 is TI2 */
+#  define TIM2_CCMR1_CC2S_CCIN2     (0x2 << TIM2_CCMR1_CC2S_SHIFT) /* 10: CC2 channel input, IC2 is TI1 */
+#  define TIM2_CCMR1_CC2S_CCINTRC   (0x3 << TIM2_CCMR1_CC2S_SHIFT) /* 11: CC2 channel input, IC2 is TRC */
+
+#define TIM2_CCMR2_CC3S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 3 Selection */
+#define TIM2_CCMR2_CC3S_MASK        (0x3 << TIM2_CCMR2_CC3S_SHIFT)
+#  define TIM2_CCMR2_CC3S_CCOUT     (0x0 << TIM2_CCMR2_CC3S_SHIFT) /* 00: CC3 channel output */
+#  define TIM2_CCMR2_CC3S_CCIN1     (0x1 << TIM2_CCMR2_CC3S_SHIFT) /* 01: CC3 channel input, IC3 is TI3 */
+#  define TIM2_CCMR2_CC3S_CCIN2     (0x2 << TIM2_CCMR2_CC3S_SHIFT) /* 10: CC3 channel input, IC3 is TI4 */
+#  define TIM2_CCMR2_CC3S_CCINTRC   (0x3 << TIM2_CCMR2_CC3S_SHIFT) /* 11: CC3 channel input, IC3 is TRC */
+
+#define TIM2_CCMR2_CC4S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 4 Selection */
+#define TIM2_CCMR2_CC4S_MASK        (0x3 << TIM2_CCMR2_CC4S_SHIFT)
+#  define TIM2_CCMR2_CC4S_CCOUT     (0x0 << TIM2_CCMR2_CC4S_SHIFT) /* 00: CC4 channel output */
+#  define TIM2_CCMR2_CC4S_CCIN1     (0x1 << TIM2_CCMR2_CC4S_SHIFT) /* 01: CC4 channel input, IC4 is TI4 */
+#  define TIM2_CCMR2_CC4S_CCIN2     (0x2 << TIM2_CCMR2_CC4S_SHIFT) /* 10: CC4 channel input, IC4 is TI3 */
+#  define TIM2_CCMR2_CC4S_CCINTRC   (0x3 << TIM2_CCMR2_CC4S_SHIFT) /* 11: CC4 channel input, IC4 is TRC */
+
+#define TIM16_CCMR1_CC1S_SHIFT      (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM16_CCMR1_CC1S_MASK       (0x3 << TIM16_CCMR1_CC1S_SHIFT)
+#  define TIM16_CCMR1_CC1S_CCOUT    (0x0 << TIM16_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM16_CCMR1_CC1S_CCIN1    (0x1 << TIM16_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+
+#define TIM17_CCMR1_CC1S_SHIFT      (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM17_CCMR1_CC1S_MASK       (0x3 << TIM17_CCMR1_CC1S_SHIFT)
+#  define TIM17_CCMR1_CC1S_CCOUT    (0x0 << TIM17_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM17_CCMR1_CC1S_CCIN1    (0x1 << TIM17_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+
+/* Capture/compare mode registers - Output compare mode */
+
+#define TIM1_CCMR1_OC1FE            (1 << 2)  /* Bit 2: Output Compare 1 Fast enable */
+#define TIM1_CCMR1_OC1PE            (1 << 3)  /* Bit 3: Output Compare 1 Preload enable */
+#define TIM1_CCMR1_OC1M_LO_SHIFT    (4)       /* Bits 4-6: Output Compare 1 Mode, bits [2:0] */
+#define TIM1_CCMR1_OC1M_HI_SHIFT    (16)      /* Bit 16: Output Compare 1 Mode, bits [3] */
+#define TIM1_CCMR1_OC1M_BITS(h,l)   ((h << TIM1_CCMR1_OC1M_HI_SHIFT) | (l << TIM1_CCMR1_OC1M_LO_SHIFT))

Review Comment:
   ditto



##########
arch/arm/src/stm32wb/hardware/stm32wb_rcc.h:
##########
@@ -0,0 +1,877 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_rcc.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_RCC_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_RCC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_RCC_CR_OFFSET           0x0000  /* Clock control register */
+#define STM32WB_RCC_ICSCR_OFFSET        0x0004  /* Internal clock sources calibration register */
+#define STM32WB_RCC_CFGR_OFFSET         0x0008  /* Clock configuration register */
+#define STM32WB_RCC_PLLCFG_OFFSET       0x000c  /* PLL configuration register */
+#define STM32WB_RCC_PLLSAI1CFG_OFFSET   0x0010  /* PLLSAI1 configuration register */
+#define STM32WB_RCC_CIER_OFFSET         0x0018  /* Clock interrupt enable register */
+#define STM32WB_RCC_CIFR_OFFSET         0x001c  /* Clock interrupt flag register */
+#define STM32WB_RCC_CICR_OFFSET         0x0020  /* Clock interrupt clear register */
+#define STM32WB_RCC_SMPSCR_OFFSET       0x0024  /* Step-down converter control register */
+#define STM32WB_RCC_AHB1RSTR_OFFSET     0x0028  /* AHB1 peripheral reset register */
+#define STM32WB_RCC_AHB2RSTR_OFFSET     0x002c  /* AHB2 peripheral reset register */
+#define STM32WB_RCC_AHB3RSTR_OFFSET     0x0030  /* AHB3 peripheral reset register */
+#define STM32WB_RCC_APB1RSTR1_OFFSET    0x0038  /* APB1 Peripheral reset register 1 */
+#define STM32WB_RCC_APB1RSTR2_OFFSET    0x003c  /* APB1 Peripheral reset register 2 */
+#define STM32WB_RCC_APB2RSTR_OFFSET     0x0040  /* APB2 Peripheral reset register */
+#define STM32WB_RCC_APB3RSTR_OFFSET     0x0044  /* APB3 Peripheral reset register */
+#define STM32WB_RCC_AHB1ENR_OFFSET      0x0048  /* AHB1 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB2ENR_OFFSET      0x004c  /* AHB2 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB3ENR_OFFSET      0x0050  /* AHB3 Peripheral Clock enable register */
+#define STM32WB_RCC_APB1ENR1_OFFSET     0x0058  /* APB1 Peripheral Clock enable register 1 */
+#define STM32WB_RCC_APB1ENR2_OFFSET     0x005c  /* APB1 Peripheral Clock enable register 2 */
+#define STM32WB_RCC_APB2ENR_OFFSET      0x0060  /* APB2 Peripheral Clock enable register */
+#define STM32WB_RCC_AHB1SMENR_OFFSET    0x0068  /* AHB1 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_AHB2SMENR_OFFSET    0x006c  /* AHB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_AHB3SMENR_OFFSET    0x0070  /* AHB3 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_APB1SMENR1_OFFSET   0x0078  /* APB1 clock enable in sleep and stop modes register 1 */
+#define STM32WB_RCC_APB1SMENR2_OFFSET   0x007c  /* APB1 clock enable in sleep and stop modes register 2 */
+#define STM32WB_RCC_APB2SMENR_OFFSET    0x0080  /* APB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_CCIPR_OFFSET        0x0088  /* Peripherals independent clock configuration register */
+#define STM32WB_RCC_BDCR_OFFSET         0x0090  /* Backup domain control register */
+#define STM32WB_RCC_CSR_OFFSET          0x0094  /* Control/status register */
+#define STM32WB_RCC_CRRCR_OFFSET        0x0098  /* Clock recovery RC register */
+#define STM32WB_RCC_HSECR_OFFSET        0x009c  /* Clock HSE register */
+#define STM32WB_RCC_EXTCFGR_OFFSET      0x0108  /* Extended clock recovery register */
+#define STM32WB_RCC_C2AHB1ENR_OFFSET    0x0148  /* CPU2 AHB1 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB2ENR_OFFSET    0x014c  /* CPU2 AHB2 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB3ENR_OFFSET    0x0150  /* CPU2 AHB3 Peripheral Clock enable register */
+#define STM32WB_RCC_C2APB1ENR1_OFFSET   0x0158  /* CPU2 APB1 Peripheral Clock enable register 1 */
+#define STM32WB_RCC_C2APB1ENR2_OFFSET   0x015c  /* CPU2 APB1 Peripheral Clock enable register 2 */
+#define STM32WB_RCC_C2APB2ENR_OFFSET    0x0160  /* CPU2 APB2 Peripheral Clock enable register */
+#define STM32WB_RCC_C2APB3ENR_OFFSET    0x0164  /* CPU2 APB3 Peripheral Clock enable register */
+#define STM32WB_RCC_C2AHB1SMENR_OFFSET  0x0168  /* CPU2 AHB1 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2AHB2SMENR_OFFSET  0x016c  /* CPU2 AHB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2AHB3SMENR_OFFSET  0x0170  /* CPU2 AHB3 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2APB1SMENR1_OFFSET 0x0178  /* CPU2 APB1 clock enable in sleep and stop modes register 1 */
+#define STM32WB_RCC_C2APB1SMENR2_OFFSET 0x017c  /* CPU2 APB1 clock enable in sleep and stop modes register 2 */
+#define STM32WB_RCC_C2APB2SMENR_OFFSET  0x0180  /* CPU2 APB2 clock enable in sleep and stop modes register */
+#define STM32WB_RCC_C2APB3SMENR_OFFSET  0x0184  /* CPU2 APB3 clock enable in sleep and stop modes register */
+
+/* Register Addresses *******************************************************/
+
+#define STM32WB_RCC_CR                (STM32WB_RCC_BASE + STM32WB_RCC_CR_OFFSET)
+#define STM32WB_RCC_ICSCR             (STM32WB_RCC_BASE + STM32WB_RCC_ICSCR_OFFSET)
+#define STM32WB_RCC_CFGR              (STM32WB_RCC_BASE + STM32WB_RCC_CFGR_OFFSET)
+#define STM32WB_RCC_PLLCFG            (STM32WB_RCC_BASE + STM32WB_RCC_PLLCFG_OFFSET)
+#define STM32WB_RCC_PLLSAI1CFG        (STM32WB_RCC_BASE + STM32WB_RCC_PLLSAI1CFG_OFFSET)
+#define STM32WB_RCC_CIER              (STM32WB_RCC_BASE + STM32WB_RCC_CIER_OFFSET)
+#define STM32WB_RCC_CIFR              (STM32WB_RCC_BASE + STM32WB_RCC_CIFR_OFFSET)
+#define STM32WB_RCC_CICR              (STM32WB_RCC_BASE + STM32WB_RCC_CICR_OFFSET)
+#define STM32WB_RCC_SMPSCR            (STM32WB_RCC_BASE + STM32WB_RCC_SMPSCR_OFFSET)
+#define STM32WB_RCC_AHB1RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB1RSTR_OFFSET)
+#define STM32WB_RCC_AHB2RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB2RSTR_OFFSET)
+#define STM32WB_RCC_AHB3RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_AHB3RSTR_OFFSET)
+#define STM32WB_RCC_APB1RSTR1         (STM32WB_RCC_BASE + STM32WB_RCC_APB1RSTR1_OFFSET)
+#define STM32WB_RCC_APB1RSTR2         (STM32WB_RCC_BASE + STM32WB_RCC_APB1RSTR2_OFFSET)
+#define STM32WB_RCC_APB2RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_APB2RSTR_OFFSET)
+#define STM32WB_RCC_APB3RSTR          (STM32WB_RCC_BASE + STM32WB_RCC_APB3RSTR_OFFSET)
+#define STM32WB_RCC_AHB1ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB1ENR_OFFSET)
+#define STM32WB_RCC_AHB2ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB2ENR_OFFSET)
+#define STM32WB_RCC_AHB3ENR           (STM32WB_RCC_BASE + STM32WB_RCC_AHB3ENR_OFFSET)
+#define STM32WB_RCC_APB1ENR1          (STM32WB_RCC_BASE + STM32WB_RCC_APB1ENR1_OFFSET)
+#define STM32WB_RCC_APB1ENR2          (STM32WB_RCC_BASE + STM32WB_RCC_APB1ENR2_OFFSET)
+#define STM32WB_RCC_APB2ENR           (STM32WB_RCC_BASE + STM32WB_RCC_APB2ENR_OFFSET)
+#define STM32WB_RCC_AHB1SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB1SMENR_OFFSET)
+#define STM32WB_RCC_AHB2SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB2SMENR_OFFSET)
+#define STM32WB_RCC_AHB3SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_AHB3SMENR_OFFSET)
+#define STM32WB_RCC_APB1SMENR1        (STM32WB_RCC_BASE + STM32WB_RCC_APB1SMENR1_OFFSET)
+#define STM32WB_RCC_APB1SMENR2        (STM32WB_RCC_BASE + STM32WB_RCC_APB1SMENR2_OFFSET)
+#define STM32WB_RCC_APB2SMENR         (STM32WB_RCC_BASE + STM32WB_RCC_APB2SMENR_OFFSET)
+#define STM32WB_RCC_CCIPR             (STM32WB_RCC_BASE + STM32WB_RCC_CCIPR_OFFSET)
+#define STM32WB_RCC_BDCR              (STM32WB_RCC_BASE + STM32WB_RCC_BDCR_OFFSET)
+#define STM32WB_RCC_CSR               (STM32WB_RCC_BASE + STM32WB_RCC_CSR_OFFSET)
+#define STM32WB_RCC_CRRCR             (STM32WB_RCC_BASE + STM32WB_RCC_CRRCR_OFFSET)
+#define STM32WB_RCC_HSECR             (STM32WB_RCC_BASE + STM32WB_RCC_HSECR_OFFSET)
+#define STM32WB_RCC_EXTCFGR           (STM32WB_RCC_BASE + STM32WB_RCC_EXTCFGR_OFFSET)
+#define STM32WB_RCC_C2AHB1ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB1ENR_OFFSET)
+#define STM32WB_RCC_C2AHB2ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB2ENR_OFFSET)
+#define STM32WB_RCC_C2AHB3ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB3ENR_OFFSET)
+#define STM32WB_RCC_C2APB1ENR1        (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1ENR1_OFFSET)
+#define STM32WB_RCC_C2APB1ENR2        (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1ENR2_OFFSET)
+#define STM32WB_RCC_C2APB2ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2APB2ENR_OFFSET)
+#define STM32WB_RCC_C2APB3ENR         (STM32WB_RCC_BASE + STM32WB_RCC_C2APB3ENR_OFFSET)
+#define STM32WB_RCC_C2AHB1SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB1SMENR_OFFSET)
+#define STM32WB_RCC_C2AHB2SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB2SMENR_OFFSET)
+#define STM32WB_RCC_C2AHB3SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2AHB3SMENR_OFFSET)
+#define STM32WB_RCC_C2APB1SMENR1      (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1SMENR1_OFFSET)
+#define STM32WB_RCC_C2APB1SMENR2      (STM32WB_RCC_BASE + STM32WB_RCC_C2APB1SMENR2_OFFSET)
+#define STM32WB_RCC_C2APB2SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2APB2SMENR_OFFSET)
+#define STM32WB_RCC_C2APB3SMENR       (STM32WB_RCC_BASE + STM32WB_RCC_C2APB3SMENR_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Clock control register */
+
+#define RCC_CR_MSION                  (1 << 0)  /* Bit 0: Internal Multi Speed clock enable */
+#define RCC_CR_MSIRDY                 (1 << 1)  /* Bit 1: Internal Multi Speed clock ready flag */
+#define RCC_CR_MSIPLLEN               (1 << 2)  /* Bit 2: MSI clock PLL enable */
+#define RCC_CR_MSIRANGE_SHIFT         (4)       /* Bits 7-4: MSI clock range */
+#define RCC_CR_MSIRANGE_MASK          (0x0f << RCC_CR_MSIRANGE_SHIFT)
+#  define RCC_CR_MSIRANGE_100K        (0  << RCC_CR_MSIRANGE_SHIFT) /* 0000: around 100 kHz */
+#  define RCC_CR_MSIRANGE_200K        (1  << RCC_CR_MSIRANGE_SHIFT) /* 0001: around 200 kHz */
+#  define RCC_CR_MSIRANGE_400K        (2  << RCC_CR_MSIRANGE_SHIFT) /* 0010: around 400 kHz */
+#  define RCC_CR_MSIRANGE_800K        (3  << RCC_CR_MSIRANGE_SHIFT) /* 0011: around 800 kHz */
+#  define RCC_CR_MSIRANGE_1M          (4  << RCC_CR_MSIRANGE_SHIFT) /* 0100: around 1 MHz */
+#  define RCC_CR_MSIRANGE_2M          (5  << RCC_CR_MSIRANGE_SHIFT) /* 0101: around 2 MHz */
+#  define RCC_CR_MSIRANGE_4M          (6  << RCC_CR_MSIRANGE_SHIFT) /* 0110: around 4 MHz */
+#  define RCC_CR_MSIRANGE_8M          (7  << RCC_CR_MSIRANGE_SHIFT) /* 0111: around 8 MHz */
+#  define RCC_CR_MSIRANGE_16M         (8  << RCC_CR_MSIRANGE_SHIFT) /* 1000: around 16 MHz */
+#  define RCC_CR_MSIRANGE_24M         (9  << RCC_CR_MSIRANGE_SHIFT) /* 1001: around 24 MHz */
+#  define RCC_CR_MSIRANGE_32M         (10 << RCC_CR_MSIRANGE_SHIFT) /* 1010: around 32 MHz */
+#  define RCC_CR_MSIRANGE_48M         (11 << RCC_CR_MSIRANGE_SHIFT) /* 1011: around 48 MHz */
+
+#define RCC_CR_HSION                  (1 << 8)  /* Bit 8:  HSI16 clock enable */
+#define RCC_CR_HSIKERON               (1 << 9)  /* Bit 9:  HSI16 always enable for peripheral kernels */
+#define RCC_CR_HSIRDY                 (1 << 10) /* Bit 10: HSI16 clock ready flag */
+#define RCC_CR_HSIASFS                (1 << 11) /* Bit 11: HSI16 automatic start from stop */
+#define RCC_CR_HSEON                  (1 << 16) /* Bit 16: HSE clock enable */
+#define RCC_CR_HSERDY                 (1 << 17) /* Bit 17: HSE clock ready flag */
+#define RCC_CR_CSSON                  (1 << 19) /* Bit 19: Clock Security System enable */
+#define RCC_CR_HSEPRE                 (1 << 20) /* Bit 20: HSE sysclk and PLL M divider prescaler */
+#define RCC_CR_PLLON                  (1 << 24) /* Bit 24: PLL enable */
+#define RCC_CR_PLLRDY                 (1 << 25) /* Bit 25: PLL clock ready flag */
+#define RCC_CR_PLLSAI1ON              (1 << 26) /* Bit 26: PLLSAI1 enable */
+#define RCC_CR_PLLSAI1RDY             (1 << 27) /* Bit 27: PLLSAI1 clock ready flag */
+
+/* Internal Clock Sources Calibration */
+
+#define RCC_CR_HSITRIM_SHIFT          (24)      /* Bits 30-24: HSI16 clock trimming */
+#define RCC_CR_HSITRIM_MASK           (0x7f << RCC_CR_HSITRIM_SHIFT)
+#define RCC_CR_HSICAL_SHIFT           (16)      /* Bits 23-16: HSI16 clock Calibration */
+#define RCC_CR_HSICAL_MASK            (0xff << RCC_CR_HSICAL_SHIFT)
+#define RCC_CR_MSITRIM_SHIFT          (8)       /* Bits 15-8:  Internal Multi Speed clock trimming */
+#define RCC_CR_MSITRIM_MASK           (0xff << RCC_CR_MSITRIM_SHIFT)
+#define RCC_CR_MSICAL_SHIFT           (0)       /* Bits 7-0:   Internal Multi Speed clock Calibration */
+#define RCC_CR_MSICAL_MASK            (0xff << RCC_CR_MSICAL_SHIFT)
+
+/* Clock configuration register */
+
+#define RCC_CFGR_SW_SHIFT             (0)       /* Bits 0-1: System clock Switch */
+#define RCC_CFGR_SW_MASK              (0x3 << RCC_CFGR_SW_SHIFT)
+#  define RCC_CFGR_SW_MSI             (0x0 << RCC_CFGR_SW_SHIFT) /* 00: MSI selected as system clock */
+#  define RCC_CFGR_SW_HSI16           (0x1 << RCC_CFGR_SW_SHIFT) /* 00: HSI16 selected as system clock */
+#  define RCC_CFGR_SW_HSE             (0x2 << RCC_CFGR_SW_SHIFT) /* 01: HSE selected as system clock */
+#  define RCC_CFGR_SW_PLL             (0x3 << RCC_CFGR_SW_SHIFT) /* 10: PLL selected as system clock */
+
+#define RCC_CFGR_SWS_SHIFT            (2)       /* Bits 2-3: System Clock Switch Status */
+#define RCC_CFGR_SWS_MASK             (0x3 << RCC_CFGR_SWS_SHIFT)
+#  define RCC_CFGR_SWS_MSI            (0x0 << RCC_CFGR_SWS_SHIFT) /* 00: MSI oscillator used as system clock */
+#  define RCC_CFGR_SWS_HSI16          (0x1 << RCC_CFGR_SWS_SHIFT) /* 00: HSI16 oscillator used as system clock */
+#  define RCC_CFGR_SWS_HSE            (0x2 << RCC_CFGR_SWS_SHIFT) /* 01: HSE oscillator used as system clock */
+#  define RCC_CFGR_SWS_PLL            (0x3 << RCC_CFGR_SWS_SHIFT) /* 10: PLL used as system clock */
+
+#define RCC_CFGR_HPRE_SHIFT           (4)       /* Bits 4-7: HCLK1 prescaler (AHB1, AHB2, AHB3 and SRAM1) */
+#define RCC_CFGR_HPRE_MASK            (0x0f << RCC_CFGR_HPRE_SHIFT)
+#  define RCC_CFGR_HPRE_SYSCLK        (0x00 << RCC_CFGR_HPRE_SHIFT) /* 0000: SYSCLK not divided */
+#  define RCC_CFGR_HPRE_SYSCLKd3      (0x01 << RCC_CFGR_HPRE_SHIFT) /* 0001: SYSCLK divided by 3 */
+#  define RCC_CFGR_HPRE_SYSCLKd5      (0x02 << RCC_CFGR_HPRE_SHIFT) /* 0010: SYSCLK divided by 5 */
+#  define RCC_CFGR_HPRE_SYSCLKd6      (0x05 << RCC_CFGR_HPRE_SHIFT) /* 0101: SYSCLK divided by 6 */
+#  define RCC_CFGR_HPRE_SYSCLKd10     (0x06 << RCC_CFGR_HPRE_SHIFT) /* 0110: SYSCLK divided by 10 */
+#  define RCC_CFGR_HPRE_SYSCLKd32     (0x07 << RCC_CFGR_HPRE_SHIFT) /* 0111: SYSCLK divided by 32 */
+#  define RCC_CFGR_HPRE_SYSCLKd2      (0x08 << RCC_CFGR_HPRE_SHIFT) /* 1000: SYSCLK divided by 2 */
+#  define RCC_CFGR_HPRE_SYSCLKd4      (0x09 << RCC_CFGR_HPRE_SHIFT) /* 1001: SYSCLK divided by 4 */
+#  define RCC_CFGR_HPRE_SYSCLKd8      (0x10 << RCC_CFGR_HPRE_SHIFT) /* 1010: SYSCLK divided by 8 */
+#  define RCC_CFGR_HPRE_SYSCLKd16     (0x11 << RCC_CFGR_HPRE_SHIFT) /* 1011: SYSCLK divided by 16 */
+#  define RCC_CFGR_HPRE_SYSCLKd64     (0x12 << RCC_CFGR_HPRE_SHIFT) /* 1100: SYSCLK divided by 64 */
+#  define RCC_CFGR_HPRE_SYSCLKd128    (0x13 << RCC_CFGR_HPRE_SHIFT) /* 1101: SYSCLK divided by 128 */
+#  define RCC_CFGR_HPRE_SYSCLKd256    (0x14 << RCC_CFGR_HPRE_SHIFT) /* 1110: SYSCLK divided by 256 */
+#  define RCC_CFGR_HPRE_SYSCLKd512    (0x15 << RCC_CFGR_HPRE_SHIFT) /* 1111: SYSCLK divided by 512 */
+
+#define RCC_CFGR_PPRE1_SHIFT          (8)       /* Bits 8-10: PCLK1 Low speed prescaler (APB1) */
+#define RCC_CFGR_PPRE1_MASK           (0x7 << RCC_CFGR_PPRE1_SHIFT)
+#  define RCC_CFGR_PPRE1_HCLK1        (0x0 << RCC_CFGR_PPRE1_SHIFT) /* 0xx: HCLK1 not divided */
+#  define RCC_CFGR_PPRE1_HCLK1d2      (0x4 << RCC_CFGR_PPRE1_SHIFT) /* 100: HCLK1 divided by 2 */
+#  define RCC_CFGR_PPRE1_HCLK1d4      (0x5 << RCC_CFGR_PPRE1_SHIFT) /* 101: HCLK1 divided by 4 */
+#  define RCC_CFGR_PPRE1_HCLK1d8      (0x6 << RCC_CFGR_PPRE1_SHIFT) /* 110: HCLK1 divided by 8 */
+#  define RCC_CFGR_PPRE1_HCLK1d16     (0x7 << RCC_CFGR_PPRE1_SHIFT) /* 111: HCLK1 divided by 16 */
+
+#define RCC_CFGR_PPRE2_SHIFT          (11)      /* Bits 11-13: PCLK2 High speed prescaler (APB2) */
+#define RCC_CFGR_PPRE2_MASK           (0x7 << RCC_CFGR_PPRE2_SHIFT)
+#  define RCC_CFGR_PPRE2_HCLK1        (0x0 << RCC_CFGR_PPRE2_SHIFT) /* 0xx: HCLK1 not divided */
+#  define RCC_CFGR_PPRE2_HCLK1d2      (0x4 << RCC_CFGR_PPRE2_SHIFT) /* 100: HCLK1 divided by 2 */
+#  define RCC_CFGR_PPRE2_HCLK1d4      (0x5 << RCC_CFGR_PPRE2_SHIFT) /* 101: HCLK1 divided by 4 */
+#  define RCC_CFGR_PPRE2_HCLK1d8      (0x6 << RCC_CFGR_PPRE2_SHIFT) /* 110: HCLK1 divided by 8 */
+#  define RCC_CFGR_PPRE2_HCLK1d16     (0x7 << RCC_CFGR_PPRE2_SHIFT) /* 111: HCLK1 divided by 16 */
+
+#define RCC_CFGR_STOPWUCK             (1 << 15) /* Bit 15: Wakeup from Stop and CSS backup clock selection */
+#  define RCC_CFGR_STOPWUCK_MSI       (0 << 15) /* 0: MSI */
+#  define RCC_CFGR_STOPWUCK_HSI16     (1 << 15) /* 0: HSI16 */
+
+#define RCC_CFGR_HPREF                (1 << 16) /* Bit 16: HCLK1 prescaler flag (AHB1, AHB2, AHB3, SRAM1) */
+#define RCC_CFGR_PPRE1F               (1 << 17) /* Bit 17: PCLK1 prescaler flag (APB1) */
+#define RCC_CFGR_PPRE2F               (1 << 18) /* Bit 18: PCLK2 prescaler flag (APB2) */
+
+#define RCC_CFGR_MCOSEL_SHIFT         (24)      /* Bits 24-27: Microcontroller Clock Output */
+#define RCC_CFGR_MCOSEL_MASK          (0xf << RCC_CFGR_MCOSEL_SHIFT)
+#  define RCC_CFGR_MCOSEL_DISABLED    (0x0 << RCC_CFGR_MCOSEL_SHIFT) /* 0000: Output disabled, no clock on MCO */
+#  define RCC_CFGR_MCOSEL_SYSCLK      (0x1 << RCC_CFGR_MCOSEL_SHIFT) /* 0001: SYSCLK system clock selected */
+#  define RCC_CFGR_MCOSEL_MSI         (0x2 << RCC_CFGR_MCOSEL_SHIFT) /* 0010: MSI clock selected */
+#  define RCC_CFGR_MCOSEL_HSI16       (0x3 << RCC_CFGR_MCOSEL_SHIFT) /* 0011: HSI16 clock selected */
+#  define RCC_CFGR_MCOSEL_HSE_AFT     (0x4 << RCC_CFGR_MCOSEL_SHIFT) /* 0100: HSE clock after stabilization */
+#  define RCC_CFGR_MCOSEL_PLL         (0x5 << RCC_CFGR_MCOSEL_SHIFT) /* 0101: Main PLLRCLK selected  */
+#  define RCC_CFGR_MCOSEL_LSI1        (0x6 << RCC_CFGR_MCOSEL_SHIFT) /* 0110: LSI1 clock selected */
+#  define RCC_CFGR_MCOSEL_LSI2        (0x7 << RCC_CFGR_MCOSEL_SHIFT) /* 0111: LSI2 clock selected */
+#  define RCC_CFGR_MCOSEL_LSE         (0x8 << RCC_CFGR_MCOSEL_SHIFT) /* 1000: LSE clock selected */
+#  define RCC_CFGR_MCOSEL_HSI48       (0x9 << RCC_CFGR_MCOSEL_SHIFT) /* 1001: HSI48 clock selected */
+#  define RCC_CFGR_MCOSEL_HSE_BFR     (0xc << RCC_CFGR_MCOSEL_SHIFT) /* 1100: HSE clock before stabilization */
+
+#define RCC_CFGR_MCOPRE_SHIFT         (28)      /* Bits 28-30: MCO prescaler */
+#define RCC_CFGR_MCOPRE_MASK          (0x7 << RCC_CFGR_MCOPRE_SHIFT)
+#  define RCC_CFGR_MCOPRE_DIV1        (0x0 << RCC_CFGR_MCOPRE_SHIFT) /* 000: no division */
+#  define RCC_CFGR_MCOPRE_DIV2        (0x1 << RCC_CFGR_MCOPRE_SHIFT) /* 001: division by 2 */
+#  define RCC_CFGR_MCOPRE_DIV4        (0x2 << RCC_CFGR_MCOPRE_SHIFT) /* 010: division by 4 */
+#  define RCC_CFGR_MCOPRE_DIV8        (0x3 << RCC_CFGR_MCOPRE_SHIFT) /* 011: division by 8 */
+#  define RCC_CFGR_MCOPRE_DIV16       (0x4 << RCC_CFGR_MCOPRE_SHIFT) /* 100: division by 16 */
+
+#define RCC_CFGR_RESET_MASK           (0x00070000)
+
+/* PLL configuration register */
+
+#define RCC_PLLCFG_PLLSRC_SHIFT       (0)       /* Bits 0-1: Main PLL and audio PLLSAI1 entry clock source */
+#define RCC_PLLCFG_PLLSRC_MASK        (0x3 << RCC_PLLCFG_PLLSRC_SHIFT)
+#  define RCC_PLLCFG_PLLSRC_NONE      (0x0 << RCC_PLLCFG_PLLSRC_SHIFT) /* 00: No clock sent to PLLs */
+#  define RCC_PLLCFG_PLLSRC_MSI       (0x1 << RCC_PLLCFG_PLLSRC_SHIFT) /* 01: MSI selected as PLLs source */
+#  define RCC_PLLCFG_PLLSRC_HSI16     (0x2 << RCC_PLLCFG_PLLSRC_SHIFT) /* 10: HSI16 selected as PLLs source */
+#  define RCC_PLLCFG_PLLSRC_HSE       (0x3 << RCC_PLLCFG_PLLSRC_SHIFT) /* 11: HSE selected as PLLs source */
+
+#define RCC_PLLCFG_PLLM_SHIFT         (4)       /* Bits 4-6: Main PLL and audio PLLSAI1 divider */
+#define RCC_PLLCFG_PLLM_MASK          (0x07 << RCC_PLLCFG_PLLM_SHIFT)
+#  define RCC_PLLCFG_PLLM(n)          ((n-1) << RCC_PLLCFG_PLLM_SHIFT) /* n = 1..8 */
+
+#define RCC_PLLCFG_PLLN_SHIFT         (8)       /* Bits 8-14: Main PLL (PLL) VCO multiplier */
+#define RCC_PLLCFG_PLLN_MASK          (0x7f << RCC_PLLCFG_PLLN_SHIFT)
+#  define RCC_PLLCFG_PLLN(n)          ((n) << RCC_PLLCFG_PLLN_SHIFT) /* n = 6..127 */
+
+#define RCC_PLLCFG_PLLPEN             (1 << 16) /* Bit 16: Main PLL PLLPCLK output enable */
+
+#define RCC_PLLCFG_PLLP_SHIFT         (17)      /* Bits 17-21: Main PLL div factor for PLLPCLK */
+#define RCC_PLLCFG_PLLP_MASK          (0x1f << RCC_PLLCFG_PLLP_SHIFT)
+#  define RCC_PLLCFG_PLLP(n)          ((n-1) << RCC_PLLCFG_PLLP_SHIFT) /* n = 2..32 */
+
+#define RCC_PLLCFG_PLLQEN             (1 << 24) /* Bit 24: Main PLL PLLQCLK output enable */
+
+#define RCC_PLLCFG_PLLQ_SHIFT         (25)      /* Bits 25-27: Main PLL division factor for PLLQCLK */
+#define RCC_PLLCFG_PLLQ_MASK          (0x7 << RCC_PLLCFG_PLLQ_SHIFT)
+#  define RCC_PLLCFG_PLLQ(n)          ((n-1) << RCC_PLLCFG_PLLQ_SHIFT) /* n = 2..8 */
+
+#define RCC_PLLCFG_PLLREN             (1 << 28) /* Bit 28: Main PLL PLLRCLK output enable */
+
+#define RCC_PLLCFG_PLLR_SHIFT         (29)      /* Bits 29-31: Main PLL division factor for PLLRCLK */
+#define RCC_PLLCFG_PLLR_MASK          (0x7 << RCC_PLLCFG_PLLR_SHIFT)
+#  define RCC_PLLCFG_PLLR(n)          ((n-1) << RCC_PLLCFG_PLLR_SHIFT) /* n = 2..8 */
+
+#define RCC_PLLCFG_RESET              (0x22040100) /* PLLCFG reset value */
+
+/* PLLSAI1 Configuration register */
+
+#define RCC_PLLSAI1CFG_PLLN_SHIFT     (8)       /* Bits 8-14: SAI1 PLL (PLLSAI1) VCO multiplier */
+#define RCC_PLLSAI1CFG_PLLN_MASK      (0x7f << RCC_PLLSAI1CFG_PLLN_SHIFT)
+#  define RCC_PLLSAI1CFG_PLLN(n)      ((n) << RCC_PLLSAI1CFG_PLLN_SHIFT) /* n = 4..86 */
+
+#define RCC_PLLSAI1CFG_PLLPEN         (1 << 16) /* Bit 16: SAI1 PLL PLLSAI1CLK output enable */
+
+#define RCC_PLLSAI1CFG_PLLP_SHIFT     (17)      /* Bit 17-21: Main PLL div factor for PLLSAI1CLK */
+#define RCC_PLLSAI1CFG_PLLP_MASK      (0x1f << RCC_PLLSAI1CFG_PLLP_SHIFT)
+#  define RCC_PLLSAI1CFG_PLLP(n)      ((n-1) << RCC_PLLSAI1CFG_PLLP_SHIFT) /* n = 2..32 */

Review Comment:
   ```suggestion
   #  define RCC_PLLSAI1CFG_PLLP(n)      (((n) - 1) << RCC_PLLSAI1CFG_PLLP_SHIFT) /* n = 2..32 */
   ```



##########
arch/arm/src/stm32wb/stm32wb_exti_pwr.c:
##########
@@ -0,0 +1,142 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_exti_pwr.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_exti.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Interrupt handlers attached to the PVD EXTI */
+
+static xcpt_t g_pvd_callback;
+static void  *g_callback_arg;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_exti_pvd_isr
+ *
+ * Description:
+ *   EXTI PVD interrupt service routine/dispatcher
+ *
+ ****************************************************************************/
+
+static int stm32wb_exti_pvd_isr(int irq, void *context, void *arg)
+{
+  int ret = OK;
+
+  /* Clear the pending EXTI interrupt */
+
+  putreg32(EXTI_PR1_PIF(EXTI_EVT_PVD), STM32WB_EXTI_PR1);
+
+  /* And dispatch the interrupt to the handler */
+
+  if (g_pvd_callback != NULL)
+    {
+      ret = g_pvd_callback(irq, context, g_callback_arg);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_exti_pvd
+ *
+ * Description:
+ *   Sets/clears EXTI PVD interrupt.
+ *
+ * Input Parameters:
+ *  - rising/falling edge: enables interrupt on rising/falling edge
+ *  - event:  generate event when set
+ *  - func:   when non-NULL, generate interrupt
+ *
+ * Returned Value:
+ *   Zero (OK) returned on success; a negated errno value is returned on
+ *   failure.
+ *
+ ****************************************************************************/
+
+int stm32wb_exti_pvd(bool risingedge, bool fallingedge, bool event,
+                        xcpt_t func, void *arg)

Review Comment:
   ```suggestion
   int stm32wb_exti_pvd(bool risingedge, bool fallingedge, bool event,
                        xcpt_t func, void *arg)
   ```



##########
arch/arm/src/stm32wb/stm32wb_dma.c:
##########
@@ -0,0 +1,1404 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_dma.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <debug.h>
+#include <assert.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include "arm_internal.h"
+#include "sched/sched.h"
+#include "stm32wb_dma.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifndef CONFIG_STM32WB_DMAMUX
+#  error "Configuration error, CONFIG_STM32WB_DMAMUX not defined!"
+#endif
+
+#define DMAMUX_NUM      1
+#define DMA_CONTROLLERS 2
+
+#ifdef CONFIG_STM32WB_DMA1
+#  define DMA1_NCHAN    7
+#else
+#  define DMA1_NCHAN    0
+#endif
+#ifdef CONFIG_STM32WB_DMA2
+#  define DMA2_NCHAN    7
+#else
+#  define DMA2_NCHAN    0
+#endif
+
+#define DMA1_FIRST       (0)
+#define DMA1_LAST        (DMA1_FIRST+DMA1_NCHAN)
+#define DMA2_FIRST       (DMA1_LAST)
+#define DMA2_LAST        (DMA2_FIRST+DMA2_NCHAN)
+
+/* All available DMA channels */
+
+#define DMA_NCHANNELS    (DMA1_NCHAN+DMA2_NCHAN)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure described one DMAMUX device */
+
+struct stm32wb_dmamux_s
+{
+  uint8_t  id;                  /* DMAMUX id */
+  uint8_t  nchan;               /* DMAMUX channels */
+  uint32_t base;                /* DMAMUX base address */
+};
+
+typedef const struct stm32wb_dmamux_s *DMA_MUX;
+
+/* This structure describes one DMA controller */
+
+struct stm32wb_dma_s
+{
+  uint8_t       first;           /* Offset in stm32wb_dmach_s array */
+  uint8_t       nchan;           /* Number of channels */
+  uint8_t       dmamux_offset;   /* DMAMUX channel offset */
+  uint32_t      base;            /* Base address */
+  DMA_MUX       dmamux;          /* DMAMUX associated with controller */
+};
+
+/* This structure describes one DMA channel (DMA1, DMA2) */
+
+struct stm32wb_dmach_s
+{
+  bool             used;         /* Channel in use */
+  uint8_t          dmamux_req;   /* Configured DMAMUX input request */
+  uint8_t          ctrl;         /* DMA controller */
+  uint8_t          chan;         /* DMA channel channel id */
+  uint8_t          irq;          /* DMA channel IRQ number */
+  uint8_t          shift;        /* IFCR bit shift value */
+  uint32_t         base;         /* DMA register channel base address */
+  dma_callback_t   callback;     /* Callback invoked when the DMA completes */
+  void             *arg;         /* Argument passed to callback function */
+};
+
+typedef struct stm32wb_dmach_s *DMA_CHANNEL;
+
+/* DMA operations */
+
+struct stm32wb_dma_ops_s
+{
+  /* Disable the DMA transfer */
+
+  void (*dma_disable)(DMA_CHANNEL dmachan);
+
+  /* DMA interrupt */
+
+  int (*dma_interrupt)(int irq, void *context, void *arg);
+
+  /* Setup the DMA */
+
+  void (*dma_setup)(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
+                    size_t ntransfers, uint32_t ccr);
+
+  /* Start the DMA */
+
+  void (*dma_start)(DMA_HANDLE handle, dma_callback_t callback,
+                    void *arg, bool half);
+
+  /* Read remaining DMA bytes */
+
+  size_t (*dma_residual)(DMA_HANDLE handle);
+
+  /* Check the DMA configuration  */
+
+  bool (*dma_capable)(uint32_t maddr, uint32_t count, uint32_t ccr);
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+  /* Sample the DMA registers */
+
+  void (*dma_sample)(DMA_HANDLE handle, struct stm32wb_dmaregs_s *regs);
+
+  /* Dump the DMA registers */
+
+  void (*dma_dump)(DMA_HANDLE handle,
+                   const struct stm32wb_dmaregs_s *regs,
+                   const char *msg);
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+#if defined(CONFIG_STM32WB_DMA1) || defined(CONFIG_STM32WB_DMA2)
+static void stm32wb_dma12_disable(DMA_CHANNEL dmachan);
+static int stm32wb_dma12_interrupt(int irq, void *context, void *arg);
+static void stm32wb_dma12_setup(DMA_HANDLE handle, uint32_t paddr,
+                                uint32_t maddr, size_t ntransfers,
+                                uint32_t ccr);
+static void stm32wb_dma12_start(DMA_HANDLE handle, dma_callback_t callback,
+                                void *arg, bool half);
+static size_t stm32wb_dma12_residual(DMA_HANDLE handle);
+#ifdef CONFIG_DEBUG_DMA_INFO
+static void stm32wb_dma12_sample(DMA_HANDLE handle,
+                                 struct stm32wb_dmaregs_s *regs);
+static void stm32wb_dma12_dump(DMA_HANDLE handle,
+                               const struct stm32wb_dmaregs_s *regs,
+                               const char *msg);
+#endif
+#endif
+
+static uint32_t dmachan_getbase(DMA_CHANNEL dmachan);
+static uint32_t dmabase_getreg(DMA_CHANNEL dmachan, uint32_t offset);
+static void dmabase_putreg(DMA_CHANNEL dmachan, uint32_t offset,
+                           uint32_t value);
+static uint32_t dmachan_getreg(DMA_CHANNEL dmachan, uint32_t offset);
+static void dmachan_putreg(DMA_CHANNEL dmachan, uint32_t offset,
+                           uint32_t value);
+static void dmamux_putreg(DMA_MUX dmamux, uint32_t offset, uint32_t value);
+#ifdef CONFIG_DEBUG_DMA_INFO
+static uint32_t dmamux_getreg(DMA_MUX dmamux, uint32_t offset);
+static void stm32wb_dmamux_sample(DMA_MUX dmamux, uint8_t chan,
+                                  struct stm32wb_dmaregs_s *regs);
+static void stm32wb_dmamux_dump(DMA_MUX dmamux, uint8_t channel,
+                                const struct stm32wb_dmaregs_s *regs);
+#endif
+static DMA_CHANNEL stm32wb_dma_channel_get(uint8_t channel,
+                                           uint8_t controller);
+static void stm32wb_gdma_limits_get(uint8_t controller, uint8_t *first,
+                                    uint8_t *last);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Operations specific to DMA controller */
+
+static const struct stm32wb_dma_ops_s g_dma_ops[DMA_CONTROLLERS] =
+{
+#ifdef CONFIG_STM32WB_DMA1
+  /* 0 - DMA1 */
+
+    {
+      .dma_disable   = stm32wb_dma12_disable,
+      .dma_interrupt = stm32wb_dma12_interrupt,
+      .dma_setup     = stm32wb_dma12_setup,
+      .dma_start     = stm32wb_dma12_start,
+      .dma_residual  = stm32wb_dma12_residual,
+#ifdef CONFIG_DEBUG_DMA_INFO
+      .dma_sample    = stm32wb_dma12_sample,
+      .dma_dump      = stm32wb_dma12_dump,
+#endif
+    },
+#else
+    {
+      NULL
+    },
+#endif
+
+#ifdef CONFIG_STM32WB_DMA2
+  /* 1 - DMA2 */
+
+    {
+      .dma_disable   = stm32wb_dma12_disable,
+      .dma_interrupt = stm32wb_dma12_interrupt,
+      .dma_setup     = stm32wb_dma12_setup,
+      .dma_start     = stm32wb_dma12_start,
+      .dma_residual  = stm32wb_dma12_residual,
+#ifdef CONFIG_DEBUG_DMA_INFO
+      .dma_sample    = stm32wb_dma12_sample,
+      .dma_dump      = stm32wb_dma12_dump,
+#endif
+    }
+#else
+    {
+      NULL
+    }
+#endif
+};
+
+/* This array describes the state of DMAMUX controller */
+
+static const struct stm32wb_dmamux_s g_dmamux[DMAMUX_NUM] =
+{
+    {
+      .id      = 1,
+      .nchan   = 14,              /* 0-6 - DMA1, 7-13 - DMA2 */
+      .base    = STM32WB_DMAMUX1_BASE
+    }
+};
+
+/* This array describes the state of each controller */
+
+static const struct stm32wb_dma_s g_dma[DMA_NCHANNELS] =
+{
+  /* 0 - DMA1 */
+
+    {
+      .base   = STM32WB_DMA1_BASE,
+      .first  = DMA1_FIRST,
+      .nchan  = DMA1_NCHAN,
+      .dmamux = &g_dmamux[DMAMUX1], /* DMAMUX1 channels 0-6 */
+      .dmamux_offset = 0
+    },
+
+  /* 1 - DMA2 */
+
+    {
+      .base   = STM32WB_DMA2_BASE,
+      .first  = DMA2_FIRST,
+      .nchan  = DMA2_NCHAN,
+      .dmamux = &g_dmamux[DMAMUX1], /* DMAMUX1 channels 7-13 */
+      .dmamux_offset = 7
+    }
+};
+
+/* This array describes the state of each DMA channel. */
+
+static struct stm32wb_dmach_s g_dmach[DMA_NCHANNELS] =
+{
+#ifdef CONFIG_STM32WB_DMA1
+  /* DMA1 */
+
+    {
+      .ctrl     = DMA1,
+      .chan     = 0,
+      .irq      = STM32WB_IRQ_DMA1CH1,
+      .shift    = DMA_CHAN_SHIFT(0),
+      .base     = STM32WB_DMA1_BASE + STM32WB_DMACHAN_OFFSET(0),
+    },
+
+    {
+      .ctrl     = DMA1,
+      .chan     = 1,
+      .irq      = STM32WB_IRQ_DMA1CH2,
+      .shift    = DMA_CHAN_SHIFT(1),
+      .base     = STM32WB_DMA1_BASE + STM32WB_DMACHAN_OFFSET(1),
+    },
+
+    {
+      .ctrl     = DMA1,
+      .chan     = 2,
+      .irq      = STM32WB_IRQ_DMA1CH3,
+      .shift    = DMA_CHAN_SHIFT(2),
+      .base     = STM32WB_DMA1_BASE + STM32WB_DMACHAN_OFFSET(2),
+    },
+
+    {
+      .ctrl     = DMA1,
+      .chan     = 3,
+      .irq      = STM32WB_IRQ_DMA1CH4,
+      .shift    = DMA_CHAN_SHIFT(3),
+      .base     = STM32WB_DMA1_BASE + STM32WB_DMACHAN_OFFSET(3),
+    },
+
+    {
+      .ctrl     = DMA1,
+      .chan     = 4,
+      .irq      = STM32WB_IRQ_DMA1CH5,
+      .shift    = DMA_CHAN_SHIFT(4),
+      .base     = STM32WB_DMA1_BASE + STM32WB_DMACHAN_OFFSET(4),
+    },
+
+    {
+      .ctrl     = DMA1,
+      .chan     = 5,
+      .irq      = STM32WB_IRQ_DMA1CH6,
+      .shift    = DMA_CHAN_SHIFT(5),
+      .base     = STM32WB_DMA1_BASE + STM32WB_DMACHAN_OFFSET(5),
+    },
+
+    {
+      .ctrl     = DMA1,
+      .chan     = 6,
+      .irq      = STM32WB_IRQ_DMA1CH7,
+      .shift    = DMA_CHAN_SHIFT(6),
+      .base     = STM32WB_DMA1_BASE + STM32WB_DMACHAN_OFFSET(6),
+    },
+#endif
+
+#ifdef CONFIG_STM32WB_DMA2
+  /* DMA2 */
+
+    {
+      .ctrl     = DMA2,
+      .chan     = 0,
+      .irq      = STM32WB_IRQ_DMA2CH1,
+      .shift    = DMA_CHAN_SHIFT(0),
+      .base     = STM32WB_DMA2_BASE + STM32WB_DMACHAN_OFFSET(0),
+    },
+
+    {
+      .ctrl     = DMA2,
+      .chan     = 1,
+      .irq      = STM32WB_IRQ_DMA2CH2,
+      .shift    = DMA_CHAN_SHIFT(1),
+      .base     = STM32WB_DMA2_BASE + STM32WB_DMACHAN_OFFSET(1),
+    },
+
+    {
+      .ctrl     = DMA2,
+      .chan     = 2,
+      .irq      = STM32WB_IRQ_DMA2CH3,
+      .shift    = DMA_CHAN_SHIFT(2),
+      .base     = STM32WB_DMA2_BASE + STM32WB_DMACHAN_OFFSET(2),
+    },
+
+    {
+      .ctrl     = DMA2,
+      .chan     = 3,
+      .irq      = STM32WB_IRQ_DMA2CH4,
+      .shift    = DMA_CHAN_SHIFT(3),
+      .base     = STM32WB_DMA2_BASE + STM32WB_DMACHAN_OFFSET(3),
+    },
+
+    {
+      .ctrl     = DMA2,
+      .chan     = 4,
+      .irq      = STM32WB_IRQ_DMA2CH5,
+      .shift    = DMA_CHAN_SHIFT(4),
+      .base     = STM32WB_DMA2_BASE + STM32WB_DMACHAN_OFFSET(4),
+    },
+
+    {
+      .ctrl     = DMA2,
+      .chan     = 5,
+      .irq      = STM32WB_IRQ_DMA2CH6,
+      .shift    = DMA_CHAN_SHIFT(5),
+      .base     = STM32WB_DMA2_BASE + STM32WB_DMACHAN_OFFSET(5),
+    },
+
+    {
+      .ctrl     = DMA2,
+      .chan     = 6,
+      .irq      = STM32WB_IRQ_DMA2CH7,
+      .shift    = DMA_CHAN_SHIFT(6),
+      .base     = STM32WB_DMA2_BASE + STM32WB_DMACHAN_OFFSET(6),
+    },
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * DMA register access functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: dmachan_getbase
+ *
+ * Description:
+ *  Get base DMA address for dmachan
+ *
+ ****************************************************************************/
+
+static uint32_t dmachan_getbase(DMA_CHANNEL dmachan)
+{
+  uint8_t controller = dmachan->ctrl;
+
+  return g_dma[controller].base;
+}
+
+/****************************************************************************
+ * Name: dmabase_getreg
+ *
+ * Description:
+ *  Get non-channel register from DMA controller
+ *
+ ****************************************************************************/
+
+static uint32_t dmabase_getreg(DMA_CHANNEL dmachan, uint32_t offset)
+{
+  uint32_t dmabase = dmachan_getbase(dmachan);
+
+  return getreg32(dmabase + offset);
+}
+
+/****************************************************************************
+ * Name: dmabase_putreg
+ *
+ * Description:
+ *  Write to non-channel register in DMA controller
+ *
+ ****************************************************************************/
+
+static void dmabase_putreg(DMA_CHANNEL dmachan, uint32_t offset,
+                           uint32_t value)
+{
+  uint32_t dmabase = dmachan_getbase(dmachan);
+
+  putreg32(value, dmabase + offset);
+}
+
+/****************************************************************************
+ * Name: dmachan_getreg
+ *
+ * Description:
+ *  Get channel register.
+ *
+ ****************************************************************************/
+
+static uint32_t dmachan_getreg(DMA_CHANNEL dmachan, uint32_t offset)
+{
+  return getreg32(dmachan->base + offset);
+}
+
+/****************************************************************************
+ * Name: dmachan_putreg
+ *
+ * Description:
+ *  Write to channel register.
+ *
+ ****************************************************************************/
+
+static void dmachan_putreg(DMA_CHANNEL dmachan, uint32_t offset,
+                           uint32_t value)
+{
+  putreg32(value, dmachan->base + offset);
+}
+
+/****************************************************************************
+ * Name: dmamux_getreg
+ *
+ * Description:
+ *  Write to DMAMUX
+ *
+ ****************************************************************************/
+
+static void dmamux_putreg(DMA_MUX dmamux, uint32_t offset, uint32_t value)
+{
+  putreg32(value, dmamux->base + offset);
+}
+
+/****************************************************************************
+ * Name: dmamux_getreg
+ *
+ * Description:
+ *  Get DMAMUX register.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+static uint32_t dmamux_getreg(DMA_MUX dmamux, uint32_t offset)
+{
+  return getreg32(dmamux->base + offset);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_dma_channel_get
+ *
+ * Description:
+ *  Get the g_dmach table entry associated with a given DMA controller
+ *  and channel number.
+ *
+ ****************************************************************************/
+
+static DMA_CHANNEL stm32wb_dma_channel_get(uint8_t channel,
+                                           uint8_t controller)
+{
+  uint8_t first = 0;
+  uint8_t nchan = 0;
+
+  /* Get limits for g_dma array */
+
+  stm32wb_gdma_limits_get(controller, &first, &nchan);
+
+  DEBUGASSERT(channel <= nchan);
+
+  return &g_dmach[first + channel];
+}
+
+/****************************************************************************
+ * Name: stm32wb_gdma_limits_get
+ *
+ * Description:
+ *  Get g_dma array limits for a given DMA controller.
+ *
+ ****************************************************************************/
+
+static void stm32wb_gdma_limits_get(uint8_t controller, uint8_t *first,
+                                    uint8_t *nchan)
+{
+  DEBUGASSERT(first != NULL);
+  DEBUGASSERT(nchan != NULL);
+
+  DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
+
+  *first = g_dma[controller].first;
+  *nchan = g_dma[controller].nchan;
+}
+
+/****************************************************************************
+ * DMA controller functions
+ ****************************************************************************/
+
+#if defined(CONFIG_STM32WB_DMA1) || defined(CONFIG_STM32WB_DMA2)
+
+/****************************************************************************
+ * Name: stm32wb_dma12_disable
+ *
+ * Description:
+ *  Disable DMA channel (DMA1/DMA2)
+ *
+ ****************************************************************************/
+
+static void stm32wb_dma12_disable(DMA_CHANNEL dmachan)
+{
+  uint32_t regval;
+
+  DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2);
+
+  /* Disable all interrupts at the DMA controller */
+
+  regval = dmachan_getreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET);
+  regval &= ~DMA_CCR_ALLINTS;
+
+  /* Disable the DMA channel */
+
+  regval &= ~DMA_CCR_EN;
+  dmachan_putreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET, regval);
+
+  /* Clear pending channel interrupts */
+
+  dmabase_putreg(dmachan, STM32WB_DMA_IFCR_OFFSET,
+                 DMA_ISR_CHAN_MASK(dmachan->chan));
+}
+
+/****************************************************************************
+ * Name: stm32wb_dma12_interrupt
+ *
+ * Description:
+ *  DMA channel interrupt handler
+ *
+ ****************************************************************************/
+
+static int stm32wb_dma12_interrupt(int irq, void *context, void *arg)
+{
+  DMA_CHANNEL dmachan;
+  uint32_t isr;
+  uint8_t channel;
+  uint8_t controller;
+
+  /* Get the channel and the controller that generated the interrupt */
+
+  if (0)
+    {
+    }
+#ifdef CONFIG_STM32WB_DMA1
+  else if (irq >= STM32WB_IRQ_DMA1CH1 && irq <= STM32WB_IRQ_DMA1CH7)
+    {
+      channel = irq - STM32WB_IRQ_DMA1CH1;
+      controller = DMA1;
+    }
+#endif
+#ifdef CONFIG_STM32WB_DMA2
+  else if (irq >= STM32WB_IRQ_DMA2CH1 && irq <= STM32WB_IRQ_DMA2CH5)
+    {
+      channel = irq - STM32WB_IRQ_DMA2CH1;
+      controller = DMA2;
+    }
+  else if (irq >= STM32WB_IRQ_DMA2CH6 && irq <= STM32WB_IRQ_DMA2CH7)
+    {
+      channel = irq - STM32WB_IRQ_DMA2CH6 + (6 - 1);
+      controller = DMA2;
+    }
+#endif
+  else
+    {
+      DEBUGPANIC();
+      return OK;
+    }
+
+  /* Get the channel structure from the stream and controller numbers */
+
+  dmachan = stm32wb_dma_channel_get(channel, controller);
+
+  /* Get the interrupt status (for this channel only) */
+
+  isr = dmabase_getreg(dmachan, STM32WB_DMA_ISR_OFFSET) &
+        DMA_ISR_CHAN_MASK(dmachan->chan);
+
+  /* Invoke the callback */
+
+  if (dmachan->callback)
+    {
+      dmachan->callback(dmachan, isr >> DMA_ISR_CHAN_SHIFT(dmachan->chan),
+                        dmachan->arg);
+    }
+
+  /* Clear the interrupts we are handling */
+
+  dmabase_putreg(dmachan, STM32WB_DMA_IFCR_OFFSET, isr);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_dma12_setup
+ *
+ * Description:
+ *   Configure DMA before using
+ *
+ ****************************************************************************/
+
+static void stm32wb_dma12_setup(DMA_HANDLE handle, uint32_t paddr,
+                                uint32_t maddr, size_t ntransfers,
+                                uint32_t ccr)
+{
+  DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
+  uint32_t regval;
+
+  DEBUGASSERT(handle != NULL);
+  DEBUGASSERT(ntransfers < 65536);
+
+  DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2);
+
+  dmainfo("paddr: %08" PRIx32 " maddr: %08" PRIx32
+          " ntransfers: %zd ccr: %08" PRIx32 "\n",
+          paddr, maddr, ntransfers, ccr);
+
+#ifdef CONFIG_STM32WB_DMACAPABLE
+  DEBUGASSERT(g_dma_ops[dmachan->ctrl].dma_capable(maddr, ntransfers, ccr));
+#endif
+
+  /* Then DMA_CNDTRx register can only be modified if the DMA channel is
+   * disabled.
+   */
+
+  regval  = dmachan_getreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET);
+  regval &= ~(DMA_CCR_EN);
+  dmachan_putreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET, regval);
+
+  /* Set the peripheral register address in the DMA_CPARx register. The data
+   * will be moved from/to this address to/from the memory after the
+   * peripheral event.
+   */
+
+  dmachan_putreg(dmachan, STM32WB_DMACHAN_CPAR_OFFSET, paddr);
+
+  /* Set the memory address in the DMA_CMARx register. The data will be
+   * written to or read from this memory after the peripheral event.
+   */
+
+  dmachan_putreg(dmachan, STM32WB_DMACHAN_CMAR_OFFSET, maddr);
+
+  /* Configure the total number of data to be transferred in the DMA_CNDTRx
+   * register.  After each peripheral event, this value will be decremented.
+   */
+
+  dmachan_putreg(dmachan, STM32WB_DMACHAN_CNDTR_OFFSET, ntransfers);
+
+  /* Configure the channel priority using the PL[1:0] bits in the DMA_CCRx
+   * register.  Configure data transfer direction, circular mode, peripheral
+   * & memory incremented mode, peripheral & memory data size, and interrupt
+   * after half and/or full transfer in the DMA_CCRx register.
+   */
+
+  regval  = dmachan_getreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET);
+  regval &= ~(DMA_CCR_MEM2MEM | DMA_CCR_PL_MASK | DMA_CCR_MSIZE_MASK |
+              DMA_CCR_PSIZE_MASK | DMA_CCR_MINC | DMA_CCR_PINC |
+              DMA_CCR_CIRC | DMA_CCR_DIR);
+  ccr    &=  (DMA_CCR_MEM2MEM | DMA_CCR_PL_MASK | DMA_CCR_MSIZE_MASK |
+              DMA_CCR_PSIZE_MASK | DMA_CCR_MINC | DMA_CCR_PINC |
+              DMA_CCR_CIRC | DMA_CCR_DIR);
+  regval |= ccr;
+  dmachan_putreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET, regval);
+}
+
+/****************************************************************************
+ * Name: stm32wb_dma12_start
+ *
+ * Description:
+ *   Start the standard DMA transfer
+ ****************************************************************************/
+
+static void stm32wb_dma12_start(DMA_HANDLE handle, dma_callback_t callback,
+                                void *arg, bool half)
+{
+  DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
+  uint32_t ccr;
+
+  DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2);
+
+  /* Save the callback info.  This will be invoked when the DMA completes */
+
+  dmachan->callback = callback;
+  dmachan->arg      = arg;
+
+  /* Activate the channel by setting the ENABLE bit in the DMA_CCRx register.
+   * As soon as the channel is enabled, it can serve any DMA request from the
+   * peripheral connected on the channel.
+   */
+
+  ccr  = dmachan_getreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET);
+  ccr |= DMA_CCR_EN;
+
+  /* In normal mode, interrupt at either half or full completion. In circular
+   * mode, always interrupt on buffer wrap, and optionally interrupt at the
+   * halfway point.
+   */
+
+  if ((ccr & DMA_CCR_CIRC) == 0)
+    {
+      /* Once half of the bytes are transferred, the half-transfer flag
+       * (HTIF) is set and an interrupt is generated if the Half-Transfer
+       * Interrupt Enable bit (HTIE) is set. At the end of the transfer,
+       * the Transfer Complete Flag (TCIF) is set and an interrupt is
+       * generated if the Transfer Complete Interrupt Enable bit (TCIE)
+       * is set.
+       */
+
+      ccr |= (half ? (DMA_CCR_HTIE | DMA_CCR_TEIE) :
+                     (DMA_CCR_TCIE | DMA_CCR_TEIE));
+    }
+  else
+    {
+      /* In nonstop mode, when the transfer completes it immediately resets
+       * and starts again.  The transfer-complete interrupt is thus always
+       * enabled, and the half-complete interrupt can be used in circular
+       * mode to determine when the buffer is half-full, or in
+       * double-buffered mode to determine when one of the two buffers
+       * is full.
+       */
+
+      ccr |= (half ? DMA_CCR_HTIE : 0) | DMA_CCR_TCIE | DMA_CCR_TEIE;
+    }
+
+  dmachan_putreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET, ccr);
+}
+
+/****************************************************************************
+ * Name: stm32wb_dma12_residual
+ ****************************************************************************/
+
+static size_t stm32wb_dma12_residual(DMA_HANDLE handle)
+{
+  DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
+
+  DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2);
+
+  return dmachan_getreg(dmachan, STM32WB_DMACHAN_CNDTR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: stm32wb_dma12_sample
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+void stm32wb_dma12_sample(DMA_HANDLE handle, struct stm32wb_dmaregs_s *regs)
+{
+  DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  regs->isr = dmabase_getreg(dmachan, STM32WB_DMA_ISR_OFFSET);
+  regs->ccr = dmachan_getreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET);
+  regs->cndtr = dmachan_getreg(dmachan, STM32WB_DMACHAN_CNDTR_OFFSET);
+  regs->cpar = dmachan_getreg(dmachan, STM32WB_DMACHAN_CPAR_OFFSET);
+  regs->cmar = dmachan_getreg(dmachan, STM32WB_DMACHAN_CMAR_OFFSET);
+
+  stm32wb_dmamux_sample(g_dma[dmachan->ctrl].dmamux,
+                        dmachan->chan + g_dma[dmachan->ctrl].dmamux_offset,
+                        regs);
+
+  leave_critical_section(flags);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_dma12_dump
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+static void stm32wb_dma12_dump(DMA_HANDLE handle,
+                               const struct stm32wb_dmaregs_s *regs,
+                               const char *msg)
+{
+  DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
+
+  DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2);
+
+  uint32_t dmabase = dmachan_getbase(dmachan);
+
+  dmainfo("DMA%d Registers: %s\n",
+          dmachan->ctrl + 1,
+          msg);
+  dmainfo("    ISR[%08x]: %08x\n",
+          dmabase + STM32WB_DMA_ISR_OFFSET,
+          regs->isr);
+  dmainfo("    CCR[%08x]: %08x\n",
+          dmachan->base + STM32WB_DMACHAN_CCR_OFFSET,
+          regs->ccr);
+  dmainfo("  CNDTR[%08x]: %08x\n",
+          dmachan->base + STM32WB_DMACHAN_CNDTR_OFFSET,
+          regs->cndtr);
+  dmainfo("   CPAR[%08x]: %08x\n",
+          dmachan->base + STM32WB_DMACHAN_CPAR_OFFSET,
+          regs->cpar);
+  dmainfo("   CMAR[%08x]: %08x\n",
+          dmachan->base + STM32WB_DMACHAN_CMAR_OFFSET,
+          regs->cmar);
+
+  stm32wb_dmamux_dump(g_dma[dmachan->ctrl].dmamux,
+                      dmachan->chan + g_dma[dmachan->ctrl].dmamux_offset,
+                      regs);
+}
+#endif
+
+#endif /* CONFIG_STM32WB_DMA1 || CONFIG_STM32WB_DMA2 */
+
+/****************************************************************************
+ * Name: stm32wb_dmamux_sample
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+static void stm32wb_dmamux_sample(DMA_MUX dmamux, uint8_t chan,
+                                  struct stm32wb_dmaregs_s *regs)
+{
+  regs->dmamux.ccr = dmamux_getreg(dmamux, STM32WB_DMAMUX_CXCR_OFFSET(chan));
+  regs->dmamux.csr = dmamux_getreg(dmamux, STM32WB_DMAMUX_CSR_OFFSET);
+  regs->dmamux.rg0cr = dmamux_getreg(dmamux, STM32WB_DMAMUX_RG0CR_OFFSET);
+  regs->dmamux.rg1cr = dmamux_getreg(dmamux, STM32WB_DMAMUX_RG1CR_OFFSET);
+  regs->dmamux.rg2cr = dmamux_getreg(dmamux, STM32WB_DMAMUX_RG2CR_OFFSET);
+  regs->dmamux.rg3cr = dmamux_getreg(dmamux, STM32WB_DMAMUX_RG3CR_OFFSET);
+  regs->dmamux.rgsr = dmamux_getreg(dmamux, STM32WB_DMAMUX_RGSR_OFFSET);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_dmamux_dump
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+static void stm32wb_dmamux_dump(DMA_MUX dmamux, uint8_t channel,
+                                const struct stm32wb_dmaregs_s *regs)
+{
+  dmainfo("DMAMUX%d CH=%d\n", dmamux->id, channel);
+  dmainfo("    CCR[%08x]: %08x\n",
+          dmamux->base + STM32WB_DMAMUX_CXCR_OFFSET(channel),
+          regs->dmamux.ccr);
+  dmainfo("    CSR[%08x]: %08x\n",
+          dmamux->base + STM32WB_DMAMUX_CSR_OFFSET, regs->dmamux.csr);
+  dmainfo("  RG0CR[%08x]: %08x\n",
+          dmamux->base + STM32WB_DMAMUX_RG0CR_OFFSET, regs->dmamux.rg0cr);
+  dmainfo("  RG1CR[%08x]: %08x\n",
+          dmamux->base + STM32WB_DMAMUX_RG1CR_OFFSET, regs->dmamux.rg1cr);
+  dmainfo("  RG2CR[%08x]: %08x\n",
+          dmamux->base + STM32WB_DMAMUX_RG2CR_OFFSET, regs->dmamux.rg2cr);
+  dmainfo("  RG3CR[%08x]: %08x\n",
+          dmamux->base + STM32WB_DMAMUX_RG3CR_OFFSET, regs->dmamux.rg3cr);
+  dmainfo("   RGSR[%08x]: %08x\n",
+          dmamux->base + STM32WB_DMAMUX_RGSR_OFFSET, regs->dmamux.rgsr);
+};
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: arm_dma_initialize
+ *
+ * Description:
+ *   Initialize the DMA subsystem (DMA1, DMA2)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void weak_function arm_dma_initialize(void)
+{
+  DMA_CHANNEL dmachan;
+  uint8_t controller;
+  int channel;
+
+  dmainfo("Initialize DMA\n");
+
+  /* Initialize DMA channels */
+
+  for (channel = 0; channel < DMA_NCHANNELS; channel++)
+    {
+      dmachan = &g_dmach[channel];
+
+      /* Initialize flag */
+
+      dmachan->used = false;
+
+      /* Get DMA controller associated with channel */
+
+      controller = dmachan->ctrl;
+
+      DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
+
+      /* Attach standard DMA interrupt vectors */
+
+      irq_attach(dmachan->irq, g_dma_ops[controller].dma_interrupt,
+                 dmachan);
+
+      /* Disable the DMA channel */
+
+      g_dma_ops[controller].dma_disable(dmachan);
+
+      /* Enable the IRQ at the NVIC (still disabled at the DMA controller) */
+
+      up_enable_irq(dmachan->irq);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32wb_dmachannel
+ *
+ * Description:
+ *   Allocate a DMA channel.  This function gives the caller mutually
+ *   exclusive access to the DMA channel specified by the 'dmamap' argument.
+ *   It is common for both DMA controllers (DMA1 and DMA2).
+ *
+ * Input Parameters:
+ *   dmamap - Identifies the stream/channel resource. For the STM32WB, this
+ *     is a bit-encoded value as provided by the DMAMAP_* definitions
+ *     in hardware/stm32wb_dmamux.h
+ *
+ * Returned Value:
+ *   One success, this function returns a non-NULL, void* DMA channel
+ *   handle.  NULL is returned on any failure.  This function can fail only
+ *   if no DMA channel is available.
+ *
+ * Assumptions:
+ *   - The caller does not hold he DMA channel.
+ *   - The caller can wait for the DMA channel to be freed if it is no
+ *     available.
+ *
+ ****************************************************************************/
+
+DMA_HANDLE stm32wb_dmachannel(unsigned int dmamap)
+{
+  DMA_CHANNEL dmachan;
+  uint8_t dmamux_req;
+  irqstate_t flags;
+  uint8_t controller;
+  uint8_t first = 0;
+  uint8_t nchan = 0;
+  int item = -1;
+  int i;
+
+  /* Get DMA controller from encoded DMAMAP value */
+
+  controller = DMAMAP_CONTROLLER(dmamap);
+  DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
+
+  /* Get DMAMUX channel from encoded DMAMAP value */
+
+  dmamux_req = DMAMAP_REQUEST(dmamap);
+
+  /* Get g_dma array limits for given controller */
+
+  stm32wb_gdma_limits_get(controller, &first, &nchan);
+
+  /* Find available channel for given controller */
+
+  flags = enter_critical_section();
+  for (i = first; i < first + nchan; i += 1)
+    {
+      if (g_dmach[i].used == false)

Review Comment:
   ```suggestion
         if (!g_dmach[i].used)
   ```



##########
arch/arm/src/stm32wb/stm32wb_freerun.h:
##########
@@ -0,0 +1,160 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_freerun.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_STM32WB_FREERUN_H
+#define __ARCH_ARM_SRC_STM32WB_STM32WB_FREERUN_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <time.h>
+#include <debug.h>
+
+#include "stm32wb_tim.h"
+
+#ifdef CONFIG_STM32WB_FREERUN
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* The freerun client must allocate an instance of this structure and called
+ * stm32wb_freerun_initialize() before using the freerun facilities.  The
+ * client should not access the contents of this structure directly since
+ * the contents are subject to change.
+ */
+
+struct stm32wb_freerun_s
+{
+  uint8_t chan;                      /* The timer/counter in use */
+  uint8_t width;                     /* Width of timer (16- or 32-bits) */
+  struct stm32wb_tim_dev_s *tch;     /* Pointer returned by
+                                      * stm32wb_tim_init() */
+  uint32_t frequency;
+
+#ifndef CONFIG_CLOCK_TIMEKEEPING
+  uint32_t overflow;                 /* Timer counter overflow */
+#endif
+
+#ifdef CONFIG_CLOCK_TIMEKEEPING
+  uint64_t counter_mask;
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_freerun_initialize
+ *
+ * Description:
+ *   Initialize the freerun timer wrapper
+ *
+ * Input Parameters:
+ *   freerun    Caller allocated instance of the freerun state structure
+ *   chan       Timer counter channel to be used.
+ *   resolution The required resolution of the timer in units of
+ *              microseconds.  NOTE that the range is restricted to the
+ *              range of uint16_t (excluding zero).
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned
+ *   on failure.
+ *
+ ****************************************************************************/
+
+int stm32wb_freerun_initialize(struct stm32wb_freerun_s *freerun, int chan,
+                             uint16_t resolution);

Review Comment:
   ```suggestion
   int stm32wb_freerun_initialize(struct stm32wb_freerun_s *freerun, int chan,
                                  uint16_t resolution);
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_tim.h:
##########
@@ -0,0 +1,1385 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_tim.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_TIM_CR1_OFFSET      0x0000  /* Control register 1 */
+#define STM32WB_TIM_CR2_OFFSET      0x0004  /* Control register 2 */
+#define STM32WB_TIM_SMCR_OFFSET     0x0008  /* Slave mode control register (TIM1, TIM2) */
+#define STM32WB_TIM_DIER_OFFSET     0x000c  /* DMA / Interrupt enable register */
+#define STM32WB_TIM_SR_OFFSET       0x0010  /* Status register */
+#define STM32WB_TIM_EGR_OFFSET      0x0014  /* Event generation register */
+#define STM32WB_TIM_CCMR1_OFFSET    0x0018  /* Capture/compare mode register 1 */
+#define STM32WB_TIM_CCMR2_OFFSET    0x001c  /* Capture/compare mode register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCER_OFFSET     0x0020  /* Capture/compare enable register */
+#define STM32WB_TIM_CNT_OFFSET      0x0024  /* Counter */
+#define STM32WB_TIM_PSC_OFFSET      0x0028  /* Prescaler */
+#define STM32WB_TIM_ARR_OFFSET      0x002c  /* Auto-reload register */
+#define STM32WB_TIM_RCR_OFFSET      0x0030  /* Repetition counter register (TIM1, TIM16/TIM17) */
+#define STM32WB_TIM_CCR1_OFFSET     0x0034  /* Capture/compare register 1 */
+#define STM32WB_TIM_CCR2_OFFSET     0x0038  /* Capture/compare register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR3_OFFSET     0x003c  /* Capture/compare register 3 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR4_OFFSET     0x0040  /* Capture/compare register 4 (TIM1, TIM2) */
+#define STM32WB_TIM_BDTR_OFFSET     0x0044  /* Break and dead-time register (TIM1, TIM16/17) */
+#define STM32WB_TIM_DCR_OFFSET      0x0048  /* DMA control register */
+#define STM32WB_TIM_DMAR_OFFSET     0x004c  /* DMA address for burst mode */
+#define STM32WB_TIM_OR1_OFFSET      0x0050  /* Option register 1 */
+#define STM32WB_TIM_CCMR3_OFFSET    0x0054  /* Capture/compare mode register 3 (TIM1) */
+#define STM32WB_TIM_CCR5_OFFSET     0x0058  /* Capture/compare register 5 (TIM1) */
+#define STM32WB_TIM_CCR6_OFFSET     0x005C  /* Capture/compare register 6 (TIM1) */
+#define STM32WB_TIM_AF1_OFFSET      0x0060  /* Alternate function register 1 */
+#define STM32WB_TIM_AF2_OFFSET      0x0064  /* Alternate function register 2 (TIM1) */
+#define STM32WB_TIM_TISEL_OFFSET    0x0068  /* Input selector register */
+
+/* Register Addresses *******************************************************/
+
+/* Advanced Timer TIM1 */
+
+#define STM32WB_TIM1_CR1            (STM32WB_TIM1_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM1_CR2            (STM32WB_TIM1_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM1_SMCR           (STM32WB_TIM1_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM1_DIER           (STM32WB_TIM1_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM1_SR             (STM32WB_TIM1_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM1_EGR            (STM32WB_TIM1_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM1_CCMR1          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM1_CCMR2          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM1_CCER           (STM32WB_TIM1_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM1_CNT            (STM32WB_TIM1_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM1_PSC            (STM32WB_TIM1_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM1_ARR            (STM32WB_TIM1_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM1_RCR            (STM32WB_TIM1_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM1_CCR1           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM1_CCR2           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM1_CCR3           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM1_CCR4           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM1_BDTR           (STM32WB_TIM1_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM1_DCR            (STM32WB_TIM1_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM1_DMAR           (STM32WB_TIM1_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM1_OR1            (STM32WB_TIM1_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM1_CCMR3          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR3_OFFSET)
+#define STM32WB_TIM1_CCR5           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR5_OFFSET)
+#define STM32WB_TIM1_CCR6           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR6_OFFSET)
+#define STM32WB_TIM1_AF1            (STM32WB_TIM1_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM1_AF2            (STM32WB_TIM1_BASE + STM32WB_TIM_AF2_OFFSET)
+#define STM32WB_TIM1_TISEL          (STM32WB_TIM1_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General 32-bit Timer TIM2 */
+
+#define STM32WB_TIM2_CR1            (STM32WB_TIM2_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM2_CR2            (STM32WB_TIM2_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM2_SMCR           (STM32WB_TIM2_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM2_DIER           (STM32WB_TIM2_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM2_SR             (STM32WB_TIM2_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM2_EGR            (STM32WB_TIM2_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM2_CCMR1          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM2_CCMR2          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM2_CCER           (STM32WB_TIM2_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM2_CNT            (STM32WB_TIM2_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM2_PSC            (STM32WB_TIM2_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM2_ARR            (STM32WB_TIM2_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM2_CCR1           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM2_CCR2           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM2_CCR3           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM2_CCR4           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM2_DCR            (STM32WB_TIM2_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM2_DMAR           (STM32WB_TIM2_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM2_OR1            (STM32WB_TIM2_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM2_AF1            (STM32WB_TIM2_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM2_TISEL          (STM32WB_TIM2_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General Timers TIM16/TIM17 */
+
+#define STM32WB_TIM16_CR1           (STM32WB_TIM16_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM16_CR2           (STM32WB_TIM16_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM16_DIER          (STM32WB_TIM16_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM16_SR            (STM32WB_TIM16_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM16_EGR           (STM32WB_TIM16_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM16_CCMR1         (STM32WB_TIM16_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM16_CCER          (STM32WB_TIM16_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM16_CNT           (STM32WB_TIM16_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM16_PSC           (STM32WB_TIM16_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM16_ARR           (STM32WB_TIM16_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM16_RCR           (STM32WB_TIM16_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM16_CCR1          (STM32WB_TIM16_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM16_BDTR          (STM32WB_TIM16_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM16_DCR           (STM32WB_TIM16_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM16_DMAR          (STM32WB_TIM16_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM16_OR1           (STM32WB_TIM16_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM16_AF1           (STM32WB_TIM16_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM16_TISEL         (STM32WB_TIM16_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+#define STM32WB_TIM17_CR1           (STM32WB_TIM17_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM17_CR2           (STM32WB_TIM17_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM17_DIER          (STM32WB_TIM17_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM17_SR            (STM32WB_TIM17_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM17_EGR           (STM32WB_TIM17_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM17_CCMR1         (STM32WB_TIM17_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM17_CCER          (STM32WB_TIM17_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM17_CNT           (STM32WB_TIM17_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM17_PSC           (STM32WB_TIM17_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM17_ARR           (STM32WB_TIM17_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM17_RCR           (STM32WB_TIM17_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM17_CCR1          (STM32WB_TIM17_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM17_BDTR          (STM32WB_TIM17_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM17_DCR           (STM32WB_TIM17_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM17_DMAR          (STM32WB_TIM17_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM17_OR1           (STM32WB_TIM17_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM17_AF1           (STM32WB_TIM17_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM17_TISEL         (STM32WB_TIM17_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* Register Value Constants *************************************************/
+
+/* Digital Filter options */
+
+#define STM32WB_DF_NOFILT           (0x0) /* 0000: No filter */
+#define STM32WB_DF_FCKINTn2         (0x1) /* 0001: fSAMPLING = fCK_INT, N=2 */
+#define STM32WB_DF_FCKINTn4         (0x2) /* 0010: fSAMPLING = fCK_INT, N=4 */
+#define STM32WB_DF_FCKINTn8         (0x3) /* 0011: fSAMPLING = fCK_INT, N=8 */
+#define STM32WB_DF_FDTSd2n6         (0x4) /* 0100: fSAMPLING = fDTS/2, N=6 */
+#define STM32WB_DF_FDTSd2n8         (0x5) /* 0101: fSAMPLING = fDTS/2, N=8 */
+#define STM32WB_DF_FDTSd4n6         (0x6) /* 0110: fSAMPLING = fDTS/4, N=6 */
+#define STM32WB_DF_FDTSd4n8         (0x7) /* 0111: fSAMPLING = fDTS/4, N=8 */
+#define STM32WB_DF_FDTSd8n6         (0x8) /* 1000: fSAMPLING = fDTS/8, N=6 */
+#define STM32WB_DF_FDTSd8n8         (0x9) /* 1001: fSAMPLING = fDTS/8, N=8 */
+#define STM32WB_DF_FDTSd16n5        (0xa) /* 1010: fSAMPLING = fDTS/16, N=5 */
+#define STM32WB_DF_FDTSd16n6        (0xb) /* 1011: fSAMPLING = fDTS/16, N=6 */
+#define STM32WB_DF_FDTSd16n8        (0xc) /* 1100: fSAMPLING = fDTS/16, N=8 */
+#define STM32WB_DF_FDTSd32n5        (0xd) /* 1101: fSAMPLING = fDTS/32, N=5 */
+#define STM32WB_DF_FDTSd32n6        (0xe) /* 1110: fSAMPLING = fDTS/32, N=6 */
+#define STM32WB_DF_FDTSd32n8        (0xf) /* 1111: fSAMPLING = fDTS/32, N=8 */
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Control register 1 */
+
+#define TIM1_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM1_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM1_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM1_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM1_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM1_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM1_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM1_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM1_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM1_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM1_CR1_CMS_MASK           (0x3 << TIM1_CR1_CMS_SHIFT)
+#  define TIM1_CR1_CMS_EDGE         (0x0 << TIM1_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM1_CR1_CMS_CNTR1        (0x1 << TIM1_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM1_CR1_CMS_CNTR2        (0x2 << TIM1_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM1_CR1_CMS_CNTR3        (0x3 << TIM1_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM1_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM1_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM1_CR1_CKD_MASK           (0x3 << TIM1_CR1_CKD_SHIFT)
+#  define TIM1_CR1_CKD_TCKINT       (0x0 << TIM1_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM1_CR1_CKD_2TCKINT      (0x1 << TIM1_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM1_CR1_CKD_4TCKINT      (0x2 << TIM1_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM1_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM2_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM2_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM2_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM2_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM2_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM2_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM2_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM2_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM2_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM2_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM2_CR1_CMS_MASK           (0x3 << TIM2_CR1_CMS_SHIFT)
+#  define TIM2_CR1_CMS_EDGE         (0x0 << TIM2_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM2_CR1_CMS_CNTR1        (0x1 << TIM2_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM2_CR1_CMS_CNTR2        (0x2 << TIM2_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM2_CR1_CMS_CNTR3        (0x3 << TIM2_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM2_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM2_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM2_CR1_CKD_MASK           (0x3 << TIM2_CR1_CKD_SHIFT)
+#  define TIM2_CR1_CKD_TCKINT       (0x0 << TIM2_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM2_CR1_CKD_2TCKINT      (0x1 << TIM2_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM2_CR1_CKD_4TCKINT      (0x2 << TIM2_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM2_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM16_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM16_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM16_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM16_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM16_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM16_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM16_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM16_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM16_CR1_CKD_MASK          (0x3 << TIM16_CR1_CKD_SHIFT)
+#  define TIM16_CR1_CKD_TCKINT      (0x0 << TIM16_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM16_CR1_CKD_2TCKINT     (0x1 << TIM16_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM16_CR1_CKD_4TCKINT     (0x2 << TIM16_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM16_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM17_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM17_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM17_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM17_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM17_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM17_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM17_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM17_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM17_CR1_CKD_MASK          (0x3 << TIM17_CR1_CKD_SHIFT)
+#  define TIM17_CR1_CKD_TCKINT      (0x0 << TIM17_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM17_CR1_CKD_2TCKINT     (0x1 << TIM17_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM17_CR1_CKD_4TCKINT     (0x2 << TIM17_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM17_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+/* Control register 2 */
+
+#define TIM1_CR2_CCPC               (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM1_CR2_CCUS               (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM1_CR2_CCUS_COMG        (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM1_CR2_CCUS_COMG_TRGI   (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM1_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM1_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM1_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM1_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM1_CR2_MMS_MASK           (0x7 << TIM1_CR2_MMS_SHIFT)
+#  define TIM1_CR2_MMS_RESET        (0x0 << TIM1_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM1_CR2_MMS_ENABLE       (0x1 << TIM1_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM1_CR2_MMS_UPDATE       (0x2 << TIM1_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM1_CR2_MMS_COMPP        (0x3 << TIM1_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS_OC1REF       (0x4 << TIM1_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM1_CR2_MMS_OC2REF       (0x5 << TIM1_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM1_CR2_MMS_OC3REF       (0x6 << TIM1_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM1_CR2_MMS_OC4REF       (0x7 << TIM1_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM1_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM1_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM1_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM1_CR2_OIS1               (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM1_CR2_OIS1N              (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+#define TIM1_CR2_OIS2               (1 << 10) /* Bit 10: Output Idle state 2 (OC2 output) */
+#define TIM1_CR2_OIS2N              (1 << 11) /* Bit 11: Output Idle state 2 (OC2N output) */
+#define TIM1_CR2_OIS3               (1 << 12) /* Bit 12: Output Idle state 3 (OC3 output) */
+#define TIM1_CR2_OIS3N              (1 << 13) /* Bit 13: Output Idle state 3 (OC3N output) */
+#define TIM1_CR2_OIS4               (1 << 14) /* Bit 14: Output Idle state 4 (OC4 output) */
+#define TIM1_CR2_OIS5               (1 << 16) /* Bit 16: Output Idle state 5 (OC5 output) */
+#define TIM1_CR2_OIS6               (1 << 18) /* Bit 18: Output Idle state 6 (OC6 output) */
+#define TIM1_CR2_MMS2_SHIFT         (20)      /* Bits 20-23: Master Mode Selection 2 */
+#define TIM1_CR2_MMS2_MASK          (0xf << TIM1_CR2_MMS2_SHIFT)
+#  define TIM1_CR2_MMS2_RESET       (0x0 << TIM1_CR2_MMS2_SHIFT) /* 0000: Reset - TIMx_EGR UG bit is TRG9 */
+#  define TIM1_CR2_MMS2_ENABLE      (0x1 << TIM1_CR2_MMS2_SHIFT) /* 0001: Enable - CNT_EN is TRGO2 */
+#  define TIM1_CR2_MMS2_UPDATE      (0x2 << TIM1_CR2_MMS2_SHIFT) /* 0010: Update event is TRGO2 */
+#  define TIM1_CR2_MMS2_COMPP       (0x3 << TIM1_CR2_MMS2_SHIFT) /* 0011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS2_OC1REF      (0x4 << TIM1_CR2_MMS2_SHIFT) /* 0100: Compare OC1REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC2REF      (0x5 << TIM1_CR2_MMS2_SHIFT) /* 0101: Compare OC2REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC3REF      (0x6 << TIM1_CR2_MMS2_SHIFT) /* 0110: Compare OC3REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC4REF      (0x7 << TIM1_CR2_MMS2_SHIFT) /* 0111: Compare OC4REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC5REF      (0x8 << TIM1_CR2_MMS2_SHIFT) /* 1000: Compare OC5REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC6REF      (0x9 << TIM1_CR2_MMS2_SHIFT) /* 1001: Compare OC6REF is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4      (0xa << TIM1_CR2_MMS2_SHIFT) /* 1010: Compare pulse - OC4REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC6      (0xb << TIM1_CR2_MMS2_SHIFT) /* 1011: Compare pulse - OC6REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4R6R   (0xc << TIM1_CR2_MMS2_SHIFT) /* 1100: Compare pulse - OC4REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC4R6F   (0xd << TIM1_CR2_MMS2_SHIFT) /* 1101: Compare pulse - OC4REF rising/OC6REF falling */
+#  define TIM1_CR2_MMS2_CMPOC5R6R   (0xe << TIM1_CR2_MMS2_SHIFT) /* 1110: Compare pulse - OC5REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC5R6F   (0xf << TIM1_CR2_MMS2_SHIFT) /* 1111: Compare pulse - OC5REF rising/OC6REF falling */
+
+#define TIM2_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM2_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM2_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM2_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM2_CR2_MMS_MASK           (0x7 << TIM2_CR2_MMS_SHIFT)
+#  define TIM2_CR2_MMS_RESET        (0x0 << TIM2_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM2_CR2_MMS_ENABLE       (0x1 << TIM2_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM2_CR2_MMS_UPDATE       (0x2 << TIM2_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM2_CR2_MMS_COMPP        (0x3 << TIM2_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM2_CR2_MMS_OC1REF       (0x4 << TIM2_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM2_CR2_MMS_OC2REF       (0x5 << TIM2_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM2_CR2_MMS_OC3REF       (0x6 << TIM2_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM2_CR2_MMS_OC4REF       (0x7 << TIM2_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM2_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM2_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM2_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM16_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM16_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM16_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM16_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM16_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM16_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM16_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM16_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM16_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+#define TIM17_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM17_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM17_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM17_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM17_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM17_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM17_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM17_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM17_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+/* Slave mode control register */
+
+#define TIM1_SMCR_SMS_LO_SHIFT      (0)       /* Bits 0-2: Slave mode selection, bits [2:0] */
+#define TIM1_SMCR_SMS_HI_SHIFT      (16)      /* Bit 16: Slave mode selection, bits [3] */
+#define TIM1_SMCR_SMS_BITS(h,l)     ((h << TIM1_SMCR_SMS_HI_SHIFT) | (l << TIM1_SMCR_SMS_LO_SHIFT))
+#define TIM1_SMCR_SMS_MASK          TIM1_SMCR_SMS_BITS(0x1, 0x7)
+#  define TIM1_SMCR_DISAB           TIM1_SMCR_SMS_BITS(0x0, 0x0) /* 0,000: Slave mode disabled */
+#  define TIM1_SMCR_ENCMD1          TIM1_SMCR_SMS_BITS(0x0, 0x1) /* 0,001: Encoder mode 1 */
+#  define TIM1_SMCR_ENCMD2          TIM1_SMCR_SMS_BITS(0x0, 0x2) /* 0,010: Encoder mode 2 */
+#  define TIM1_SMCR_ENCMD3          TIM1_SMCR_SMS_BITS(0x0, 0x3) /* 0,011: Encoder mode 3 */
+#  define TIM1_SMCR_RESET           TIM1_SMCR_SMS_BITS(0x0, 0x4) /* 0,100: Reset Mode */
+#  define TIM1_SMCR_GATED           TIM1_SMCR_SMS_BITS(0x0, 0x5) /* 0,101: Gated Mode */
+#  define TIM1_SMCR_TRIGGER         TIM1_SMCR_SMS_BITS(0x0, 0x6) /* 0,110: Trigger Mode */
+#  define TIM1_SMCR_EXTCLK1         TIM1_SMCR_SMS_BITS(0x0, 0x7) /* 0,111: External Clock Mode 1 */
+#  define TIM1_SMCR_SMS_COMBINED    TIM1_SMCR_SMS_BITS(0x1, 0x0) /* 1,000: Combined Reset and Trigger mode */
+
+#define TIM1_SMCR_OCCS              (1 << 3)  /* Bit 3: OCREF clear selection */
+#  define TIM1_SMCR_OCCS_CLR        (0 << 3)  /* 0: OCREF clear triggered by CLR input */
+#  define TIM1_SMCR_OCCS_ETRF       (1 << 3)  /* 1: OCREF clear triggered by ETRF */
+
+#define TIM1_SMCR_TS_LO_SHIFT       (4)       /* Bits 4-6: Trigger selection, bits [2:0] */
+#define TIM1_SMCR_TS_HI_SHIFT       (20)      /* Bits 20-21: Trigger selection, bits [1:0] */
+#define TIM1_SMCR_TS_BITS(h,l)      ((h << TIM1_SMCR_TS_HI_SHIFT) | (l << TIM1_SMCR_TS_LO_SHIFT))
+#define TIM1_SMCR_TS_MASK           TIM1_SMCR_TS_BITS(0x3, 0x7)
+#  define TIM1_SMCR_ITR0            TIM1_SMCR_TS_BITS(0x0, 0x0) /* 00,000: Internal trigger 0 (ITR0) */
+#  define TIM1_SMCR_ITR1            TIM1_SMCR_TS_BITS(0x0, 0x1) /* 00,001: Internal trigger 1 (ITR1) */
+#  define TIM1_SMCR_ITR2            TIM1_SMCR_TS_BITS(0x0, 0x2) /* 00,010: Internal trigger 2 (ITR2) */
+#  define TIM1_SMCR_ITR3            TIM1_SMCR_TS_BITS(0x0, 0x3) /* 00,011: Internal trigger 3 (ITR3) */
+#  define TIM1_SMCR_T1FED           TIM1_SMCR_TS_BITS(0x0, 0x4) /* 00,100: TI1 Edge detector (TI1F_ED) */
+#  define TIM1_SMCR_TI1FP1          TIM1_SMCR_TS_BITS(0x0, 0x5) /* 00,101: Filtered timer input 1 (TI1FP1) */
+#  define TIM1_SMCR_T12FP2          TIM1_SMCR_TS_BITS(0x0, 0x6) /* 00,110: Filtered timer input 2 (TI2FP2) */
+#  define TIM1_SMCR_ETRF            TIM1_SMCR_TS_BITS(0x0, 0x7) /* 00,111: External trigger input (ETRF) */
+
+#define TIM1_SMCR_MSM               (1 << 7)  /* Bit 7: Master/slave mode */
+#define TIM1_SMCR_ETF_SHIFT         (8)       /* Bits 8-11: External trigger filter */
+#define TIM1_SMCR_ETF_MASK          (0xf << TIM1_SMCR_ETF_SHIFT)
+#  define TIM1_SMCR_ETF(f)          ((f) << TIM1_SMCR_ETF_SHIFT) /* f = STM32WB_DF_[digital filter option] */
+
+#define TIM1_SMCR_ETPS_SHIFT        (12)      /* Bits 12-13: External trigger prescaler */
+#define TIM1_SMCR_ETPS_MASK         (0x3 << TIM1_SMCR_ETPS_SHIFT)
+#  define TIM1_SMCR_PSCOFF          (0x0 << TIM1_SMCR_ETPS_SHIFT) /* 00: Prescaler OFF */
+#  define TIM1_SMCR_ETRPd2          (0x1 << TIM1_SMCR_ETPS_SHIFT) /* 01: ETRP frequency divided by 2 */
+#  define TIM1_SMCR_ETRPd4          (0x2 << TIM1_SMCR_ETPS_SHIFT) /* 10: ETRP frequency divided by 4 */
+#  define TIM1_SMCR_ETRPd8          (0x3 << TIM1_SMCR_ETPS_SHIFT) /* 11: ETRP frequency divided by 8 */
+
+#define TIM1_SMCR_ECE               (1 << 14) /* Bit 14: External clock enable */
+#define TIM1_SMCR_ETP               (1 << 15) /* Bit 15: External trigger polarity */
+#  define TIM1_SMCR_ETP_HIGH        (0 << 15) /* 0: ETR is non-inverted, active at high level or rising edge */
+#  define TIM1_SMCR_ETP_LOW         (1 << 15) /* 1: ETR is inverted, active at low level or falling edge */
+
+#define TIM2_SMCR_SMS_LO_SHIFT      (0)       /* Bits 0-2: Slave mode selection, bits [2:0] */
+#define TIM2_SMCR_SMS_HI_SHIFT      (16)      /* Bit 16: Slave mode selection, bits [3] */
+#define TIM2_SMCR_SMS_BITS(h,l)     ((h << TIM2_SMCR_SMS_HI_SHIFT) | (l << TIM2_SMCR_SMS_LO_SHIFT))
+#define TIM2_SMCR_SMS_MASK          TIM2_SMCR_SMS_BITS(0x1, 0x7)
+#  define TIM2_SMCR_DISAB           TIM2_SMCR_SMS_BITS(0x0, 0x0) /* 0,000: Slave mode disabled */
+#  define TIM2_SMCR_ENCMD1          TIM2_SMCR_SMS_BITS(0x0, 0x1) /* 0,001: Encoder mode 1 */
+#  define TIM2_SMCR_ENCMD2          TIM2_SMCR_SMS_BITS(0x0, 0x2) /* 0,010: Encoder mode 2 */
+#  define TIM2_SMCR_ENCMD3          TIM2_SMCR_SMS_BITS(0x0, 0x3) /* 0,011: Encoder mode 3 */
+#  define TIM2_SMCR_RESET           TIM2_SMCR_SMS_BITS(0x0, 0x4) /* 0,100: Reset Mode */
+#  define TIM2_SMCR_GATED           TIM2_SMCR_SMS_BITS(0x0, 0x5) /* 0,101: Gated Mode */
+#  define TIM2_SMCR_TRIGGER         TIM2_SMCR_SMS_BITS(0x0, 0x6) /* 0,110: Trigger Mode */
+#  define TIM2_SMCR_EXTCLK1         TIM2_SMCR_SMS_BITS(0x0, 0x7) /* 0,111: External Clock Mode 1 */
+#  define TIM2_SMCR_SMS_COMBINED    TIM2_SMCR_SMS_BITS(0x1, 0x0) /* 1,000: Combined Reset and Trigger mode */
+
+#define TIM2_SMCR_OCCS              (1 << 3)  /* Bit 3: OCREF clear selection */
+#  define TIM2_SMCR_OCCS_CLR        (0 << 3)  /* 0: OCREF clear triggered by CLR input */
+#  define TIM2_SMCR_OCCS_ETRF       (1 << 3)  /* 1: OCREF clear triggered by ETRF */
+
+#define TIM2_SMCR_TS_LO_SHIFT       (4)       /* Bits 4-6: Trigger selection, bits [2:0] */
+#define TIM2_SMCR_TS_HI_SHIFT       (20)      /* Bits 20-21: Trigger selection, bits [1:0] */
+#define TIM2_SMCR_TS_BITS(h,l)      ((h << TIM2_SMCR_TS_HI_SHIFT) | (l << TIM2_SMCR_TS_LO_SHIFT))
+#define TIM2_SMCR_TS_MASK           TIM2_SMCR_TS_BITS(0x3, 0x7)
+#  define TIM2_SMCR_ITR0            TIM2_SMCR_TS_BITS(0x0, 0x0) /* 00,000: Internal trigger 0 (ITR0) */
+#  define TIM2_SMCR_ITR1            TIM2_SMCR_TS_BITS(0x0, 0x1) /* 00,001: Internal trigger 1 (ITR1) */
+#  define TIM2_SMCR_ITR2            TIM2_SMCR_TS_BITS(0x0, 0x2) /* 00,010: Internal trigger 2 (ITR2) */
+#  define TIM2_SMCR_ITR3            TIM2_SMCR_TS_BITS(0x0, 0x3) /* 00,011: Internal trigger 3 (ITR3) */
+#  define TIM2_SMCR_T1FED           TIM2_SMCR_TS_BITS(0x0, 0x4) /* 00,100: TI1 Edge detector (TI1F_ED) */
+#  define TIM2_SMCR_TI1FP1          TIM2_SMCR_TS_BITS(0x0, 0x5) /* 00,101: Filtered timer Input 1 (TI1FP1) */
+#  define TIM2_SMCR_T12FP2          TIM2_SMCR_TS_BITS(0x0, 0x6) /* 00,110: Filtered timer Input 2 (TI2FP2) */
+#  define TIM2_SMCR_ETRF            TIM2_SMCR_TS_BITS(0x0, 0x7) /* 00,111: External trigger input (ETRF) */
+#  define TIM2_SMCR_ITR4            TIM2_SMCR_TS_BITS(0x1, 0x0) /* 01,000: Internal trigger 4 (ITR4) */
+#  define TIM2_SMCR_ITR5            TIM2_SMCR_TS_BITS(0x1, 0x1) /* 01,001: Internal trigger 5 (ITR5) */
+#  define TIM2_SMCR_ITR6            TIM2_SMCR_TS_BITS(0x1, 0x2) /* 01,010: Internal trigger 6 (ITR6) */
+#  define TIM2_SMCR_ITR7            TIM2_SMCR_TS_BITS(0x1, 0x3) /* 01,011: Internal trigger 7 (ITR7) */
+#  define TIM2_SMCR_ITR8            TIM2_SMCR_TS_BITS(0x1, 0x4) /* 01,100: Internal trigger 8 (ITR8) */
+
+#define TIM2_SMCR_MSM               (1 << 7)  /* Bit 7: Master/slave mode */
+#define TIM2_SMCR_ETF_SHIFT         (8)       /* Bits 8-11: External trigger filter */
+#define TIM2_SMCR_ETF_MASK          (0xf << TIM2_SMCR_ETF_SHIFT)
+#  define TIM2_SMCR_ETF(f)          ((f) << TIM2_SMCR_ETF_SHIFT) /* f = STM32WB_DF_[digital filter option] */
+
+#define TIM2_SMCR_ETPS_SHIFT        (12)      /* Bits 12-13: External trigger prescaler */
+#define TIM2_SMCR_ETPS_MASK         (0x3 << TIM2_SMCR_ETPS_SHIFT)
+#  define TIM2_SMCR_PSCOFF          (0x0 << TIM2_SMCR_ETPS_SHIFT) /* 00: Prescaler OFF */
+#  define TIM2_SMCR_ETRPd2          (0x1 << TIM2_SMCR_ETPS_SHIFT) /* 01: ETRP frequency divided by 2 */
+#  define TIM2_SMCR_ETRPd4          (0x2 << TIM2_SMCR_ETPS_SHIFT) /* 10: ETRP frequency divided by 4 */
+#  define TIM2_SMCR_ETRPd8          (0x3 << TIM2_SMCR_ETPS_SHIFT) /* 11: ETRP frequency divided by 8 */
+
+#define TIM2_SMCR_ECE               (1 << 14) /* Bit 14: External clock enable */
+#define TIM2_SMCR_ETP               (1 << 15) /* Bit 15: External trigger polarity */
+#  define TIM2_SMCR_ETP_HIGH        (0 << 15) /* 0: ETR is non-inverted, active at high level or rising edge */
+#  define TIM2_SMCR_ETP_LOW         (1 << 15) /* 1: ETR is inverted, active at low level or falling edge */
+
+/* Timer DMA / Interrupt enable register */
+
+#define TIM1_DIER_UIE               (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM1_DIER_CC1IE             (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM1_DIER_CC2IE             (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt enable */
+#define TIM1_DIER_CC3IE             (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt enable */
+#define TIM1_DIER_CC4IE             (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt enable */
+#define TIM1_DIER_COMIE             (1 << 5)  /* Bit 5: COM interrupt enable */
+#define TIM1_DIER_TIE               (1 << 6)  /* Bit 6: Trigger interrupt enable */
+#define TIM1_DIER_BIE               (1 << 7)  /* Bit 7: Break interrupt enable */
+#define TIM1_DIER_UDE               (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM1_DIER_CC1DE             (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+#define TIM1_DIER_CC2DE             (1 << 10) /* Bit 10: Capture/Compare 2 DMA request enable */
+#define TIM1_DIER_CC3DE             (1 << 11) /* Bit 11: Capture/Compare 3 DMA request enable */
+#define TIM1_DIER_CC4DE             (1 << 12) /* Bit 12: Capture/Compare 4 DMA request enable */
+#define TIM1_DIER_COMDE             (1 << 13) /* Bit 13: COM DMA request enable */
+#define TIM1_DIER_TDE               (1 << 14) /* Bit 14: Trigger DMA request enable */
+
+#define TIM2_DIER_UIE               (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM2_DIER_CC1IE             (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM2_DIER_CC2IE             (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt enable */
+#define TIM2_DIER_CC3IE             (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt enable */
+#define TIM2_DIER_CC4IE             (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt enable */
+#define TIM2_DIER_TIE               (1 << 6)  /* Bit 6: Trigger interrupt enable */
+#define TIM2_DIER_UDE               (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM2_DIER_CC1DE             (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+#define TIM2_DIER_CC2DE             (1 << 10) /* Bit 10: Capture/Compare 2 DMA request enable */
+#define TIM2_DIER_CC3DE             (1 << 11) /* Bit 11: Capture/Compare 3 DMA request enable */
+#define TIM2_DIER_CC4DE             (1 << 12) /* Bit 12: Capture/Compare 4 DMA request enable */
+
+#define TIM16_DIER_UIE              (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM16_DIER_CC1IE            (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM16_DIER_COMIE            (1 << 5)  /* Bit 5: COM interrupt enable */
+#define TIM16_DIER_BIE              (1 << 7)  /* Bit 7: Break interrupt enable */
+#define TIM16_DIER_UDE              (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM16_DIER_CC1DE            (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+
+#define TIM17_DIER_UIE              (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM17_DIER_CC1IE            (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM17_DIER_COMIE            (1 << 5)  /* Bit 5: COM interrupt enable */
+#define TIM17_DIER_BIE              (1 << 7)  /* Bit 7: Break interrupt enable */
+#define TIM17_DIER_UDE              (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM17_DIER_CC1DE            (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+
+/* Status register */
+
+#define TIM1_SR_UIF                 (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM1_SR_CC1IF               (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM1_SR_CC2IF               (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt Flag */
+#define TIM1_SR_CC3IF               (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt Flag */
+#define TIM1_SR_CC4IF               (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt Flag */
+#define TIM1_SR_COMIF               (1 << 5)  /* Bit 5: COM interrupt Flag */
+#define TIM1_SR_TIF                 (1 << 6)  /* Bit 6: Trigger interrupt Flag */
+#define TIM1_SR_BIF                 (1 << 7)  /* Bit 7: Break interrupt Flag */
+#define TIM1_SR_B2IF                (1 << 8)  /* Bit 8: Break 2 interrupt Flag */
+#define TIM1_SR_CC1OF               (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+#define TIM1_SR_CC2OF               (1 << 10) /* Bit 10: Capture/Compare 2 Overcapture Flag */
+#define TIM1_SR_CC3OF               (1 << 11) /* Bit 11: Capture/Compare 3 Overcapture Flag */
+#define TIM1_SR_CC4OF               (1 << 12) /* Bit 12: Capture/Compare 4 Overcapture Flag */
+#define TIM1_SR_SBIF                (1 << 13) /* Bit 13: System break interrupt Flag */
+#define TIM1_SR_CC5IF               (1 << 16) /* Bit 16: Compare 5 interrupt flag */
+#define TIM1_SR_CC6IF               (1 << 17) /* Bit 17: Compare 6 interrupt flag */
+
+#define TIM2_SR_UIF                 (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM2_SR_CC1IF               (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM2_SR_CC2IF               (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt Flag */
+#define TIM2_SR_CC3IF               (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt Flag */
+#define TIM2_SR_CC4IF               (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt Flag */
+#define TIM2_SR_TIF                 (1 << 6)  /* Bit 6: Trigger interrupt Flag */
+#define TIM2_SR_CC1OF               (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+#define TIM2_SR_CC2OF               (1 << 10) /* Bit 10: Capture/Compare 2 Overcapture Flag */
+#define TIM2_SR_CC3OF               (1 << 11) /* Bit 11: Capture/Compare 3 Overcapture Flag */
+#define TIM2_SR_CC4OF               (1 << 12) /* Bit 12: Capture/Compare 4 Overcapture Flag */
+
+#define TIM16_SR_UIF                (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM16_SR_CC1IF              (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM16_SR_COMIF              (1 << 5)  /* Bit 5: COM interrupt Flag */
+#define TIM16_SR_BIF                (1 << 7)  /* Bit 7: Break interrupt Flag */
+#define TIM16_SR_CC1OF              (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+
+#define TIM17_SR_UIF                (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM17_SR_CC1IF              (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM17_SR_COMIF              (1 << 5)  /* Bit 5: COM interrupt Flag */
+#define TIM17_SR_BIF                (1 << 7)  /* Bit 7: Break interrupt Flag */
+#define TIM17_SR_CC1OF              (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+
+/* Event generation register */
+
+#define TIM1_EGR_UG                 (1 << 0)  /* Bit 0: Update Generation */
+#define TIM1_EGR_CC1G               (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM1_EGR_CC2G               (1 << 2)  /* Bit 2: Capture/Compare 2 Generation */
+#define TIM1_EGR_CC3G               (1 << 3)  /* Bit 3: Capture/Compare 3 Generation */
+#define TIM1_EGR_CC4G               (1 << 4)  /* Bit 4: Capture/Compare 4 Generation */
+#define TIM1_EGR_COMG               (1 << 5)  /* Bit 5: Capture/Compare Control Update Generation */
+#define TIM1_EGR_TG                 (1 << 6)  /* Bit 6: Trigger Generation */
+#define TIM1_EGR_BG                 (1 << 7)  /* Bit 7: Break Generation */
+#define TIM1_EGR_B2G                (1 << 8)  /* Bit 8: Break 2 Generation */
+
+#define TIM2_EGR_UG                 (1 << 0)  /* Bit 0: Update Generation */
+#define TIM2_EGR_CC1G               (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM2_EGR_CC2G               (1 << 2)  /* Bit 2: Capture/Compare 2 Generation */
+#define TIM2_EGR_CC3G               (1 << 3)  /* Bit 3: Capture/Compare 3 Generation */
+#define TIM2_EGR_CC4G               (1 << 4)  /* Bit 4: Capture/Compare 4 Generation */
+#define TIM2_EGR_TG                 (1 << 6)  /* Bit 6: Trigger Generation */
+
+#define TIM16_EGR_UG                (1 << 0)  /* Bit 0: Update Generation */
+#define TIM16_EGR_CC1G              (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM16_EGR_COMG              (1 << 5)  /* Bit 5: Capture/Compare Control Update Generation */
+#define TIM16_EGR_BG                (1 << 7)  /* Bit 7: Break Generation */
+
+#define TIM17_EGR_UG                (1 << 0)  /* Bit 0: Update Generation */
+#define TIM17_EGR_CC1G              (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM17_EGR_COMG              (1 << 5)  /* Bit 5: Capture/Compare Control Update Generation */
+#define TIM17_EGR_BG                (1 << 7)  /* Bit 7: Break Generation */
+
+/* Capture/compare mode registers - capture/compare mode selection */
+
+#define TIM1_CCMR1_CC1S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM1_CCMR1_CC1S_MASK        (0x3 << TIM1_CCMR1_CC1S_SHIFT)
+#  define TIM1_CCMR1_CC1S_CCOUT     (0x0 << TIM1_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM1_CCMR1_CC1S_CCIN1     (0x1 << TIM1_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+#  define TIM1_CCMR1_CC1S_CCIN2     (0x2 << TIM1_CCMR1_CC1S_SHIFT) /* 10: CC1 channel input, IC1 is TI2 */
+#  define TIM1_CCMR1_CC1S_CCINTRC   (0x3 << TIM1_CCMR1_CC1S_SHIFT) /* 11: CC1 channel input, IC1 is TRC */
+
+#define TIM1_CCMR1_CC2S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 2 Selection */
+#define TIM1_CCMR1_CC2S_MASK        (0x3 << TIM1_CCMR1_CC2S_SHIFT)
+#  define TIM1_CCMR1_CC2S_CCOUT     (0x0 << TIM1_CCMR1_CC2S_SHIFT) /* 00: CC2 channel output */
+#  define TIM1_CCMR1_CC2S_CCIN1     (0x1 << TIM1_CCMR1_CC2S_SHIFT) /* 01: CC2 channel input, IC2 is TI2 */
+#  define TIM1_CCMR1_CC2S_CCIN2     (0x2 << TIM1_CCMR1_CC2S_SHIFT) /* 10: CC2 channel input, IC2 is TI1 */
+#  define TIM1_CCMR1_CC2S_CCINTRC   (0x3 << TIM1_CCMR1_CC2S_SHIFT) /* 11: CC2 channel input, IC2 is TRC */
+
+#define TIM1_CCMR2_CC3S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 3 Selection */
+#define TIM1_CCMR2_CC3S_MASK        (0x3 << TIM1_CCMR2_CC3S_SHIFT)
+#  define TIM1_CCMR2_CC3S_CCOUT     (0x0 << TIM1_CCMR2_CC3S_SHIFT) /* 00: CC3 channel output */
+#  define TIM1_CCMR2_CC3S_CCIN1     (0x1 << TIM1_CCMR2_CC3S_SHIFT) /* 01: CC3 channel input, IC3 is TI3 */
+#  define TIM1_CCMR2_CC3S_CCIN2     (0x2 << TIM1_CCMR2_CC3S_SHIFT) /* 10: CC3 channel input, IC3 is TI4 */
+#  define TIM1_CCMR2_CC3S_CCINTRC   (0x3 << TIM1_CCMR2_CC3S_SHIFT) /* 11: CC3 channel input, IC3 is TRC */
+
+#define TIM1_CCMR2_CC4S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 4 Selection */
+#define TIM1_CCMR2_CC4S_MASK        (0x3 << TIM1_CCMR2_CC4S_SHIFT)
+#  define TIM1_CCMR2_CC4S_CCOUT     (0x0 << TIM1_CCMR2_CC4S_SHIFT) /* 00: CC4 channel output */
+#  define TIM1_CCMR2_CC4S_CCIN1     (0x1 << TIM1_CCMR2_CC4S_SHIFT) /* 01: CC4 channel input, IC4 is TI4 */
+#  define TIM1_CCMR2_CC4S_CCIN2     (0x2 << TIM1_CCMR2_CC4S_SHIFT) /* 10: CC4 channel input, IC4 is TI3 */
+#  define TIM1_CCMR2_CC4S_CCINTRC   (0x3 << TIM1_CCMR2_CC4S_SHIFT) /* 11: CC4 channel input, IC4 is TRC */
+
+#define TIM2_CCMR1_CC1S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM2_CCMR1_CC1S_MASK        (0x3 << TIM2_CCMR1_CC1S_SHIFT)
+#  define TIM2_CCMR1_CC1S_CCOUT     (0x0 << TIM2_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM2_CCMR1_CC1S_CCIN1     (0x1 << TIM2_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+#  define TIM2_CCMR1_CC1S_CCIN2     (0x2 << TIM2_CCMR1_CC1S_SHIFT) /* 10: CC1 channel input, IC1 is TI2 */
+#  define TIM2_CCMR1_CC1S_CCINTRC   (0x3 << TIM2_CCMR1_CC1S_SHIFT) /* 11: CC1 channel input, IC1 is TRC */
+
+#define TIM2_CCMR1_CC2S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 2 Selection */
+#define TIM2_CCMR1_CC2S_MASK        (0x3 << TIM2_CCMR1_CC2S_SHIFT)
+#  define TIM2_CCMR1_CC2S_CCOUT     (0x0 << TIM2_CCMR1_CC2S_SHIFT) /* 00: CC2 channel output */
+#  define TIM2_CCMR1_CC2S_CCIN1     (0x1 << TIM2_CCMR1_CC2S_SHIFT) /* 01: CC2 channel input, IC2 is TI2 */
+#  define TIM2_CCMR1_CC2S_CCIN2     (0x2 << TIM2_CCMR1_CC2S_SHIFT) /* 10: CC2 channel input, IC2 is TI1 */
+#  define TIM2_CCMR1_CC2S_CCINTRC   (0x3 << TIM2_CCMR1_CC2S_SHIFT) /* 11: CC2 channel input, IC2 is TRC */
+
+#define TIM2_CCMR2_CC3S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 3 Selection */
+#define TIM2_CCMR2_CC3S_MASK        (0x3 << TIM2_CCMR2_CC3S_SHIFT)
+#  define TIM2_CCMR2_CC3S_CCOUT     (0x0 << TIM2_CCMR2_CC3S_SHIFT) /* 00: CC3 channel output */
+#  define TIM2_CCMR2_CC3S_CCIN1     (0x1 << TIM2_CCMR2_CC3S_SHIFT) /* 01: CC3 channel input, IC3 is TI3 */
+#  define TIM2_CCMR2_CC3S_CCIN2     (0x2 << TIM2_CCMR2_CC3S_SHIFT) /* 10: CC3 channel input, IC3 is TI4 */
+#  define TIM2_CCMR2_CC3S_CCINTRC   (0x3 << TIM2_CCMR2_CC3S_SHIFT) /* 11: CC3 channel input, IC3 is TRC */
+
+#define TIM2_CCMR2_CC4S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 4 Selection */
+#define TIM2_CCMR2_CC4S_MASK        (0x3 << TIM2_CCMR2_CC4S_SHIFT)
+#  define TIM2_CCMR2_CC4S_CCOUT     (0x0 << TIM2_CCMR2_CC4S_SHIFT) /* 00: CC4 channel output */
+#  define TIM2_CCMR2_CC4S_CCIN1     (0x1 << TIM2_CCMR2_CC4S_SHIFT) /* 01: CC4 channel input, IC4 is TI4 */
+#  define TIM2_CCMR2_CC4S_CCIN2     (0x2 << TIM2_CCMR2_CC4S_SHIFT) /* 10: CC4 channel input, IC4 is TI3 */
+#  define TIM2_CCMR2_CC4S_CCINTRC   (0x3 << TIM2_CCMR2_CC4S_SHIFT) /* 11: CC4 channel input, IC4 is TRC */
+
+#define TIM16_CCMR1_CC1S_SHIFT      (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM16_CCMR1_CC1S_MASK       (0x3 << TIM16_CCMR1_CC1S_SHIFT)
+#  define TIM16_CCMR1_CC1S_CCOUT    (0x0 << TIM16_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM16_CCMR1_CC1S_CCIN1    (0x1 << TIM16_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+
+#define TIM17_CCMR1_CC1S_SHIFT      (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM17_CCMR1_CC1S_MASK       (0x3 << TIM17_CCMR1_CC1S_SHIFT)
+#  define TIM17_CCMR1_CC1S_CCOUT    (0x0 << TIM17_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM17_CCMR1_CC1S_CCIN1    (0x1 << TIM17_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+
+/* Capture/compare mode registers - Output compare mode */
+
+#define TIM1_CCMR1_OC1FE            (1 << 2)  /* Bit 2: Output Compare 1 Fast enable */
+#define TIM1_CCMR1_OC1PE            (1 << 3)  /* Bit 3: Output Compare 1 Preload enable */
+#define TIM1_CCMR1_OC1M_LO_SHIFT    (4)       /* Bits 4-6: Output Compare 1 Mode, bits [2:0] */
+#define TIM1_CCMR1_OC1M_HI_SHIFT    (16)      /* Bit 16: Output Compare 1 Mode, bits [3] */
+#define TIM1_CCMR1_OC1M_BITS(h,l)   ((h << TIM1_CCMR1_OC1M_HI_SHIFT) | (l << TIM1_CCMR1_OC1M_LO_SHIFT))
+#define TIM1_CCMR1_OC1M_MASK        TIM1_CCMR1_OC1M_BITS(0x1, 0x7)
+#  define TIM1_CCMR1_OC1M_FRZN      TIM1_CCMR1_OC1M_BITS(0x0, 0x0)  /* 0,000: Frozen */
+#  define TIM1_CCMR1_OC1M_CHACT     TIM1_CCMR1_OC1M_BITS(0x0, 0x1)  /* 0,001: Channel 1 active on match */
+#  define TIM1_CCMR1_OC1M_CHINACT   TIM1_CCMR1_OC1M_BITS(0x0, 0x2)  /* 0,010: Channel 1 inactive on match */
+#  define TIM1_CCMR1_OC1M_OCREFTOG  TIM1_CCMR1_OC1M_BITS(0x0, 0x3)  /* 0,011: OC1REF toggle TIM_CNT=TIM_CCR1 */
+#  define TIM1_CCMR1_OC1M_OCREFLO   TIM1_CCMR1_OC1M_BITS(0x0, 0x4)  /* 0,100: OC1REF forced low */
+#  define TIM1_CCMR1_OC1M_OCREFHI   TIM1_CCMR1_OC1M_BITS(0x0, 0x5)  /* 0,101: OC1REF forced high */
+#  define TIM1_CCMR1_OC1M_PWM1      TIM1_CCMR1_OC1M_BITS(0x0, 0x6)  /* 0,110: PWM mode 1 */
+#  define TIM1_CCMR1_OC1M_PWM2      TIM1_CCMR1_OC1M_BITS(0x0, 0x7)  /* 0,111: PWM mode 2 */
+#  define TIM1_CCMR1_OC1M_OPM1      TIM1_CCMR1_OC1M_BITS(0x1, 0x0)  /* 1,000: OPM mode 1 */
+#  define TIM1_CCMR1_OC1M_OPM2      TIM1_CCMR1_OC1M_BITS(0x1, 0x1)  /* 1,001: OPM mode 2 */
+#  define TIM1_CCMR1_OC1M_COMBINED1 TIM1_CCMR1_OC1M_BITS(0x1, 0x4)  /* 1,100: Combined PWM mode 1 */
+#  define TIM1_CCMR1_OC1M_COMBINED2 TIM1_CCMR1_OC1M_BITS(0x1, 0x5)  /* 1,101: Combined PWM mode 2 */
+#  define TIM1_CCMR1_OC1M_ASYMM1    TIM1_CCMR1_OC1M_BITS(0x1, 0x6)  /* 1,110: Asymmetric PWM mode 1 */
+#  define TIM1_CCMR1_OC1M_ASYMM2    TIM1_CCMR1_OC1M_BITS(0x1, 0x7)  /* 1,111: Asymmetric PWM mode 2 */
+
+#define TIM1_CCMR1_OC1CE            (1 << 7)  /* Bit 7: Output Compare 1 Clear Enable */
+
+#define TIM1_CCMR1_OC2FE            (1 << 10) /* Bit 10: Output Compare 2 Fast enable */
+#define TIM1_CCMR1_OC2PE            (1 << 11) /* Bit 11: Output Compare 2 Preload enable */
+#define TIM1_CCMR1_OC2M_LO_SHIFT    (12)      /* Bits 12-14: Output Compare 2 Mode, bits [2:0] */
+#define TIM1_CCMR1_OC2M_HI_SHIFT    (24)      /* Bit 24: Output Compare 2 Mode, bits [3] */
+#define TIM1_CCMR1_OC2M_BITS(h,l)   ((h << TIM1_CCMR1_OC2M_HI_SHIFT) | (l << TIM1_CCMR1_OC2M_LO_SHIFT))

Review Comment:
   ditto



##########
arch/arm/src/stm32wb/stm32wb_mpuinit.c:
##########
@@ -0,0 +1,100 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_mpuinit.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+
+#include <nuttx/userspace.h>
+
+#include "mpu.h"
+#include "stm32wb_mpuinit.h"
+
+#if defined(CONFIG_BUILD_PROTECTED) && defined(CONFIG_ARM_MPU)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifndef MAX
+#  define MAX(a,b) a > b ? a : b
+#endif
+
+#ifndef MIN
+#  define MIN(a,b) a < b ? a : b

Review Comment:
   ```suggestion
   #  define MIN(a,b) ((a) < (b) ? (a) : (b))
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_tim.h:
##########
@@ -0,0 +1,1385 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_tim.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_TIM_CR1_OFFSET      0x0000  /* Control register 1 */
+#define STM32WB_TIM_CR2_OFFSET      0x0004  /* Control register 2 */
+#define STM32WB_TIM_SMCR_OFFSET     0x0008  /* Slave mode control register (TIM1, TIM2) */
+#define STM32WB_TIM_DIER_OFFSET     0x000c  /* DMA / Interrupt enable register */
+#define STM32WB_TIM_SR_OFFSET       0x0010  /* Status register */
+#define STM32WB_TIM_EGR_OFFSET      0x0014  /* Event generation register */
+#define STM32WB_TIM_CCMR1_OFFSET    0x0018  /* Capture/compare mode register 1 */
+#define STM32WB_TIM_CCMR2_OFFSET    0x001c  /* Capture/compare mode register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCER_OFFSET     0x0020  /* Capture/compare enable register */
+#define STM32WB_TIM_CNT_OFFSET      0x0024  /* Counter */
+#define STM32WB_TIM_PSC_OFFSET      0x0028  /* Prescaler */
+#define STM32WB_TIM_ARR_OFFSET      0x002c  /* Auto-reload register */
+#define STM32WB_TIM_RCR_OFFSET      0x0030  /* Repetition counter register (TIM1, TIM16/TIM17) */
+#define STM32WB_TIM_CCR1_OFFSET     0x0034  /* Capture/compare register 1 */
+#define STM32WB_TIM_CCR2_OFFSET     0x0038  /* Capture/compare register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR3_OFFSET     0x003c  /* Capture/compare register 3 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR4_OFFSET     0x0040  /* Capture/compare register 4 (TIM1, TIM2) */
+#define STM32WB_TIM_BDTR_OFFSET     0x0044  /* Break and dead-time register (TIM1, TIM16/17) */
+#define STM32WB_TIM_DCR_OFFSET      0x0048  /* DMA control register */
+#define STM32WB_TIM_DMAR_OFFSET     0x004c  /* DMA address for burst mode */
+#define STM32WB_TIM_OR1_OFFSET      0x0050  /* Option register 1 */
+#define STM32WB_TIM_CCMR3_OFFSET    0x0054  /* Capture/compare mode register 3 (TIM1) */
+#define STM32WB_TIM_CCR5_OFFSET     0x0058  /* Capture/compare register 5 (TIM1) */
+#define STM32WB_TIM_CCR6_OFFSET     0x005C  /* Capture/compare register 6 (TIM1) */
+#define STM32WB_TIM_AF1_OFFSET      0x0060  /* Alternate function register 1 */
+#define STM32WB_TIM_AF2_OFFSET      0x0064  /* Alternate function register 2 (TIM1) */
+#define STM32WB_TIM_TISEL_OFFSET    0x0068  /* Input selector register */
+
+/* Register Addresses *******************************************************/
+
+/* Advanced Timer TIM1 */
+
+#define STM32WB_TIM1_CR1            (STM32WB_TIM1_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM1_CR2            (STM32WB_TIM1_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM1_SMCR           (STM32WB_TIM1_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM1_DIER           (STM32WB_TIM1_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM1_SR             (STM32WB_TIM1_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM1_EGR            (STM32WB_TIM1_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM1_CCMR1          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM1_CCMR2          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM1_CCER           (STM32WB_TIM1_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM1_CNT            (STM32WB_TIM1_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM1_PSC            (STM32WB_TIM1_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM1_ARR            (STM32WB_TIM1_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM1_RCR            (STM32WB_TIM1_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM1_CCR1           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM1_CCR2           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM1_CCR3           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM1_CCR4           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM1_BDTR           (STM32WB_TIM1_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM1_DCR            (STM32WB_TIM1_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM1_DMAR           (STM32WB_TIM1_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM1_OR1            (STM32WB_TIM1_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM1_CCMR3          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR3_OFFSET)
+#define STM32WB_TIM1_CCR5           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR5_OFFSET)
+#define STM32WB_TIM1_CCR6           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR6_OFFSET)
+#define STM32WB_TIM1_AF1            (STM32WB_TIM1_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM1_AF2            (STM32WB_TIM1_BASE + STM32WB_TIM_AF2_OFFSET)
+#define STM32WB_TIM1_TISEL          (STM32WB_TIM1_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General 32-bit Timer TIM2 */
+
+#define STM32WB_TIM2_CR1            (STM32WB_TIM2_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM2_CR2            (STM32WB_TIM2_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM2_SMCR           (STM32WB_TIM2_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM2_DIER           (STM32WB_TIM2_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM2_SR             (STM32WB_TIM2_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM2_EGR            (STM32WB_TIM2_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM2_CCMR1          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM2_CCMR2          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM2_CCER           (STM32WB_TIM2_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM2_CNT            (STM32WB_TIM2_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM2_PSC            (STM32WB_TIM2_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM2_ARR            (STM32WB_TIM2_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM2_CCR1           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM2_CCR2           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM2_CCR3           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM2_CCR4           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM2_DCR            (STM32WB_TIM2_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM2_DMAR           (STM32WB_TIM2_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM2_OR1            (STM32WB_TIM2_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM2_AF1            (STM32WB_TIM2_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM2_TISEL          (STM32WB_TIM2_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General Timers TIM16/TIM17 */
+
+#define STM32WB_TIM16_CR1           (STM32WB_TIM16_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM16_CR2           (STM32WB_TIM16_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM16_DIER          (STM32WB_TIM16_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM16_SR            (STM32WB_TIM16_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM16_EGR           (STM32WB_TIM16_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM16_CCMR1         (STM32WB_TIM16_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM16_CCER          (STM32WB_TIM16_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM16_CNT           (STM32WB_TIM16_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM16_PSC           (STM32WB_TIM16_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM16_ARR           (STM32WB_TIM16_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM16_RCR           (STM32WB_TIM16_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM16_CCR1          (STM32WB_TIM16_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM16_BDTR          (STM32WB_TIM16_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM16_DCR           (STM32WB_TIM16_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM16_DMAR          (STM32WB_TIM16_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM16_OR1           (STM32WB_TIM16_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM16_AF1           (STM32WB_TIM16_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM16_TISEL         (STM32WB_TIM16_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+#define STM32WB_TIM17_CR1           (STM32WB_TIM17_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM17_CR2           (STM32WB_TIM17_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM17_DIER          (STM32WB_TIM17_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM17_SR            (STM32WB_TIM17_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM17_EGR           (STM32WB_TIM17_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM17_CCMR1         (STM32WB_TIM17_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM17_CCER          (STM32WB_TIM17_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM17_CNT           (STM32WB_TIM17_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM17_PSC           (STM32WB_TIM17_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM17_ARR           (STM32WB_TIM17_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM17_RCR           (STM32WB_TIM17_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM17_CCR1          (STM32WB_TIM17_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM17_BDTR          (STM32WB_TIM17_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM17_DCR           (STM32WB_TIM17_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM17_DMAR          (STM32WB_TIM17_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM17_OR1           (STM32WB_TIM17_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM17_AF1           (STM32WB_TIM17_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM17_TISEL         (STM32WB_TIM17_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* Register Value Constants *************************************************/
+
+/* Digital Filter options */
+
+#define STM32WB_DF_NOFILT           (0x0) /* 0000: No filter */
+#define STM32WB_DF_FCKINTn2         (0x1) /* 0001: fSAMPLING = fCK_INT, N=2 */
+#define STM32WB_DF_FCKINTn4         (0x2) /* 0010: fSAMPLING = fCK_INT, N=4 */
+#define STM32WB_DF_FCKINTn8         (0x3) /* 0011: fSAMPLING = fCK_INT, N=8 */
+#define STM32WB_DF_FDTSd2n6         (0x4) /* 0100: fSAMPLING = fDTS/2, N=6 */
+#define STM32WB_DF_FDTSd2n8         (0x5) /* 0101: fSAMPLING = fDTS/2, N=8 */
+#define STM32WB_DF_FDTSd4n6         (0x6) /* 0110: fSAMPLING = fDTS/4, N=6 */
+#define STM32WB_DF_FDTSd4n8         (0x7) /* 0111: fSAMPLING = fDTS/4, N=8 */
+#define STM32WB_DF_FDTSd8n6         (0x8) /* 1000: fSAMPLING = fDTS/8, N=6 */
+#define STM32WB_DF_FDTSd8n8         (0x9) /* 1001: fSAMPLING = fDTS/8, N=8 */
+#define STM32WB_DF_FDTSd16n5        (0xa) /* 1010: fSAMPLING = fDTS/16, N=5 */
+#define STM32WB_DF_FDTSd16n6        (0xb) /* 1011: fSAMPLING = fDTS/16, N=6 */
+#define STM32WB_DF_FDTSd16n8        (0xc) /* 1100: fSAMPLING = fDTS/16, N=8 */
+#define STM32WB_DF_FDTSd32n5        (0xd) /* 1101: fSAMPLING = fDTS/32, N=5 */
+#define STM32WB_DF_FDTSd32n6        (0xe) /* 1110: fSAMPLING = fDTS/32, N=6 */
+#define STM32WB_DF_FDTSd32n8        (0xf) /* 1111: fSAMPLING = fDTS/32, N=8 */
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Control register 1 */
+
+#define TIM1_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM1_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM1_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM1_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM1_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM1_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM1_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM1_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM1_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM1_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM1_CR1_CMS_MASK           (0x3 << TIM1_CR1_CMS_SHIFT)
+#  define TIM1_CR1_CMS_EDGE         (0x0 << TIM1_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM1_CR1_CMS_CNTR1        (0x1 << TIM1_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM1_CR1_CMS_CNTR2        (0x2 << TIM1_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM1_CR1_CMS_CNTR3        (0x3 << TIM1_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM1_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM1_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM1_CR1_CKD_MASK           (0x3 << TIM1_CR1_CKD_SHIFT)
+#  define TIM1_CR1_CKD_TCKINT       (0x0 << TIM1_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM1_CR1_CKD_2TCKINT      (0x1 << TIM1_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM1_CR1_CKD_4TCKINT      (0x2 << TIM1_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM1_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM2_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM2_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM2_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM2_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM2_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM2_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM2_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM2_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM2_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM2_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM2_CR1_CMS_MASK           (0x3 << TIM2_CR1_CMS_SHIFT)
+#  define TIM2_CR1_CMS_EDGE         (0x0 << TIM2_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM2_CR1_CMS_CNTR1        (0x1 << TIM2_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM2_CR1_CMS_CNTR2        (0x2 << TIM2_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM2_CR1_CMS_CNTR3        (0x3 << TIM2_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM2_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM2_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM2_CR1_CKD_MASK           (0x3 << TIM2_CR1_CKD_SHIFT)
+#  define TIM2_CR1_CKD_TCKINT       (0x0 << TIM2_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM2_CR1_CKD_2TCKINT      (0x1 << TIM2_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM2_CR1_CKD_4TCKINT      (0x2 << TIM2_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM2_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM16_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM16_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM16_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM16_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM16_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM16_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM16_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM16_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM16_CR1_CKD_MASK          (0x3 << TIM16_CR1_CKD_SHIFT)
+#  define TIM16_CR1_CKD_TCKINT      (0x0 << TIM16_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM16_CR1_CKD_2TCKINT     (0x1 << TIM16_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM16_CR1_CKD_4TCKINT     (0x2 << TIM16_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM16_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM17_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM17_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM17_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM17_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM17_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM17_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM17_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM17_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM17_CR1_CKD_MASK          (0x3 << TIM17_CR1_CKD_SHIFT)
+#  define TIM17_CR1_CKD_TCKINT      (0x0 << TIM17_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM17_CR1_CKD_2TCKINT     (0x1 << TIM17_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM17_CR1_CKD_4TCKINT     (0x2 << TIM17_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM17_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+/* Control register 2 */
+
+#define TIM1_CR2_CCPC               (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM1_CR2_CCUS               (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM1_CR2_CCUS_COMG        (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM1_CR2_CCUS_COMG_TRGI   (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM1_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM1_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM1_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM1_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM1_CR2_MMS_MASK           (0x7 << TIM1_CR2_MMS_SHIFT)
+#  define TIM1_CR2_MMS_RESET        (0x0 << TIM1_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM1_CR2_MMS_ENABLE       (0x1 << TIM1_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM1_CR2_MMS_UPDATE       (0x2 << TIM1_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM1_CR2_MMS_COMPP        (0x3 << TIM1_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS_OC1REF       (0x4 << TIM1_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM1_CR2_MMS_OC2REF       (0x5 << TIM1_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM1_CR2_MMS_OC3REF       (0x6 << TIM1_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM1_CR2_MMS_OC4REF       (0x7 << TIM1_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM1_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM1_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM1_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM1_CR2_OIS1               (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM1_CR2_OIS1N              (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+#define TIM1_CR2_OIS2               (1 << 10) /* Bit 10: Output Idle state 2 (OC2 output) */
+#define TIM1_CR2_OIS2N              (1 << 11) /* Bit 11: Output Idle state 2 (OC2N output) */
+#define TIM1_CR2_OIS3               (1 << 12) /* Bit 12: Output Idle state 3 (OC3 output) */
+#define TIM1_CR2_OIS3N              (1 << 13) /* Bit 13: Output Idle state 3 (OC3N output) */
+#define TIM1_CR2_OIS4               (1 << 14) /* Bit 14: Output Idle state 4 (OC4 output) */
+#define TIM1_CR2_OIS5               (1 << 16) /* Bit 16: Output Idle state 5 (OC5 output) */
+#define TIM1_CR2_OIS6               (1 << 18) /* Bit 18: Output Idle state 6 (OC6 output) */
+#define TIM1_CR2_MMS2_SHIFT         (20)      /* Bits 20-23: Master Mode Selection 2 */
+#define TIM1_CR2_MMS2_MASK          (0xf << TIM1_CR2_MMS2_SHIFT)
+#  define TIM1_CR2_MMS2_RESET       (0x0 << TIM1_CR2_MMS2_SHIFT) /* 0000: Reset - TIMx_EGR UG bit is TRG9 */
+#  define TIM1_CR2_MMS2_ENABLE      (0x1 << TIM1_CR2_MMS2_SHIFT) /* 0001: Enable - CNT_EN is TRGO2 */
+#  define TIM1_CR2_MMS2_UPDATE      (0x2 << TIM1_CR2_MMS2_SHIFT) /* 0010: Update event is TRGO2 */
+#  define TIM1_CR2_MMS2_COMPP       (0x3 << TIM1_CR2_MMS2_SHIFT) /* 0011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS2_OC1REF      (0x4 << TIM1_CR2_MMS2_SHIFT) /* 0100: Compare OC1REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC2REF      (0x5 << TIM1_CR2_MMS2_SHIFT) /* 0101: Compare OC2REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC3REF      (0x6 << TIM1_CR2_MMS2_SHIFT) /* 0110: Compare OC3REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC4REF      (0x7 << TIM1_CR2_MMS2_SHIFT) /* 0111: Compare OC4REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC5REF      (0x8 << TIM1_CR2_MMS2_SHIFT) /* 1000: Compare OC5REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC6REF      (0x9 << TIM1_CR2_MMS2_SHIFT) /* 1001: Compare OC6REF is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4      (0xa << TIM1_CR2_MMS2_SHIFT) /* 1010: Compare pulse - OC4REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC6      (0xb << TIM1_CR2_MMS2_SHIFT) /* 1011: Compare pulse - OC6REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4R6R   (0xc << TIM1_CR2_MMS2_SHIFT) /* 1100: Compare pulse - OC4REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC4R6F   (0xd << TIM1_CR2_MMS2_SHIFT) /* 1101: Compare pulse - OC4REF rising/OC6REF falling */
+#  define TIM1_CR2_MMS2_CMPOC5R6R   (0xe << TIM1_CR2_MMS2_SHIFT) /* 1110: Compare pulse - OC5REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC5R6F   (0xf << TIM1_CR2_MMS2_SHIFT) /* 1111: Compare pulse - OC5REF rising/OC6REF falling */
+
+#define TIM2_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM2_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM2_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM2_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM2_CR2_MMS_MASK           (0x7 << TIM2_CR2_MMS_SHIFT)
+#  define TIM2_CR2_MMS_RESET        (0x0 << TIM2_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM2_CR2_MMS_ENABLE       (0x1 << TIM2_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM2_CR2_MMS_UPDATE       (0x2 << TIM2_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM2_CR2_MMS_COMPP        (0x3 << TIM2_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM2_CR2_MMS_OC1REF       (0x4 << TIM2_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM2_CR2_MMS_OC2REF       (0x5 << TIM2_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM2_CR2_MMS_OC3REF       (0x6 << TIM2_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM2_CR2_MMS_OC4REF       (0x7 << TIM2_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM2_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM2_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM2_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM16_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM16_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM16_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM16_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM16_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM16_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM16_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM16_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM16_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+#define TIM17_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM17_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM17_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM17_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM17_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM17_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM17_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM17_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM17_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+/* Slave mode control register */
+
+#define TIM1_SMCR_SMS_LO_SHIFT      (0)       /* Bits 0-2: Slave mode selection, bits [2:0] */
+#define TIM1_SMCR_SMS_HI_SHIFT      (16)      /* Bit 16: Slave mode selection, bits [3] */
+#define TIM1_SMCR_SMS_BITS(h,l)     ((h << TIM1_SMCR_SMS_HI_SHIFT) | (l << TIM1_SMCR_SMS_LO_SHIFT))
+#define TIM1_SMCR_SMS_MASK          TIM1_SMCR_SMS_BITS(0x1, 0x7)
+#  define TIM1_SMCR_DISAB           TIM1_SMCR_SMS_BITS(0x0, 0x0) /* 0,000: Slave mode disabled */
+#  define TIM1_SMCR_ENCMD1          TIM1_SMCR_SMS_BITS(0x0, 0x1) /* 0,001: Encoder mode 1 */
+#  define TIM1_SMCR_ENCMD2          TIM1_SMCR_SMS_BITS(0x0, 0x2) /* 0,010: Encoder mode 2 */
+#  define TIM1_SMCR_ENCMD3          TIM1_SMCR_SMS_BITS(0x0, 0x3) /* 0,011: Encoder mode 3 */
+#  define TIM1_SMCR_RESET           TIM1_SMCR_SMS_BITS(0x0, 0x4) /* 0,100: Reset Mode */
+#  define TIM1_SMCR_GATED           TIM1_SMCR_SMS_BITS(0x0, 0x5) /* 0,101: Gated Mode */
+#  define TIM1_SMCR_TRIGGER         TIM1_SMCR_SMS_BITS(0x0, 0x6) /* 0,110: Trigger Mode */
+#  define TIM1_SMCR_EXTCLK1         TIM1_SMCR_SMS_BITS(0x0, 0x7) /* 0,111: External Clock Mode 1 */
+#  define TIM1_SMCR_SMS_COMBINED    TIM1_SMCR_SMS_BITS(0x1, 0x0) /* 1,000: Combined Reset and Trigger mode */
+
+#define TIM1_SMCR_OCCS              (1 << 3)  /* Bit 3: OCREF clear selection */
+#  define TIM1_SMCR_OCCS_CLR        (0 << 3)  /* 0: OCREF clear triggered by CLR input */
+#  define TIM1_SMCR_OCCS_ETRF       (1 << 3)  /* 1: OCREF clear triggered by ETRF */
+
+#define TIM1_SMCR_TS_LO_SHIFT       (4)       /* Bits 4-6: Trigger selection, bits [2:0] */
+#define TIM1_SMCR_TS_HI_SHIFT       (20)      /* Bits 20-21: Trigger selection, bits [1:0] */
+#define TIM1_SMCR_TS_BITS(h,l)      ((h << TIM1_SMCR_TS_HI_SHIFT) | (l << TIM1_SMCR_TS_LO_SHIFT))
+#define TIM1_SMCR_TS_MASK           TIM1_SMCR_TS_BITS(0x3, 0x7)
+#  define TIM1_SMCR_ITR0            TIM1_SMCR_TS_BITS(0x0, 0x0) /* 00,000: Internal trigger 0 (ITR0) */
+#  define TIM1_SMCR_ITR1            TIM1_SMCR_TS_BITS(0x0, 0x1) /* 00,001: Internal trigger 1 (ITR1) */
+#  define TIM1_SMCR_ITR2            TIM1_SMCR_TS_BITS(0x0, 0x2) /* 00,010: Internal trigger 2 (ITR2) */
+#  define TIM1_SMCR_ITR3            TIM1_SMCR_TS_BITS(0x0, 0x3) /* 00,011: Internal trigger 3 (ITR3) */
+#  define TIM1_SMCR_T1FED           TIM1_SMCR_TS_BITS(0x0, 0x4) /* 00,100: TI1 Edge detector (TI1F_ED) */
+#  define TIM1_SMCR_TI1FP1          TIM1_SMCR_TS_BITS(0x0, 0x5) /* 00,101: Filtered timer input 1 (TI1FP1) */
+#  define TIM1_SMCR_T12FP2          TIM1_SMCR_TS_BITS(0x0, 0x6) /* 00,110: Filtered timer input 2 (TI2FP2) */
+#  define TIM1_SMCR_ETRF            TIM1_SMCR_TS_BITS(0x0, 0x7) /* 00,111: External trigger input (ETRF) */
+
+#define TIM1_SMCR_MSM               (1 << 7)  /* Bit 7: Master/slave mode */
+#define TIM1_SMCR_ETF_SHIFT         (8)       /* Bits 8-11: External trigger filter */
+#define TIM1_SMCR_ETF_MASK          (0xf << TIM1_SMCR_ETF_SHIFT)
+#  define TIM1_SMCR_ETF(f)          ((f) << TIM1_SMCR_ETF_SHIFT) /* f = STM32WB_DF_[digital filter option] */
+
+#define TIM1_SMCR_ETPS_SHIFT        (12)      /* Bits 12-13: External trigger prescaler */
+#define TIM1_SMCR_ETPS_MASK         (0x3 << TIM1_SMCR_ETPS_SHIFT)
+#  define TIM1_SMCR_PSCOFF          (0x0 << TIM1_SMCR_ETPS_SHIFT) /* 00: Prescaler OFF */
+#  define TIM1_SMCR_ETRPd2          (0x1 << TIM1_SMCR_ETPS_SHIFT) /* 01: ETRP frequency divided by 2 */
+#  define TIM1_SMCR_ETRPd4          (0x2 << TIM1_SMCR_ETPS_SHIFT) /* 10: ETRP frequency divided by 4 */
+#  define TIM1_SMCR_ETRPd8          (0x3 << TIM1_SMCR_ETPS_SHIFT) /* 11: ETRP frequency divided by 8 */
+
+#define TIM1_SMCR_ECE               (1 << 14) /* Bit 14: External clock enable */
+#define TIM1_SMCR_ETP               (1 << 15) /* Bit 15: External trigger polarity */
+#  define TIM1_SMCR_ETP_HIGH        (0 << 15) /* 0: ETR is non-inverted, active at high level or rising edge */
+#  define TIM1_SMCR_ETP_LOW         (1 << 15) /* 1: ETR is inverted, active at low level or falling edge */
+
+#define TIM2_SMCR_SMS_LO_SHIFT      (0)       /* Bits 0-2: Slave mode selection, bits [2:0] */
+#define TIM2_SMCR_SMS_HI_SHIFT      (16)      /* Bit 16: Slave mode selection, bits [3] */
+#define TIM2_SMCR_SMS_BITS(h,l)     ((h << TIM2_SMCR_SMS_HI_SHIFT) | (l << TIM2_SMCR_SMS_LO_SHIFT))
+#define TIM2_SMCR_SMS_MASK          TIM2_SMCR_SMS_BITS(0x1, 0x7)
+#  define TIM2_SMCR_DISAB           TIM2_SMCR_SMS_BITS(0x0, 0x0) /* 0,000: Slave mode disabled */
+#  define TIM2_SMCR_ENCMD1          TIM2_SMCR_SMS_BITS(0x0, 0x1) /* 0,001: Encoder mode 1 */
+#  define TIM2_SMCR_ENCMD2          TIM2_SMCR_SMS_BITS(0x0, 0x2) /* 0,010: Encoder mode 2 */
+#  define TIM2_SMCR_ENCMD3          TIM2_SMCR_SMS_BITS(0x0, 0x3) /* 0,011: Encoder mode 3 */
+#  define TIM2_SMCR_RESET           TIM2_SMCR_SMS_BITS(0x0, 0x4) /* 0,100: Reset Mode */
+#  define TIM2_SMCR_GATED           TIM2_SMCR_SMS_BITS(0x0, 0x5) /* 0,101: Gated Mode */
+#  define TIM2_SMCR_TRIGGER         TIM2_SMCR_SMS_BITS(0x0, 0x6) /* 0,110: Trigger Mode */
+#  define TIM2_SMCR_EXTCLK1         TIM2_SMCR_SMS_BITS(0x0, 0x7) /* 0,111: External Clock Mode 1 */
+#  define TIM2_SMCR_SMS_COMBINED    TIM2_SMCR_SMS_BITS(0x1, 0x0) /* 1,000: Combined Reset and Trigger mode */
+
+#define TIM2_SMCR_OCCS              (1 << 3)  /* Bit 3: OCREF clear selection */
+#  define TIM2_SMCR_OCCS_CLR        (0 << 3)  /* 0: OCREF clear triggered by CLR input */
+#  define TIM2_SMCR_OCCS_ETRF       (1 << 3)  /* 1: OCREF clear triggered by ETRF */
+
+#define TIM2_SMCR_TS_LO_SHIFT       (4)       /* Bits 4-6: Trigger selection, bits [2:0] */
+#define TIM2_SMCR_TS_HI_SHIFT       (20)      /* Bits 20-21: Trigger selection, bits [1:0] */
+#define TIM2_SMCR_TS_BITS(h,l)      ((h << TIM2_SMCR_TS_HI_SHIFT) | (l << TIM2_SMCR_TS_LO_SHIFT))
+#define TIM2_SMCR_TS_MASK           TIM2_SMCR_TS_BITS(0x3, 0x7)
+#  define TIM2_SMCR_ITR0            TIM2_SMCR_TS_BITS(0x0, 0x0) /* 00,000: Internal trigger 0 (ITR0) */
+#  define TIM2_SMCR_ITR1            TIM2_SMCR_TS_BITS(0x0, 0x1) /* 00,001: Internal trigger 1 (ITR1) */
+#  define TIM2_SMCR_ITR2            TIM2_SMCR_TS_BITS(0x0, 0x2) /* 00,010: Internal trigger 2 (ITR2) */
+#  define TIM2_SMCR_ITR3            TIM2_SMCR_TS_BITS(0x0, 0x3) /* 00,011: Internal trigger 3 (ITR3) */
+#  define TIM2_SMCR_T1FED           TIM2_SMCR_TS_BITS(0x0, 0x4) /* 00,100: TI1 Edge detector (TI1F_ED) */
+#  define TIM2_SMCR_TI1FP1          TIM2_SMCR_TS_BITS(0x0, 0x5) /* 00,101: Filtered timer Input 1 (TI1FP1) */
+#  define TIM2_SMCR_T12FP2          TIM2_SMCR_TS_BITS(0x0, 0x6) /* 00,110: Filtered timer Input 2 (TI2FP2) */
+#  define TIM2_SMCR_ETRF            TIM2_SMCR_TS_BITS(0x0, 0x7) /* 00,111: External trigger input (ETRF) */
+#  define TIM2_SMCR_ITR4            TIM2_SMCR_TS_BITS(0x1, 0x0) /* 01,000: Internal trigger 4 (ITR4) */
+#  define TIM2_SMCR_ITR5            TIM2_SMCR_TS_BITS(0x1, 0x1) /* 01,001: Internal trigger 5 (ITR5) */
+#  define TIM2_SMCR_ITR6            TIM2_SMCR_TS_BITS(0x1, 0x2) /* 01,010: Internal trigger 6 (ITR6) */
+#  define TIM2_SMCR_ITR7            TIM2_SMCR_TS_BITS(0x1, 0x3) /* 01,011: Internal trigger 7 (ITR7) */
+#  define TIM2_SMCR_ITR8            TIM2_SMCR_TS_BITS(0x1, 0x4) /* 01,100: Internal trigger 8 (ITR8) */
+
+#define TIM2_SMCR_MSM               (1 << 7)  /* Bit 7: Master/slave mode */
+#define TIM2_SMCR_ETF_SHIFT         (8)       /* Bits 8-11: External trigger filter */
+#define TIM2_SMCR_ETF_MASK          (0xf << TIM2_SMCR_ETF_SHIFT)
+#  define TIM2_SMCR_ETF(f)          ((f) << TIM2_SMCR_ETF_SHIFT) /* f = STM32WB_DF_[digital filter option] */
+
+#define TIM2_SMCR_ETPS_SHIFT        (12)      /* Bits 12-13: External trigger prescaler */
+#define TIM2_SMCR_ETPS_MASK         (0x3 << TIM2_SMCR_ETPS_SHIFT)
+#  define TIM2_SMCR_PSCOFF          (0x0 << TIM2_SMCR_ETPS_SHIFT) /* 00: Prescaler OFF */
+#  define TIM2_SMCR_ETRPd2          (0x1 << TIM2_SMCR_ETPS_SHIFT) /* 01: ETRP frequency divided by 2 */
+#  define TIM2_SMCR_ETRPd4          (0x2 << TIM2_SMCR_ETPS_SHIFT) /* 10: ETRP frequency divided by 4 */
+#  define TIM2_SMCR_ETRPd8          (0x3 << TIM2_SMCR_ETPS_SHIFT) /* 11: ETRP frequency divided by 8 */
+
+#define TIM2_SMCR_ECE               (1 << 14) /* Bit 14: External clock enable */
+#define TIM2_SMCR_ETP               (1 << 15) /* Bit 15: External trigger polarity */
+#  define TIM2_SMCR_ETP_HIGH        (0 << 15) /* 0: ETR is non-inverted, active at high level or rising edge */
+#  define TIM2_SMCR_ETP_LOW         (1 << 15) /* 1: ETR is inverted, active at low level or falling edge */
+
+/* Timer DMA / Interrupt enable register */
+
+#define TIM1_DIER_UIE               (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM1_DIER_CC1IE             (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM1_DIER_CC2IE             (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt enable */
+#define TIM1_DIER_CC3IE             (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt enable */
+#define TIM1_DIER_CC4IE             (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt enable */
+#define TIM1_DIER_COMIE             (1 << 5)  /* Bit 5: COM interrupt enable */
+#define TIM1_DIER_TIE               (1 << 6)  /* Bit 6: Trigger interrupt enable */
+#define TIM1_DIER_BIE               (1 << 7)  /* Bit 7: Break interrupt enable */
+#define TIM1_DIER_UDE               (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM1_DIER_CC1DE             (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+#define TIM1_DIER_CC2DE             (1 << 10) /* Bit 10: Capture/Compare 2 DMA request enable */
+#define TIM1_DIER_CC3DE             (1 << 11) /* Bit 11: Capture/Compare 3 DMA request enable */
+#define TIM1_DIER_CC4DE             (1 << 12) /* Bit 12: Capture/Compare 4 DMA request enable */
+#define TIM1_DIER_COMDE             (1 << 13) /* Bit 13: COM DMA request enable */
+#define TIM1_DIER_TDE               (1 << 14) /* Bit 14: Trigger DMA request enable */
+
+#define TIM2_DIER_UIE               (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM2_DIER_CC1IE             (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM2_DIER_CC2IE             (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt enable */
+#define TIM2_DIER_CC3IE             (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt enable */
+#define TIM2_DIER_CC4IE             (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt enable */
+#define TIM2_DIER_TIE               (1 << 6)  /* Bit 6: Trigger interrupt enable */
+#define TIM2_DIER_UDE               (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM2_DIER_CC1DE             (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+#define TIM2_DIER_CC2DE             (1 << 10) /* Bit 10: Capture/Compare 2 DMA request enable */
+#define TIM2_DIER_CC3DE             (1 << 11) /* Bit 11: Capture/Compare 3 DMA request enable */
+#define TIM2_DIER_CC4DE             (1 << 12) /* Bit 12: Capture/Compare 4 DMA request enable */
+
+#define TIM16_DIER_UIE              (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM16_DIER_CC1IE            (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM16_DIER_COMIE            (1 << 5)  /* Bit 5: COM interrupt enable */
+#define TIM16_DIER_BIE              (1 << 7)  /* Bit 7: Break interrupt enable */
+#define TIM16_DIER_UDE              (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM16_DIER_CC1DE            (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+
+#define TIM17_DIER_UIE              (1 << 0)  /* Bit 0: Update interrupt enable */
+#define TIM17_DIER_CC1IE            (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt enable */
+#define TIM17_DIER_COMIE            (1 << 5)  /* Bit 5: COM interrupt enable */
+#define TIM17_DIER_BIE              (1 << 7)  /* Bit 7: Break interrupt enable */
+#define TIM17_DIER_UDE              (1 << 8)  /* Bit 8: Update DMA request enable */
+#define TIM17_DIER_CC1DE            (1 << 9)  /* Bit 9: Capture/Compare 1 DMA request enable */
+
+/* Status register */
+
+#define TIM1_SR_UIF                 (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM1_SR_CC1IF               (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM1_SR_CC2IF               (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt Flag */
+#define TIM1_SR_CC3IF               (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt Flag */
+#define TIM1_SR_CC4IF               (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt Flag */
+#define TIM1_SR_COMIF               (1 << 5)  /* Bit 5: COM interrupt Flag */
+#define TIM1_SR_TIF                 (1 << 6)  /* Bit 6: Trigger interrupt Flag */
+#define TIM1_SR_BIF                 (1 << 7)  /* Bit 7: Break interrupt Flag */
+#define TIM1_SR_B2IF                (1 << 8)  /* Bit 8: Break 2 interrupt Flag */
+#define TIM1_SR_CC1OF               (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+#define TIM1_SR_CC2OF               (1 << 10) /* Bit 10: Capture/Compare 2 Overcapture Flag */
+#define TIM1_SR_CC3OF               (1 << 11) /* Bit 11: Capture/Compare 3 Overcapture Flag */
+#define TIM1_SR_CC4OF               (1 << 12) /* Bit 12: Capture/Compare 4 Overcapture Flag */
+#define TIM1_SR_SBIF                (1 << 13) /* Bit 13: System break interrupt Flag */
+#define TIM1_SR_CC5IF               (1 << 16) /* Bit 16: Compare 5 interrupt flag */
+#define TIM1_SR_CC6IF               (1 << 17) /* Bit 17: Compare 6 interrupt flag */
+
+#define TIM2_SR_UIF                 (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM2_SR_CC1IF               (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM2_SR_CC2IF               (1 << 2)  /* Bit 2: Capture/Compare 2 interrupt Flag */
+#define TIM2_SR_CC3IF               (1 << 3)  /* Bit 3: Capture/Compare 3 interrupt Flag */
+#define TIM2_SR_CC4IF               (1 << 4)  /* Bit 4: Capture/Compare 4 interrupt Flag */
+#define TIM2_SR_TIF                 (1 << 6)  /* Bit 6: Trigger interrupt Flag */
+#define TIM2_SR_CC1OF               (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+#define TIM2_SR_CC2OF               (1 << 10) /* Bit 10: Capture/Compare 2 Overcapture Flag */
+#define TIM2_SR_CC3OF               (1 << 11) /* Bit 11: Capture/Compare 3 Overcapture Flag */
+#define TIM2_SR_CC4OF               (1 << 12) /* Bit 12: Capture/Compare 4 Overcapture Flag */
+
+#define TIM16_SR_UIF                (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM16_SR_CC1IF              (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM16_SR_COMIF              (1 << 5)  /* Bit 5: COM interrupt Flag */
+#define TIM16_SR_BIF                (1 << 7)  /* Bit 7: Break interrupt Flag */
+#define TIM16_SR_CC1OF              (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+
+#define TIM17_SR_UIF                (1 << 0)  /* Bit 0: Update interrupt Flag */
+#define TIM17_SR_CC1IF              (1 << 1)  /* Bit 1: Capture/Compare 1 interrupt Flag */
+#define TIM17_SR_COMIF              (1 << 5)  /* Bit 5: COM interrupt Flag */
+#define TIM17_SR_BIF                (1 << 7)  /* Bit 7: Break interrupt Flag */
+#define TIM17_SR_CC1OF              (1 << 9)  /* Bit 9: Capture/Compare 1 Overcapture Flag */
+
+/* Event generation register */
+
+#define TIM1_EGR_UG                 (1 << 0)  /* Bit 0: Update Generation */
+#define TIM1_EGR_CC1G               (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM1_EGR_CC2G               (1 << 2)  /* Bit 2: Capture/Compare 2 Generation */
+#define TIM1_EGR_CC3G               (1 << 3)  /* Bit 3: Capture/Compare 3 Generation */
+#define TIM1_EGR_CC4G               (1 << 4)  /* Bit 4: Capture/Compare 4 Generation */
+#define TIM1_EGR_COMG               (1 << 5)  /* Bit 5: Capture/Compare Control Update Generation */
+#define TIM1_EGR_TG                 (1 << 6)  /* Bit 6: Trigger Generation */
+#define TIM1_EGR_BG                 (1 << 7)  /* Bit 7: Break Generation */
+#define TIM1_EGR_B2G                (1 << 8)  /* Bit 8: Break 2 Generation */
+
+#define TIM2_EGR_UG                 (1 << 0)  /* Bit 0: Update Generation */
+#define TIM2_EGR_CC1G               (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM2_EGR_CC2G               (1 << 2)  /* Bit 2: Capture/Compare 2 Generation */
+#define TIM2_EGR_CC3G               (1 << 3)  /* Bit 3: Capture/Compare 3 Generation */
+#define TIM2_EGR_CC4G               (1 << 4)  /* Bit 4: Capture/Compare 4 Generation */
+#define TIM2_EGR_TG                 (1 << 6)  /* Bit 6: Trigger Generation */
+
+#define TIM16_EGR_UG                (1 << 0)  /* Bit 0: Update Generation */
+#define TIM16_EGR_CC1G              (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM16_EGR_COMG              (1 << 5)  /* Bit 5: Capture/Compare Control Update Generation */
+#define TIM16_EGR_BG                (1 << 7)  /* Bit 7: Break Generation */
+
+#define TIM17_EGR_UG                (1 << 0)  /* Bit 0: Update Generation */
+#define TIM17_EGR_CC1G              (1 << 1)  /* Bit 1: Capture/Compare 1 Generation */
+#define TIM17_EGR_COMG              (1 << 5)  /* Bit 5: Capture/Compare Control Update Generation */
+#define TIM17_EGR_BG                (1 << 7)  /* Bit 7: Break Generation */
+
+/* Capture/compare mode registers - capture/compare mode selection */
+
+#define TIM1_CCMR1_CC1S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM1_CCMR1_CC1S_MASK        (0x3 << TIM1_CCMR1_CC1S_SHIFT)
+#  define TIM1_CCMR1_CC1S_CCOUT     (0x0 << TIM1_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM1_CCMR1_CC1S_CCIN1     (0x1 << TIM1_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+#  define TIM1_CCMR1_CC1S_CCIN2     (0x2 << TIM1_CCMR1_CC1S_SHIFT) /* 10: CC1 channel input, IC1 is TI2 */
+#  define TIM1_CCMR1_CC1S_CCINTRC   (0x3 << TIM1_CCMR1_CC1S_SHIFT) /* 11: CC1 channel input, IC1 is TRC */
+
+#define TIM1_CCMR1_CC2S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 2 Selection */
+#define TIM1_CCMR1_CC2S_MASK        (0x3 << TIM1_CCMR1_CC2S_SHIFT)
+#  define TIM1_CCMR1_CC2S_CCOUT     (0x0 << TIM1_CCMR1_CC2S_SHIFT) /* 00: CC2 channel output */
+#  define TIM1_CCMR1_CC2S_CCIN1     (0x1 << TIM1_CCMR1_CC2S_SHIFT) /* 01: CC2 channel input, IC2 is TI2 */
+#  define TIM1_CCMR1_CC2S_CCIN2     (0x2 << TIM1_CCMR1_CC2S_SHIFT) /* 10: CC2 channel input, IC2 is TI1 */
+#  define TIM1_CCMR1_CC2S_CCINTRC   (0x3 << TIM1_CCMR1_CC2S_SHIFT) /* 11: CC2 channel input, IC2 is TRC */
+
+#define TIM1_CCMR2_CC3S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 3 Selection */
+#define TIM1_CCMR2_CC3S_MASK        (0x3 << TIM1_CCMR2_CC3S_SHIFT)
+#  define TIM1_CCMR2_CC3S_CCOUT     (0x0 << TIM1_CCMR2_CC3S_SHIFT) /* 00: CC3 channel output */
+#  define TIM1_CCMR2_CC3S_CCIN1     (0x1 << TIM1_CCMR2_CC3S_SHIFT) /* 01: CC3 channel input, IC3 is TI3 */
+#  define TIM1_CCMR2_CC3S_CCIN2     (0x2 << TIM1_CCMR2_CC3S_SHIFT) /* 10: CC3 channel input, IC3 is TI4 */
+#  define TIM1_CCMR2_CC3S_CCINTRC   (0x3 << TIM1_CCMR2_CC3S_SHIFT) /* 11: CC3 channel input, IC3 is TRC */
+
+#define TIM1_CCMR2_CC4S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 4 Selection */
+#define TIM1_CCMR2_CC4S_MASK        (0x3 << TIM1_CCMR2_CC4S_SHIFT)
+#  define TIM1_CCMR2_CC4S_CCOUT     (0x0 << TIM1_CCMR2_CC4S_SHIFT) /* 00: CC4 channel output */
+#  define TIM1_CCMR2_CC4S_CCIN1     (0x1 << TIM1_CCMR2_CC4S_SHIFT) /* 01: CC4 channel input, IC4 is TI4 */
+#  define TIM1_CCMR2_CC4S_CCIN2     (0x2 << TIM1_CCMR2_CC4S_SHIFT) /* 10: CC4 channel input, IC4 is TI3 */
+#  define TIM1_CCMR2_CC4S_CCINTRC   (0x3 << TIM1_CCMR2_CC4S_SHIFT) /* 11: CC4 channel input, IC4 is TRC */
+
+#define TIM2_CCMR1_CC1S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM2_CCMR1_CC1S_MASK        (0x3 << TIM2_CCMR1_CC1S_SHIFT)
+#  define TIM2_CCMR1_CC1S_CCOUT     (0x0 << TIM2_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM2_CCMR1_CC1S_CCIN1     (0x1 << TIM2_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+#  define TIM2_CCMR1_CC1S_CCIN2     (0x2 << TIM2_CCMR1_CC1S_SHIFT) /* 10: CC1 channel input, IC1 is TI2 */
+#  define TIM2_CCMR1_CC1S_CCINTRC   (0x3 << TIM2_CCMR1_CC1S_SHIFT) /* 11: CC1 channel input, IC1 is TRC */
+
+#define TIM2_CCMR1_CC2S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 2 Selection */
+#define TIM2_CCMR1_CC2S_MASK        (0x3 << TIM2_CCMR1_CC2S_SHIFT)
+#  define TIM2_CCMR1_CC2S_CCOUT     (0x0 << TIM2_CCMR1_CC2S_SHIFT) /* 00: CC2 channel output */
+#  define TIM2_CCMR1_CC2S_CCIN1     (0x1 << TIM2_CCMR1_CC2S_SHIFT) /* 01: CC2 channel input, IC2 is TI2 */
+#  define TIM2_CCMR1_CC2S_CCIN2     (0x2 << TIM2_CCMR1_CC2S_SHIFT) /* 10: CC2 channel input, IC2 is TI1 */
+#  define TIM2_CCMR1_CC2S_CCINTRC   (0x3 << TIM2_CCMR1_CC2S_SHIFT) /* 11: CC2 channel input, IC2 is TRC */
+
+#define TIM2_CCMR2_CC3S_SHIFT       (0)       /* Bits 0-1: Capture/Compare 3 Selection */
+#define TIM2_CCMR2_CC3S_MASK        (0x3 << TIM2_CCMR2_CC3S_SHIFT)
+#  define TIM2_CCMR2_CC3S_CCOUT     (0x0 << TIM2_CCMR2_CC3S_SHIFT) /* 00: CC3 channel output */
+#  define TIM2_CCMR2_CC3S_CCIN1     (0x1 << TIM2_CCMR2_CC3S_SHIFT) /* 01: CC3 channel input, IC3 is TI3 */
+#  define TIM2_CCMR2_CC3S_CCIN2     (0x2 << TIM2_CCMR2_CC3S_SHIFT) /* 10: CC3 channel input, IC3 is TI4 */
+#  define TIM2_CCMR2_CC3S_CCINTRC   (0x3 << TIM2_CCMR2_CC3S_SHIFT) /* 11: CC3 channel input, IC3 is TRC */
+
+#define TIM2_CCMR2_CC4S_SHIFT       (8)       /* Bits 8-9: Capture/Compare 4 Selection */
+#define TIM2_CCMR2_CC4S_MASK        (0x3 << TIM2_CCMR2_CC4S_SHIFT)
+#  define TIM2_CCMR2_CC4S_CCOUT     (0x0 << TIM2_CCMR2_CC4S_SHIFT) /* 00: CC4 channel output */
+#  define TIM2_CCMR2_CC4S_CCIN1     (0x1 << TIM2_CCMR2_CC4S_SHIFT) /* 01: CC4 channel input, IC4 is TI4 */
+#  define TIM2_CCMR2_CC4S_CCIN2     (0x2 << TIM2_CCMR2_CC4S_SHIFT) /* 10: CC4 channel input, IC4 is TI3 */
+#  define TIM2_CCMR2_CC4S_CCINTRC   (0x3 << TIM2_CCMR2_CC4S_SHIFT) /* 11: CC4 channel input, IC4 is TRC */
+
+#define TIM16_CCMR1_CC1S_SHIFT      (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM16_CCMR1_CC1S_MASK       (0x3 << TIM16_CCMR1_CC1S_SHIFT)
+#  define TIM16_CCMR1_CC1S_CCOUT    (0x0 << TIM16_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM16_CCMR1_CC1S_CCIN1    (0x1 << TIM16_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+
+#define TIM17_CCMR1_CC1S_SHIFT      (0)       /* Bits 0-1: Capture/Compare 1 Selection */
+#define TIM17_CCMR1_CC1S_MASK       (0x3 << TIM17_CCMR1_CC1S_SHIFT)
+#  define TIM17_CCMR1_CC1S_CCOUT    (0x0 << TIM17_CCMR1_CC1S_SHIFT) /* 00: CC1 channel output */
+#  define TIM17_CCMR1_CC1S_CCIN1    (0x1 << TIM17_CCMR1_CC1S_SHIFT) /* 01: CC1 channel input, IC1 is TI1 */
+
+/* Capture/compare mode registers - Output compare mode */
+
+#define TIM1_CCMR1_OC1FE            (1 << 2)  /* Bit 2: Output Compare 1 Fast enable */
+#define TIM1_CCMR1_OC1PE            (1 << 3)  /* Bit 3: Output Compare 1 Preload enable */
+#define TIM1_CCMR1_OC1M_LO_SHIFT    (4)       /* Bits 4-6: Output Compare 1 Mode, bits [2:0] */
+#define TIM1_CCMR1_OC1M_HI_SHIFT    (16)      /* Bit 16: Output Compare 1 Mode, bits [3] */
+#define TIM1_CCMR1_OC1M_BITS(h,l)   ((h << TIM1_CCMR1_OC1M_HI_SHIFT) | (l << TIM1_CCMR1_OC1M_LO_SHIFT))
+#define TIM1_CCMR1_OC1M_MASK        TIM1_CCMR1_OC1M_BITS(0x1, 0x7)
+#  define TIM1_CCMR1_OC1M_FRZN      TIM1_CCMR1_OC1M_BITS(0x0, 0x0)  /* 0,000: Frozen */
+#  define TIM1_CCMR1_OC1M_CHACT     TIM1_CCMR1_OC1M_BITS(0x0, 0x1)  /* 0,001: Channel 1 active on match */
+#  define TIM1_CCMR1_OC1M_CHINACT   TIM1_CCMR1_OC1M_BITS(0x0, 0x2)  /* 0,010: Channel 1 inactive on match */
+#  define TIM1_CCMR1_OC1M_OCREFTOG  TIM1_CCMR1_OC1M_BITS(0x0, 0x3)  /* 0,011: OC1REF toggle TIM_CNT=TIM_CCR1 */
+#  define TIM1_CCMR1_OC1M_OCREFLO   TIM1_CCMR1_OC1M_BITS(0x0, 0x4)  /* 0,100: OC1REF forced low */
+#  define TIM1_CCMR1_OC1M_OCREFHI   TIM1_CCMR1_OC1M_BITS(0x0, 0x5)  /* 0,101: OC1REF forced high */
+#  define TIM1_CCMR1_OC1M_PWM1      TIM1_CCMR1_OC1M_BITS(0x0, 0x6)  /* 0,110: PWM mode 1 */
+#  define TIM1_CCMR1_OC1M_PWM2      TIM1_CCMR1_OC1M_BITS(0x0, 0x7)  /* 0,111: PWM mode 2 */
+#  define TIM1_CCMR1_OC1M_OPM1      TIM1_CCMR1_OC1M_BITS(0x1, 0x0)  /* 1,000: OPM mode 1 */
+#  define TIM1_CCMR1_OC1M_OPM2      TIM1_CCMR1_OC1M_BITS(0x1, 0x1)  /* 1,001: OPM mode 2 */
+#  define TIM1_CCMR1_OC1M_COMBINED1 TIM1_CCMR1_OC1M_BITS(0x1, 0x4)  /* 1,100: Combined PWM mode 1 */
+#  define TIM1_CCMR1_OC1M_COMBINED2 TIM1_CCMR1_OC1M_BITS(0x1, 0x5)  /* 1,101: Combined PWM mode 2 */
+#  define TIM1_CCMR1_OC1M_ASYMM1    TIM1_CCMR1_OC1M_BITS(0x1, 0x6)  /* 1,110: Asymmetric PWM mode 1 */
+#  define TIM1_CCMR1_OC1M_ASYMM2    TIM1_CCMR1_OC1M_BITS(0x1, 0x7)  /* 1,111: Asymmetric PWM mode 2 */
+
+#define TIM1_CCMR1_OC1CE            (1 << 7)  /* Bit 7: Output Compare 1 Clear Enable */
+
+#define TIM1_CCMR1_OC2FE            (1 << 10) /* Bit 10: Output Compare 2 Fast enable */
+#define TIM1_CCMR1_OC2PE            (1 << 11) /* Bit 11: Output Compare 2 Preload enable */
+#define TIM1_CCMR1_OC2M_LO_SHIFT    (12)      /* Bits 12-14: Output Compare 2 Mode, bits [2:0] */
+#define TIM1_CCMR1_OC2M_HI_SHIFT    (24)      /* Bit 24: Output Compare 2 Mode, bits [3] */
+#define TIM1_CCMR1_OC2M_BITS(h,l)   ((h << TIM1_CCMR1_OC2M_HI_SHIFT) | (l << TIM1_CCMR1_OC2M_LO_SHIFT))
+#define TIM1_CCMR1_OC2M_MASK        TIM1_CCMR1_OC2M_BITS(0x1, 0x7)
+#  define TIM1_CCMR1_OC2M_FRZN      TIM1_CCMR1_OC2M_BITS(0x0, 0x0)  /* 0,000: Frozen */
+#  define TIM1_CCMR1_OC2M_CHACT     TIM1_CCMR1_OC2M_BITS(0x0, 0x1)  /* 0,001: Channel 2 active on match */
+#  define TIM1_CCMR1_OC2M_CHINACT   TIM1_CCMR1_OC2M_BITS(0x0, 0x2)  /* 0,010: Channel 2 inactive on match */
+#  define TIM1_CCMR1_OC2M_OCREFTOG  TIM1_CCMR1_OC2M_BITS(0x0, 0x3)  /* 0,011: OC2REF toggle TIM_CNT=TIM_CCR2 */
+#  define TIM1_CCMR1_OC2M_OCREFLO   TIM1_CCMR1_OC2M_BITS(0x0, 0x4)  /* 0,100: OC2REF forced low */
+#  define TIM1_CCMR1_OC2M_OCREFHI   TIM1_CCMR1_OC2M_BITS(0x0, 0x5)  /* 0,101: OC2REF forced high */
+#  define TIM1_CCMR1_OC2M_PWM1      TIM1_CCMR1_OC2M_BITS(0x0, 0x6)  /* 0,110: PWM mode 1 */
+#  define TIM1_CCMR1_OC2M_PWM2      TIM1_CCMR1_OC2M_BITS(0x0, 0x7)  /* 0,111: PWM mode 2 */
+#  define TIM1_CCMR1_OC2M_OPM1      TIM1_CCMR1_OC2M_BITS(0x1, 0x0)  /* 1,000: OPM mode 1 */
+#  define TIM1_CCMR1_OC2M_OPM2      TIM1_CCMR1_OC2M_BITS(0x1, 0x1)  /* 1,001: OPM mode 2 */
+#  define TIM1_CCMR1_OC2M_COMBINED1 TIM1_CCMR1_OC2M_BITS(0x1, 0x4)  /* 1,100: Combined PWM mode 1 */
+#  define TIM1_CCMR1_OC2M_COMBINED2 TIM1_CCMR1_OC2M_BITS(0x1, 0x5)  /* 1,101: Combined PWM mode 2 */
+#  define TIM1_CCMR1_OC2M_ASYMM1    TIM1_CCMR1_OC2M_BITS(0x1, 0x6)  /* 1,110: Asymmetric PWM mode 1 */
+#  define TIM1_CCMR1_OC2M_ASYMM2    TIM1_CCMR1_OC2M_BITS(0x1, 0x7)  /* 1,111: Asymmetric PWM mode 2 */
+
+#define TIM1_CCMR1_OC2CE            (1 << 15) /* Bit 15: Output Compare 2 Clear Enable */
+
+#define TIM1_CCMR2_OC3FE            (1 << 2)  /* Bit 2: Output Compare 3 Fast enable */
+#define TIM1_CCMR2_OC3PE            (1 << 3)  /* Bit 3: Output Compare 3 Preload enable */
+#define TIM1_CCMR2_OC3M_LO_SHIFT    (4)       /* Bits 4-6: Output Compare 3 Mode, bits [2:0] */
+#define TIM1_CCMR2_OC3M_HI_SHIFT    (16)      /* Bit 16: Output Compare 3 Mode, bits [3] */
+#define TIM1_CCMR2_OC3M_BITS(h,l)   ((h << TIM1_CCMR2_OC3M_HI_SHIFT) | (l << TIM1_CCMR2_OC3M_LO_SHIFT))
+#define TIM1_CCMR2_OC3M_MASK        TIM1_CCMR2_OC3M_BITS(0x1, 0x7)
+#  define TIM1_CCMR2_OC3M_FRZN      TIM1_CCMR2_OC3M_BITS(0x0, 0x0)  /* 0,000: Frozen */
+#  define TIM1_CCMR2_OC3M_CHACT     TIM1_CCMR2_OC3M_BITS(0x0, 0x1)  /* 0,001: Channel 3 active on match */
+#  define TIM1_CCMR2_OC3M_CHINACT   TIM1_CCMR2_OC3M_BITS(0x0, 0x2)  /* 0,010: Channel 3 inactive on match */
+#  define TIM1_CCMR2_OC3M_OCREFTOG  TIM1_CCMR2_OC3M_BITS(0x0, 0x3)  /* 0,011: OC3REF toggle TIM_CNT=TIM_CCR3 */
+#  define TIM1_CCMR2_OC3M_OCREFLO   TIM1_CCMR2_OC3M_BITS(0x0, 0x4)  /* 0,100: OC3REF forced low */
+#  define TIM1_CCMR2_OC3M_OCREFHI   TIM1_CCMR2_OC3M_BITS(0x0, 0x5)  /* 0,101: OC3REF forced high */
+#  define TIM1_CCMR2_OC3M_PWM1      TIM1_CCMR2_OC3M_BITS(0x0, 0x6)  /* 0,110: PWM mode 1 */
+#  define TIM1_CCMR2_OC3M_PWM2      TIM1_CCMR2_OC3M_BITS(0x0, 0x7)  /* 0,111: PWM mode 2 */
+#  define TIM1_CCMR2_OC3M_OPM1      TIM1_CCMR2_OC3M_BITS(0x1, 0x0)  /* 1,000: OPM mode 1 */
+#  define TIM1_CCMR2_OC3M_OPM2      TIM1_CCMR2_OC3M_BITS(0x1, 0x1)  /* 1,001: OPM mode 2 */
+#  define TIM1_CCMR2_OC3M_COMBINED1 TIM1_CCMR2_OC3M_BITS(0x1, 0x4)  /* 1,100: Combined PWM mode 1 */
+#  define TIM1_CCMR2_OC3M_COMBINED2 TIM1_CCMR2_OC3M_BITS(0x1, 0x5)  /* 1,101: Combined PWM mode 2 */
+#  define TIM1_CCMR2_OC3M_ASYMM1    TIM1_CCMR2_OC3M_BITS(0x1, 0x6)  /* 1,110: Asymmetric PWM mode 1 */
+#  define TIM1_CCMR2_OC3M_ASYMM2    TIM1_CCMR2_OC3M_BITS(0x1, 0x7)  /* 1,111: Asymmetric PWM mode 2 */
+
+#define TIM1_CCMR2_OC3CE            (1 << 7)  /* Bit 7: Output Compare 3 Clear Enable */
+
+#define TIM1_CCMR2_OC4FE            (1 << 10) /* Bit 10: Output Compare 4 Fast enable */
+#define TIM1_CCMR2_OC4PE            (1 << 11) /* Bit 11: Output Compare 4 Preload enable */
+#define TIM1_CCMR2_OC4M_LO_SHIFT    (12)      /* Bits 12-14: Output Compare 4 Mode, bits [2:0] */
+#define TIM1_CCMR2_OC4M_HI_SHIFT    (24)      /* Bit 24: Output Compare 4 Mode, bits [3] */
+#define TIM1_CCMR2_OC4M_BITS(h,l)   ((h << TIM1_CCMR2_OC4M_HI_SHIFT) | (l << TIM1_CCMR2_OC4M_LO_SHIFT))

Review Comment:
   ditto



##########
arch/arm/src/stm32wb/stm32wb_freerun.h:
##########
@@ -0,0 +1,160 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_freerun.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_STM32WB_FREERUN_H
+#define __ARCH_ARM_SRC_STM32WB_STM32WB_FREERUN_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <time.h>
+#include <debug.h>
+
+#include "stm32wb_tim.h"
+
+#ifdef CONFIG_STM32WB_FREERUN
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* The freerun client must allocate an instance of this structure and called
+ * stm32wb_freerun_initialize() before using the freerun facilities.  The
+ * client should not access the contents of this structure directly since
+ * the contents are subject to change.
+ */
+
+struct stm32wb_freerun_s
+{
+  uint8_t chan;                      /* The timer/counter in use */
+  uint8_t width;                     /* Width of timer (16- or 32-bits) */
+  struct stm32wb_tim_dev_s *tch;     /* Pointer returned by
+                                      * stm32wb_tim_init() */
+  uint32_t frequency;
+
+#ifndef CONFIG_CLOCK_TIMEKEEPING
+  uint32_t overflow;                 /* Timer counter overflow */
+#endif
+
+#ifdef CONFIG_CLOCK_TIMEKEEPING
+  uint64_t counter_mask;
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_freerun_initialize
+ *
+ * Description:
+ *   Initialize the freerun timer wrapper
+ *
+ * Input Parameters:
+ *   freerun    Caller allocated instance of the freerun state structure
+ *   chan       Timer counter channel to be used.
+ *   resolution The required resolution of the timer in units of
+ *              microseconds.  NOTE that the range is restricted to the
+ *              range of uint16_t (excluding zero).
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned
+ *   on failure.
+ *
+ ****************************************************************************/
+
+int stm32wb_freerun_initialize(struct stm32wb_freerun_s *freerun, int chan,
+                             uint16_t resolution);
+
+/****************************************************************************
+ * Name: stm32wb_freerun_counter
+ *
+ * Description:
+ *   Read the counter register of the free-running timer.
+ *
+ * Input Parameters:
+ *   freerun Caller allocated instance of the freerun state structure.  This
+ *           structure must have been previously initialized via a call to
+ *           stm32wb_freerun_initialize();
+ *   ts      The location in which to return the time remaining on the
+ *           oneshot timer.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned
+ *   on failure.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_CLOCK_TIMEKEEPING
+
+int stm32wb_freerun_counter(struct stm32wb_freerun_s *freerun,
+                          struct timespec *ts);

Review Comment:
   ```suggestion
   int stm32wb_freerun_counter(struct stm32wb_freerun_s *freerun,
                               struct timespec *ts);
   ```



##########
arch/arm/src/stm32wb/stm32wb_oneshot.c:
##########
@@ -0,0 +1,460 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_oneshot.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sched.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+
+#include "stm32wb_oneshot.h"
+
+#ifdef CONFIG_STM32WB_ONESHOT
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int stm32wb_oneshot_handler(int irq, void *context, void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct stm32wb_oneshot_s *g_oneshot[CONFIG_STM32WB_ONESHOT_MAXTIMERS];
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_handler
+ *
+ * Description:
+ *   Common timer interrupt callback.  When any oneshot timer interrupt
+ *   expires, this function will be called.  It will forward the call to
+ *   the next level up.
+ *
+ * Input Parameters:
+ *   oneshot - The state associated with the expired timer
+ *
+ * Returned Value:
+ *   Always returns OK
+ *
+ ****************************************************************************/
+
+static int stm32wb_oneshot_handler(int irq, void *context, void *arg)
+{
+  struct stm32wb_oneshot_s *oneshot = (struct stm32wb_oneshot_s *) arg;

Review Comment:
   ```suggestion
     struct stm32wb_oneshot_s *oneshot = (struct stm32wb_oneshot_s *)arg;
   ```



##########
arch/arm/src/stm32wb/stm32wb_oneshot.c:
##########
@@ -0,0 +1,460 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_oneshot.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sched.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+
+#include "stm32wb_oneshot.h"
+
+#ifdef CONFIG_STM32WB_ONESHOT
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int stm32wb_oneshot_handler(int irq, void *context, void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct stm32wb_oneshot_s *g_oneshot[CONFIG_STM32WB_ONESHOT_MAXTIMERS];
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_handler
+ *
+ * Description:
+ *   Common timer interrupt callback.  When any oneshot timer interrupt
+ *   expires, this function will be called.  It will forward the call to
+ *   the next level up.
+ *
+ * Input Parameters:
+ *   oneshot - The state associated with the expired timer
+ *
+ * Returned Value:
+ *   Always returns OK
+ *
+ ****************************************************************************/
+
+static int stm32wb_oneshot_handler(int irq, void *context, void *arg)
+{
+  struct stm32wb_oneshot_s *oneshot = (struct stm32wb_oneshot_s *) arg;
+  oneshot_handler_t oneshot_handler;
+  void *oneshot_arg;
+
+  tmrinfo("Expired...\n");
+  DEBUGASSERT(oneshot != NULL && oneshot->handler);
+
+  /* The clock was stopped, but not disabled when the RC match occurred.
+   * Disable the TC now and disable any further interrupts.
+   */
+
+  STM32WB_TIM_SETISR(oneshot->tch, NULL, NULL, 0);
+  STM32WB_TIM_DISABLEINT(oneshot->tch, GTIM_DIER_UIE);
+  STM32WB_TIM_SETMODE(oneshot->tch, STM32WB_TIM_MODE_DISABLED);
+  STM32WB_TIM_ACKINT(oneshot->tch, GTIM_SR_UIF);
+
+  /* The timer is no longer running */
+
+  oneshot->running = false;
+
+  /* Forward the event, clearing out any vestiges */
+
+  oneshot_handler  = (oneshot_handler_t)oneshot->handler;
+  oneshot->handler = NULL;
+  oneshot_arg      = (void *)oneshot->arg;
+  oneshot->arg     = NULL;
+
+  oneshot_handler(oneshot_arg);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_allocate_handler
+ *
+ * Description:
+ *   Allocate a timer callback handler for the oneshot instance.
+ *
+ * Input Parameters:
+ *   oneshot - The state instance the new oneshot timer
+ *
+ * Returned Value:
+ *   Returns zero (OK) on success.  This can only fail if the number of
+ *   timers exceeds CONFIG_STM32WB_ONESHOT_MAXTIMERS.
+ *
+ ****************************************************************************/
+
+static inline int stm32wb_allocate_handler(struct stm32wb_oneshot_s *oneshot)
+{
+#if CONFIG_STM32WB_ONESHOT_MAXTIMERS > 1
+  int ret = -EBUSY;
+  int i;
+
+  /* Search for an unused handler */
+
+  sched_lock();
+  for (i = 0; i < CONFIG_STM32WB_ONESHOT_MAXTIMERS; i++)
+    {
+      /* Is this handler available? */
+
+      if (g_oneshot[i] == NULL)
+        {
+          /* Yes... assign it to this oneshot */
+
+          g_oneshot[i]   = oneshot;
+          oneshot->cbndx = i;
+          ret            = OK;
+          break;
+        }
+    }
+
+  sched_unlock();
+  return ret;
+
+#else
+  if (g_oneshot[0] == NULL)
+    {
+      g_oneshot[0] = oneshot;
+      return OK;
+    }
+
+  return -EBUSY;
+#endif
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_initialize
+ *
+ * Description:
+ *   Initialize the oneshot timer wrapper
+ *
+ * Input Parameters:
+ *   oneshot    Caller allocated instance of the oneshot state structure
+ *   chan       Timer counter channel to be used.
+ *   resolution The required resolution of the timer in units of
+ *              microseconds.  NOTE that the range is restricted to the
+ *              range of uint16_t (excluding zero).
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned
+ *   on failure.
+ *
+ ****************************************************************************/
+
+int stm32wb_oneshot_initialize(struct stm32wb_oneshot_s *oneshot,
+                               int chan, uint16_t resolution)
+{
+  uint32_t frequency;
+
+  tmrinfo("chan=%d resolution=%d usec\n", chan, resolution);
+  DEBUGASSERT(oneshot && resolution > 0);
+
+  /* Get the TC frequency the corresponds to the requested resolution */
+
+  frequency = USEC_PER_SEC / (uint32_t)resolution;
+  oneshot->frequency = frequency;
+
+  oneshot->tch = stm32wb_tim_init(chan);
+  if (!oneshot->tch)
+    {
+      tmrerr("ERROR: Failed to allocate TIM%d\n", chan);
+      return -EBUSY;
+    }
+
+  STM32WB_TIM_SETCLOCK(oneshot->tch, frequency);
+
+  /* Initialize the remaining fields in the state structure. */
+
+  oneshot->chan       = chan;
+  oneshot->running    = false;
+  oneshot->handler    = NULL;
+  oneshot->arg        = NULL;
+
+  /* Assign a callback handler to the oneshot */
+
+  return stm32wb_allocate_handler(oneshot);
+}
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_max_delay
+ *
+ * Description:
+ *   Determine the maximum delay of the one-shot timer (in microseconds)
+ *
+ ****************************************************************************/
+
+int stm32wb_oneshot_max_delay(struct stm32wb_oneshot_s *oneshot,
+                              uint64_t *usec)
+{
+  DEBUGASSERT(oneshot != NULL && usec != NULL);
+
+  *usec = (uint64_t)(UINT32_MAX / oneshot->frequency) *
+          (uint64_t)USEC_PER_SEC;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_start
+ *
+ * Description:
+ *   Start the oneshot timer
+ *
+ * Input Parameters:
+ *   oneshot Caller allocated instance of the oneshot state structure.  This
+ *           structure must have been previously initialized via a call to
+ *           stm32wb_oneshot_initialize();
+ *   handler The function to call when when the oneshot timer expires.
+ *   arg     An opaque argument that will accompany the callback.
+ *   ts      Provides the duration of the one shot timer.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned
+ *   on failure.
+ *
+ ****************************************************************************/
+
+int stm32wb_oneshot_start(struct stm32wb_oneshot_s *oneshot,
+                          oneshot_handler_t handler, void *arg,
+                          const struct timespec *ts)
+{
+  uint64_t usec;
+  uint64_t period;
+  irqstate_t flags;
+
+  tmrinfo("handler=%p arg=%p, ts=(%lu, %lu)\n",
+         handler, arg, (unsigned long)ts->tv_sec,
+         (unsigned long)ts->tv_nsec);

Review Comment:
   ```suggestion
     tmrinfo("handler=%p arg=%p, ts=(%lu, %lu)\n",
             handler, arg, (unsigned long)ts->tv_sec,
             (unsigned long)ts->tv_nsec);
   ```



##########
arch/arm/src/stm32wb/stm32wb_mpuinit.c:
##########
@@ -0,0 +1,100 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_mpuinit.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+
+#include <nuttx/userspace.h>
+
+#include "mpu.h"
+#include "stm32wb_mpuinit.h"
+
+#if defined(CONFIG_BUILD_PROTECTED) && defined(CONFIG_ARM_MPU)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifndef MAX
+#  define MAX(a,b) a > b ? a : b

Review Comment:
   ```suggestion
   #  define MAX(a,b) ((a) > (b) ? (a) : (b))
   ```



##########
arch/arm/src/stm32wb/stm32wb_dma.c:
##########
@@ -0,0 +1,1404 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_dma.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <debug.h>
+#include <assert.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include "arm_internal.h"
+#include "sched/sched.h"
+#include "stm32wb_dma.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifndef CONFIG_STM32WB_DMAMUX
+#  error "Configuration error, CONFIG_STM32WB_DMAMUX not defined!"
+#endif
+
+#define DMAMUX_NUM      1
+#define DMA_CONTROLLERS 2
+
+#ifdef CONFIG_STM32WB_DMA1
+#  define DMA1_NCHAN    7
+#else
+#  define DMA1_NCHAN    0
+#endif
+#ifdef CONFIG_STM32WB_DMA2
+#  define DMA2_NCHAN    7
+#else
+#  define DMA2_NCHAN    0
+#endif
+
+#define DMA1_FIRST       (0)
+#define DMA1_LAST        (DMA1_FIRST+DMA1_NCHAN)
+#define DMA2_FIRST       (DMA1_LAST)
+#define DMA2_LAST        (DMA2_FIRST+DMA2_NCHAN)
+
+/* All available DMA channels */
+
+#define DMA_NCHANNELS    (DMA1_NCHAN+DMA2_NCHAN)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure described one DMAMUX device */
+
+struct stm32wb_dmamux_s
+{
+  uint8_t  id;                  /* DMAMUX id */
+  uint8_t  nchan;               /* DMAMUX channels */
+  uint32_t base;                /* DMAMUX base address */
+};
+
+typedef const struct stm32wb_dmamux_s *DMA_MUX;
+
+/* This structure describes one DMA controller */
+
+struct stm32wb_dma_s
+{
+  uint8_t       first;           /* Offset in stm32wb_dmach_s array */
+  uint8_t       nchan;           /* Number of channels */
+  uint8_t       dmamux_offset;   /* DMAMUX channel offset */
+  uint32_t      base;            /* Base address */
+  DMA_MUX       dmamux;          /* DMAMUX associated with controller */
+};
+
+/* This structure describes one DMA channel (DMA1, DMA2) */
+
+struct stm32wb_dmach_s
+{
+  bool             used;         /* Channel in use */
+  uint8_t          dmamux_req;   /* Configured DMAMUX input request */
+  uint8_t          ctrl;         /* DMA controller */
+  uint8_t          chan;         /* DMA channel channel id */
+  uint8_t          irq;          /* DMA channel IRQ number */
+  uint8_t          shift;        /* IFCR bit shift value */
+  uint32_t         base;         /* DMA register channel base address */
+  dma_callback_t   callback;     /* Callback invoked when the DMA completes */
+  void             *arg;         /* Argument passed to callback function */
+};
+
+typedef struct stm32wb_dmach_s *DMA_CHANNEL;
+
+/* DMA operations */
+
+struct stm32wb_dma_ops_s
+{
+  /* Disable the DMA transfer */
+
+  void (*dma_disable)(DMA_CHANNEL dmachan);
+
+  /* DMA interrupt */
+
+  int (*dma_interrupt)(int irq, void *context, void *arg);
+
+  /* Setup the DMA */
+
+  void (*dma_setup)(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
+                    size_t ntransfers, uint32_t ccr);
+
+  /* Start the DMA */
+
+  void (*dma_start)(DMA_HANDLE handle, dma_callback_t callback,
+                    void *arg, bool half);
+
+  /* Read remaining DMA bytes */
+
+  size_t (*dma_residual)(DMA_HANDLE handle);
+
+  /* Check the DMA configuration  */
+
+  bool (*dma_capable)(uint32_t maddr, uint32_t count, uint32_t ccr);
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+  /* Sample the DMA registers */
+
+  void (*dma_sample)(DMA_HANDLE handle, struct stm32wb_dmaregs_s *regs);
+
+  /* Dump the DMA registers */
+
+  void (*dma_dump)(DMA_HANDLE handle,
+                   const struct stm32wb_dmaregs_s *regs,
+                   const char *msg);
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+#if defined(CONFIG_STM32WB_DMA1) || defined(CONFIG_STM32WB_DMA2)
+static void stm32wb_dma12_disable(DMA_CHANNEL dmachan);
+static int stm32wb_dma12_interrupt(int irq, void *context, void *arg);
+static void stm32wb_dma12_setup(DMA_HANDLE handle, uint32_t paddr,
+                                uint32_t maddr, size_t ntransfers,
+                                uint32_t ccr);
+static void stm32wb_dma12_start(DMA_HANDLE handle, dma_callback_t callback,
+                                void *arg, bool half);
+static size_t stm32wb_dma12_residual(DMA_HANDLE handle);
+#ifdef CONFIG_DEBUG_DMA_INFO
+static void stm32wb_dma12_sample(DMA_HANDLE handle,
+                                 struct stm32wb_dmaregs_s *regs);
+static void stm32wb_dma12_dump(DMA_HANDLE handle,
+                               const struct stm32wb_dmaregs_s *regs,
+                               const char *msg);
+#endif
+#endif
+
+static uint32_t dmachan_getbase(DMA_CHANNEL dmachan);
+static uint32_t dmabase_getreg(DMA_CHANNEL dmachan, uint32_t offset);
+static void dmabase_putreg(DMA_CHANNEL dmachan, uint32_t offset,
+                           uint32_t value);
+static uint32_t dmachan_getreg(DMA_CHANNEL dmachan, uint32_t offset);
+static void dmachan_putreg(DMA_CHANNEL dmachan, uint32_t offset,
+                           uint32_t value);
+static void dmamux_putreg(DMA_MUX dmamux, uint32_t offset, uint32_t value);
+#ifdef CONFIG_DEBUG_DMA_INFO
+static uint32_t dmamux_getreg(DMA_MUX dmamux, uint32_t offset);
+static void stm32wb_dmamux_sample(DMA_MUX dmamux, uint8_t chan,
+                                  struct stm32wb_dmaregs_s *regs);
+static void stm32wb_dmamux_dump(DMA_MUX dmamux, uint8_t channel,
+                                const struct stm32wb_dmaregs_s *regs);
+#endif
+static DMA_CHANNEL stm32wb_dma_channel_get(uint8_t channel,
+                                           uint8_t controller);
+static void stm32wb_gdma_limits_get(uint8_t controller, uint8_t *first,
+                                    uint8_t *last);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Operations specific to DMA controller */
+
+static const struct stm32wb_dma_ops_s g_dma_ops[DMA_CONTROLLERS] =
+{
+#ifdef CONFIG_STM32WB_DMA1
+  /* 0 - DMA1 */
+
+    {
+      .dma_disable   = stm32wb_dma12_disable,
+      .dma_interrupt = stm32wb_dma12_interrupt,
+      .dma_setup     = stm32wb_dma12_setup,
+      .dma_start     = stm32wb_dma12_start,
+      .dma_residual  = stm32wb_dma12_residual,
+#ifdef CONFIG_DEBUG_DMA_INFO
+      .dma_sample    = stm32wb_dma12_sample,
+      .dma_dump      = stm32wb_dma12_dump,
+#endif
+    },
+#else
+    {
+      NULL
+    },
+#endif
+
+#ifdef CONFIG_STM32WB_DMA2
+  /* 1 - DMA2 */
+
+    {
+      .dma_disable   = stm32wb_dma12_disable,
+      .dma_interrupt = stm32wb_dma12_interrupt,
+      .dma_setup     = stm32wb_dma12_setup,
+      .dma_start     = stm32wb_dma12_start,
+      .dma_residual  = stm32wb_dma12_residual,
+#ifdef CONFIG_DEBUG_DMA_INFO
+      .dma_sample    = stm32wb_dma12_sample,
+      .dma_dump      = stm32wb_dma12_dump,
+#endif
+    }
+#else
+    {
+      NULL
+    }
+#endif
+};
+
+/* This array describes the state of DMAMUX controller */
+
+static const struct stm32wb_dmamux_s g_dmamux[DMAMUX_NUM] =
+{
+    {
+      .id      = 1,
+      .nchan   = 14,              /* 0-6 - DMA1, 7-13 - DMA2 */
+      .base    = STM32WB_DMAMUX1_BASE
+    }
+};
+
+/* This array describes the state of each controller */
+
+static const struct stm32wb_dma_s g_dma[DMA_NCHANNELS] =
+{
+  /* 0 - DMA1 */
+
+    {
+      .base   = STM32WB_DMA1_BASE,
+      .first  = DMA1_FIRST,
+      .nchan  = DMA1_NCHAN,
+      .dmamux = &g_dmamux[DMAMUX1], /* DMAMUX1 channels 0-6 */
+      .dmamux_offset = 0
+    },
+
+  /* 1 - DMA2 */
+
+    {
+      .base   = STM32WB_DMA2_BASE,
+      .first  = DMA2_FIRST,
+      .nchan  = DMA2_NCHAN,
+      .dmamux = &g_dmamux[DMAMUX1], /* DMAMUX1 channels 7-13 */
+      .dmamux_offset = 7
+    }
+};
+
+/* This array describes the state of each DMA channel. */
+
+static struct stm32wb_dmach_s g_dmach[DMA_NCHANNELS] =
+{
+#ifdef CONFIG_STM32WB_DMA1
+  /* DMA1 */
+
+    {
+      .ctrl     = DMA1,
+      .chan     = 0,
+      .irq      = STM32WB_IRQ_DMA1CH1,
+      .shift    = DMA_CHAN_SHIFT(0),
+      .base     = STM32WB_DMA1_BASE + STM32WB_DMACHAN_OFFSET(0),
+    },
+
+    {
+      .ctrl     = DMA1,
+      .chan     = 1,
+      .irq      = STM32WB_IRQ_DMA1CH2,
+      .shift    = DMA_CHAN_SHIFT(1),
+      .base     = STM32WB_DMA1_BASE + STM32WB_DMACHAN_OFFSET(1),
+    },
+
+    {
+      .ctrl     = DMA1,
+      .chan     = 2,
+      .irq      = STM32WB_IRQ_DMA1CH3,
+      .shift    = DMA_CHAN_SHIFT(2),
+      .base     = STM32WB_DMA1_BASE + STM32WB_DMACHAN_OFFSET(2),
+    },
+
+    {
+      .ctrl     = DMA1,
+      .chan     = 3,
+      .irq      = STM32WB_IRQ_DMA1CH4,
+      .shift    = DMA_CHAN_SHIFT(3),
+      .base     = STM32WB_DMA1_BASE + STM32WB_DMACHAN_OFFSET(3),
+    },
+
+    {
+      .ctrl     = DMA1,
+      .chan     = 4,
+      .irq      = STM32WB_IRQ_DMA1CH5,
+      .shift    = DMA_CHAN_SHIFT(4),
+      .base     = STM32WB_DMA1_BASE + STM32WB_DMACHAN_OFFSET(4),
+    },
+
+    {
+      .ctrl     = DMA1,
+      .chan     = 5,
+      .irq      = STM32WB_IRQ_DMA1CH6,
+      .shift    = DMA_CHAN_SHIFT(5),
+      .base     = STM32WB_DMA1_BASE + STM32WB_DMACHAN_OFFSET(5),
+    },
+
+    {
+      .ctrl     = DMA1,
+      .chan     = 6,
+      .irq      = STM32WB_IRQ_DMA1CH7,
+      .shift    = DMA_CHAN_SHIFT(6),
+      .base     = STM32WB_DMA1_BASE + STM32WB_DMACHAN_OFFSET(6),
+    },
+#endif
+
+#ifdef CONFIG_STM32WB_DMA2
+  /* DMA2 */
+
+    {
+      .ctrl     = DMA2,
+      .chan     = 0,
+      .irq      = STM32WB_IRQ_DMA2CH1,
+      .shift    = DMA_CHAN_SHIFT(0),
+      .base     = STM32WB_DMA2_BASE + STM32WB_DMACHAN_OFFSET(0),
+    },
+
+    {
+      .ctrl     = DMA2,
+      .chan     = 1,
+      .irq      = STM32WB_IRQ_DMA2CH2,
+      .shift    = DMA_CHAN_SHIFT(1),
+      .base     = STM32WB_DMA2_BASE + STM32WB_DMACHAN_OFFSET(1),
+    },
+
+    {
+      .ctrl     = DMA2,
+      .chan     = 2,
+      .irq      = STM32WB_IRQ_DMA2CH3,
+      .shift    = DMA_CHAN_SHIFT(2),
+      .base     = STM32WB_DMA2_BASE + STM32WB_DMACHAN_OFFSET(2),
+    },
+
+    {
+      .ctrl     = DMA2,
+      .chan     = 3,
+      .irq      = STM32WB_IRQ_DMA2CH4,
+      .shift    = DMA_CHAN_SHIFT(3),
+      .base     = STM32WB_DMA2_BASE + STM32WB_DMACHAN_OFFSET(3),
+    },
+
+    {
+      .ctrl     = DMA2,
+      .chan     = 4,
+      .irq      = STM32WB_IRQ_DMA2CH5,
+      .shift    = DMA_CHAN_SHIFT(4),
+      .base     = STM32WB_DMA2_BASE + STM32WB_DMACHAN_OFFSET(4),
+    },
+
+    {
+      .ctrl     = DMA2,
+      .chan     = 5,
+      .irq      = STM32WB_IRQ_DMA2CH6,
+      .shift    = DMA_CHAN_SHIFT(5),
+      .base     = STM32WB_DMA2_BASE + STM32WB_DMACHAN_OFFSET(5),
+    },
+
+    {
+      .ctrl     = DMA2,
+      .chan     = 6,
+      .irq      = STM32WB_IRQ_DMA2CH7,
+      .shift    = DMA_CHAN_SHIFT(6),
+      .base     = STM32WB_DMA2_BASE + STM32WB_DMACHAN_OFFSET(6),
+    },
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * DMA register access functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: dmachan_getbase
+ *
+ * Description:
+ *  Get base DMA address for dmachan
+ *
+ ****************************************************************************/
+
+static uint32_t dmachan_getbase(DMA_CHANNEL dmachan)
+{
+  uint8_t controller = dmachan->ctrl;
+
+  return g_dma[controller].base;
+}
+
+/****************************************************************************
+ * Name: dmabase_getreg
+ *
+ * Description:
+ *  Get non-channel register from DMA controller
+ *
+ ****************************************************************************/
+
+static uint32_t dmabase_getreg(DMA_CHANNEL dmachan, uint32_t offset)
+{
+  uint32_t dmabase = dmachan_getbase(dmachan);
+
+  return getreg32(dmabase + offset);
+}
+
+/****************************************************************************
+ * Name: dmabase_putreg
+ *
+ * Description:
+ *  Write to non-channel register in DMA controller
+ *
+ ****************************************************************************/
+
+static void dmabase_putreg(DMA_CHANNEL dmachan, uint32_t offset,
+                           uint32_t value)
+{
+  uint32_t dmabase = dmachan_getbase(dmachan);
+
+  putreg32(value, dmabase + offset);
+}
+
+/****************************************************************************
+ * Name: dmachan_getreg
+ *
+ * Description:
+ *  Get channel register.
+ *
+ ****************************************************************************/
+
+static uint32_t dmachan_getreg(DMA_CHANNEL dmachan, uint32_t offset)
+{
+  return getreg32(dmachan->base + offset);
+}
+
+/****************************************************************************
+ * Name: dmachan_putreg
+ *
+ * Description:
+ *  Write to channel register.
+ *
+ ****************************************************************************/
+
+static void dmachan_putreg(DMA_CHANNEL dmachan, uint32_t offset,
+                           uint32_t value)
+{
+  putreg32(value, dmachan->base + offset);
+}
+
+/****************************************************************************
+ * Name: dmamux_getreg
+ *
+ * Description:
+ *  Write to DMAMUX
+ *
+ ****************************************************************************/
+
+static void dmamux_putreg(DMA_MUX dmamux, uint32_t offset, uint32_t value)
+{
+  putreg32(value, dmamux->base + offset);
+}
+
+/****************************************************************************
+ * Name: dmamux_getreg
+ *
+ * Description:
+ *  Get DMAMUX register.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+static uint32_t dmamux_getreg(DMA_MUX dmamux, uint32_t offset)
+{
+  return getreg32(dmamux->base + offset);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_dma_channel_get
+ *
+ * Description:
+ *  Get the g_dmach table entry associated with a given DMA controller
+ *  and channel number.
+ *
+ ****************************************************************************/
+
+static DMA_CHANNEL stm32wb_dma_channel_get(uint8_t channel,
+                                           uint8_t controller)
+{
+  uint8_t first = 0;
+  uint8_t nchan = 0;
+
+  /* Get limits for g_dma array */
+
+  stm32wb_gdma_limits_get(controller, &first, &nchan);
+
+  DEBUGASSERT(channel <= nchan);
+
+  return &g_dmach[first + channel];
+}
+
+/****************************************************************************
+ * Name: stm32wb_gdma_limits_get
+ *
+ * Description:
+ *  Get g_dma array limits for a given DMA controller.
+ *
+ ****************************************************************************/
+
+static void stm32wb_gdma_limits_get(uint8_t controller, uint8_t *first,
+                                    uint8_t *nchan)
+{
+  DEBUGASSERT(first != NULL);
+  DEBUGASSERT(nchan != NULL);
+
+  DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
+
+  *first = g_dma[controller].first;
+  *nchan = g_dma[controller].nchan;
+}
+
+/****************************************************************************
+ * DMA controller functions
+ ****************************************************************************/
+
+#if defined(CONFIG_STM32WB_DMA1) || defined(CONFIG_STM32WB_DMA2)
+
+/****************************************************************************
+ * Name: stm32wb_dma12_disable
+ *
+ * Description:
+ *  Disable DMA channel (DMA1/DMA2)
+ *
+ ****************************************************************************/
+
+static void stm32wb_dma12_disable(DMA_CHANNEL dmachan)
+{
+  uint32_t regval;
+
+  DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2);
+
+  /* Disable all interrupts at the DMA controller */
+
+  regval = dmachan_getreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET);
+  regval &= ~DMA_CCR_ALLINTS;
+
+  /* Disable the DMA channel */
+
+  regval &= ~DMA_CCR_EN;
+  dmachan_putreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET, regval);
+
+  /* Clear pending channel interrupts */
+
+  dmabase_putreg(dmachan, STM32WB_DMA_IFCR_OFFSET,
+                 DMA_ISR_CHAN_MASK(dmachan->chan));
+}
+
+/****************************************************************************
+ * Name: stm32wb_dma12_interrupt
+ *
+ * Description:
+ *  DMA channel interrupt handler
+ *
+ ****************************************************************************/
+
+static int stm32wb_dma12_interrupt(int irq, void *context, void *arg)
+{
+  DMA_CHANNEL dmachan;
+  uint32_t isr;
+  uint8_t channel;
+  uint8_t controller;
+
+  /* Get the channel and the controller that generated the interrupt */
+
+  if (0)
+    {
+    }
+#ifdef CONFIG_STM32WB_DMA1
+  else if (irq >= STM32WB_IRQ_DMA1CH1 && irq <= STM32WB_IRQ_DMA1CH7)
+    {
+      channel = irq - STM32WB_IRQ_DMA1CH1;
+      controller = DMA1;
+    }
+#endif
+#ifdef CONFIG_STM32WB_DMA2
+  else if (irq >= STM32WB_IRQ_DMA2CH1 && irq <= STM32WB_IRQ_DMA2CH5)
+    {
+      channel = irq - STM32WB_IRQ_DMA2CH1;
+      controller = DMA2;
+    }
+  else if (irq >= STM32WB_IRQ_DMA2CH6 && irq <= STM32WB_IRQ_DMA2CH7)
+    {
+      channel = irq - STM32WB_IRQ_DMA2CH6 + (6 - 1);
+      controller = DMA2;
+    }
+#endif
+  else
+    {
+      DEBUGPANIC();
+      return OK;
+    }
+
+  /* Get the channel structure from the stream and controller numbers */
+
+  dmachan = stm32wb_dma_channel_get(channel, controller);
+
+  /* Get the interrupt status (for this channel only) */
+
+  isr = dmabase_getreg(dmachan, STM32WB_DMA_ISR_OFFSET) &
+        DMA_ISR_CHAN_MASK(dmachan->chan);
+
+  /* Invoke the callback */
+
+  if (dmachan->callback)
+    {
+      dmachan->callback(dmachan, isr >> DMA_ISR_CHAN_SHIFT(dmachan->chan),
+                        dmachan->arg);
+    }
+
+  /* Clear the interrupts we are handling */
+
+  dmabase_putreg(dmachan, STM32WB_DMA_IFCR_OFFSET, isr);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_dma12_setup
+ *
+ * Description:
+ *   Configure DMA before using
+ *
+ ****************************************************************************/
+
+static void stm32wb_dma12_setup(DMA_HANDLE handle, uint32_t paddr,
+                                uint32_t maddr, size_t ntransfers,
+                                uint32_t ccr)
+{
+  DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
+  uint32_t regval;
+
+  DEBUGASSERT(handle != NULL);
+  DEBUGASSERT(ntransfers < 65536);
+
+  DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2);
+
+  dmainfo("paddr: %08" PRIx32 " maddr: %08" PRIx32
+          " ntransfers: %zd ccr: %08" PRIx32 "\n",
+          paddr, maddr, ntransfers, ccr);
+
+#ifdef CONFIG_STM32WB_DMACAPABLE
+  DEBUGASSERT(g_dma_ops[dmachan->ctrl].dma_capable(maddr, ntransfers, ccr));
+#endif
+
+  /* Then DMA_CNDTRx register can only be modified if the DMA channel is
+   * disabled.
+   */
+
+  regval  = dmachan_getreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET);
+  regval &= ~(DMA_CCR_EN);
+  dmachan_putreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET, regval);
+
+  /* Set the peripheral register address in the DMA_CPARx register. The data
+   * will be moved from/to this address to/from the memory after the
+   * peripheral event.
+   */
+
+  dmachan_putreg(dmachan, STM32WB_DMACHAN_CPAR_OFFSET, paddr);
+
+  /* Set the memory address in the DMA_CMARx register. The data will be
+   * written to or read from this memory after the peripheral event.
+   */
+
+  dmachan_putreg(dmachan, STM32WB_DMACHAN_CMAR_OFFSET, maddr);
+
+  /* Configure the total number of data to be transferred in the DMA_CNDTRx
+   * register.  After each peripheral event, this value will be decremented.
+   */
+
+  dmachan_putreg(dmachan, STM32WB_DMACHAN_CNDTR_OFFSET, ntransfers);
+
+  /* Configure the channel priority using the PL[1:0] bits in the DMA_CCRx
+   * register.  Configure data transfer direction, circular mode, peripheral
+   * & memory incremented mode, peripheral & memory data size, and interrupt
+   * after half and/or full transfer in the DMA_CCRx register.
+   */
+
+  regval  = dmachan_getreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET);
+  regval &= ~(DMA_CCR_MEM2MEM | DMA_CCR_PL_MASK | DMA_CCR_MSIZE_MASK |
+              DMA_CCR_PSIZE_MASK | DMA_CCR_MINC | DMA_CCR_PINC |
+              DMA_CCR_CIRC | DMA_CCR_DIR);
+  ccr    &=  (DMA_CCR_MEM2MEM | DMA_CCR_PL_MASK | DMA_CCR_MSIZE_MASK |
+              DMA_CCR_PSIZE_MASK | DMA_CCR_MINC | DMA_CCR_PINC |
+              DMA_CCR_CIRC | DMA_CCR_DIR);
+  regval |= ccr;
+  dmachan_putreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET, regval);
+}
+
+/****************************************************************************
+ * Name: stm32wb_dma12_start
+ *
+ * Description:
+ *   Start the standard DMA transfer
+ ****************************************************************************/
+
+static void stm32wb_dma12_start(DMA_HANDLE handle, dma_callback_t callback,
+                                void *arg, bool half)
+{
+  DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
+  uint32_t ccr;
+
+  DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2);
+
+  /* Save the callback info.  This will be invoked when the DMA completes */
+
+  dmachan->callback = callback;
+  dmachan->arg      = arg;
+
+  /* Activate the channel by setting the ENABLE bit in the DMA_CCRx register.
+   * As soon as the channel is enabled, it can serve any DMA request from the
+   * peripheral connected on the channel.
+   */
+
+  ccr  = dmachan_getreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET);
+  ccr |= DMA_CCR_EN;
+
+  /* In normal mode, interrupt at either half or full completion. In circular
+   * mode, always interrupt on buffer wrap, and optionally interrupt at the
+   * halfway point.
+   */
+
+  if ((ccr & DMA_CCR_CIRC) == 0)
+    {
+      /* Once half of the bytes are transferred, the half-transfer flag
+       * (HTIF) is set and an interrupt is generated if the Half-Transfer
+       * Interrupt Enable bit (HTIE) is set. At the end of the transfer,
+       * the Transfer Complete Flag (TCIF) is set and an interrupt is
+       * generated if the Transfer Complete Interrupt Enable bit (TCIE)
+       * is set.
+       */
+
+      ccr |= (half ? (DMA_CCR_HTIE | DMA_CCR_TEIE) :
+                     (DMA_CCR_TCIE | DMA_CCR_TEIE));
+    }
+  else
+    {
+      /* In nonstop mode, when the transfer completes it immediately resets
+       * and starts again.  The transfer-complete interrupt is thus always
+       * enabled, and the half-complete interrupt can be used in circular
+       * mode to determine when the buffer is half-full, or in
+       * double-buffered mode to determine when one of the two buffers
+       * is full.
+       */
+
+      ccr |= (half ? DMA_CCR_HTIE : 0) | DMA_CCR_TCIE | DMA_CCR_TEIE;
+    }
+
+  dmachan_putreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET, ccr);
+}
+
+/****************************************************************************
+ * Name: stm32wb_dma12_residual
+ ****************************************************************************/
+
+static size_t stm32wb_dma12_residual(DMA_HANDLE handle)
+{
+  DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
+
+  DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2);
+
+  return dmachan_getreg(dmachan, STM32WB_DMACHAN_CNDTR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: stm32wb_dma12_sample
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+void stm32wb_dma12_sample(DMA_HANDLE handle, struct stm32wb_dmaregs_s *regs)
+{
+  DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  regs->isr = dmabase_getreg(dmachan, STM32WB_DMA_ISR_OFFSET);
+  regs->ccr = dmachan_getreg(dmachan, STM32WB_DMACHAN_CCR_OFFSET);
+  regs->cndtr = dmachan_getreg(dmachan, STM32WB_DMACHAN_CNDTR_OFFSET);
+  regs->cpar = dmachan_getreg(dmachan, STM32WB_DMACHAN_CPAR_OFFSET);
+  regs->cmar = dmachan_getreg(dmachan, STM32WB_DMACHAN_CMAR_OFFSET);
+
+  stm32wb_dmamux_sample(g_dma[dmachan->ctrl].dmamux,
+                        dmachan->chan + g_dma[dmachan->ctrl].dmamux_offset,
+                        regs);
+
+  leave_critical_section(flags);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_dma12_dump
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+static void stm32wb_dma12_dump(DMA_HANDLE handle,
+                               const struct stm32wb_dmaregs_s *regs,
+                               const char *msg)
+{
+  DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
+
+  DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2);
+
+  uint32_t dmabase = dmachan_getbase(dmachan);
+
+  dmainfo("DMA%d Registers: %s\n",
+          dmachan->ctrl + 1,
+          msg);
+  dmainfo("    ISR[%08x]: %08x\n",
+          dmabase + STM32WB_DMA_ISR_OFFSET,
+          regs->isr);
+  dmainfo("    CCR[%08x]: %08x\n",
+          dmachan->base + STM32WB_DMACHAN_CCR_OFFSET,
+          regs->ccr);
+  dmainfo("  CNDTR[%08x]: %08x\n",
+          dmachan->base + STM32WB_DMACHAN_CNDTR_OFFSET,
+          regs->cndtr);
+  dmainfo("   CPAR[%08x]: %08x\n",
+          dmachan->base + STM32WB_DMACHAN_CPAR_OFFSET,
+          regs->cpar);
+  dmainfo("   CMAR[%08x]: %08x\n",
+          dmachan->base + STM32WB_DMACHAN_CMAR_OFFSET,
+          regs->cmar);
+
+  stm32wb_dmamux_dump(g_dma[dmachan->ctrl].dmamux,
+                      dmachan->chan + g_dma[dmachan->ctrl].dmamux_offset,
+                      regs);
+}
+#endif
+
+#endif /* CONFIG_STM32WB_DMA1 || CONFIG_STM32WB_DMA2 */
+
+/****************************************************************************
+ * Name: stm32wb_dmamux_sample
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+static void stm32wb_dmamux_sample(DMA_MUX dmamux, uint8_t chan,
+                                  struct stm32wb_dmaregs_s *regs)
+{
+  regs->dmamux.ccr = dmamux_getreg(dmamux, STM32WB_DMAMUX_CXCR_OFFSET(chan));
+  regs->dmamux.csr = dmamux_getreg(dmamux, STM32WB_DMAMUX_CSR_OFFSET);
+  regs->dmamux.rg0cr = dmamux_getreg(dmamux, STM32WB_DMAMUX_RG0CR_OFFSET);
+  regs->dmamux.rg1cr = dmamux_getreg(dmamux, STM32WB_DMAMUX_RG1CR_OFFSET);
+  regs->dmamux.rg2cr = dmamux_getreg(dmamux, STM32WB_DMAMUX_RG2CR_OFFSET);
+  regs->dmamux.rg3cr = dmamux_getreg(dmamux, STM32WB_DMAMUX_RG3CR_OFFSET);
+  regs->dmamux.rgsr = dmamux_getreg(dmamux, STM32WB_DMAMUX_RGSR_OFFSET);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_dmamux_dump
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+static void stm32wb_dmamux_dump(DMA_MUX dmamux, uint8_t channel,
+                                const struct stm32wb_dmaregs_s *regs)
+{
+  dmainfo("DMAMUX%d CH=%d\n", dmamux->id, channel);
+  dmainfo("    CCR[%08x]: %08x\n",
+          dmamux->base + STM32WB_DMAMUX_CXCR_OFFSET(channel),
+          regs->dmamux.ccr);
+  dmainfo("    CSR[%08x]: %08x\n",
+          dmamux->base + STM32WB_DMAMUX_CSR_OFFSET, regs->dmamux.csr);
+  dmainfo("  RG0CR[%08x]: %08x\n",
+          dmamux->base + STM32WB_DMAMUX_RG0CR_OFFSET, regs->dmamux.rg0cr);
+  dmainfo("  RG1CR[%08x]: %08x\n",
+          dmamux->base + STM32WB_DMAMUX_RG1CR_OFFSET, regs->dmamux.rg1cr);
+  dmainfo("  RG2CR[%08x]: %08x\n",
+          dmamux->base + STM32WB_DMAMUX_RG2CR_OFFSET, regs->dmamux.rg2cr);
+  dmainfo("  RG3CR[%08x]: %08x\n",
+          dmamux->base + STM32WB_DMAMUX_RG3CR_OFFSET, regs->dmamux.rg3cr);
+  dmainfo("   RGSR[%08x]: %08x\n",
+          dmamux->base + STM32WB_DMAMUX_RGSR_OFFSET, regs->dmamux.rgsr);
+};
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: arm_dma_initialize
+ *
+ * Description:
+ *   Initialize the DMA subsystem (DMA1, DMA2)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void weak_function arm_dma_initialize(void)
+{
+  DMA_CHANNEL dmachan;
+  uint8_t controller;
+  int channel;
+
+  dmainfo("Initialize DMA\n");
+
+  /* Initialize DMA channels */
+
+  for (channel = 0; channel < DMA_NCHANNELS; channel++)
+    {
+      dmachan = &g_dmach[channel];
+
+      /* Initialize flag */
+
+      dmachan->used = false;
+
+      /* Get DMA controller associated with channel */
+
+      controller = dmachan->ctrl;
+
+      DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
+
+      /* Attach standard DMA interrupt vectors */
+
+      irq_attach(dmachan->irq, g_dma_ops[controller].dma_interrupt,
+                 dmachan);
+
+      /* Disable the DMA channel */
+
+      g_dma_ops[controller].dma_disable(dmachan);
+
+      /* Enable the IRQ at the NVIC (still disabled at the DMA controller) */
+
+      up_enable_irq(dmachan->irq);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32wb_dmachannel
+ *
+ * Description:
+ *   Allocate a DMA channel.  This function gives the caller mutually
+ *   exclusive access to the DMA channel specified by the 'dmamap' argument.
+ *   It is common for both DMA controllers (DMA1 and DMA2).
+ *
+ * Input Parameters:
+ *   dmamap - Identifies the stream/channel resource. For the STM32WB, this
+ *     is a bit-encoded value as provided by the DMAMAP_* definitions
+ *     in hardware/stm32wb_dmamux.h
+ *
+ * Returned Value:
+ *   One success, this function returns a non-NULL, void* DMA channel
+ *   handle.  NULL is returned on any failure.  This function can fail only
+ *   if no DMA channel is available.
+ *
+ * Assumptions:
+ *   - The caller does not hold he DMA channel.
+ *   - The caller can wait for the DMA channel to be freed if it is no
+ *     available.
+ *
+ ****************************************************************************/
+
+DMA_HANDLE stm32wb_dmachannel(unsigned int dmamap)
+{
+  DMA_CHANNEL dmachan;
+  uint8_t dmamux_req;
+  irqstate_t flags;
+  uint8_t controller;
+  uint8_t first = 0;
+  uint8_t nchan = 0;
+  int item = -1;
+  int i;
+
+  /* Get DMA controller from encoded DMAMAP value */
+
+  controller = DMAMAP_CONTROLLER(dmamap);
+  DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
+
+  /* Get DMAMUX channel from encoded DMAMAP value */
+
+  dmamux_req = DMAMAP_REQUEST(dmamap);
+
+  /* Get g_dma array limits for given controller */
+
+  stm32wb_gdma_limits_get(controller, &first, &nchan);
+
+  /* Find available channel for given controller */
+
+  flags = enter_critical_section();
+  for (i = first; i < first + nchan; i += 1)

Review Comment:
   ```suggestion
     for (i = first; i < first + nchan; i++)
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_tim.h:
##########
@@ -0,0 +1,1385 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_tim.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_TIM_CR1_OFFSET      0x0000  /* Control register 1 */
+#define STM32WB_TIM_CR2_OFFSET      0x0004  /* Control register 2 */
+#define STM32WB_TIM_SMCR_OFFSET     0x0008  /* Slave mode control register (TIM1, TIM2) */
+#define STM32WB_TIM_DIER_OFFSET     0x000c  /* DMA / Interrupt enable register */
+#define STM32WB_TIM_SR_OFFSET       0x0010  /* Status register */
+#define STM32WB_TIM_EGR_OFFSET      0x0014  /* Event generation register */
+#define STM32WB_TIM_CCMR1_OFFSET    0x0018  /* Capture/compare mode register 1 */
+#define STM32WB_TIM_CCMR2_OFFSET    0x001c  /* Capture/compare mode register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCER_OFFSET     0x0020  /* Capture/compare enable register */
+#define STM32WB_TIM_CNT_OFFSET      0x0024  /* Counter */
+#define STM32WB_TIM_PSC_OFFSET      0x0028  /* Prescaler */
+#define STM32WB_TIM_ARR_OFFSET      0x002c  /* Auto-reload register */
+#define STM32WB_TIM_RCR_OFFSET      0x0030  /* Repetition counter register (TIM1, TIM16/TIM17) */
+#define STM32WB_TIM_CCR1_OFFSET     0x0034  /* Capture/compare register 1 */
+#define STM32WB_TIM_CCR2_OFFSET     0x0038  /* Capture/compare register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR3_OFFSET     0x003c  /* Capture/compare register 3 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR4_OFFSET     0x0040  /* Capture/compare register 4 (TIM1, TIM2) */
+#define STM32WB_TIM_BDTR_OFFSET     0x0044  /* Break and dead-time register (TIM1, TIM16/17) */
+#define STM32WB_TIM_DCR_OFFSET      0x0048  /* DMA control register */
+#define STM32WB_TIM_DMAR_OFFSET     0x004c  /* DMA address for burst mode */
+#define STM32WB_TIM_OR1_OFFSET      0x0050  /* Option register 1 */
+#define STM32WB_TIM_CCMR3_OFFSET    0x0054  /* Capture/compare mode register 3 (TIM1) */
+#define STM32WB_TIM_CCR5_OFFSET     0x0058  /* Capture/compare register 5 (TIM1) */
+#define STM32WB_TIM_CCR6_OFFSET     0x005C  /* Capture/compare register 6 (TIM1) */
+#define STM32WB_TIM_AF1_OFFSET      0x0060  /* Alternate function register 1 */
+#define STM32WB_TIM_AF2_OFFSET      0x0064  /* Alternate function register 2 (TIM1) */
+#define STM32WB_TIM_TISEL_OFFSET    0x0068  /* Input selector register */
+
+/* Register Addresses *******************************************************/
+
+/* Advanced Timer TIM1 */
+
+#define STM32WB_TIM1_CR1            (STM32WB_TIM1_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM1_CR2            (STM32WB_TIM1_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM1_SMCR           (STM32WB_TIM1_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM1_DIER           (STM32WB_TIM1_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM1_SR             (STM32WB_TIM1_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM1_EGR            (STM32WB_TIM1_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM1_CCMR1          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM1_CCMR2          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM1_CCER           (STM32WB_TIM1_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM1_CNT            (STM32WB_TIM1_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM1_PSC            (STM32WB_TIM1_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM1_ARR            (STM32WB_TIM1_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM1_RCR            (STM32WB_TIM1_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM1_CCR1           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM1_CCR2           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM1_CCR3           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM1_CCR4           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM1_BDTR           (STM32WB_TIM1_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM1_DCR            (STM32WB_TIM1_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM1_DMAR           (STM32WB_TIM1_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM1_OR1            (STM32WB_TIM1_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM1_CCMR3          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR3_OFFSET)
+#define STM32WB_TIM1_CCR5           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR5_OFFSET)
+#define STM32WB_TIM1_CCR6           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR6_OFFSET)
+#define STM32WB_TIM1_AF1            (STM32WB_TIM1_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM1_AF2            (STM32WB_TIM1_BASE + STM32WB_TIM_AF2_OFFSET)
+#define STM32WB_TIM1_TISEL          (STM32WB_TIM1_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General 32-bit Timer TIM2 */
+
+#define STM32WB_TIM2_CR1            (STM32WB_TIM2_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM2_CR2            (STM32WB_TIM2_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM2_SMCR           (STM32WB_TIM2_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM2_DIER           (STM32WB_TIM2_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM2_SR             (STM32WB_TIM2_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM2_EGR            (STM32WB_TIM2_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM2_CCMR1          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM2_CCMR2          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM2_CCER           (STM32WB_TIM2_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM2_CNT            (STM32WB_TIM2_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM2_PSC            (STM32WB_TIM2_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM2_ARR            (STM32WB_TIM2_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM2_CCR1           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM2_CCR2           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM2_CCR3           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM2_CCR4           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM2_DCR            (STM32WB_TIM2_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM2_DMAR           (STM32WB_TIM2_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM2_OR1            (STM32WB_TIM2_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM2_AF1            (STM32WB_TIM2_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM2_TISEL          (STM32WB_TIM2_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General Timers TIM16/TIM17 */
+
+#define STM32WB_TIM16_CR1           (STM32WB_TIM16_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM16_CR2           (STM32WB_TIM16_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM16_DIER          (STM32WB_TIM16_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM16_SR            (STM32WB_TIM16_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM16_EGR           (STM32WB_TIM16_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM16_CCMR1         (STM32WB_TIM16_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM16_CCER          (STM32WB_TIM16_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM16_CNT           (STM32WB_TIM16_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM16_PSC           (STM32WB_TIM16_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM16_ARR           (STM32WB_TIM16_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM16_RCR           (STM32WB_TIM16_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM16_CCR1          (STM32WB_TIM16_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM16_BDTR          (STM32WB_TIM16_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM16_DCR           (STM32WB_TIM16_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM16_DMAR          (STM32WB_TIM16_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM16_OR1           (STM32WB_TIM16_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM16_AF1           (STM32WB_TIM16_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM16_TISEL         (STM32WB_TIM16_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+#define STM32WB_TIM17_CR1           (STM32WB_TIM17_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM17_CR2           (STM32WB_TIM17_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM17_DIER          (STM32WB_TIM17_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM17_SR            (STM32WB_TIM17_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM17_EGR           (STM32WB_TIM17_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM17_CCMR1         (STM32WB_TIM17_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM17_CCER          (STM32WB_TIM17_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM17_CNT           (STM32WB_TIM17_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM17_PSC           (STM32WB_TIM17_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM17_ARR           (STM32WB_TIM17_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM17_RCR           (STM32WB_TIM17_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM17_CCR1          (STM32WB_TIM17_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM17_BDTR          (STM32WB_TIM17_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM17_DCR           (STM32WB_TIM17_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM17_DMAR          (STM32WB_TIM17_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM17_OR1           (STM32WB_TIM17_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM17_AF1           (STM32WB_TIM17_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM17_TISEL         (STM32WB_TIM17_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* Register Value Constants *************************************************/
+
+/* Digital Filter options */
+
+#define STM32WB_DF_NOFILT           (0x0) /* 0000: No filter */
+#define STM32WB_DF_FCKINTn2         (0x1) /* 0001: fSAMPLING = fCK_INT, N=2 */
+#define STM32WB_DF_FCKINTn4         (0x2) /* 0010: fSAMPLING = fCK_INT, N=4 */
+#define STM32WB_DF_FCKINTn8         (0x3) /* 0011: fSAMPLING = fCK_INT, N=8 */
+#define STM32WB_DF_FDTSd2n6         (0x4) /* 0100: fSAMPLING = fDTS/2, N=6 */
+#define STM32WB_DF_FDTSd2n8         (0x5) /* 0101: fSAMPLING = fDTS/2, N=8 */
+#define STM32WB_DF_FDTSd4n6         (0x6) /* 0110: fSAMPLING = fDTS/4, N=6 */
+#define STM32WB_DF_FDTSd4n8         (0x7) /* 0111: fSAMPLING = fDTS/4, N=8 */
+#define STM32WB_DF_FDTSd8n6         (0x8) /* 1000: fSAMPLING = fDTS/8, N=6 */
+#define STM32WB_DF_FDTSd8n8         (0x9) /* 1001: fSAMPLING = fDTS/8, N=8 */
+#define STM32WB_DF_FDTSd16n5        (0xa) /* 1010: fSAMPLING = fDTS/16, N=5 */
+#define STM32WB_DF_FDTSd16n6        (0xb) /* 1011: fSAMPLING = fDTS/16, N=6 */
+#define STM32WB_DF_FDTSd16n8        (0xc) /* 1100: fSAMPLING = fDTS/16, N=8 */
+#define STM32WB_DF_FDTSd32n5        (0xd) /* 1101: fSAMPLING = fDTS/32, N=5 */
+#define STM32WB_DF_FDTSd32n6        (0xe) /* 1110: fSAMPLING = fDTS/32, N=6 */
+#define STM32WB_DF_FDTSd32n8        (0xf) /* 1111: fSAMPLING = fDTS/32, N=8 */
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Control register 1 */
+
+#define TIM1_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM1_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM1_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM1_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM1_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM1_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM1_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM1_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM1_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM1_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM1_CR1_CMS_MASK           (0x3 << TIM1_CR1_CMS_SHIFT)
+#  define TIM1_CR1_CMS_EDGE         (0x0 << TIM1_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM1_CR1_CMS_CNTR1        (0x1 << TIM1_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM1_CR1_CMS_CNTR2        (0x2 << TIM1_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM1_CR1_CMS_CNTR3        (0x3 << TIM1_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM1_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM1_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM1_CR1_CKD_MASK           (0x3 << TIM1_CR1_CKD_SHIFT)
+#  define TIM1_CR1_CKD_TCKINT       (0x0 << TIM1_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM1_CR1_CKD_2TCKINT      (0x1 << TIM1_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM1_CR1_CKD_4TCKINT      (0x2 << TIM1_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM1_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM2_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM2_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM2_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM2_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM2_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM2_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM2_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM2_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM2_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM2_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM2_CR1_CMS_MASK           (0x3 << TIM2_CR1_CMS_SHIFT)
+#  define TIM2_CR1_CMS_EDGE         (0x0 << TIM2_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM2_CR1_CMS_CNTR1        (0x1 << TIM2_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM2_CR1_CMS_CNTR2        (0x2 << TIM2_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM2_CR1_CMS_CNTR3        (0x3 << TIM2_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM2_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM2_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM2_CR1_CKD_MASK           (0x3 << TIM2_CR1_CKD_SHIFT)
+#  define TIM2_CR1_CKD_TCKINT       (0x0 << TIM2_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM2_CR1_CKD_2TCKINT      (0x1 << TIM2_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM2_CR1_CKD_4TCKINT      (0x2 << TIM2_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM2_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM16_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM16_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM16_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM16_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM16_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM16_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM16_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM16_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM16_CR1_CKD_MASK          (0x3 << TIM16_CR1_CKD_SHIFT)
+#  define TIM16_CR1_CKD_TCKINT      (0x0 << TIM16_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM16_CR1_CKD_2TCKINT     (0x1 << TIM16_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM16_CR1_CKD_4TCKINT     (0x2 << TIM16_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM16_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM17_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM17_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM17_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM17_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM17_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM17_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM17_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM17_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM17_CR1_CKD_MASK          (0x3 << TIM17_CR1_CKD_SHIFT)
+#  define TIM17_CR1_CKD_TCKINT      (0x0 << TIM17_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM17_CR1_CKD_2TCKINT     (0x1 << TIM17_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM17_CR1_CKD_4TCKINT     (0x2 << TIM17_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM17_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+/* Control register 2 */
+
+#define TIM1_CR2_CCPC               (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM1_CR2_CCUS               (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM1_CR2_CCUS_COMG        (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM1_CR2_CCUS_COMG_TRGI   (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM1_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM1_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM1_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM1_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM1_CR2_MMS_MASK           (0x7 << TIM1_CR2_MMS_SHIFT)
+#  define TIM1_CR2_MMS_RESET        (0x0 << TIM1_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM1_CR2_MMS_ENABLE       (0x1 << TIM1_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM1_CR2_MMS_UPDATE       (0x2 << TIM1_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM1_CR2_MMS_COMPP        (0x3 << TIM1_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS_OC1REF       (0x4 << TIM1_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM1_CR2_MMS_OC2REF       (0x5 << TIM1_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM1_CR2_MMS_OC3REF       (0x6 << TIM1_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM1_CR2_MMS_OC4REF       (0x7 << TIM1_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM1_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM1_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM1_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM1_CR2_OIS1               (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM1_CR2_OIS1N              (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+#define TIM1_CR2_OIS2               (1 << 10) /* Bit 10: Output Idle state 2 (OC2 output) */
+#define TIM1_CR2_OIS2N              (1 << 11) /* Bit 11: Output Idle state 2 (OC2N output) */
+#define TIM1_CR2_OIS3               (1 << 12) /* Bit 12: Output Idle state 3 (OC3 output) */
+#define TIM1_CR2_OIS3N              (1 << 13) /* Bit 13: Output Idle state 3 (OC3N output) */
+#define TIM1_CR2_OIS4               (1 << 14) /* Bit 14: Output Idle state 4 (OC4 output) */
+#define TIM1_CR2_OIS5               (1 << 16) /* Bit 16: Output Idle state 5 (OC5 output) */
+#define TIM1_CR2_OIS6               (1 << 18) /* Bit 18: Output Idle state 6 (OC6 output) */
+#define TIM1_CR2_MMS2_SHIFT         (20)      /* Bits 20-23: Master Mode Selection 2 */
+#define TIM1_CR2_MMS2_MASK          (0xf << TIM1_CR2_MMS2_SHIFT)
+#  define TIM1_CR2_MMS2_RESET       (0x0 << TIM1_CR2_MMS2_SHIFT) /* 0000: Reset - TIMx_EGR UG bit is TRG9 */
+#  define TIM1_CR2_MMS2_ENABLE      (0x1 << TIM1_CR2_MMS2_SHIFT) /* 0001: Enable - CNT_EN is TRGO2 */
+#  define TIM1_CR2_MMS2_UPDATE      (0x2 << TIM1_CR2_MMS2_SHIFT) /* 0010: Update event is TRGO2 */
+#  define TIM1_CR2_MMS2_COMPP       (0x3 << TIM1_CR2_MMS2_SHIFT) /* 0011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS2_OC1REF      (0x4 << TIM1_CR2_MMS2_SHIFT) /* 0100: Compare OC1REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC2REF      (0x5 << TIM1_CR2_MMS2_SHIFT) /* 0101: Compare OC2REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC3REF      (0x6 << TIM1_CR2_MMS2_SHIFT) /* 0110: Compare OC3REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC4REF      (0x7 << TIM1_CR2_MMS2_SHIFT) /* 0111: Compare OC4REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC5REF      (0x8 << TIM1_CR2_MMS2_SHIFT) /* 1000: Compare OC5REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC6REF      (0x9 << TIM1_CR2_MMS2_SHIFT) /* 1001: Compare OC6REF is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4      (0xa << TIM1_CR2_MMS2_SHIFT) /* 1010: Compare pulse - OC4REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC6      (0xb << TIM1_CR2_MMS2_SHIFT) /* 1011: Compare pulse - OC6REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4R6R   (0xc << TIM1_CR2_MMS2_SHIFT) /* 1100: Compare pulse - OC4REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC4R6F   (0xd << TIM1_CR2_MMS2_SHIFT) /* 1101: Compare pulse - OC4REF rising/OC6REF falling */
+#  define TIM1_CR2_MMS2_CMPOC5R6R   (0xe << TIM1_CR2_MMS2_SHIFT) /* 1110: Compare pulse - OC5REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC5R6F   (0xf << TIM1_CR2_MMS2_SHIFT) /* 1111: Compare pulse - OC5REF rising/OC6REF falling */
+
+#define TIM2_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM2_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM2_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM2_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM2_CR2_MMS_MASK           (0x7 << TIM2_CR2_MMS_SHIFT)
+#  define TIM2_CR2_MMS_RESET        (0x0 << TIM2_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM2_CR2_MMS_ENABLE       (0x1 << TIM2_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM2_CR2_MMS_UPDATE       (0x2 << TIM2_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM2_CR2_MMS_COMPP        (0x3 << TIM2_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM2_CR2_MMS_OC1REF       (0x4 << TIM2_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM2_CR2_MMS_OC2REF       (0x5 << TIM2_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM2_CR2_MMS_OC3REF       (0x6 << TIM2_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM2_CR2_MMS_OC4REF       (0x7 << TIM2_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM2_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM2_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM2_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM16_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM16_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM16_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM16_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM16_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM16_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM16_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM16_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM16_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+#define TIM17_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM17_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM17_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM17_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM17_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM17_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM17_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM17_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM17_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+/* Slave mode control register */
+
+#define TIM1_SMCR_SMS_LO_SHIFT      (0)       /* Bits 0-2: Slave mode selection, bits [2:0] */
+#define TIM1_SMCR_SMS_HI_SHIFT      (16)      /* Bit 16: Slave mode selection, bits [3] */
+#define TIM1_SMCR_SMS_BITS(h,l)     ((h << TIM1_SMCR_SMS_HI_SHIFT) | (l << TIM1_SMCR_SMS_LO_SHIFT))
+#define TIM1_SMCR_SMS_MASK          TIM1_SMCR_SMS_BITS(0x1, 0x7)
+#  define TIM1_SMCR_DISAB           TIM1_SMCR_SMS_BITS(0x0, 0x0) /* 0,000: Slave mode disabled */
+#  define TIM1_SMCR_ENCMD1          TIM1_SMCR_SMS_BITS(0x0, 0x1) /* 0,001: Encoder mode 1 */
+#  define TIM1_SMCR_ENCMD2          TIM1_SMCR_SMS_BITS(0x0, 0x2) /* 0,010: Encoder mode 2 */
+#  define TIM1_SMCR_ENCMD3          TIM1_SMCR_SMS_BITS(0x0, 0x3) /* 0,011: Encoder mode 3 */
+#  define TIM1_SMCR_RESET           TIM1_SMCR_SMS_BITS(0x0, 0x4) /* 0,100: Reset Mode */
+#  define TIM1_SMCR_GATED           TIM1_SMCR_SMS_BITS(0x0, 0x5) /* 0,101: Gated Mode */
+#  define TIM1_SMCR_TRIGGER         TIM1_SMCR_SMS_BITS(0x0, 0x6) /* 0,110: Trigger Mode */
+#  define TIM1_SMCR_EXTCLK1         TIM1_SMCR_SMS_BITS(0x0, 0x7) /* 0,111: External Clock Mode 1 */
+#  define TIM1_SMCR_SMS_COMBINED    TIM1_SMCR_SMS_BITS(0x1, 0x0) /* 1,000: Combined Reset and Trigger mode */
+
+#define TIM1_SMCR_OCCS              (1 << 3)  /* Bit 3: OCREF clear selection */
+#  define TIM1_SMCR_OCCS_CLR        (0 << 3)  /* 0: OCREF clear triggered by CLR input */
+#  define TIM1_SMCR_OCCS_ETRF       (1 << 3)  /* 1: OCREF clear triggered by ETRF */
+
+#define TIM1_SMCR_TS_LO_SHIFT       (4)       /* Bits 4-6: Trigger selection, bits [2:0] */
+#define TIM1_SMCR_TS_HI_SHIFT       (20)      /* Bits 20-21: Trigger selection, bits [1:0] */
+#define TIM1_SMCR_TS_BITS(h,l)      ((h << TIM1_SMCR_TS_HI_SHIFT) | (l << TIM1_SMCR_TS_LO_SHIFT))

Review Comment:
   ```suggestion
   #define TIM1_SMCR_TS_BITS(h,l)      (((h) << TIM1_SMCR_TS_HI_SHIFT) | ((l) << TIM1_SMCR_TS_LO_SHIFT))
   ```



##########
arch/arm/src/stm32wb/hardware/stm32wb_tim.h:
##########
@@ -0,0 +1,1385 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_tim.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_TIM_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_TIM_CR1_OFFSET      0x0000  /* Control register 1 */
+#define STM32WB_TIM_CR2_OFFSET      0x0004  /* Control register 2 */
+#define STM32WB_TIM_SMCR_OFFSET     0x0008  /* Slave mode control register (TIM1, TIM2) */
+#define STM32WB_TIM_DIER_OFFSET     0x000c  /* DMA / Interrupt enable register */
+#define STM32WB_TIM_SR_OFFSET       0x0010  /* Status register */
+#define STM32WB_TIM_EGR_OFFSET      0x0014  /* Event generation register */
+#define STM32WB_TIM_CCMR1_OFFSET    0x0018  /* Capture/compare mode register 1 */
+#define STM32WB_TIM_CCMR2_OFFSET    0x001c  /* Capture/compare mode register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCER_OFFSET     0x0020  /* Capture/compare enable register */
+#define STM32WB_TIM_CNT_OFFSET      0x0024  /* Counter */
+#define STM32WB_TIM_PSC_OFFSET      0x0028  /* Prescaler */
+#define STM32WB_TIM_ARR_OFFSET      0x002c  /* Auto-reload register */
+#define STM32WB_TIM_RCR_OFFSET      0x0030  /* Repetition counter register (TIM1, TIM16/TIM17) */
+#define STM32WB_TIM_CCR1_OFFSET     0x0034  /* Capture/compare register 1 */
+#define STM32WB_TIM_CCR2_OFFSET     0x0038  /* Capture/compare register 2 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR3_OFFSET     0x003c  /* Capture/compare register 3 (TIM1, TIM2) */
+#define STM32WB_TIM_CCR4_OFFSET     0x0040  /* Capture/compare register 4 (TIM1, TIM2) */
+#define STM32WB_TIM_BDTR_OFFSET     0x0044  /* Break and dead-time register (TIM1, TIM16/17) */
+#define STM32WB_TIM_DCR_OFFSET      0x0048  /* DMA control register */
+#define STM32WB_TIM_DMAR_OFFSET     0x004c  /* DMA address for burst mode */
+#define STM32WB_TIM_OR1_OFFSET      0x0050  /* Option register 1 */
+#define STM32WB_TIM_CCMR3_OFFSET    0x0054  /* Capture/compare mode register 3 (TIM1) */
+#define STM32WB_TIM_CCR5_OFFSET     0x0058  /* Capture/compare register 5 (TIM1) */
+#define STM32WB_TIM_CCR6_OFFSET     0x005C  /* Capture/compare register 6 (TIM1) */
+#define STM32WB_TIM_AF1_OFFSET      0x0060  /* Alternate function register 1 */
+#define STM32WB_TIM_AF2_OFFSET      0x0064  /* Alternate function register 2 (TIM1) */
+#define STM32WB_TIM_TISEL_OFFSET    0x0068  /* Input selector register */
+
+/* Register Addresses *******************************************************/
+
+/* Advanced Timer TIM1 */
+
+#define STM32WB_TIM1_CR1            (STM32WB_TIM1_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM1_CR2            (STM32WB_TIM1_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM1_SMCR           (STM32WB_TIM1_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM1_DIER           (STM32WB_TIM1_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM1_SR             (STM32WB_TIM1_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM1_EGR            (STM32WB_TIM1_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM1_CCMR1          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM1_CCMR2          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM1_CCER           (STM32WB_TIM1_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM1_CNT            (STM32WB_TIM1_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM1_PSC            (STM32WB_TIM1_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM1_ARR            (STM32WB_TIM1_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM1_RCR            (STM32WB_TIM1_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM1_CCR1           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM1_CCR2           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM1_CCR3           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM1_CCR4           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM1_BDTR           (STM32WB_TIM1_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM1_DCR            (STM32WB_TIM1_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM1_DMAR           (STM32WB_TIM1_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM1_OR1            (STM32WB_TIM1_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM1_CCMR3          (STM32WB_TIM1_BASE + STM32WB_TIM_CCMR3_OFFSET)
+#define STM32WB_TIM1_CCR5           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR5_OFFSET)
+#define STM32WB_TIM1_CCR6           (STM32WB_TIM1_BASE + STM32WB_TIM_CCR6_OFFSET)
+#define STM32WB_TIM1_AF1            (STM32WB_TIM1_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM1_AF2            (STM32WB_TIM1_BASE + STM32WB_TIM_AF2_OFFSET)
+#define STM32WB_TIM1_TISEL          (STM32WB_TIM1_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General 32-bit Timer TIM2 */
+
+#define STM32WB_TIM2_CR1            (STM32WB_TIM2_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM2_CR2            (STM32WB_TIM2_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM2_SMCR           (STM32WB_TIM2_BASE + STM32WB_TIM_SMCR_OFFSET)
+#define STM32WB_TIM2_DIER           (STM32WB_TIM2_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM2_SR             (STM32WB_TIM2_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM2_EGR            (STM32WB_TIM2_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM2_CCMR1          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM2_CCMR2          (STM32WB_TIM2_BASE + STM32WB_TIM_CCMR2_OFFSET)
+#define STM32WB_TIM2_CCER           (STM32WB_TIM2_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM2_CNT            (STM32WB_TIM2_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM2_PSC            (STM32WB_TIM2_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM2_ARR            (STM32WB_TIM2_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM2_CCR1           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM2_CCR2           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR2_OFFSET)
+#define STM32WB_TIM2_CCR3           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR3_OFFSET)
+#define STM32WB_TIM2_CCR4           (STM32WB_TIM2_BASE + STM32WB_TIM_CCR4_OFFSET)
+#define STM32WB_TIM2_DCR            (STM32WB_TIM2_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM2_DMAR           (STM32WB_TIM2_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM2_OR1            (STM32WB_TIM2_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM2_AF1            (STM32WB_TIM2_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM2_TISEL          (STM32WB_TIM2_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* General Timers TIM16/TIM17 */
+
+#define STM32WB_TIM16_CR1           (STM32WB_TIM16_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM16_CR2           (STM32WB_TIM16_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM16_DIER          (STM32WB_TIM16_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM16_SR            (STM32WB_TIM16_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM16_EGR           (STM32WB_TIM16_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM16_CCMR1         (STM32WB_TIM16_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM16_CCER          (STM32WB_TIM16_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM16_CNT           (STM32WB_TIM16_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM16_PSC           (STM32WB_TIM16_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM16_ARR           (STM32WB_TIM16_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM16_RCR           (STM32WB_TIM16_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM16_CCR1          (STM32WB_TIM16_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM16_BDTR          (STM32WB_TIM16_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM16_DCR           (STM32WB_TIM16_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM16_DMAR          (STM32WB_TIM16_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM16_OR1           (STM32WB_TIM16_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM16_AF1           (STM32WB_TIM16_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM16_TISEL         (STM32WB_TIM16_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+#define STM32WB_TIM17_CR1           (STM32WB_TIM17_BASE + STM32WB_TIM_CR1_OFFSET)
+#define STM32WB_TIM17_CR2           (STM32WB_TIM17_BASE + STM32WB_TIM_CR2_OFFSET)
+#define STM32WB_TIM17_DIER          (STM32WB_TIM17_BASE + STM32WB_TIM_DIER_OFFSET)
+#define STM32WB_TIM17_SR            (STM32WB_TIM17_BASE + STM32WB_TIM_SR_OFFSET)
+#define STM32WB_TIM17_EGR           (STM32WB_TIM17_BASE + STM32WB_TIM_EGR_OFFSET)
+#define STM32WB_TIM17_CCMR1         (STM32WB_TIM17_BASE + STM32WB_TIM_CCMR1_OFFSET)
+#define STM32WB_TIM17_CCER          (STM32WB_TIM17_BASE + STM32WB_TIM_CCER_OFFSET)
+#define STM32WB_TIM17_CNT           (STM32WB_TIM17_BASE + STM32WB_TIM_CNT_OFFSET)
+#define STM32WB_TIM17_PSC           (STM32WB_TIM17_BASE + STM32WB_TIM_PSC_OFFSET)
+#define STM32WB_TIM17_ARR           (STM32WB_TIM17_BASE + STM32WB_TIM_ARR_OFFSET)
+#define STM32WB_TIM17_RCR           (STM32WB_TIM17_BASE + STM32WB_TIM_RCR_OFFSET)
+#define STM32WB_TIM17_CCR1          (STM32WB_TIM17_BASE + STM32WB_TIM_CCR1_OFFSET)
+#define STM32WB_TIM17_BDTR          (STM32WB_TIM17_BASE + STM32WB_TIM_BDTR_OFFSET)
+#define STM32WB_TIM17_DCR           (STM32WB_TIM17_BASE + STM32WB_TIM_DCR_OFFSET)
+#define STM32WB_TIM17_DMAR          (STM32WB_TIM17_BASE + STM32WB_TIM_DMAR_OFFSET)
+#define STM32WB_TIM17_OR1           (STM32WB_TIM17_BASE + STM32WB_TIM_OR1_OFFSET)
+#define STM32WB_TIM17_AF1           (STM32WB_TIM17_BASE + STM32WB_TIM_AF1_OFFSET)
+#define STM32WB_TIM17_TISEL         (STM32WB_TIM17_BASE + STM32WB_TIM_TISEL_OFFSET)
+
+/* Register Value Constants *************************************************/
+
+/* Digital Filter options */
+
+#define STM32WB_DF_NOFILT           (0x0) /* 0000: No filter */
+#define STM32WB_DF_FCKINTn2         (0x1) /* 0001: fSAMPLING = fCK_INT, N=2 */
+#define STM32WB_DF_FCKINTn4         (0x2) /* 0010: fSAMPLING = fCK_INT, N=4 */
+#define STM32WB_DF_FCKINTn8         (0x3) /* 0011: fSAMPLING = fCK_INT, N=8 */
+#define STM32WB_DF_FDTSd2n6         (0x4) /* 0100: fSAMPLING = fDTS/2, N=6 */
+#define STM32WB_DF_FDTSd2n8         (0x5) /* 0101: fSAMPLING = fDTS/2, N=8 */
+#define STM32WB_DF_FDTSd4n6         (0x6) /* 0110: fSAMPLING = fDTS/4, N=6 */
+#define STM32WB_DF_FDTSd4n8         (0x7) /* 0111: fSAMPLING = fDTS/4, N=8 */
+#define STM32WB_DF_FDTSd8n6         (0x8) /* 1000: fSAMPLING = fDTS/8, N=6 */
+#define STM32WB_DF_FDTSd8n8         (0x9) /* 1001: fSAMPLING = fDTS/8, N=8 */
+#define STM32WB_DF_FDTSd16n5        (0xa) /* 1010: fSAMPLING = fDTS/16, N=5 */
+#define STM32WB_DF_FDTSd16n6        (0xb) /* 1011: fSAMPLING = fDTS/16, N=6 */
+#define STM32WB_DF_FDTSd16n8        (0xc) /* 1100: fSAMPLING = fDTS/16, N=8 */
+#define STM32WB_DF_FDTSd32n5        (0xd) /* 1101: fSAMPLING = fDTS/32, N=5 */
+#define STM32WB_DF_FDTSd32n6        (0xe) /* 1110: fSAMPLING = fDTS/32, N=6 */
+#define STM32WB_DF_FDTSd32n8        (0xf) /* 1111: fSAMPLING = fDTS/32, N=8 */
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Control register 1 */
+
+#define TIM1_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM1_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM1_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM1_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM1_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM1_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM1_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM1_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM1_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM1_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM1_CR1_CMS_MASK           (0x3 << TIM1_CR1_CMS_SHIFT)
+#  define TIM1_CR1_CMS_EDGE         (0x0 << TIM1_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM1_CR1_CMS_CNTR1        (0x1 << TIM1_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM1_CR1_CMS_CNTR2        (0x2 << TIM1_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM1_CR1_CMS_CNTR3        (0x3 << TIM1_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM1_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM1_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM1_CR1_CKD_MASK           (0x3 << TIM1_CR1_CKD_SHIFT)
+#  define TIM1_CR1_CKD_TCKINT       (0x0 << TIM1_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM1_CR1_CKD_2TCKINT      (0x1 << TIM1_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM1_CR1_CKD_4TCKINT      (0x2 << TIM1_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM1_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM2_CR1_CEN                (1 << 0)  /* Bit 0: Counter enable */
+#define TIM2_CR1_UDIS               (1 << 1)  /* Bit 1: Update disable */
+#define TIM2_CR1_URS                (1 << 2)  /* Bit 2: Update request source */
+#  define TIM2_CR1_URS_CNT_UP_DMA   (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM2_CR1_URS_CNT_DMA      (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM2_CR1_OPM                (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM2_CR1_DIR                (1 << 4)  /* Bit 4: Counter direction */
+#  define TIM2_CR1_DIR_UP           (0 << 4)  /* 0: Upcounter mode */
+#  define TIM2_CR1_DIR_DOWN         (1 << 4)  /* 1: Downcounter mode */
+
+#define TIM2_CR1_CMS_SHIFT          (5)       /* Bits 5-6: Center-aligned mode selection */
+#define TIM2_CR1_CMS_MASK           (0x3 << TIM2_CR1_CMS_SHIFT)
+#  define TIM2_CR1_CMS_EDGE         (0x0 << TIM2_CR1_CMS_SHIFT) /* 00: Edge-aligned mode */
+#  define TIM2_CR1_CMS_CNTR1        (0x1 << TIM2_CR1_CMS_SHIFT) /* 01: Center-aligned mode 1 */
+#  define TIM2_CR1_CMS_CNTR2        (0x2 << TIM2_CR1_CMS_SHIFT) /* 10: Center-aligned mode 2 */
+#  define TIM2_CR1_CMS_CNTR3        (0x3 << TIM2_CR1_CMS_SHIFT) /* 11: Center-aligned mode 3 */
+
+#define TIM2_CR1_ARPE               (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM2_CR1_CKD_SHIFT          (8)       /* Bits 8-9: Clock division */
+#define TIM2_CR1_CKD_MASK           (0x3 << TIM2_CR1_CKD_SHIFT)
+#  define TIM2_CR1_CKD_TCKINT       (0x0 << TIM2_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM2_CR1_CKD_2TCKINT      (0x1 << TIM2_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM2_CR1_CKD_4TCKINT      (0x2 << TIM2_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM2_CR1_UIFREMAP           (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM16_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM16_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM16_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM16_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM16_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM16_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM16_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM16_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM16_CR1_CKD_MASK          (0x3 << TIM16_CR1_CKD_SHIFT)
+#  define TIM16_CR1_CKD_TCKINT      (0x0 << TIM16_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM16_CR1_CKD_2TCKINT     (0x1 << TIM16_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM16_CR1_CKD_4TCKINT     (0x2 << TIM16_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM16_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+#define TIM17_CR1_CEN               (1 << 0)  /* Bit 0: Counter enable */
+#define TIM17_CR1_UDIS              (1 << 1)  /* Bit 1: Update disable */
+#define TIM17_CR1_URS               (1 << 2)  /* Bit 2: Update request source */
+#  define TIM17_CR1_URS_CNT_UP_DMA  (0 << 2)  /* 0: Counter overflow/underflow, Update from slave or UG, DMA */
+#  define TIM17_CR1_URS_CNT_DMA     (1 << 2)  /* 1: Counter overflow/underflow or DMA */
+
+#define TIM17_CR1_OPM               (1 << 3)  /* Bit 3: One pulse mode */
+#define TIM17_CR1_ARPE              (1 << 7)  /* Bit 7: Auto-reload preload enable */
+#define TIM17_CR1_CKD_SHIFT         (8)       /* Bits 8-9: Clock division */
+#define TIM17_CR1_CKD_MASK          (0x3 << TIM17_CR1_CKD_SHIFT)
+#  define TIM17_CR1_CKD_TCKINT      (0x0 << TIM17_CR1_CKD_SHIFT) /* 00: tDTS=tCK_INT */
+#  define TIM17_CR1_CKD_2TCKINT     (0x1 << TIM17_CR1_CKD_SHIFT) /* 01: tDTS=2*tCK_INT */
+#  define TIM17_CR1_CKD_4TCKINT     (0x2 << TIM17_CR1_CKD_SHIFT) /* 10: tDTS=4*tCK_INT */
+
+#define TIM17_CR1_UIFREMAP          (1 << 11) /* Bit 11: UIF status bit remap enable */
+
+/* Control register 2 */
+
+#define TIM1_CR2_CCPC               (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM1_CR2_CCUS               (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM1_CR2_CCUS_COMG        (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM1_CR2_CCUS_COMG_TRGI   (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM1_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM1_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM1_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM1_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM1_CR2_MMS_MASK           (0x7 << TIM1_CR2_MMS_SHIFT)
+#  define TIM1_CR2_MMS_RESET        (0x0 << TIM1_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM1_CR2_MMS_ENABLE       (0x1 << TIM1_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM1_CR2_MMS_UPDATE       (0x2 << TIM1_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM1_CR2_MMS_COMPP        (0x3 << TIM1_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS_OC1REF       (0x4 << TIM1_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM1_CR2_MMS_OC2REF       (0x5 << TIM1_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM1_CR2_MMS_OC3REF       (0x6 << TIM1_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM1_CR2_MMS_OC4REF       (0x7 << TIM1_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM1_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM1_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM1_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM1_CR2_OIS1               (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM1_CR2_OIS1N              (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+#define TIM1_CR2_OIS2               (1 << 10) /* Bit 10: Output Idle state 2 (OC2 output) */
+#define TIM1_CR2_OIS2N              (1 << 11) /* Bit 11: Output Idle state 2 (OC2N output) */
+#define TIM1_CR2_OIS3               (1 << 12) /* Bit 12: Output Idle state 3 (OC3 output) */
+#define TIM1_CR2_OIS3N              (1 << 13) /* Bit 13: Output Idle state 3 (OC3N output) */
+#define TIM1_CR2_OIS4               (1 << 14) /* Bit 14: Output Idle state 4 (OC4 output) */
+#define TIM1_CR2_OIS5               (1 << 16) /* Bit 16: Output Idle state 5 (OC5 output) */
+#define TIM1_CR2_OIS6               (1 << 18) /* Bit 18: Output Idle state 6 (OC6 output) */
+#define TIM1_CR2_MMS2_SHIFT         (20)      /* Bits 20-23: Master Mode Selection 2 */
+#define TIM1_CR2_MMS2_MASK          (0xf << TIM1_CR2_MMS2_SHIFT)
+#  define TIM1_CR2_MMS2_RESET       (0x0 << TIM1_CR2_MMS2_SHIFT) /* 0000: Reset - TIMx_EGR UG bit is TRG9 */
+#  define TIM1_CR2_MMS2_ENABLE      (0x1 << TIM1_CR2_MMS2_SHIFT) /* 0001: Enable - CNT_EN is TRGO2 */
+#  define TIM1_CR2_MMS2_UPDATE      (0x2 << TIM1_CR2_MMS2_SHIFT) /* 0010: Update event is TRGO2 */
+#  define TIM1_CR2_MMS2_COMPP       (0x3 << TIM1_CR2_MMS2_SHIFT) /* 0011: Compare Pulse - CC1IF flag */
+#  define TIM1_CR2_MMS2_OC1REF      (0x4 << TIM1_CR2_MMS2_SHIFT) /* 0100: Compare OC1REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC2REF      (0x5 << TIM1_CR2_MMS2_SHIFT) /* 0101: Compare OC2REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC3REF      (0x6 << TIM1_CR2_MMS2_SHIFT) /* 0110: Compare OC3REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC4REF      (0x7 << TIM1_CR2_MMS2_SHIFT) /* 0111: Compare OC4REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC5REF      (0x8 << TIM1_CR2_MMS2_SHIFT) /* 1000: Compare OC5REF is TRGO2 */
+#  define TIM1_CR2_MMS2_OC6REF      (0x9 << TIM1_CR2_MMS2_SHIFT) /* 1001: Compare OC6REF is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4      (0xa << TIM1_CR2_MMS2_SHIFT) /* 1010: Compare pulse - OC4REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC6      (0xb << TIM1_CR2_MMS2_SHIFT) /* 1011: Compare pulse - OC6REF edge is TRGO2 */
+#  define TIM1_CR2_MMS2_CMPOC4R6R   (0xc << TIM1_CR2_MMS2_SHIFT) /* 1100: Compare pulse - OC4REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC4R6F   (0xd << TIM1_CR2_MMS2_SHIFT) /* 1101: Compare pulse - OC4REF rising/OC6REF falling */
+#  define TIM1_CR2_MMS2_CMPOC5R6R   (0xe << TIM1_CR2_MMS2_SHIFT) /* 1110: Compare pulse - OC5REF/OC6REF rising */
+#  define TIM1_CR2_MMS2_CMPOC5R6F   (0xf << TIM1_CR2_MMS2_SHIFT) /* 1111: Compare pulse - OC5REF rising/OC6REF falling */
+
+#define TIM2_CR2_CCDS               (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM2_CR2_CCDS_CCE         (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM2_CR2_CCDS_UPDE        (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM2_CR2_MMS_SHIFT          (4)       /* Bits 4-6: Master mode selection */
+#define TIM2_CR2_MMS_MASK           (0x7 << TIM2_CR2_MMS_SHIFT)
+#  define TIM2_CR2_MMS_RESET        (0x0 << TIM2_CR2_MMS_SHIFT) /* 000: Reset - TIMx_EGR UG bit is TRGO */
+#  define TIM2_CR2_MMS_ENABLE       (0x1 << TIM2_CR2_MMS_SHIFT) /* 001: Enable - CNT_EN is TRGO */
+#  define TIM2_CR2_MMS_UPDATE       (0x2 << TIM2_CR2_MMS_SHIFT) /* 010: Update event is TRGO */
+#  define TIM2_CR2_MMS_COMPP        (0x3 << TIM2_CR2_MMS_SHIFT) /* 011: Compare Pulse - CC1IF flag */
+#  define TIM2_CR2_MMS_OC1REF       (0x4 << TIM2_CR2_MMS_SHIFT) /* 100: Compare OC1REF is TRGO */
+#  define TIM2_CR2_MMS_OC2REF       (0x5 << TIM2_CR2_MMS_SHIFT) /* 101: Compare OC2REF is TRGO */
+#  define TIM2_CR2_MMS_OC3REF       (0x6 << TIM2_CR2_MMS_SHIFT) /* 110: Compare OC3REF is TRGO */
+#  define TIM2_CR2_MMS_OC4REF       (0x7 << TIM2_CR2_MMS_SHIFT) /* 111: Compare OC4REF is TRGO */
+
+#define TIM2_CR2_TI1S               (1 << 7)  /* Bit 7: TI1 Selection */
+#  define TIM2_CR2_TI1S_CH1         (0 << 7)  /* 0: CH1 pin connected to TI1 input */
+#  define TIM2_CR2_TI1S_CH1CH2CH3   (1 << 7)  /* 1: CH1, CH2, CH3 pins connected to TI1 input (XOR logic) */
+
+#define TIM16_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM16_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM16_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM16_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM16_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM16_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM16_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM16_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM16_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+#define TIM17_CR2_CCPC              (1 << 0)  /* Bit 0:  Capture/Compare preloaded control (enable bit) */
+#define TIM17_CR2_CCUS              (1 << 2)  /* Bit 2:  Capture/Compare control Update selection */
+#  define TIM17_CR2_CCUS_COMG       (0 << 2)  /* 0: updated by setting COMG bit only */
+#  define TIM17_CR2_CCUS_COMG_TRGI  (1 << 2)  /* 1: updated by setting COMG or TRGI rising edge */
+
+#define TIM17_CR2_CCDS              (1 << 3)  /* Bit 3:  Capture/Compare DMA selection */
+#  define TIM17_CR2_CCDS_CCE        (0 << 3)  /* 0: CCx event triggers DMA request */
+#  define TIM17_CR2_CCDS_UPDE       (1 << 3)  /* 1: Update event triggers DMA request */
+
+#define TIM17_CR2_OIS1              (1 << 8)  /* Bit 8:  Output Idle state 1 (OC1 output) */
+#define TIM17_CR2_OIS1N             (1 << 9)  /* Bit 9:  Output Idle state 1 (OC1N output) */
+
+/* Slave mode control register */
+
+#define TIM1_SMCR_SMS_LO_SHIFT      (0)       /* Bits 0-2: Slave mode selection, bits [2:0] */
+#define TIM1_SMCR_SMS_HI_SHIFT      (16)      /* Bit 16: Slave mode selection, bits [3] */
+#define TIM1_SMCR_SMS_BITS(h,l)     ((h << TIM1_SMCR_SMS_HI_SHIFT) | (l << TIM1_SMCR_SMS_LO_SHIFT))

Review Comment:
   ```suggestion
   #define TIM1_SMCR_SMS_BITS(h,l)     (((h) << TIM1_SMCR_SMS_HI_SHIFT) | ((l) << TIM1_SMCR_SMS_LO_SHIFT))
   ```



##########
arch/arm/src/stm32wb/stm32wb_pmstandby.c:
##########
@@ -0,0 +1,84 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_pmstandby.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+
+#include "nvic.h"
+#include "stm32wb_pwr.h"
+#include "stm32wb_pm.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_pmstandby
+ *
+ * Description:
+ *   Enter STANDBY mode.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   On success, this function will not return (STANDBY mode can only be
+ *   terminated with a reset event).  Otherwise, STANDBY mode did not occur
+ *   and a negated errno value is returned to indicate the cause of the
+ *   failure.
+ *
+ ****************************************************************************/
+
+int stm32wb_pmstandby(void)
+{
+  uint32_t regval;
+
+  /* Clear the Wake-Up Flags by setting the CWUFx bits in the power status
+   * clear register
+   */
+
+  regval = PWR_SCR_CWUF1 | PWR_SCR_CWUF2 | PWR_SCR_CWUF3 |
+           PWR_SCR_CWUF4 | PWR_SCR_CWUF5;
+  putreg32(regval, STM32WB_PWR_SCR);
+
+  /* Select Standby mode */
+
+  regval  = getreg32(STM32WB_PWR_CR1);
+  regval &= ~PWR_CR1_LPMS_MASK;
+  regval |= PWR_CR1_LPMS_STANDBY;
+
+  putreg32(regval, STM32WB_PWR_CR1);
+
+  /* Set SLEEPDEEP bit of Cortex System Control Register */
+
+  regval  = getreg32(NVIC_SYSCON);
+  regval |= NVIC_SYSCON_SLEEPDEEP;
+  putreg32(regval, NVIC_SYSCON);
+
+  /* Sleep until the wakeup reset occurs */
+
+  asm("wfi");

Review Comment:
   ditto



##########
arch/arm/src/stm32wb/stm32wb_oneshot.c:
##########
@@ -0,0 +1,460 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_oneshot.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sched.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+
+#include "stm32wb_oneshot.h"
+
+#ifdef CONFIG_STM32WB_ONESHOT
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int stm32wb_oneshot_handler(int irq, void *context, void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct stm32wb_oneshot_s *g_oneshot[CONFIG_STM32WB_ONESHOT_MAXTIMERS];
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_handler
+ *
+ * Description:
+ *   Common timer interrupt callback.  When any oneshot timer interrupt
+ *   expires, this function will be called.  It will forward the call to
+ *   the next level up.
+ *
+ * Input Parameters:
+ *   oneshot - The state associated with the expired timer
+ *
+ * Returned Value:
+ *   Always returns OK
+ *
+ ****************************************************************************/
+
+static int stm32wb_oneshot_handler(int irq, void *context, void *arg)
+{
+  struct stm32wb_oneshot_s *oneshot = (struct stm32wb_oneshot_s *) arg;
+  oneshot_handler_t oneshot_handler;
+  void *oneshot_arg;
+
+  tmrinfo("Expired...\n");
+  DEBUGASSERT(oneshot != NULL && oneshot->handler);
+
+  /* The clock was stopped, but not disabled when the RC match occurred.
+   * Disable the TC now and disable any further interrupts.
+   */
+
+  STM32WB_TIM_SETISR(oneshot->tch, NULL, NULL, 0);
+  STM32WB_TIM_DISABLEINT(oneshot->tch, GTIM_DIER_UIE);
+  STM32WB_TIM_SETMODE(oneshot->tch, STM32WB_TIM_MODE_DISABLED);
+  STM32WB_TIM_ACKINT(oneshot->tch, GTIM_SR_UIF);
+
+  /* The timer is no longer running */
+
+  oneshot->running = false;
+
+  /* Forward the event, clearing out any vestiges */
+
+  oneshot_handler  = (oneshot_handler_t)oneshot->handler;
+  oneshot->handler = NULL;
+  oneshot_arg      = (void *)oneshot->arg;
+  oneshot->arg     = NULL;
+
+  oneshot_handler(oneshot_arg);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_allocate_handler
+ *
+ * Description:
+ *   Allocate a timer callback handler for the oneshot instance.
+ *
+ * Input Parameters:
+ *   oneshot - The state instance the new oneshot timer
+ *
+ * Returned Value:
+ *   Returns zero (OK) on success.  This can only fail if the number of
+ *   timers exceeds CONFIG_STM32WB_ONESHOT_MAXTIMERS.
+ *
+ ****************************************************************************/
+
+static inline int stm32wb_allocate_handler(struct stm32wb_oneshot_s *oneshot)
+{
+#if CONFIG_STM32WB_ONESHOT_MAXTIMERS > 1
+  int ret = -EBUSY;
+  int i;
+
+  /* Search for an unused handler */
+
+  sched_lock();
+  for (i = 0; i < CONFIG_STM32WB_ONESHOT_MAXTIMERS; i++)
+    {
+      /* Is this handler available? */
+
+      if (g_oneshot[i] == NULL)
+        {
+          /* Yes... assign it to this oneshot */
+
+          g_oneshot[i]   = oneshot;
+          oneshot->cbndx = i;
+          ret            = OK;
+          break;
+        }
+    }
+
+  sched_unlock();
+  return ret;
+
+#else
+  if (g_oneshot[0] == NULL)
+    {
+      g_oneshot[0] = oneshot;
+      return OK;
+    }
+
+  return -EBUSY;
+#endif
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_initialize
+ *
+ * Description:
+ *   Initialize the oneshot timer wrapper
+ *
+ * Input Parameters:
+ *   oneshot    Caller allocated instance of the oneshot state structure
+ *   chan       Timer counter channel to be used.
+ *   resolution The required resolution of the timer in units of
+ *              microseconds.  NOTE that the range is restricted to the
+ *              range of uint16_t (excluding zero).
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned
+ *   on failure.
+ *
+ ****************************************************************************/
+
+int stm32wb_oneshot_initialize(struct stm32wb_oneshot_s *oneshot,
+                               int chan, uint16_t resolution)
+{
+  uint32_t frequency;
+
+  tmrinfo("chan=%d resolution=%d usec\n", chan, resolution);
+  DEBUGASSERT(oneshot && resolution > 0);
+
+  /* Get the TC frequency the corresponds to the requested resolution */
+
+  frequency = USEC_PER_SEC / (uint32_t)resolution;
+  oneshot->frequency = frequency;
+
+  oneshot->tch = stm32wb_tim_init(chan);
+  if (!oneshot->tch)
+    {
+      tmrerr("ERROR: Failed to allocate TIM%d\n", chan);
+      return -EBUSY;
+    }
+
+  STM32WB_TIM_SETCLOCK(oneshot->tch, frequency);
+
+  /* Initialize the remaining fields in the state structure. */
+
+  oneshot->chan       = chan;
+  oneshot->running    = false;
+  oneshot->handler    = NULL;
+  oneshot->arg        = NULL;
+
+  /* Assign a callback handler to the oneshot */
+
+  return stm32wb_allocate_handler(oneshot);
+}
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_max_delay
+ *
+ * Description:
+ *   Determine the maximum delay of the one-shot timer (in microseconds)
+ *
+ ****************************************************************************/
+
+int stm32wb_oneshot_max_delay(struct stm32wb_oneshot_s *oneshot,
+                              uint64_t *usec)
+{
+  DEBUGASSERT(oneshot != NULL && usec != NULL);
+
+  *usec = (uint64_t)(UINT32_MAX / oneshot->frequency) *
+          (uint64_t)USEC_PER_SEC;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_start
+ *
+ * Description:
+ *   Start the oneshot timer
+ *
+ * Input Parameters:
+ *   oneshot Caller allocated instance of the oneshot state structure.  This
+ *           structure must have been previously initialized via a call to
+ *           stm32wb_oneshot_initialize();
+ *   handler The function to call when when the oneshot timer expires.
+ *   arg     An opaque argument that will accompany the callback.
+ *   ts      Provides the duration of the one shot timer.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned
+ *   on failure.
+ *
+ ****************************************************************************/
+
+int stm32wb_oneshot_start(struct stm32wb_oneshot_s *oneshot,
+                          oneshot_handler_t handler, void *arg,
+                          const struct timespec *ts)
+{
+  uint64_t usec;
+  uint64_t period;
+  irqstate_t flags;
+
+  tmrinfo("handler=%p arg=%p, ts=(%lu, %lu)\n",
+         handler, arg, (unsigned long)ts->tv_sec,
+         (unsigned long)ts->tv_nsec);
+  DEBUGASSERT(oneshot && handler && ts);
+  DEBUGASSERT(oneshot->tch);
+
+  /* Was the oneshot already running? */
+
+  flags = enter_critical_section();
+  if (oneshot->running)
+    {
+      /* Yes.. then cancel it */
+
+      tmrinfo("Already running... cancelling\n");
+      stm32wb_oneshot_cancel(oneshot, NULL);
+    }
+
+  /* Save the new handler and its argument */
+
+  oneshot->handler = handler;
+  oneshot->arg     = arg;
+
+  /* Express the delay in microseconds */
+
+  usec = (uint64_t)ts->tv_sec * USEC_PER_SEC +
+         (uint64_t)(ts->tv_nsec / NSEC_PER_USEC);
+
+  /* Get the timer counter frequency and determine the number of counts need
+   * to achieve the requested delay.
+   *
+   *   frequency = ticks / second
+   *   ticks     = seconds * frequency
+   *             = (usecs * frequency) / USEC_PER_SEC;
+   */
+
+  period = (usec * (uint64_t)oneshot->frequency) / USEC_PER_SEC;
+
+  tmrinfo("usec=%llu period=%08llx\n", usec, period);
+  DEBUGASSERT(period > 0 && period <= UINT32_MAX);
+
+  /* Set up to receive the callback when the interrupt occurs */
+
+  STM32WB_TIM_SETISR(oneshot->tch, stm32wb_oneshot_handler, oneshot, 0);
+
+  /* Set timer period */
+
+  oneshot->period = (uint32_t)period;
+  STM32WB_TIM_SETPERIOD(oneshot->tch, (uint32_t)period);
+
+  /* Start the counter */
+
+  STM32WB_TIM_SETMODE(oneshot->tch, STM32WB_TIM_MODE_PULSE);
+
+  STM32WB_TIM_ACKINT(oneshot->tch, GTIM_SR_UIF);
+  STM32WB_TIM_ENABLEINT(oneshot->tch, GTIM_DIER_UIE);
+
+  /* Enable interrupts.  We should get the callback when the interrupt
+   * occurs.
+   */
+
+  oneshot->running = true;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_cancel
+ *
+ * Description:
+ *   Cancel the oneshot timer and return the time remaining on the timer.
+ *
+ *   NOTE: This function may execute at a high rate with no timer running (as
+ *   when pre-emption is enabled and disabled).
+ *
+ * Input Parameters:
+ *   oneshot Caller allocated instance of the oneshot state structure.  This
+ *           structure must have been previously initialized via a call to
+ *           stm32wb_oneshot_initialize();
+ *   ts      The location in which to return the time remaining on the
+ *           oneshot timer.  A time of zero is returned if the timer is
+ *           not running.  ts may be zero in which case the time remaining
+ *           is not returned.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success.  A call to up_timer_cancel() when
+ *   the timer is not active should also return success; a negated errno
+ *   value is returned on any failure.
+ *
+ ****************************************************************************/
+
+int stm32wb_oneshot_cancel(struct stm32wb_oneshot_s *oneshot,
+                           struct timespec *ts)
+{
+  irqstate_t flags;
+  uint64_t usec;
+  uint64_t sec;
+  uint64_t nsec;
+  uint32_t count;
+  uint32_t period;
+
+  /* Was the timer running? */
+
+  flags = enter_critical_section();
+  if (!oneshot->running)
+    {
+      /* No.. Just return zero timer remaining and successful cancellation.
+       * This function may execute at a high rate with no timer running
+       * (as when pre-emption is enabled and disabled).
+       */
+
+      ts->tv_sec  = 0;
+      ts->tv_nsec = 0;
+      leave_critical_section(flags);
+      return OK;
+    }
+
+  /* Yes.. Get the timer counter and period registers and stop the counter.
+   * If the counter expires while we are doing this, the counter clock will
+   * be stopped, but the clock will not be disabled.
+   *
+   * The expected behavior is that the counter register will freezes at
+   * a value equal to the RC register when the timer expires.  The counter
+   * should have values between 0 and RC in all other cased.
+   *
+   * REVISIT:  This does not appear to be the case.
+   */
+
+  tmrinfo("Cancelling...\n");
+
+  count  = STM32WB_TIM_GETCOUNTER(oneshot->tch);
+  period = oneshot->period;
+
+  /* Now we can disable the interrupt and stop the timer. */
+
+  STM32WB_TIM_DISABLEINT(oneshot->tch, GTIM_DIER_UIE);
+  STM32WB_TIM_SETISR(oneshot->tch, NULL, NULL, 0);
+  STM32WB_TIM_SETMODE(oneshot->tch, STM32WB_TIM_MODE_DISABLED);
+
+  oneshot->running = false;
+  oneshot->handler = NULL;
+  oneshot->arg     = NULL;
+  leave_critical_section(flags);
+
+  /* Did the caller provide us with a location to return the time
+   * remaining?
+   */
+
+  if (ts)
+    {
+      /* Yes.. then calculate and return the time remaining on the
+       * oneshot timer.
+       */
+
+      tmrinfo("period=%lu count=%lu\n",
+             (unsigned long)period, (unsigned long)count);

Review Comment:
   ```suggestion
         tmrinfo("period=%lu count=%lu\n",
                 (unsigned long)period, (unsigned long)count);
   ```



##########
arch/arm/src/stm32wb/stm32wb_oneshot.c:
##########
@@ -0,0 +1,460 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_oneshot.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sched.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+
+#include "stm32wb_oneshot.h"
+
+#ifdef CONFIG_STM32WB_ONESHOT
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int stm32wb_oneshot_handler(int irq, void *context, void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct stm32wb_oneshot_s *g_oneshot[CONFIG_STM32WB_ONESHOT_MAXTIMERS];
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_handler
+ *
+ * Description:
+ *   Common timer interrupt callback.  When any oneshot timer interrupt
+ *   expires, this function will be called.  It will forward the call to
+ *   the next level up.
+ *
+ * Input Parameters:
+ *   oneshot - The state associated with the expired timer
+ *
+ * Returned Value:
+ *   Always returns OK
+ *
+ ****************************************************************************/
+
+static int stm32wb_oneshot_handler(int irq, void *context, void *arg)
+{
+  struct stm32wb_oneshot_s *oneshot = (struct stm32wb_oneshot_s *) arg;
+  oneshot_handler_t oneshot_handler;
+  void *oneshot_arg;
+
+  tmrinfo("Expired...\n");
+  DEBUGASSERT(oneshot != NULL && oneshot->handler);
+
+  /* The clock was stopped, but not disabled when the RC match occurred.
+   * Disable the TC now and disable any further interrupts.
+   */
+
+  STM32WB_TIM_SETISR(oneshot->tch, NULL, NULL, 0);
+  STM32WB_TIM_DISABLEINT(oneshot->tch, GTIM_DIER_UIE);
+  STM32WB_TIM_SETMODE(oneshot->tch, STM32WB_TIM_MODE_DISABLED);
+  STM32WB_TIM_ACKINT(oneshot->tch, GTIM_SR_UIF);
+
+  /* The timer is no longer running */
+
+  oneshot->running = false;
+
+  /* Forward the event, clearing out any vestiges */
+
+  oneshot_handler  = (oneshot_handler_t)oneshot->handler;
+  oneshot->handler = NULL;
+  oneshot_arg      = (void *)oneshot->arg;
+  oneshot->arg     = NULL;
+
+  oneshot_handler(oneshot_arg);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_allocate_handler
+ *
+ * Description:
+ *   Allocate a timer callback handler for the oneshot instance.
+ *
+ * Input Parameters:
+ *   oneshot - The state instance the new oneshot timer
+ *
+ * Returned Value:
+ *   Returns zero (OK) on success.  This can only fail if the number of
+ *   timers exceeds CONFIG_STM32WB_ONESHOT_MAXTIMERS.
+ *
+ ****************************************************************************/
+
+static inline int stm32wb_allocate_handler(struct stm32wb_oneshot_s *oneshot)
+{
+#if CONFIG_STM32WB_ONESHOT_MAXTIMERS > 1
+  int ret = -EBUSY;
+  int i;
+
+  /* Search for an unused handler */
+
+  sched_lock();
+  for (i = 0; i < CONFIG_STM32WB_ONESHOT_MAXTIMERS; i++)
+    {
+      /* Is this handler available? */
+
+      if (g_oneshot[i] == NULL)
+        {
+          /* Yes... assign it to this oneshot */
+
+          g_oneshot[i]   = oneshot;
+          oneshot->cbndx = i;
+          ret            = OK;
+          break;
+        }
+    }
+
+  sched_unlock();
+  return ret;
+
+#else
+  if (g_oneshot[0] == NULL)
+    {
+      g_oneshot[0] = oneshot;
+      return OK;
+    }
+
+  return -EBUSY;
+#endif
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_initialize
+ *
+ * Description:
+ *   Initialize the oneshot timer wrapper
+ *
+ * Input Parameters:
+ *   oneshot    Caller allocated instance of the oneshot state structure
+ *   chan       Timer counter channel to be used.
+ *   resolution The required resolution of the timer in units of
+ *              microseconds.  NOTE that the range is restricted to the
+ *              range of uint16_t (excluding zero).
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned
+ *   on failure.
+ *
+ ****************************************************************************/
+
+int stm32wb_oneshot_initialize(struct stm32wb_oneshot_s *oneshot,
+                               int chan, uint16_t resolution)
+{
+  uint32_t frequency;
+
+  tmrinfo("chan=%d resolution=%d usec\n", chan, resolution);
+  DEBUGASSERT(oneshot && resolution > 0);
+
+  /* Get the TC frequency the corresponds to the requested resolution */
+
+  frequency = USEC_PER_SEC / (uint32_t)resolution;
+  oneshot->frequency = frequency;
+
+  oneshot->tch = stm32wb_tim_init(chan);
+  if (!oneshot->tch)
+    {
+      tmrerr("ERROR: Failed to allocate TIM%d\n", chan);
+      return -EBUSY;
+    }
+
+  STM32WB_TIM_SETCLOCK(oneshot->tch, frequency);
+
+  /* Initialize the remaining fields in the state structure. */
+
+  oneshot->chan       = chan;
+  oneshot->running    = false;
+  oneshot->handler    = NULL;
+  oneshot->arg        = NULL;
+
+  /* Assign a callback handler to the oneshot */
+
+  return stm32wb_allocate_handler(oneshot);
+}
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_max_delay
+ *
+ * Description:
+ *   Determine the maximum delay of the one-shot timer (in microseconds)
+ *
+ ****************************************************************************/
+
+int stm32wb_oneshot_max_delay(struct stm32wb_oneshot_s *oneshot,
+                              uint64_t *usec)
+{
+  DEBUGASSERT(oneshot != NULL && usec != NULL);
+
+  *usec = (uint64_t)(UINT32_MAX / oneshot->frequency) *
+          (uint64_t)USEC_PER_SEC;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_start
+ *
+ * Description:
+ *   Start the oneshot timer
+ *
+ * Input Parameters:
+ *   oneshot Caller allocated instance of the oneshot state structure.  This
+ *           structure must have been previously initialized via a call to
+ *           stm32wb_oneshot_initialize();
+ *   handler The function to call when when the oneshot timer expires.
+ *   arg     An opaque argument that will accompany the callback.
+ *   ts      Provides the duration of the one shot timer.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned
+ *   on failure.
+ *
+ ****************************************************************************/
+
+int stm32wb_oneshot_start(struct stm32wb_oneshot_s *oneshot,
+                          oneshot_handler_t handler, void *arg,
+                          const struct timespec *ts)
+{
+  uint64_t usec;
+  uint64_t period;
+  irqstate_t flags;
+
+  tmrinfo("handler=%p arg=%p, ts=(%lu, %lu)\n",
+         handler, arg, (unsigned long)ts->tv_sec,
+         (unsigned long)ts->tv_nsec);
+  DEBUGASSERT(oneshot && handler && ts);
+  DEBUGASSERT(oneshot->tch);
+
+  /* Was the oneshot already running? */
+
+  flags = enter_critical_section();
+  if (oneshot->running)
+    {
+      /* Yes.. then cancel it */
+
+      tmrinfo("Already running... cancelling\n");
+      stm32wb_oneshot_cancel(oneshot, NULL);
+    }
+
+  /* Save the new handler and its argument */
+
+  oneshot->handler = handler;
+  oneshot->arg     = arg;
+
+  /* Express the delay in microseconds */
+
+  usec = (uint64_t)ts->tv_sec * USEC_PER_SEC +
+         (uint64_t)(ts->tv_nsec / NSEC_PER_USEC);
+
+  /* Get the timer counter frequency and determine the number of counts need
+   * to achieve the requested delay.
+   *
+   *   frequency = ticks / second
+   *   ticks     = seconds * frequency
+   *             = (usecs * frequency) / USEC_PER_SEC;
+   */
+
+  period = (usec * (uint64_t)oneshot->frequency) / USEC_PER_SEC;
+
+  tmrinfo("usec=%llu period=%08llx\n", usec, period);
+  DEBUGASSERT(period > 0 && period <= UINT32_MAX);
+
+  /* Set up to receive the callback when the interrupt occurs */
+
+  STM32WB_TIM_SETISR(oneshot->tch, stm32wb_oneshot_handler, oneshot, 0);
+
+  /* Set timer period */
+
+  oneshot->period = (uint32_t)period;
+  STM32WB_TIM_SETPERIOD(oneshot->tch, (uint32_t)period);
+
+  /* Start the counter */
+
+  STM32WB_TIM_SETMODE(oneshot->tch, STM32WB_TIM_MODE_PULSE);
+
+  STM32WB_TIM_ACKINT(oneshot->tch, GTIM_SR_UIF);
+  STM32WB_TIM_ENABLEINT(oneshot->tch, GTIM_DIER_UIE);
+
+  /* Enable interrupts.  We should get the callback when the interrupt
+   * occurs.
+   */
+
+  oneshot->running = true;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_oneshot_cancel
+ *
+ * Description:
+ *   Cancel the oneshot timer and return the time remaining on the timer.
+ *
+ *   NOTE: This function may execute at a high rate with no timer running (as
+ *   when pre-emption is enabled and disabled).
+ *
+ * Input Parameters:
+ *   oneshot Caller allocated instance of the oneshot state structure.  This
+ *           structure must have been previously initialized via a call to
+ *           stm32wb_oneshot_initialize();
+ *   ts      The location in which to return the time remaining on the
+ *           oneshot timer.  A time of zero is returned if the timer is
+ *           not running.  ts may be zero in which case the time remaining
+ *           is not returned.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success.  A call to up_timer_cancel() when
+ *   the timer is not active should also return success; a negated errno
+ *   value is returned on any failure.
+ *
+ ****************************************************************************/
+
+int stm32wb_oneshot_cancel(struct stm32wb_oneshot_s *oneshot,
+                           struct timespec *ts)
+{
+  irqstate_t flags;
+  uint64_t usec;
+  uint64_t sec;
+  uint64_t nsec;
+  uint32_t count;
+  uint32_t period;
+
+  /* Was the timer running? */
+
+  flags = enter_critical_section();
+  if (!oneshot->running)
+    {
+      /* No.. Just return zero timer remaining and successful cancellation.
+       * This function may execute at a high rate with no timer running
+       * (as when pre-emption is enabled and disabled).
+       */
+
+      ts->tv_sec  = 0;
+      ts->tv_nsec = 0;
+      leave_critical_section(flags);
+      return OK;
+    }
+
+  /* Yes.. Get the timer counter and period registers and stop the counter.
+   * If the counter expires while we are doing this, the counter clock will
+   * be stopped, but the clock will not be disabled.
+   *
+   * The expected behavior is that the counter register will freezes at
+   * a value equal to the RC register when the timer expires.  The counter
+   * should have values between 0 and RC in all other cased.
+   *
+   * REVISIT:  This does not appear to be the case.
+   */
+
+  tmrinfo("Cancelling...\n");
+
+  count  = STM32WB_TIM_GETCOUNTER(oneshot->tch);
+  period = oneshot->period;
+
+  /* Now we can disable the interrupt and stop the timer. */
+
+  STM32WB_TIM_DISABLEINT(oneshot->tch, GTIM_DIER_UIE);
+  STM32WB_TIM_SETISR(oneshot->tch, NULL, NULL, 0);
+  STM32WB_TIM_SETMODE(oneshot->tch, STM32WB_TIM_MODE_DISABLED);
+
+  oneshot->running = false;
+  oneshot->handler = NULL;
+  oneshot->arg     = NULL;
+  leave_critical_section(flags);
+
+  /* Did the caller provide us with a location to return the time
+   * remaining?
+   */
+
+  if (ts)
+    {
+      /* Yes.. then calculate and return the time remaining on the
+       * oneshot timer.
+       */
+
+      tmrinfo("period=%lu count=%lu\n",
+             (unsigned long)period, (unsigned long)count);
+
+      /* REVISIT: I am not certain why the timer counter value sometimes
+       * exceeds RC.  Might be a bug, or perhaps the counter does not stop
+       * in all cases.
+       */
+
+      if (count >= period)
+        {
+          /* No time remaining (?) */
+
+          ts->tv_sec  = 0;
+          ts->tv_nsec = 0;
+        }
+      else
+        {
+          /* The total time remaining is the difference.  Convert that
+           * to units of microseconds.
+           *
+           *   frequency = ticks / second
+           *   seconds   = ticks * frequency
+           *   usecs     = (ticks * USEC_PER_SEC) / frequency;
+           */
+
+          usec        = (((uint64_t)(period - count)) * USEC_PER_SEC) /
+                        oneshot->frequency;
+
+          /* Return the time remaining in the correct form */
+
+          sec         = usec / USEC_PER_SEC;
+          nsec        = ((usec) - (sec * USEC_PER_SEC)) * NSEC_PER_USEC;

Review Comment:
   ```suggestion
             nsec        = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC;
   ```



##########
arch/arm/src/stm32wb/stm32wb_rtc.c:
##########
@@ -0,0 +1,1835 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_rtc.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <errno.h>
+#include <debug.h>
+#include <assert.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/time.h>
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb_rcc.h"
+#include "stm32wb_pwr.h"
+#include "stm32wb_rtc.h"
+#include "stm32wb_exti.h"
+
+#ifdef CONFIG_STM32WB_RTC
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* This RTC implementation supports
+ *  - date/time RTC hardware
+ *  - extended functions Alarm A and B
+ * */
+
+#ifndef CONFIG_RTC_DATETIME
+#  error "CONFIG_RTC_DATETIME must be set to use this driver"
+#endif
+
+#ifdef CONFIG_RTC_HIRES
+#  error "CONFIG_RTC_HIRES must NOT be set with this driver"
+#endif
+
+#ifndef CONFIG_STM32WB_PWR
+#  error "CONFIG_STM32WB_PWR must selected to use this driver"
+#endif
+
+/* Constants ****************************************************************/
+
+#define SYNCHRO_TIMEOUT               (0x00020000)
+#define INITMODE_TIMEOUT              (0x00010000)
+
+#define RTC_ALRMR_ENABLE              (0x00000000)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+typedef unsigned int rtc_alarmreg_t;
+
+struct alm_cbinfo_s
+{
+  volatile alm_callback_t ac_cb; /* Client callback function */
+  volatile void *ac_arg;         /* Argument to pass with the callback function */
+};
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+/* Callback to use when an EXTI is activated  */
+
+static struct alm_cbinfo_s g_alarmcb[RTC_ALARM_LAST];
+static bool g_alarm_enabled;  /* True: Alarm interrupts are enabled */
+#endif
+
+#ifdef CONFIG_RTC_PERIODIC
+static wakeupcb_t g_wakeupcb;
+static bool g_wakeup_enabled;  /* True: Wakeup interrupts are enabled */
+#endif
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/* g_rtc_enabled is set true after the RTC has successfully initialized */
+
+volatile bool g_rtc_enabled = false;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int rtchw_check_alrawf(void);
+static int rtchw_set_alrmar(rtc_alarmreg_t alarmreg);
+#if CONFIG_RTC_NALARMS > 1
+static int rtchw_check_alrbwf(void);
+static int rtchw_set_alrmbr(rtc_alarmreg_t alarmreg);
+#endif
+static inline void rtc_enable_alarm(void);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rtc_dumpregs
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_RTC_INFO
+static void rtc_dumpregs(const char *msg)
+{
+  rtcinfo("%s:\n", msg);
+  rtcinfo("      TR: %08x\n", getreg32(STM32WB_RTC_TR));
+  rtcinfo("      DR: %08x\n", getreg32(STM32WB_RTC_DR));
+  rtcinfo("      CR: %08x\n", getreg32(STM32WB_RTC_CR));
+  rtcinfo("     ISR: %08x\n", getreg32(STM32WB_RTC_ISR));
+  rtcinfo("    PRER: %08x\n", getreg32(STM32WB_RTC_PRER));
+  rtcinfo("    WUTR: %08x\n", getreg32(STM32WB_RTC_WUTR));
+  rtcinfo("  ALRMAR: %08x\n", getreg32(STM32WB_RTC_ALRMAR));
+  rtcinfo("  ALRMBR: %08x\n", getreg32(STM32WB_RTC_ALRMBR));
+  rtcinfo("  SHIFTR: %08x\n", getreg32(STM32WB_RTC_SHIFTR));
+  rtcinfo("    TSTR: %08x\n", getreg32(STM32WB_RTC_TSTR));
+  rtcinfo("    TSDR: %08x\n", getreg32(STM32WB_RTC_TSDR));
+  rtcinfo("   TSSSR: %08x\n", getreg32(STM32WB_RTC_TSSSR));
+  rtcinfo("    CALR: %08x\n", getreg32(STM32WB_RTC_CALR));
+  rtcinfo("  TAMPCR: %08x\n", getreg32(STM32WB_RTC_TAMPCR));
+  rtcinfo("ALRMASSR: %08x\n", getreg32(STM32WB_RTC_ALRMASSR));
+  rtcinfo("ALRMBSSR: %08x\n", getreg32(STM32WB_RTC_ALRMBSSR));
+  rtcinfo("      OR: %08x\n", getreg32(STM32WB_RTC_OR));
+  rtcinfo("MAGICREG: %08x\n", getreg32(RTC_MAGIC_REG));
+}
+#else
+#  define rtc_dumpregs(msg)
+#endif
+
+/****************************************************************************
+ * Name: rtc_dumptime
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_RTC_INFO
+static void rtc_dumptime(const struct tm *tp, const char *msg)
+{
+  rtcinfo("%s:\n", msg);
+  rtcinfo("  tm_sec: %08x\n", tp->tm_sec);
+  rtcinfo("  tm_min: %08x\n", tp->tm_min);
+  rtcinfo(" tm_hour: %08x\n", tp->tm_hour);
+  rtcinfo(" tm_mday: %08x\n", tp->tm_mday);
+  rtcinfo("  tm_mon: %08x\n", tp->tm_mon);
+  rtcinfo(" tm_year: %08x\n", tp->tm_year);
+}
+#else
+#  define rtc_dumptime(tp, msg)
+#endif
+
+/****************************************************************************
+ * Name: rtc_wprunlock
+ *
+ * Description:
+ *    Disable RTC write protection
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void rtc_wprunlock(void)
+{
+  /* Enable write access to the backup domain. */
+
+  stm32wb_pwr_enablebkp(true);
+
+  /* The following steps are required to unlock the write protection on
+   * all the RTC registers (except for RTC_ISR[13:8], RTC_TAFCR, and
+   * RTC_BKPxR).
+   *
+   * 1. Write 0xca into the RTC_WPR register.
+   * 2. Write 0x53 into the RTC_WPR register.
+   *
+   * Writing a wrong key re-activates the write protection.
+   */
+
+  putreg32(RTC_WPR_KEY1, STM32WB_RTC_WPR);
+  putreg32(RTC_WPR_KEY2, STM32WB_RTC_WPR);
+}
+
+/****************************************************************************
+ * Name: rtc_wprlock
+ *
+ * Description:
+ *    Enable RTC write protection
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void rtc_wprlock(void)
+{
+  /* Writing any wrong key re-activates the write protection. */
+
+  putreg32(0xff, STM32WB_RTC_WPR);
+
+  /* Disable write access to the backup domain. */
+
+  stm32wb_pwr_enablebkp(false);
+}
+
+/****************************************************************************
+ * Name: rtc_synchwait
+ *
+ * Description:
+ *   Waits until the RTC Time and Date registers (RTC_TR and RTC_DR) are
+ *   synchronized with RTC APB clock.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int rtc_synchwait(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret;
+
+  /* Clear Registers synchronization flag (RSF) */
+
+  regval  = getreg32(STM32WB_RTC_ISR);
+  regval &= ~RTC_ISR_RSF;
+  putreg32(regval, STM32WB_RTC_ISR);
+
+  /* Now wait the registers to become synchronised */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < SYNCHRO_TIMEOUT; timeout++)
+    {
+      regval = getreg32(STM32WB_RTC_ISR);
+      if ((regval & RTC_ISR_RSF) != 0)
+        {
+          /* Synchronized */
+
+          ret = OK;
+          break;
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: rtc_enterinit
+ *
+ * Description:
+ *   Enter RTC initialization mode.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int rtc_enterinit(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret;
+
+  /* Check if the Initialization mode is already set */
+
+  regval = getreg32(STM32WB_RTC_ISR);
+
+  ret = OK;
+  if ((regval & RTC_ISR_INITF) == 0)
+    {
+      /* Set the Initialization mode */
+
+      putreg32(RTC_ISR_INIT, STM32WB_RTC_ISR);
+
+      /* Wait until the RTC is in the INIT state (or a timeout occurs) */
+
+      ret = -ETIMEDOUT;
+      for (timeout = 0; timeout < INITMODE_TIMEOUT; timeout++)
+        {
+          regval = getreg32(STM32WB_RTC_ISR);
+          if ((regval & RTC_ISR_INITF) != 0)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: rtc_exitinit
+ *
+ * Description:
+ *   Exit RTC initialization mode.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static void rtc_exitinit(void)
+{
+  uint32_t regval;
+
+  regval = getreg32(STM32WB_RTC_ISR);
+  regval &= ~(RTC_ISR_INIT);
+  putreg32(regval, STM32WB_RTC_ISR);
+}
+
+/****************************************************************************
+ * Name: rtc_bin2bcd
+ *
+ * Description:
+ *   Converts a 2 digit binary to BCD format
+ *
+ * Input Parameters:
+ *   value - The byte to be converted.
+ *
+ * Returned Value:
+ *   The value in BCD representation
+ *
+ ****************************************************************************/
+
+static uint32_t rtc_bin2bcd(int value)
+{
+  uint32_t msbcd = 0;
+
+  while (value >= 10)
+    {
+      msbcd++;
+      value -= 10;
+    }
+
+  return (msbcd << 4) | value;
+}
+
+/****************************************************************************
+ * Name: rtc_bin2bcd
+ *
+ * Description:
+ *   Convert from 2 digit BCD to binary.
+ *
+ * Input Parameters:
+ *   value - The BCD value to be converted.
+ *
+ * Returned Value:
+ *   The value in binary representation
+ *
+ ****************************************************************************/
+
+static int rtc_bcd2bin(uint32_t value)
+{
+  uint32_t tens = (value >> 4) * 10;
+  return (int)(tens + (value & 0x0f));
+}
+
+/****************************************************************************
+ * Name: rtc_resume
+ *
+ * Description:
+ *   Called when the RTC was already initialized on a previous power cycle.
+ *   This just brings the RTC back into full operation.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static void rtc_resume(void)
+{
+#ifdef CONFIG_RTC_ALARM
+  uint32_t regval;
+
+  /* Clear the RTC alarm flags */
+
+  regval  = getreg32(STM32WB_RTC_ISR);
+  regval &= ~(RTC_ISR_ALRAF | RTC_ISR_ALRBF);
+  putreg32(regval, STM32WB_RTC_ISR);
+
+  /* Clear the EXTI Line 17 Pending bit (Connected internally to RTC Alarm) */
+
+  putreg32(EXTI_PR1_PIF(EXTI_EVT_RTCALARM), STM32WB_EXTI_PR1);
+#endif
+}
+
+/****************************************************************************
+ * Name: stm32wb_rtc_alarm_handler
+ *
+ * Description:
+ *   RTC ALARM interrupt service routine through the EXTI line
+ *
+ * Input Parameters:
+ *   irq - The IRQ number that generated the interrupt
+ *   context - Architecture specific register save information.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; A negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int stm32wb_rtc_alarm_handler(int irq, void *context,
+                                     void *rtc_handler_arg)
+{
+  struct alm_cbinfo_s *cbinfo;
+  alm_callback_t cb;
+  void *arg;
+  uint32_t isr;
+  uint32_t cr;
+  int ret = OK;
+
+  /* Enable write access to the backup domain (RTC registers, RTC
+   * backup data registers and backup SRAM).
+   */
+
+  stm32wb_pwr_enablebkp(true);
+
+  /* Check for EXTI from Alarm A or B and handle according */
+
+  cr  = getreg32(STM32WB_RTC_CR);
+  if ((cr & RTC_CR_ALRAIE) != 0)
+    {
+      isr  = getreg32(STM32WB_RTC_ISR);
+      if ((isr & RTC_ISR_ALRAF) != 0)
+        {
+          cbinfo = &g_alarmcb[RTC_ALARMA];
+          if (cbinfo->ac_cb != NULL)
+            {
+              /* Alarm A callback */
+
+              cb  = cbinfo->ac_cb;
+              arg = (void *)cbinfo->ac_arg;
+
+              cbinfo->ac_cb  = NULL;
+              cbinfo->ac_arg = NULL;
+
+              cb(arg, RTC_ALARMA);
+            }
+
+          /* note, bits 8-13 do /not/ require the write enable procedure */
+
+          isr  = getreg32(STM32WB_RTC_ISR);
+          isr &= ~RTC_ISR_ALRAF;
+          putreg32(isr, STM32WB_RTC_ISR);
+        }
+    }
+
+#if CONFIG_RTC_NALARMS > 1
+  cr  = getreg32(STM32WB_RTC_CR);
+  if ((cr & RTC_CR_ALRBIE) != 0)
+    {
+      isr  = getreg32(STM32WB_RTC_ISR);
+      if ((isr & RTC_ISR_ALRBF) != 0)
+        {
+          cbinfo = &g_alarmcb[RTC_ALARMB];
+          if (cbinfo->ac_cb != NULL)
+            {
+              /* Alarm B callback */
+
+              cb  = cbinfo->ac_cb;
+              arg = (void *)cbinfo->ac_arg;
+
+              cbinfo->ac_cb  = NULL;
+              cbinfo->ac_arg = NULL;
+
+              cb(arg, RTC_ALARMB);
+            }
+
+          /* note, bits 8-13 do /not/ require the write enable procedure */
+
+          isr  = getreg32(STM32WB_RTC_ISR);
+          isr &= ~RTC_ISR_ALRBF;
+          putreg32(isr, STM32WB_RTC_ISR);
+        }
+    }
+#endif
+
+  /* Disable write access to the backup domain (RTC registers, RTC backup
+   * data registers and backup SRAM).
+   */
+
+  stm32wb_pwr_enablebkp(false);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: rtchw_check_alrXwf X= a or B
+ *
+ * Description:
+ *   Check registers
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int rtchw_check_alrawf(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret = -ETIMEDOUT;
+
+  /* Check RTC_ISR ALRAWF for access to alarm register,
+   * Can take 2 RTCCLK cycles or timeout
+   * CubeMX use GetTick.
+   */
+
+  for (timeout = 0; timeout < INITMODE_TIMEOUT; timeout++)
+    {
+      regval = getreg32(STM32WB_RTC_ISR);
+      if ((regval & RTC_ISR_ALRAWF) != 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  return ret;
+}
+#endif
+
+#if defined(CONFIG_RTC_ALARM) && CONFIG_RTC_NALARMS > 1
+static int rtchw_check_alrbwf(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret = -ETIMEDOUT;
+
+  /* Check RTC_ISR ALRBWF for access to alarm register,
+   * can take 2 RTCCLK cycles or timeout
+   * CubeMX use GetTick.
+   */
+
+  for (timeout = 0; timeout < INITMODE_TIMEOUT; timeout++)
+    {
+      regval = getreg32(STM32WB_RTC_ISR);
+      if ((regval & RTC_ISR_ALRBWF) != 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_rtchw_set_alrmXr X is a or b
+ *
+ * Description:
+ *   Set the alarm (A or B) hardware registers, using the required hardware
+ *   access protocol
+ *
+ * Input Parameters:
+ *   alarmreg - the register
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int rtchw_set_alrmar(rtc_alarmreg_t alarmreg)
+{
+  int isr;
+  int ret = -EBUSY;
+
+  /* Disable the write protection for RTC registers */
+
+  rtc_wprunlock();
+
+  /* Disable RTC alarm A & Interrupt A */
+
+  modifyreg32(STM32WB_RTC_CR, (RTC_CR_ALRAE | RTC_CR_ALRAIE), 0);
+
+  /* Ensure Alarm A flag reset; this is edge triggered */
+
+  isr  = getreg32(STM32WB_RTC_ISR) & ~RTC_ISR_ALRAF;
+  putreg32(isr, STM32WB_RTC_ISR);
+
+  /* Wait for Alarm A to be writable */
+
+  ret = rtchw_check_alrawf();
+  if (ret != OK)
+    {
+      goto errout_with_wprunlock;
+    }
+
+  /* Set the RTC Alarm A register */
+
+  putreg32(alarmreg, STM32WB_RTC_ALRMAR);
+  putreg32(0, STM32WB_RTC_ALRMASSR);
+  rtcinfo("  ALRMAR: %08" PRIx32 "\n", getreg32(STM32WB_RTC_ALRMAR));
+
+  /* Enable RTC alarm A */
+
+  modifyreg32(STM32WB_RTC_CR, 0, (RTC_CR_ALRAE | RTC_CR_ALRAIE));
+
+errout_with_wprunlock:
+  rtc_wprlock();
+  return ret;
+}
+#endif
+
+#if defined(CONFIG_RTC_ALARM) && CONFIG_RTC_NALARMS > 1
+static int rtchw_set_alrmbr(rtc_alarmreg_t alarmreg)
+{
+  int isr;
+  int ret = -EBUSY;
+
+  /* Disable the write protection for RTC registers */
+
+  rtc_wprunlock();
+
+  /* Disable RTC alarm B & Interrupt B */
+
+  modifyreg32(STM32WB_RTC_CR, (RTC_CR_ALRBE | RTC_CR_ALRBIE), 0);
+
+  /* Ensure Alarm B flag reset; this is edge triggered */
+
+  isr  = getreg32(STM32WB_RTC_ISR) & ~RTC_ISR_ALRBF;
+  putreg32(isr, STM32WB_RTC_ISR);
+
+  /* Wait for Alarm B to be writable */
+
+  ret = rtchw_check_alrbwf();
+  if (ret != OK)
+    {
+      goto rtchw_set_alrmbr_exit;
+    }
+
+  /* Set the RTC Alarm B register */
+
+  putreg32(alarmreg, STM32WB_RTC_ALRMBR);
+  putreg32(0, STM32WB_RTC_ALRMBSSR);
+  rtcinfo("  ALRMBR: %08" PRIx32 "\n", getreg32(STM32WB_RTC_ALRMBR));
+
+  /* Enable RTC alarm B */
+
+  modifyreg32(STM32WB_RTC_CR, 0, (RTC_CR_ALRBE | RTC_CR_ALRBIE));
+
+rtchw_set_alrmbr_exit:
+  rtc_wprlock();
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: rtc_enable_alarm
+ *
+ * Description:
+ *   Enable ALARM interrupts
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static inline void rtc_enable_alarm(void)
+{
+  /* Is the alarm already enabled? */
+
+  if (!g_alarm_enabled)
+    {
+      /* Configure RTC interrupt to catch alarm interrupts. All RTC
+       * interrupts are connected to the EXTI controller.  To enable the
+       * RTC Alarm interrupt, the following sequence is required:
+       *
+       * 1. Configure and enable the EXTI line 17 (RTC ALARM) in interrupt
+       *    mode and select the rising edge sensitivity.
+       *    EXTI line 18 RTC Tamper or Timestamp or CSS_LSE
+       *    EXTI line 19 RTC Wakeup
+       * 2. Configure and enable the RTC_Alarm IRQ channel in the NVIC.
+       * 3. Configure the RTC to generate RTC alarms (Alarm A or Alarm B).
+       */
+
+      stm32wb_exti_alarm(true, false, true, stm32wb_rtc_alarm_handler, NULL);
+      g_alarm_enabled = true;
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_rtc_getalarmdatetime
+ *
+ * Description:
+ *   Get the current date and time for a RTC alarm.
+ *
+ * Input Parameters:
+ *   reg - RTC alarm register
+ *   tp - The location to return the high resolution time value.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int stm32wb_rtc_getalarmdatetime(rtc_alarmreg_t reg,
+                                        struct tm *tp)
+{
+  uint32_t data;
+  uint32_t tmp;
+
+  DEBUGASSERT(tp != NULL);
+
+  /* Sample the data time register. */
+
+  data = getreg32(reg);
+
+  /* Convert the RTC time to fields in struct tm format.  All of the STM32WB
+   * ranges of values correspond between struct tm and the time register.
+   */
+
+  tmp = (data & (RTC_ALRMR_SU_MASK | RTC_ALRMR_ST_MASK)) >>
+        RTC_ALRMR_SU_SHIFT;
+  tp->tm_sec = rtc_bcd2bin(tmp);
+
+  tmp = (data & (RTC_ALRMR_MNU_MASK | RTC_ALRMR_MNT_MASK)) >>
+        RTC_ALRMR_MNU_SHIFT;
+  tp->tm_min = rtc_bcd2bin(tmp);
+
+  tmp = (data & (RTC_ALRMR_HU_MASK | RTC_ALRMR_HT_MASK)) >>
+        RTC_ALRMR_HU_SHIFT;
+  tp->tm_hour = rtc_bcd2bin(tmp);
+
+  tmp = (data & (RTC_ALRMR_DU_MASK | RTC_ALRMR_DT_MASK)) >>
+        RTC_ALRMR_DU_SHIFT;
+  tp->tm_mday = rtc_bcd2bin(tmp);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_rtc_is_initialized
+ *
+ * Description:
+ *    Returns 'true' if the RTC has been initialized
+ *    Returns 'false' if the RTC has never been initialized since first
+ *    time power up, and the counters are stopped until it is first
+ *    initialized.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Returns true if RTC has been initialized.
+ *
+ ****************************************************************************/
+
+bool stm32wb_rtc_is_initialized(void)
+{
+  uint32_t regval;
+
+  regval = getreg32(RTC_MAGIC_REG);
+
+  return regval == RTC_MAGIC || regval == RTC_MAGIC_TIME_SET;
+}
+
+/****************************************************************************
+ * Name: up_rtc_initialize
+ *
+ * Description:
+ *   Initialize the hardware RTC per the selected configuration.  This
+ *   function is called once during the OS initialization sequence
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int up_rtc_initialize(void)
+{
+  bool init_stat;
+  uint32_t regval;
+
+  rtc_dumpregs("Before Initialization");
+
+  /* See if the clock has already been initialized; since it is battery
+   * backed, we don't need or want to re-initialize on each reset.
+   */
+
+  init_stat = stm32wb_rtc_is_initialized();
+  if (!init_stat)
+    {
+      /* Enable write access to the backup domain (RTC registers, RTC
+       * backup data registers and backup SRAM).
+       */
+
+      stm32wb_pwr_enablebkp(true);
+
+#if defined(CONFIG_STM32WB_RTC_HSECLOCK)
+      modifyreg32(STM32WB_RCC_BDCR, RCC_BDCR_RTCSEL_MASK,
+                  RCC_BDCR_RTCSEL_HSE);
+#elif defined(CONFIG_STM32WB_RTC_LSICLOCK)
+      modifyreg32(STM32WB_RCC_BDCR, RCC_BDCR_RTCSEL_MASK,
+                  RCC_BDCR_RTCSEL_LSI);
+#elif defined(CONFIG_STM32WB_RTC_LSECLOCK)
+      modifyreg32(STM32WB_RCC_BDCR, RCC_BDCR_RTCSEL_MASK,
+                  RCC_BDCR_RTCSEL_LSE);
+#else
+#  error "No clock for RTC!"
+#endif
+
+      /* Enable the RTC Clock by setting the RTCEN bit in the RCC register */
+
+      modifyreg32(STM32WB_RCC_BDCR, 0, RCC_BDCR_RTCEN);
+
+      /* Disable the write protection for RTC registers */
+
+      rtc_wprunlock();
+
+      /* Set Initialization mode */
+
+      if (OK != rtc_enterinit())
+        {
+          /* Enable the write protection for RTC registers */
+
+          rtc_wprlock();
+
+          /* Disable write access to the backup domain (RTC registers,
+           * RTC backup data registers and backup SRAM).
+           */
+
+          stm32wb_pwr_enablebkp(false);
+
+          rtc_dumpregs("After Failed Initialization");
+
+          return ERROR;
+        }
+      else
+        {
+          /* Clear RTC_CR FMT, OSEL and POL Bits */
+
+          regval = getreg32(STM32WB_RTC_CR);
+          regval &= ~(RTC_CR_FMT | RTC_CR_OSEL_MASK | RTC_CR_POL);
+          putreg32(regval, STM32WB_RTC_CR);
+
+          /* Configure RTC pre-scaler with the required values */
+
+#ifdef CONFIG_STM32WB_RTC_HSECLOCK
+          /* The HSE is divided by 32 prior to the prescaler we set here.
+           * 1953
+           * NOTE: max HSE/32 is 4 MHz if it is to be used with RTC
+           */
+
+          /* For a 1 MHz clock this yields 0.9999360041 Hz on the second
+           * timer - which is pretty close.
+           */
+
+          putreg32(((uint32_t)7812 << RTC_PRER_PREDIV_S_SHIFT) |
+                  ((uint32_t)0x7f << RTC_PRER_PREDIV_A_SHIFT),
+                  STM32WB_RTC_PRER);
+#elif defined(CONFIG_STM32WB_RTC_LSICLOCK)
+          /* Suitable values for 32.000 KHz LSI clock (29.5 - 34 KHz,
+           * though)
+           */
+
+          putreg32(((uint32_t)0xf9 << RTC_PRER_PREDIV_S_SHIFT) |
+                  ((uint32_t)0x7f << RTC_PRER_PREDIV_A_SHIFT),
+                  STM32WB_RTC_PRER);
+#else /* defined(CONFIG_STM32WB_RTC_LSECLOCK) */
+          /* Correct values for 32.768 KHz LSE clock */
+
+          putreg32(((uint32_t)0xff << RTC_PRER_PREDIV_S_SHIFT) |
+                  ((uint32_t)0x7f << RTC_PRER_PREDIV_A_SHIFT),
+                  STM32WB_RTC_PRER);
+#endif
+
+          /* Exit Initialization mode */
+
+          rtc_exitinit();
+
+          /* Wait for the RTC Time and Date registers to be synchronized
+           * with RTC APB clock.
+           */
+
+          rtc_synchwait();
+
+          /* Keep the fact that the RTC is initialized */
+
+          putreg32(RTC_MAGIC, RTC_MAGIC_REG);
+
+          /* Enable the write protection for RTC registers */
+
+          rtc_wprlock();
+
+          /* Disable write access to the backup domain (RTC registers,
+           * RTC backup data registers and backup SRAM).
+           */
+
+          stm32wb_pwr_enablebkp(false);
+        }
+    }
+  else
+    {
+      /* Enable write access to the backup domain (RTC registers, RTC
+       * backup data registers and backup SRAM).
+       */
+
+      stm32wb_pwr_enablebkp(true);
+
+      /* Write protection for RTC registers does not need to be disabled. */
+
+      rtc_resume();
+
+      /* Disable write access to the backup domain (RTC registers, RTC backup
+       * data registers and backup SRAM).
+       */
+
+      stm32wb_pwr_enablebkp(false);
+    }
+
+  g_rtc_enabled = true;
+  rtc_dumpregs("After Initialization");
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_rtc_getdatetime_with_subseconds
+ *
+ * Description:
+ *   Get the current date and time from the date/time RTC.  This interface
+ *   is only supported by the date/time RTC hardware implementation.
+ *   It is used to replace the system timer.  It is only used by the RTOS
+ *   during initialization to set up the system time when CONFIG_RTC and
+ *   CONFIG_RTC_DATETIME are selected.
+ *
+ *   Sub-second accuracy is returned through 'nsec'.
+ *
+ * Input Parameters:
+ *   tp - The location to return the high resolution time value.
+ *   nsec - The location to return the subsecond time value.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int stm32wb_rtc_getdatetime_with_subseconds(struct tm *tp,
+                                            long *nsec)
+{
+#ifdef CONFIG_STM32WB_HAVE_RTC_SUBSECONDS
+  uint32_t ssr;
+#endif
+  uint32_t dr;
+  uint32_t tr;
+  uint32_t tmp;
+
+  /* Sample the data time registers.  There is a race condition here... If
+   * we sample the time just before midnight on December 31, the date could
+   * be wrong because the day rolled over while were sampling. Thus loop for
+   * checking overflow here is needed.  There is a race condition with
+   * subseconds too. If we sample TR register just before second rolling
+   * and subseconds are read at wrong second, we get wrong time.
+   */
+
+  do
+    {
+      dr  = getreg32(STM32WB_RTC_DR);
+      tr  = getreg32(STM32WB_RTC_TR);
+#ifdef CONFIG_STM32WB_HAVE_RTC_SUBSECONDS
+      ssr = getreg32(STM32WB_RTC_SSR);
+      tmp = getreg32(STM32WB_RTC_TR);
+      if (tmp != tr)
+        {
+          continue;
+        }
+#endif
+
+      tmp = getreg32(STM32WB_RTC_DR);
+      if (tmp == dr)
+        {
+          break;
+        }
+    }
+  while (1);

Review Comment:
   ```suggestion
       }
     while (tmp != dr);
   ```



##########
arch/arm/src/stm32wb/stm32wb_rtc.c:
##########
@@ -0,0 +1,1835 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_rtc.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <errno.h>
+#include <debug.h>
+#include <assert.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/time.h>
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb_rcc.h"
+#include "stm32wb_pwr.h"
+#include "stm32wb_rtc.h"
+#include "stm32wb_exti.h"
+
+#ifdef CONFIG_STM32WB_RTC
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* This RTC implementation supports
+ *  - date/time RTC hardware
+ *  - extended functions Alarm A and B
+ * */
+
+#ifndef CONFIG_RTC_DATETIME
+#  error "CONFIG_RTC_DATETIME must be set to use this driver"
+#endif
+
+#ifdef CONFIG_RTC_HIRES
+#  error "CONFIG_RTC_HIRES must NOT be set with this driver"
+#endif
+
+#ifndef CONFIG_STM32WB_PWR
+#  error "CONFIG_STM32WB_PWR must selected to use this driver"
+#endif
+
+/* Constants ****************************************************************/
+
+#define SYNCHRO_TIMEOUT               (0x00020000)
+#define INITMODE_TIMEOUT              (0x00010000)
+
+#define RTC_ALRMR_ENABLE              (0x00000000)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+typedef unsigned int rtc_alarmreg_t;
+
+struct alm_cbinfo_s
+{
+  volatile alm_callback_t ac_cb; /* Client callback function */
+  volatile void *ac_arg;         /* Argument to pass with the callback function */
+};
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+/* Callback to use when an EXTI is activated  */
+
+static struct alm_cbinfo_s g_alarmcb[RTC_ALARM_LAST];
+static bool g_alarm_enabled;  /* True: Alarm interrupts are enabled */
+#endif
+
+#ifdef CONFIG_RTC_PERIODIC
+static wakeupcb_t g_wakeupcb;
+static bool g_wakeup_enabled;  /* True: Wakeup interrupts are enabled */
+#endif
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/* g_rtc_enabled is set true after the RTC has successfully initialized */
+
+volatile bool g_rtc_enabled = false;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int rtchw_check_alrawf(void);
+static int rtchw_set_alrmar(rtc_alarmreg_t alarmreg);
+#if CONFIG_RTC_NALARMS > 1
+static int rtchw_check_alrbwf(void);
+static int rtchw_set_alrmbr(rtc_alarmreg_t alarmreg);
+#endif
+static inline void rtc_enable_alarm(void);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rtc_dumpregs
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_RTC_INFO
+static void rtc_dumpregs(const char *msg)
+{
+  rtcinfo("%s:\n", msg);
+  rtcinfo("      TR: %08x\n", getreg32(STM32WB_RTC_TR));
+  rtcinfo("      DR: %08x\n", getreg32(STM32WB_RTC_DR));
+  rtcinfo("      CR: %08x\n", getreg32(STM32WB_RTC_CR));
+  rtcinfo("     ISR: %08x\n", getreg32(STM32WB_RTC_ISR));
+  rtcinfo("    PRER: %08x\n", getreg32(STM32WB_RTC_PRER));
+  rtcinfo("    WUTR: %08x\n", getreg32(STM32WB_RTC_WUTR));
+  rtcinfo("  ALRMAR: %08x\n", getreg32(STM32WB_RTC_ALRMAR));
+  rtcinfo("  ALRMBR: %08x\n", getreg32(STM32WB_RTC_ALRMBR));
+  rtcinfo("  SHIFTR: %08x\n", getreg32(STM32WB_RTC_SHIFTR));
+  rtcinfo("    TSTR: %08x\n", getreg32(STM32WB_RTC_TSTR));
+  rtcinfo("    TSDR: %08x\n", getreg32(STM32WB_RTC_TSDR));
+  rtcinfo("   TSSSR: %08x\n", getreg32(STM32WB_RTC_TSSSR));
+  rtcinfo("    CALR: %08x\n", getreg32(STM32WB_RTC_CALR));
+  rtcinfo("  TAMPCR: %08x\n", getreg32(STM32WB_RTC_TAMPCR));
+  rtcinfo("ALRMASSR: %08x\n", getreg32(STM32WB_RTC_ALRMASSR));
+  rtcinfo("ALRMBSSR: %08x\n", getreg32(STM32WB_RTC_ALRMBSSR));
+  rtcinfo("      OR: %08x\n", getreg32(STM32WB_RTC_OR));
+  rtcinfo("MAGICREG: %08x\n", getreg32(RTC_MAGIC_REG));
+}
+#else
+#  define rtc_dumpregs(msg)
+#endif
+
+/****************************************************************************
+ * Name: rtc_dumptime
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_RTC_INFO
+static void rtc_dumptime(const struct tm *tp, const char *msg)
+{
+  rtcinfo("%s:\n", msg);
+  rtcinfo("  tm_sec: %08x\n", tp->tm_sec);
+  rtcinfo("  tm_min: %08x\n", tp->tm_min);
+  rtcinfo(" tm_hour: %08x\n", tp->tm_hour);
+  rtcinfo(" tm_mday: %08x\n", tp->tm_mday);
+  rtcinfo("  tm_mon: %08x\n", tp->tm_mon);
+  rtcinfo(" tm_year: %08x\n", tp->tm_year);
+}
+#else
+#  define rtc_dumptime(tp, msg)
+#endif
+
+/****************************************************************************
+ * Name: rtc_wprunlock
+ *
+ * Description:
+ *    Disable RTC write protection
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void rtc_wprunlock(void)
+{
+  /* Enable write access to the backup domain. */
+
+  stm32wb_pwr_enablebkp(true);
+
+  /* The following steps are required to unlock the write protection on
+   * all the RTC registers (except for RTC_ISR[13:8], RTC_TAFCR, and
+   * RTC_BKPxR).
+   *
+   * 1. Write 0xca into the RTC_WPR register.
+   * 2. Write 0x53 into the RTC_WPR register.
+   *
+   * Writing a wrong key re-activates the write protection.
+   */
+
+  putreg32(RTC_WPR_KEY1, STM32WB_RTC_WPR);
+  putreg32(RTC_WPR_KEY2, STM32WB_RTC_WPR);
+}
+
+/****************************************************************************
+ * Name: rtc_wprlock
+ *
+ * Description:
+ *    Enable RTC write protection
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void rtc_wprlock(void)
+{
+  /* Writing any wrong key re-activates the write protection. */
+
+  putreg32(0xff, STM32WB_RTC_WPR);
+
+  /* Disable write access to the backup domain. */
+
+  stm32wb_pwr_enablebkp(false);
+}
+
+/****************************************************************************
+ * Name: rtc_synchwait
+ *
+ * Description:
+ *   Waits until the RTC Time and Date registers (RTC_TR and RTC_DR) are
+ *   synchronized with RTC APB clock.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int rtc_synchwait(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret;
+
+  /* Clear Registers synchronization flag (RSF) */
+
+  regval  = getreg32(STM32WB_RTC_ISR);
+  regval &= ~RTC_ISR_RSF;
+  putreg32(regval, STM32WB_RTC_ISR);
+
+  /* Now wait the registers to become synchronised */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < SYNCHRO_TIMEOUT; timeout++)
+    {
+      regval = getreg32(STM32WB_RTC_ISR);
+      if ((regval & RTC_ISR_RSF) != 0)
+        {
+          /* Synchronized */
+
+          ret = OK;
+          break;
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: rtc_enterinit
+ *
+ * Description:
+ *   Enter RTC initialization mode.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int rtc_enterinit(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret;
+
+  /* Check if the Initialization mode is already set */
+
+  regval = getreg32(STM32WB_RTC_ISR);
+
+  ret = OK;
+  if ((regval & RTC_ISR_INITF) == 0)
+    {
+      /* Set the Initialization mode */
+
+      putreg32(RTC_ISR_INIT, STM32WB_RTC_ISR);
+
+      /* Wait until the RTC is in the INIT state (or a timeout occurs) */
+
+      ret = -ETIMEDOUT;
+      for (timeout = 0; timeout < INITMODE_TIMEOUT; timeout++)
+        {
+          regval = getreg32(STM32WB_RTC_ISR);
+          if ((regval & RTC_ISR_INITF) != 0)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: rtc_exitinit
+ *
+ * Description:
+ *   Exit RTC initialization mode.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static void rtc_exitinit(void)
+{
+  uint32_t regval;
+
+  regval = getreg32(STM32WB_RTC_ISR);
+  regval &= ~(RTC_ISR_INIT);
+  putreg32(regval, STM32WB_RTC_ISR);
+}
+
+/****************************************************************************
+ * Name: rtc_bin2bcd
+ *
+ * Description:
+ *   Converts a 2 digit binary to BCD format
+ *
+ * Input Parameters:
+ *   value - The byte to be converted.
+ *
+ * Returned Value:
+ *   The value in BCD representation
+ *
+ ****************************************************************************/
+
+static uint32_t rtc_bin2bcd(int value)
+{
+  uint32_t msbcd = 0;
+
+  while (value >= 10)
+    {
+      msbcd++;
+      value -= 10;
+    }
+
+  return (msbcd << 4) | value;
+}
+
+/****************************************************************************
+ * Name: rtc_bin2bcd
+ *
+ * Description:
+ *   Convert from 2 digit BCD to binary.
+ *
+ * Input Parameters:
+ *   value - The BCD value to be converted.
+ *
+ * Returned Value:
+ *   The value in binary representation
+ *
+ ****************************************************************************/
+
+static int rtc_bcd2bin(uint32_t value)
+{
+  uint32_t tens = (value >> 4) * 10;
+  return (int)(tens + (value & 0x0f));
+}
+
+/****************************************************************************
+ * Name: rtc_resume
+ *
+ * Description:
+ *   Called when the RTC was already initialized on a previous power cycle.
+ *   This just brings the RTC back into full operation.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static void rtc_resume(void)
+{
+#ifdef CONFIG_RTC_ALARM
+  uint32_t regval;
+
+  /* Clear the RTC alarm flags */
+
+  regval  = getreg32(STM32WB_RTC_ISR);
+  regval &= ~(RTC_ISR_ALRAF | RTC_ISR_ALRBF);
+  putreg32(regval, STM32WB_RTC_ISR);
+
+  /* Clear the EXTI Line 17 Pending bit (Connected internally to RTC Alarm) */
+
+  putreg32(EXTI_PR1_PIF(EXTI_EVT_RTCALARM), STM32WB_EXTI_PR1);
+#endif
+}
+
+/****************************************************************************
+ * Name: stm32wb_rtc_alarm_handler
+ *
+ * Description:
+ *   RTC ALARM interrupt service routine through the EXTI line
+ *
+ * Input Parameters:
+ *   irq - The IRQ number that generated the interrupt
+ *   context - Architecture specific register save information.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; A negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int stm32wb_rtc_alarm_handler(int irq, void *context,
+                                     void *rtc_handler_arg)
+{
+  struct alm_cbinfo_s *cbinfo;
+  alm_callback_t cb;
+  void *arg;
+  uint32_t isr;
+  uint32_t cr;
+  int ret = OK;
+
+  /* Enable write access to the backup domain (RTC registers, RTC
+   * backup data registers and backup SRAM).
+   */
+
+  stm32wb_pwr_enablebkp(true);
+
+  /* Check for EXTI from Alarm A or B and handle according */
+
+  cr  = getreg32(STM32WB_RTC_CR);
+  if ((cr & RTC_CR_ALRAIE) != 0)
+    {
+      isr  = getreg32(STM32WB_RTC_ISR);
+      if ((isr & RTC_ISR_ALRAF) != 0)
+        {
+          cbinfo = &g_alarmcb[RTC_ALARMA];
+          if (cbinfo->ac_cb != NULL)
+            {
+              /* Alarm A callback */
+
+              cb  = cbinfo->ac_cb;
+              arg = (void *)cbinfo->ac_arg;
+
+              cbinfo->ac_cb  = NULL;
+              cbinfo->ac_arg = NULL;
+
+              cb(arg, RTC_ALARMA);
+            }
+
+          /* note, bits 8-13 do /not/ require the write enable procedure */
+
+          isr  = getreg32(STM32WB_RTC_ISR);
+          isr &= ~RTC_ISR_ALRAF;
+          putreg32(isr, STM32WB_RTC_ISR);
+        }
+    }
+
+#if CONFIG_RTC_NALARMS > 1
+  cr  = getreg32(STM32WB_RTC_CR);
+  if ((cr & RTC_CR_ALRBIE) != 0)
+    {
+      isr  = getreg32(STM32WB_RTC_ISR);
+      if ((isr & RTC_ISR_ALRBF) != 0)
+        {
+          cbinfo = &g_alarmcb[RTC_ALARMB];
+          if (cbinfo->ac_cb != NULL)
+            {
+              /* Alarm B callback */
+
+              cb  = cbinfo->ac_cb;
+              arg = (void *)cbinfo->ac_arg;
+
+              cbinfo->ac_cb  = NULL;
+              cbinfo->ac_arg = NULL;
+
+              cb(arg, RTC_ALARMB);
+            }
+
+          /* note, bits 8-13 do /not/ require the write enable procedure */
+
+          isr  = getreg32(STM32WB_RTC_ISR);
+          isr &= ~RTC_ISR_ALRBF;
+          putreg32(isr, STM32WB_RTC_ISR);
+        }
+    }
+#endif
+
+  /* Disable write access to the backup domain (RTC registers, RTC backup
+   * data registers and backup SRAM).
+   */
+
+  stm32wb_pwr_enablebkp(false);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: rtchw_check_alrXwf X= a or B
+ *
+ * Description:
+ *   Check registers
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int rtchw_check_alrawf(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret = -ETIMEDOUT;
+
+  /* Check RTC_ISR ALRAWF for access to alarm register,
+   * Can take 2 RTCCLK cycles or timeout
+   * CubeMX use GetTick.
+   */
+
+  for (timeout = 0; timeout < INITMODE_TIMEOUT; timeout++)
+    {
+      regval = getreg32(STM32WB_RTC_ISR);
+      if ((regval & RTC_ISR_ALRAWF) != 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  return ret;
+}
+#endif
+
+#if defined(CONFIG_RTC_ALARM) && CONFIG_RTC_NALARMS > 1
+static int rtchw_check_alrbwf(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret = -ETIMEDOUT;
+
+  /* Check RTC_ISR ALRBWF for access to alarm register,
+   * can take 2 RTCCLK cycles or timeout
+   * CubeMX use GetTick.
+   */
+
+  for (timeout = 0; timeout < INITMODE_TIMEOUT; timeout++)
+    {
+      regval = getreg32(STM32WB_RTC_ISR);
+      if ((regval & RTC_ISR_ALRBWF) != 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_rtchw_set_alrmXr X is a or b
+ *
+ * Description:
+ *   Set the alarm (A or B) hardware registers, using the required hardware
+ *   access protocol
+ *
+ * Input Parameters:
+ *   alarmreg - the register
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int rtchw_set_alrmar(rtc_alarmreg_t alarmreg)
+{
+  int isr;
+  int ret = -EBUSY;
+
+  /* Disable the write protection for RTC registers */
+
+  rtc_wprunlock();
+
+  /* Disable RTC alarm A & Interrupt A */
+
+  modifyreg32(STM32WB_RTC_CR, (RTC_CR_ALRAE | RTC_CR_ALRAIE), 0);
+
+  /* Ensure Alarm A flag reset; this is edge triggered */
+
+  isr  = getreg32(STM32WB_RTC_ISR) & ~RTC_ISR_ALRAF;
+  putreg32(isr, STM32WB_RTC_ISR);
+
+  /* Wait for Alarm A to be writable */
+
+  ret = rtchw_check_alrawf();
+  if (ret != OK)
+    {
+      goto errout_with_wprunlock;
+    }
+
+  /* Set the RTC Alarm A register */
+
+  putreg32(alarmreg, STM32WB_RTC_ALRMAR);
+  putreg32(0, STM32WB_RTC_ALRMASSR);
+  rtcinfo("  ALRMAR: %08" PRIx32 "\n", getreg32(STM32WB_RTC_ALRMAR));
+
+  /* Enable RTC alarm A */
+
+  modifyreg32(STM32WB_RTC_CR, 0, (RTC_CR_ALRAE | RTC_CR_ALRAIE));
+
+errout_with_wprunlock:
+  rtc_wprlock();
+  return ret;
+}
+#endif
+
+#if defined(CONFIG_RTC_ALARM) && CONFIG_RTC_NALARMS > 1
+static int rtchw_set_alrmbr(rtc_alarmreg_t alarmreg)
+{
+  int isr;
+  int ret = -EBUSY;
+
+  /* Disable the write protection for RTC registers */
+
+  rtc_wprunlock();
+
+  /* Disable RTC alarm B & Interrupt B */
+
+  modifyreg32(STM32WB_RTC_CR, (RTC_CR_ALRBE | RTC_CR_ALRBIE), 0);
+
+  /* Ensure Alarm B flag reset; this is edge triggered */
+
+  isr  = getreg32(STM32WB_RTC_ISR) & ~RTC_ISR_ALRBF;
+  putreg32(isr, STM32WB_RTC_ISR);
+
+  /* Wait for Alarm B to be writable */
+
+  ret = rtchw_check_alrbwf();
+  if (ret != OK)
+    {
+      goto rtchw_set_alrmbr_exit;
+    }
+
+  /* Set the RTC Alarm B register */
+
+  putreg32(alarmreg, STM32WB_RTC_ALRMBR);
+  putreg32(0, STM32WB_RTC_ALRMBSSR);
+  rtcinfo("  ALRMBR: %08" PRIx32 "\n", getreg32(STM32WB_RTC_ALRMBR));
+
+  /* Enable RTC alarm B */
+
+  modifyreg32(STM32WB_RTC_CR, 0, (RTC_CR_ALRBE | RTC_CR_ALRBIE));
+
+rtchw_set_alrmbr_exit:
+  rtc_wprlock();
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: rtc_enable_alarm
+ *
+ * Description:
+ *   Enable ALARM interrupts
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static inline void rtc_enable_alarm(void)
+{
+  /* Is the alarm already enabled? */
+
+  if (!g_alarm_enabled)
+    {
+      /* Configure RTC interrupt to catch alarm interrupts. All RTC
+       * interrupts are connected to the EXTI controller.  To enable the
+       * RTC Alarm interrupt, the following sequence is required:
+       *
+       * 1. Configure and enable the EXTI line 17 (RTC ALARM) in interrupt
+       *    mode and select the rising edge sensitivity.
+       *    EXTI line 18 RTC Tamper or Timestamp or CSS_LSE
+       *    EXTI line 19 RTC Wakeup
+       * 2. Configure and enable the RTC_Alarm IRQ channel in the NVIC.
+       * 3. Configure the RTC to generate RTC alarms (Alarm A or Alarm B).
+       */
+
+      stm32wb_exti_alarm(true, false, true, stm32wb_rtc_alarm_handler, NULL);
+      g_alarm_enabled = true;
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_rtc_getalarmdatetime
+ *
+ * Description:
+ *   Get the current date and time for a RTC alarm.
+ *
+ * Input Parameters:
+ *   reg - RTC alarm register
+ *   tp - The location to return the high resolution time value.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int stm32wb_rtc_getalarmdatetime(rtc_alarmreg_t reg,
+                                        struct tm *tp)
+{
+  uint32_t data;
+  uint32_t tmp;
+
+  DEBUGASSERT(tp != NULL);
+
+  /* Sample the data time register. */
+
+  data = getreg32(reg);
+
+  /* Convert the RTC time to fields in struct tm format.  All of the STM32WB
+   * ranges of values correspond between struct tm and the time register.
+   */
+
+  tmp = (data & (RTC_ALRMR_SU_MASK | RTC_ALRMR_ST_MASK)) >>
+        RTC_ALRMR_SU_SHIFT;
+  tp->tm_sec = rtc_bcd2bin(tmp);
+
+  tmp = (data & (RTC_ALRMR_MNU_MASK | RTC_ALRMR_MNT_MASK)) >>
+        RTC_ALRMR_MNU_SHIFT;
+  tp->tm_min = rtc_bcd2bin(tmp);
+
+  tmp = (data & (RTC_ALRMR_HU_MASK | RTC_ALRMR_HT_MASK)) >>
+        RTC_ALRMR_HU_SHIFT;
+  tp->tm_hour = rtc_bcd2bin(tmp);
+
+  tmp = (data & (RTC_ALRMR_DU_MASK | RTC_ALRMR_DT_MASK)) >>
+        RTC_ALRMR_DU_SHIFT;
+  tp->tm_mday = rtc_bcd2bin(tmp);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_rtc_is_initialized
+ *
+ * Description:
+ *    Returns 'true' if the RTC has been initialized
+ *    Returns 'false' if the RTC has never been initialized since first
+ *    time power up, and the counters are stopped until it is first
+ *    initialized.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Returns true if RTC has been initialized.
+ *
+ ****************************************************************************/
+
+bool stm32wb_rtc_is_initialized(void)
+{
+  uint32_t regval;
+
+  regval = getreg32(RTC_MAGIC_REG);
+
+  return regval == RTC_MAGIC || regval == RTC_MAGIC_TIME_SET;
+}
+
+/****************************************************************************
+ * Name: up_rtc_initialize
+ *
+ * Description:
+ *   Initialize the hardware RTC per the selected configuration.  This
+ *   function is called once during the OS initialization sequence
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int up_rtc_initialize(void)
+{
+  bool init_stat;
+  uint32_t regval;
+
+  rtc_dumpregs("Before Initialization");
+
+  /* See if the clock has already been initialized; since it is battery
+   * backed, we don't need or want to re-initialize on each reset.
+   */
+
+  init_stat = stm32wb_rtc_is_initialized();
+  if (!init_stat)
+    {
+      /* Enable write access to the backup domain (RTC registers, RTC
+       * backup data registers and backup SRAM).
+       */
+
+      stm32wb_pwr_enablebkp(true);
+
+#if defined(CONFIG_STM32WB_RTC_HSECLOCK)
+      modifyreg32(STM32WB_RCC_BDCR, RCC_BDCR_RTCSEL_MASK,
+                  RCC_BDCR_RTCSEL_HSE);
+#elif defined(CONFIG_STM32WB_RTC_LSICLOCK)
+      modifyreg32(STM32WB_RCC_BDCR, RCC_BDCR_RTCSEL_MASK,
+                  RCC_BDCR_RTCSEL_LSI);
+#elif defined(CONFIG_STM32WB_RTC_LSECLOCK)
+      modifyreg32(STM32WB_RCC_BDCR, RCC_BDCR_RTCSEL_MASK,
+                  RCC_BDCR_RTCSEL_LSE);
+#else
+#  error "No clock for RTC!"
+#endif
+
+      /* Enable the RTC Clock by setting the RTCEN bit in the RCC register */
+
+      modifyreg32(STM32WB_RCC_BDCR, 0, RCC_BDCR_RTCEN);
+
+      /* Disable the write protection for RTC registers */
+
+      rtc_wprunlock();
+
+      /* Set Initialization mode */
+
+      if (OK != rtc_enterinit())
+        {
+          /* Enable the write protection for RTC registers */
+
+          rtc_wprlock();
+
+          /* Disable write access to the backup domain (RTC registers,
+           * RTC backup data registers and backup SRAM).
+           */
+
+          stm32wb_pwr_enablebkp(false);
+
+          rtc_dumpregs("After Failed Initialization");
+
+          return ERROR;
+        }
+      else
+        {
+          /* Clear RTC_CR FMT, OSEL and POL Bits */
+
+          regval = getreg32(STM32WB_RTC_CR);
+          regval &= ~(RTC_CR_FMT | RTC_CR_OSEL_MASK | RTC_CR_POL);
+          putreg32(regval, STM32WB_RTC_CR);
+
+          /* Configure RTC pre-scaler with the required values */
+
+#ifdef CONFIG_STM32WB_RTC_HSECLOCK
+          /* The HSE is divided by 32 prior to the prescaler we set here.
+           * 1953
+           * NOTE: max HSE/32 is 4 MHz if it is to be used with RTC
+           */
+
+          /* For a 1 MHz clock this yields 0.9999360041 Hz on the second
+           * timer - which is pretty close.
+           */
+
+          putreg32(((uint32_t)7812 << RTC_PRER_PREDIV_S_SHIFT) |
+                  ((uint32_t)0x7f << RTC_PRER_PREDIV_A_SHIFT),
+                  STM32WB_RTC_PRER);
+#elif defined(CONFIG_STM32WB_RTC_LSICLOCK)
+          /* Suitable values for 32.000 KHz LSI clock (29.5 - 34 KHz,
+           * though)
+           */
+
+          putreg32(((uint32_t)0xf9 << RTC_PRER_PREDIV_S_SHIFT) |
+                  ((uint32_t)0x7f << RTC_PRER_PREDIV_A_SHIFT),
+                  STM32WB_RTC_PRER);
+#else /* defined(CONFIG_STM32WB_RTC_LSECLOCK) */
+          /* Correct values for 32.768 KHz LSE clock */
+
+          putreg32(((uint32_t)0xff << RTC_PRER_PREDIV_S_SHIFT) |
+                  ((uint32_t)0x7f << RTC_PRER_PREDIV_A_SHIFT),
+                  STM32WB_RTC_PRER);
+#endif
+
+          /* Exit Initialization mode */
+
+          rtc_exitinit();
+
+          /* Wait for the RTC Time and Date registers to be synchronized
+           * with RTC APB clock.
+           */
+
+          rtc_synchwait();
+
+          /* Keep the fact that the RTC is initialized */
+
+          putreg32(RTC_MAGIC, RTC_MAGIC_REG);
+
+          /* Enable the write protection for RTC registers */
+
+          rtc_wprlock();
+
+          /* Disable write access to the backup domain (RTC registers,
+           * RTC backup data registers and backup SRAM).
+           */
+
+          stm32wb_pwr_enablebkp(false);
+        }
+    }
+  else
+    {
+      /* Enable write access to the backup domain (RTC registers, RTC
+       * backup data registers and backup SRAM).
+       */
+
+      stm32wb_pwr_enablebkp(true);
+
+      /* Write protection for RTC registers does not need to be disabled. */
+
+      rtc_resume();
+
+      /* Disable write access to the backup domain (RTC registers, RTC backup
+       * data registers and backup SRAM).
+       */
+
+      stm32wb_pwr_enablebkp(false);
+    }
+
+  g_rtc_enabled = true;
+  rtc_dumpregs("After Initialization");
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_rtc_getdatetime_with_subseconds
+ *
+ * Description:
+ *   Get the current date and time from the date/time RTC.  This interface
+ *   is only supported by the date/time RTC hardware implementation.
+ *   It is used to replace the system timer.  It is only used by the RTOS
+ *   during initialization to set up the system time when CONFIG_RTC and
+ *   CONFIG_RTC_DATETIME are selected.
+ *
+ *   Sub-second accuracy is returned through 'nsec'.
+ *
+ * Input Parameters:
+ *   tp - The location to return the high resolution time value.
+ *   nsec - The location to return the subsecond time value.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int stm32wb_rtc_getdatetime_with_subseconds(struct tm *tp,
+                                            long *nsec)
+{
+#ifdef CONFIG_STM32WB_HAVE_RTC_SUBSECONDS
+  uint32_t ssr;
+#endif
+  uint32_t dr;
+  uint32_t tr;
+  uint32_t tmp;
+
+  /* Sample the data time registers.  There is a race condition here... If
+   * we sample the time just before midnight on December 31, the date could
+   * be wrong because the day rolled over while were sampling. Thus loop for
+   * checking overflow here is needed.  There is a race condition with
+   * subseconds too. If we sample TR register just before second rolling
+   * and subseconds are read at wrong second, we get wrong time.
+   */
+
+  do
+    {
+      dr  = getreg32(STM32WB_RTC_DR);
+      tr  = getreg32(STM32WB_RTC_TR);
+#ifdef CONFIG_STM32WB_HAVE_RTC_SUBSECONDS
+      ssr = getreg32(STM32WB_RTC_SSR);
+      tmp = getreg32(STM32WB_RTC_TR);
+      if (tmp != tr)
+        {
+          continue;
+        }
+#endif
+
+      tmp = getreg32(STM32WB_RTC_DR);
+      if (tmp == dr)
+        {
+          break;
+        }
+    }
+  while (1);
+
+  rtc_dumpregs("Reading Time");
+
+  /* Convert the RTC time to fields in struct tm format. All of the STM32WB
+   * ranges of values correspond between struct tm and the time register.
+   */
+
+  tmp = (tr & (RTC_TR_SU_MASK | RTC_TR_ST_MASK)) >> RTC_TR_SU_SHIFT;
+  tp->tm_sec = rtc_bcd2bin(tmp);
+
+  tmp = (tr & (RTC_TR_MNU_MASK | RTC_TR_MNT_MASK)) >> RTC_TR_MNU_SHIFT;
+  tp->tm_min = rtc_bcd2bin(tmp);
+
+  tmp = (tr & (RTC_TR_HU_MASK | RTC_TR_HT_MASK)) >> RTC_TR_HU_SHIFT;
+  tp->tm_hour = rtc_bcd2bin(tmp);
+
+  /* Now convert the RTC date to fields in struct tm format:
+   * Days: 1-31 match in both cases.
+   * Month: STM32 is 1-12, struct tm is 0-11.
+   * Years: STM32 is 00-99, struct tm is years since 1900.
+   * WeekDay: STM32 is 1 = Mon - 7 = Sun
+   *
+   * Issue:  I am not sure what the STM32 years mean.  Are these the
+   * years 2000-2099?  I'll assume so.
+   */
+
+  tmp = (dr & (RTC_DR_DU_MASK | RTC_DR_DT_MASK)) >> RTC_DR_DU_SHIFT;
+  tp->tm_mday = rtc_bcd2bin(tmp);
+
+  tmp = (dr & (RTC_DR_MU_MASK | RTC_DR_MT)) >> RTC_DR_MU_SHIFT;
+  tp->tm_mon = rtc_bcd2bin(tmp) - 1;
+
+  tmp = (dr & (RTC_DR_YU_MASK | RTC_DR_YT_MASK)) >> RTC_DR_YU_SHIFT;
+  tp->tm_year = rtc_bcd2bin(tmp) + 100;
+
+  tmp = (dr & RTC_DR_WDU_MASK) >> RTC_DR_WDU_SHIFT;
+  tp->tm_wday = tmp % 7;
+  tp->tm_yday = tp->tm_mday +
+                clock_daysbeforemonth(tp->tm_mon,
+                                      clock_isleapyear(tp->tm_year + 1900));
+  tp->tm_isdst = 0;
+
+  /* Return RTC sub-seconds if a non-NULL value
+   * of nsec has been provided to receive the sub-second value.
+   */
+
+#ifdef CONFIG_STM32WB_HAVE_RTC_SUBSECONDS
+  if (nsec)
+    {
+      uint32_t prediv_s;
+      uint32_t usecs;
+
+      prediv_s   = getreg32(STM32WB_RTC_PRER) & RTC_PRER_PREDIV_S_MASK;
+      prediv_s >>= RTC_PRER_PREDIV_S_SHIFT;
+
+      ssr &= RTC_SSR_MASK;
+
+      /* Maximum prediv_s is 0x7fff, thus we can multiply by 100000 and
+       * still fit 32-bit unsigned integer.
+       */
+
+      usecs = (((prediv_s - ssr) * 100000) / (prediv_s + 1)) * 10;
+      *nsec = usecs * 1000;
+    }
+#else
+  DEBUGASSERT(nsec == NULL);
+#endif
+
+  rtc_dumptime(tp, "Returning");
+  return OK;
+}
+
+/****************************************************************************
+ * Name: up_rtc_getdatetime
+ *
+ * Description:
+ *   Get the current date and time from the date/time RTC.  This interface
+ *   is only supported by the date/time RTC hardware implementation.
+ *   It is used to replace the system timer.  It is only used by the RTOS
+ *   during initialization to set up the system time when CONFIG_RTC and
+ *   CONFIG_RTC_DATETIME are selected.
+ *
+ *   NOTE: Some date/time RTC hardware is capability of sub-second accuracy.
+ *   That sub-second accuracy is lost in this interface.  However, since the
+ *   system time is reinitialized on each power-up/reset, there will be no
+ *   timing inaccuracy in the long run.
+ *
+ * Input Parameters:
+ *   tp - The location to return the high resolution time value.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int up_rtc_getdatetime(struct tm *tp)
+{
+  return stm32wb_rtc_getdatetime_with_subseconds(tp, NULL);
+}
+
+/****************************************************************************
+ * Name: up_rtc_getdatetime_with_subseconds
+ *
+ * Description:
+ *   Get the current date and time from the date/time RTC.  This interface
+ *   is only supported by the date/time RTC hardware implementation.
+ *   It is used to replace the system timer.  It is only used by the RTOS
+ *   during initialization to set up the system time when CONFIG_RTC and
+ *   CONFIG_RTC_DATETIME are selected (and CONFIG_RTC_HIRES is not).
+ *
+ *   NOTE: This interface exposes sub-second accuracy capability of RTC
+ *   hardware.  This interface allow maintaining timing accuracy when system
+ *   time needs constant resynchronization with RTC, for example with board
+ *   level power-save mode utilizing deep-sleep modes such as STOP on STM32WB
+ *   MCUs.
+ *
+ * Input Parameters:
+ *   tp - The location to return the high resolution time value.
+ *   nsec - The location to return the subsecond time value.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ARCH_HAVE_RTC_SUBSECONDS
+#  ifndef CONFIG_STM32WB_HAVE_RTC_SUBSECONDS
+#    error "Invalid config, enable CONFIG_STM32WB_HAVE_RTC_SUBSECONDS."
+#  endif
+int up_rtc_getdatetime_with_subseconds(struct tm *tp, long *nsec)
+{
+  return stm32wb_rtc_getdatetime_with_subseconds(tp, nsec);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_rtc_setdatetime
+ *
+ * Description:
+ *   Set the RTC to the provided time. RTC implementations which provide
+ *   up_rtc_getdatetime() (CONFIG_RTC_DATETIME is selected) should provide
+ *   this function.
+ *
+ * Input Parameters:
+ *   tp - the time to use
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int stm32wb_rtc_setdatetime(const struct tm *tp)
+{
+  uint32_t tr;
+  uint32_t dr;
+  int ret;
+
+  rtc_dumptime(tp, "Setting time");
+
+  /* Then write the broken out values to the RTC */
+
+  /* Convert the struct tm format to RTC time register fields.
+   * All of the ranges of values correspond between struct tm and the time
+   * register.
+   */
+
+  tr = (rtc_bin2bcd(tp->tm_sec)  << RTC_TR_SU_SHIFT) |
+       (rtc_bin2bcd(tp->tm_min)  << RTC_TR_MNU_SHIFT) |
+       (rtc_bin2bcd(tp->tm_hour) << RTC_TR_HU_SHIFT);
+  tr &= ~RTC_TR_RESERVED_BITS;
+
+  /* Now convert the fields in struct tm format to the RTC date register
+   * fields:
+   * Days: 1-31 match in both cases.
+   * Month: STM32 is 1-12, struct tm is 0-11.
+   * Years: STM32 is 00-99, struct tm is years since 1900.
+   * WeekDay: STM32 is 1 = Mon - 7 = Sun
+   * Issue:  I am not sure what the STM32 years mean.  Are these the
+   * years 2000-2099?  I'll assume so.
+   */
+
+  dr = (rtc_bin2bcd(tp->tm_mday) << RTC_DR_DU_SHIFT) |
+       ((rtc_bin2bcd(tp->tm_mon + 1))  << RTC_DR_MU_SHIFT) |
+       ((tp->tm_wday == 0 ? 7 : (tp->tm_wday & 7))  << RTC_DR_WDU_SHIFT) |
+       ((rtc_bin2bcd(tp->tm_year - 100)) << RTC_DR_YU_SHIFT);
+
+  dr &= ~RTC_DR_RESERVED_BITS;
+
+  /* Disable the write protection for RTC registers */
+
+  rtc_wprunlock();
+
+  /* Set Initialization mode */
+
+  ret = rtc_enterinit();
+  if (ret == OK)
+    {
+      /* Set the RTC TR and DR registers */
+
+      putreg32(tr, STM32WB_RTC_TR);
+      putreg32(dr, STM32WB_RTC_DR);
+
+      /* Exit Initialization mode and wait for the RTC Time and Date
+       * registers to be synchronized with RTC APB clock.
+       */
+
+      rtc_exitinit();
+      ret = rtc_synchwait();
+    }
+
+  /* Remember that the RTC is initialized and had its time set. */
+
+  if (getreg32(RTC_MAGIC_REG) != RTC_MAGIC_TIME_SET)
+    {
+      stm32wb_pwr_enablebkp(true);
+      putreg32(RTC_MAGIC_TIME_SET, RTC_MAGIC_REG);
+      stm32wb_pwr_enablebkp(false);
+    }
+
+  /* Re-enable the write protection for RTC registers */
+
+  rtc_wprlock();
+  rtc_dumpregs("New time setting");
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_rtc_havesettime
+ *
+ * Description:
+ *   Check if RTC time has been set.
+ *
+ * Returned Value:
+ *   Returns true if RTC date-time have been previously set.
+ *
+ ****************************************************************************/
+
+bool stm32wb_rtc_havesettime(void)
+{
+  return getreg32(RTC_MAGIC_REG) == RTC_MAGIC_TIME_SET;
+}
+
+/****************************************************************************
+ * Name: up_rtc_settime
+ *
+ * Description:
+ *   Set the RTC to the provided time.  All RTC implementations must be able
+ *   to set their time based on a standard timespec.
+ *
+ * Input Parameters:
+ *   tp - the time to use
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int up_rtc_settime(const struct timespec *tp)
+{
+  struct tm newtime;
+
+  /* Break out the time values (not that the time is set only to units of
+   * seconds)
+   */
+
+  gmtime_r(&tp->tv_sec, &newtime);
+  return stm32wb_rtc_setdatetime(&newtime);
+}
+
+/****************************************************************************
+ * Name: stm32wb_rtc_setalarm
+ *
+ * Description:
+ *   Set an alarm to an absolute time using associated hardware.
+ *
+ * Input Parameters:
+ *  alminfo - Information about the alarm configuration.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+int stm32wb_rtc_setalarm(struct alm_setalarm_s *alminfo)
+{
+  struct alm_cbinfo_s *cbinfo;
+  rtc_alarmreg_t alarmreg;
+  int ret = -EINVAL;
+
+  DEBUGASSERT(alminfo != NULL);
+  DEBUGASSERT(RTC_ALARM_LAST > alminfo->as_id);
+
+  /* Make sure the alarm interrupt is enabled at the NVIC */
+
+  rtc_enable_alarm();
+
+  /* REVISIT:  Should test that the time is in the future */
+
+  rtc_dumptime(&alminfo->as_time, "New alarm time");
+
+  /* Break out the values to the HW alarm register format.  The values in
+   * all STM32WB fields match the fields of struct tm in this case.  Notice
+   * that the alarm is limited to one month.
+   */
+
+  alarmreg = (rtc_bin2bcd(alminfo->as_time.tm_sec)  << RTC_ALRMR_SU_SHIFT) |
+             (rtc_bin2bcd(alminfo->as_time.tm_min)  << RTC_ALRMR_MNU_SHIFT) |
+             (rtc_bin2bcd(alminfo->as_time.tm_hour) << RTC_ALRMR_HU_SHIFT) |
+             (rtc_bin2bcd(alminfo->as_time.tm_mday) << RTC_ALRMR_DU_SHIFT);
+
+  /* Set the alarm in hardware and enable interrupts from the RTC */
+
+  switch (alminfo->as_id)
+    {
+      case RTC_ALARMA:
+        {
+          cbinfo         = &g_alarmcb[RTC_ALARMA];
+          cbinfo->ac_cb  = alminfo->as_cb;
+          cbinfo->ac_arg = alminfo->as_arg;
+
+          ret = rtchw_set_alrmar(alarmreg | RTC_ALRMR_ENABLE);
+          if (ret < 0)
+            {
+              cbinfo->ac_cb  = NULL;
+              cbinfo->ac_arg = NULL;
+            }
+        }
+        break;
+
+#if CONFIG_RTC_NALARMS > 1
+      case RTC_ALARMB:
+        {
+          cbinfo         = &g_alarmcb[RTC_ALARMB];
+          cbinfo->ac_cb  = alminfo->as_cb;
+          cbinfo->ac_arg = alminfo->as_arg;
+
+          ret = rtchw_set_alrmbr(alarmreg | RTC_ALRMR_ENABLE);
+          if (ret < 0)
+            {
+              cbinfo->ac_cb  = NULL;
+              cbinfo->ac_arg = NULL;
+            }
+        }
+        break;
+#endif
+
+      default:
+        rtcerr("ERROR: Invalid ALARM%d\n", alminfo->as_id);
+        break;
+    }
+
+  rtc_dumpregs("After alarm setting");
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_rtc_cancelalarm
+ *
+ * Description:
+ *   Cancel an alarm.
+ *
+ * Input Parameters:
+ *  alarmid - Identifies the alarm to be cancelled
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+int stm32wb_rtc_cancelalarm(enum alm_id_e alarmid)
+{
+  int ret = -EINVAL;
+
+  DEBUGASSERT(RTC_ALARM_LAST > alarmid);
+
+  /* Cancel the alarm in hardware and disable interrupts */
+
+  switch (alarmid)
+    {
+      case RTC_ALARMA:
+        {
+          /* Cancel the global callback function */
+
+           g_alarmcb[alarmid].ac_cb  = NULL;
+           g_alarmcb[alarmid].ac_arg = NULL;
+
+          /* Disable the write protection for RTC registers */
+
+          rtc_wprunlock();
+
+          /* Disable RTC alarm and interrupt */
+
+          modifyreg32(STM32WB_RTC_CR, (RTC_CR_ALRAE | RTC_CR_ALRAIE), 0);
+
+          ret = rtchw_check_alrawf();
+          if (ret < 0)
+            {
+              goto errout_with_wprunlock;
+            }
+
+          /* Unset the alarm */
+
+          putreg32(-1, STM32WB_RTC_ALRMAR);

Review Comment:
   ```suggestion
             putreg32(0xffffffff, STM32WB_RTC_ALRMAR);
   ```



##########
arch/arm/src/stm32wb/stm32wb_rtc_lowerhalf.c:
##########
@@ -0,0 +1,719 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_rtc_lowerhalf.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/timers/rtc.h>
+
+#include "chip.h"
+#include "stm32wb_rtc.h"
+
+#ifdef CONFIG_RTC_DRIVER
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define STM32WB_NALARMS 2
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+struct stm32wb_cbinfo_s
+{
+  volatile rtc_alarm_callback_t cb;  /* Callback when the alarm expires */
+  volatile void *priv;               /* Private argument for callback */
+  uint8_t id;                        /* Identifies the alarm */
+};
+#endif
+
+/* This is the private type for the RTC state.  It must be cast compatible
+ * with struct rtc_lowerhalf_s.
+ */
+
+struct stm32wb_lowerhalf_s
+{
+  /* This is the contained reference to the read-only, lower-half
+   * operations vtable (which may lie in FLASH or ROM)
+   */
+
+  const struct rtc_ops_s *ops;
+
+  /* Data following is private to this driver and not visible outside of
+   * this file.
+   */
+
+  sem_t devsem;         /* Threads can only exclusively access the RTC */
+
+#ifdef CONFIG_RTC_ALARM
+  /* Alarm callback information */
+
+  struct stm32wb_cbinfo_s cbinfo[STM32WB_NALARMS];
+#endif
+
+#ifdef CONFIG_RTC_PERIODIC
+  /* Periodic wakeup information */
+
+  struct lower_setperiodic_s periodic;
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Prototypes for static methods in struct rtc_ops_s */
+
+static int stm32wb_rdtime(struct rtc_lowerhalf_s *lower,
+                          struct rtc_time *rtctime);
+static int stm32wb_settime(struct rtc_lowerhalf_s *lower,
+                           const struct rtc_time *rtctime);
+static bool stm32wb_havesettime(struct rtc_lowerhalf_s *lower);
+
+#ifdef CONFIG_RTC_ALARM
+static int stm32wb_setalarm(struct rtc_lowerhalf_s *lower,
+                            const struct lower_setalarm_s *alarminfo);
+static int
+stm32wb_setrelative(struct rtc_lowerhalf_s *lower,
+                   const struct lower_setrelative_s *alarminfo);

Review Comment:
   ```suggestion
   stm32wb_setrelative(struct rtc_lowerhalf_s *lower,
                       const struct lower_setrelative_s *alarminfo);
   ```



##########
arch/arm/src/stm32wb/stm32wb_serial.c:
##########
@@ -0,0 +1,2896 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_serial.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/serial/serial.h>
+#include <nuttx/power/pm.h>
+#include <arch/board/board.h>
+
+#ifdef CONFIG_SERIAL_TERMIOS
+#  include <termios.h>
+#endif
+
+#include "arm_internal.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_uart.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Some sanity checks *******************************************************/
+
+/* DMA configuration */
+
+#ifdef SERIAL_HAVE_RXDMA
+
+/* Currently RS-485 support cannot be enabled when RXDMA is in use due to
+ * lack of testing - RS-485 support was developed on STM32F1x
+ */
+
+#  if (defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_RS485))
+#    error "RXDMA and RS-485 cannot be enabled at the same time for the same U[S]ART"
+#  endif
+
+/* For the stm32wb, there are alternate DMA channels for USART1.
+ * Logic in the board.h file make the DMA channel selection by defining
+ * the following in the board.h file.
+ */
+
+#  if defined(CONFIG_USART1_RXDMA) && !defined(DMAMAP_USART1_RX)
+#    error "USART1 DMA channel not defined (DMAMAP_USART1_RX)"
+#  endif
+
+/* The DMA buffer size when using RX DMA to emulate a FIFO.
+ *
+ * When streaming data, the generic serial layer will be called
+ * every time the FIFO receives half this number of bytes.
+ *
+ * The buffer size should be an even multiple of ARMV7M_DCACHE_LINESIZE.
+ */
+
+#  if !defined(CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE) || \
+      CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE == 0
+#    define RXDMA_BUFFER_SIZE 32
+#  else
+#    define RXDMA_BUFFER_SIZE ((CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE + 31) & ~31)
+#  endif
+
+/* DMA priority */
+
+#  ifndef CONFIG_USART_RXDMAPRIO
+#    define CONFIG_USART_RXDMAPRIO  DMA_CCR_PRIMED
+#  endif
+#  if (CONFIG_USART_RXDMAPRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_USART_RXDMAPRIO"
+#  endif
+
+/* DMA control words */
+
+#  define SERIAL_DMA_CONTROL_WORD      \
+              (DMA_CCR_CIRC          | \
+               DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  ifdef CONFIG_SERIAL_IFLOWCONTROL
+#    define SERIAL_DMA_IFLOW_CONTROL_WORD \
+              (DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  endif
+
+#endif
+
+/* Power management definitions */
+
+#if defined(CONFIG_PM) && !defined(CONFIG_STM32WB_PM_SERIAL_ACTIVITY)
+#  define CONFIG_STM32WB_PM_SERIAL_ACTIVITY  10
+#endif
+#if defined(CONFIG_PM)
+#  define PM_IDLE_DOMAIN             0 /* Revisit */
+#endif
+
+/* Keep track if a Break was set
+ *
+ * Note:
+ *
+ * 1) This value is set in the priv->ie but never written to the control
+ *    register. It must not collide with USART_CR1_USED_INTS or USART_CR3_EIE
+ * 2) USART_CR3_EIE is also carried in the up_dev_s ie member.
+ *
+ * See stm32wb_serial_restoreusartint where the masking is done.
+ */
+
+#ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+#  define USART_CR1_IE_BREAK_INPROGRESS_SHFTS 15
+#  define USART_CR1_IE_BREAK_INPROGRESS (1 << USART_CR1_IE_BREAK_INPROGRESS_SHFTS)
+#endif
+
+#ifdef USE_SERIALDRIVER
+#ifdef HAVE_UART
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_serial_s
+{
+  struct uart_dev_s dev;       /* Generic UART device */
+  uint16_t          ie;        /* Saved interrupt mask bits value */
+  uint16_t          sr;        /* Saved status bits */
+
+  /* Has been initialized and HW is setup. */
+
+  bool              initialized;
+
+#ifdef CONFIG_PM
+  bool              suspended; /* UART device has been suspended. */
+
+  /* Interrupt mask value stored before suspending for stop mode. */
+
+  uint16_t          suspended_ie;
+#endif
+
+  /* If termios are supported, then the following fields may vary at
+   * runtime.
+   */
+
+#ifdef CONFIG_SERIAL_TERMIOS
+  uint8_t           parity;    /* 0=none, 1=odd, 2=even */
+  uint8_t           bits;      /* Number of bits (7 or 8) */
+  bool              stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  bool              iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  bool              oflow;     /* output flow control (CTS) enabled */
+#endif
+  uint32_t          baud;      /* Configured baud */
+#else
+  const uint8_t     parity;    /* 0=none, 1=odd, 2=even */
+  const uint8_t     bits;      /* Number of bits (7 or 8) */
+  const bool        stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const bool        iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const bool        oflow;     /* output flow control (CTS) enabled */
+#endif
+  const uint32_t    baud;      /* Configured baud */
+#endif
+
+  const uint8_t     irq;       /* IRQ associated with this USART */
+  const uint32_t    apbclock;  /* PCLK 1 or 2 frequency */
+  const uint32_t    usartbase; /* Base address of USART registers */
+  const uint32_t    tx_gpio;   /* U[S]ART TX GPIO pin configuration */
+  const uint32_t    rx_gpio;   /* U[S]ART RX GPIO pin configuration */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const uint32_t    rts_gpio;  /* U[S]ART RTS GPIO pin configuration */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const uint32_t    cts_gpio;  /* U[S]ART CTS GPIO pin configuration */
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+  const unsigned int rxdma_channel; /* DMA channel assigned */
+#endif
+
+  /* RX DMA state */
+
+#ifdef SERIAL_HAVE_RXDMA
+  DMA_HANDLE        rxdma;     /* currently-open receive DMA stream */
+  bool              rxenable;  /* DMA-based reception en/disable */
+#ifdef CONFIG_PM
+  bool              rxdmasusp; /* Rx DMA suspended */
+#endif
+  uint32_t          rxdmanext; /* Next byte in the DMA buffer to be read */
+  char       *const rxfifo;    /* Receive DMA buffer */
+#endif
+
+#ifdef HAVE_RS485
+  const uint32_t    rs485_dir_gpio;     /* U[S]ART RS-485 DIR GPIO pin configuration */
+  const bool        rs485_dir_polarity; /* U[S]ART RS-485 DIR pin state for TX enabled */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev);
+#endif
+static int  stm32wb_serial_setup(struct uart_dev_s *dev);
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_attach(struct uart_dev_s *dev);
+static void stm32wb_serial_detach(struct uart_dev_s *dev);
+static int  up_interrupt(int irq, void *context, void *arg);
+static int  stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                 unsigned long arg);
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int  stm32wb_serial_receive(struct uart_dev_s *dev,
+                                   unsigned int *status);
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev);
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper);
+#endif
+static void stm32wb_serial_send(struct uart_dev_s *dev, int ch);
+static void stm32wb_serial_txint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_txready(struct uart_dev_s *dev);
+
+#ifdef SERIAL_HAVE_RXDMA
+static int  stm32wb_serial_dmasetup(struct uart_dev_s *dev);
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                      unsigned int *status);
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv);
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_dmaiflowrestart(struct stm32wb_serial_s *priv);
+#endif
+static void stm32wb_serial_dmarxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_dmarxavailable(struct uart_dev_s *dev);
+
+static void stm32wb_serial_dmarxcallback(DMA_HANDLE handle, uint8_t status,
+                                         void *arg);
+#endif
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend);
+static void stm32wb_serial_pm_setsuspend(bool suspend);
+static void stm32wb_serial_pmnotify(struct pm_callback_s *cb,
+                                    int domain, enum pm_state_e pmstate);
+static int  stm32wb_serial_pmprepare(struct pm_callback_s *cb,
+                                     int domain, enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static const struct uart_ops_s g_uart_ops =
+{
+  .setup          = stm32wb_serial_setup,
+  .shutdown       = stm32wb_serial_shutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_receive,
+  .rxint          = stm32wb_serial_rxint,
+  .rxavailable    = stm32wb_serial_rxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+static const struct uart_ops_s g_uart_dma_ops =
+{
+  .setup          = stm32wb_serial_dmasetup,
+  .shutdown       = stm32wb_serial_dmashutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_dmareceive,
+  .rxint          = stm32wb_serial_dmarxint,
+  .rxavailable    = stm32wb_serial_dmarxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+/* I/O buffers */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static char g_lpuart1rxbuffer[CONFIG_LPUART1_RXBUFSIZE];
+static char g_lpuart1txbuffer[CONFIG_LPUART1_TXBUFSIZE];
+# ifdef CONFIG_LPUART1_RXDMA
+static char g_lpuart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static char g_usart1rxbuffer[CONFIG_USART1_RXBUFSIZE];
+static char g_usart1txbuffer[CONFIG_USART1_TXBUFSIZE];
+# ifdef CONFIG_USART1_RXDMA
+static char g_usart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+/* This describes the state of the STM32WB LPUART1 port. */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static struct stm32wb_serial_s g_lpuart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 1
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_LPUART1_RXBUFSIZE,
+        .buffer  = g_lpuart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_LPUART1_TXBUFSIZE,
+        .buffer  = g_lpuart1txbuffer,
+      },
+#ifdef CONFIG_LPUART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_lpuart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_LPUART1,
+  .parity        = CONFIG_LPUART1_PARITY,
+  .bits          = CONFIG_LPUART1_BITS,
+  .stopbits2     = CONFIG_LPUART1_2STOP,
+  .baud          = CONFIG_LPUART1_BAUD,
+  .apbclock      = STM32WB_PCLK1_FREQUENCY,
+  .usartbase     = STM32WB_LPUART1_BASE,
+  .tx_gpio       = GPIO_LPUART1_TX,
+  .rx_gpio       = GPIO_LPUART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_LPUART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_LPUART1_RTS,
+#endif
+#ifdef CONFIG_LPUART1_RXDMA
+  .rxdma_channel = DMAMAP_LPUART1_RX,
+  .rxfifo        = g_lpuart1rxfifo,
+#endif
+
+#ifdef CONFIG_LPUART1_RS485
+  .rs485_dir_gpio = GPIO_LPUART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This describes the state of the STM32WB USART1 port. */
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static struct stm32wb_serial_s g_usart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 2
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_USART1_RXBUFSIZE,
+        .buffer  = g_usart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_USART1_TXBUFSIZE,
+        .buffer  = g_usart1txbuffer,
+      },
+#ifdef CONFIG_USART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_usart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_USART1,
+  .parity        = CONFIG_USART1_PARITY,
+  .bits          = CONFIG_USART1_BITS,
+  .stopbits2     = CONFIG_USART1_2STOP,
+  .baud          = CONFIG_USART1_BAUD,
+  .apbclock      = STM32WB_PCLK2_FREQUENCY,
+  .usartbase     = STM32WB_USART1_BASE,
+  .tx_gpio       = GPIO_USART1_TX,
+  .rx_gpio       = GPIO_USART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_USART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_USART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_USART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_USART1_RTS,
+#endif
+#ifdef CONFIG_USART1_RXDMA
+  .rxdma_channel = DMAMAP_USART1_RX,
+  .rxfifo        = g_usart1rxfifo,
+#endif
+
+#ifdef CONFIG_USART1_RS485
+  .rs485_dir_gpio = GPIO_USART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This table lets us iterate over the configured USARTs */
+
+static struct stm32wb_serial_s *
+const g_uart_devs[STM32WB_NLPUART + STM32WB_NUSART] =
+{
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  [0] = &g_lpuart1priv,
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+  [1] = &g_usart1priv,
+#endif
+};
+
+#ifdef CONFIG_PM
+static struct
+{
+  struct pm_callback_s pm_cb;
+  bool serial_suspended;
+} g_serialpm =
+  {
+    .pm_cb.notify  = stm32wb_serial_pmnotify,
+    .pm_cb.prepare = stm32wb_serial_pmprepare,
+    .serial_suspended = false
+  };
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_serial_getreg
+ ****************************************************************************/
+
+static inline
+uint32_t stm32wb_serial_getreg(struct stm32wb_serial_s *priv, int offset)
+{
+  return getreg32(priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_putreg
+ ****************************************************************************/
+
+static inline void stm32wb_serial_putreg(struct stm32wb_serial_s *priv,
+                                         int offset, uint32_t value)
+{
+  putreg32(value, priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setusartint
+ ****************************************************************************/
+
+static inline
+void stm32wb_serial_setusartint(struct stm32wb_serial_s *priv, uint16_t ie)
+{
+  uint32_t cr;
+
+  /* Save the interrupt mask */
+
+  priv->ie = ie;
+
+  /* And restore the interrupt state (see the interrupt
+   * enable/usage table above)
+   */
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  cr &= ~(USART_CR1_USED_INTS);
+  cr |= (ie & (USART_CR1_USED_INTS));
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr);
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  cr &= ~USART_CR3_EIE;
+  cr |= (ie & USART_CR3_EIE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+}
+
+/****************************************************************************
+ * Name: up_restoreusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_restoreusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  stm32wb_serial_setusartint(priv, ie);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_disableusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_disableusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t *ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  if (ie)
+    {
+      uint32_t cr1;
+      uint32_t cr3;
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning            Usage
+       * ------------------ --------------- ------------------ ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected          (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete          (used only
+       *                                                        for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag        (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag          (not used)
+       */
+
+      cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+      cr3 = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+      /* Return the current interrupt mask value for the used interrupts.
+       * Notice that this depends on the fact that none of the used interrupt
+       * enable bits overlap. This logic would fail if we needed the break
+       * interrupt!
+       */
+
+      *ie = (cr1 & (USART_CR1_USED_INTS)) | (cr3 & USART_CR3_EIE);
+    }
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_setusartint(priv, 0);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmanextrx
+ *
+ * Description:
+ *   Returns the index into the RX FIFO where the DMA will place the next
+ *   byte that it receives.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmanextrx(struct stm32wb_serial_s *priv)
+{
+  size_t dmaresidual;
+
+  dmaresidual = stm32wb_dmaresidual(priv->rxdma);
+
+  return (RXDMA_BUFFER_SIZE - (int)dmaresidual);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_usart
+ *
+ * Description:
+ *   Set the serial line baud rate (USART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setbaud_usart(struct stm32wb_serial_s *priv)
+{
+  /* This first implementation is for U[S]ARTs that support oversampling
+   * by 8 in additional to the standard oversampling by 16.
+   */
+
+  uint32_t usartdiv8;
+  uint32_t cr1;
+  uint32_t brr;
+
+  /* In case of oversampling by 8, the equation is:
+   *
+   *   baud      = 2 * fCK / usartdiv8
+   *   usartdiv8 = 2 * fCK / baud
+   */
+
+  usartdiv8 = ((priv->apbclock << 1) + (priv->baud >> 1)) / priv->baud;
+
+  /* Baud rate for standard USART (SPI mode included):
+   *
+   * In case of oversampling by 16, the equation is:
+   *   baud       = fCK / usartdiv16
+   *   usartdiv16 = fCK / baud
+   *              = 2 * usartdiv8
+   */
+
+  /* Use oversamply by 8 only if the divisor is small.  But what is small? */
+
+  cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  brr = stm32wb_serial_getreg(priv, STM32WB_USART_BRR_OFFSET);
+  brr &= ~(USART_BRR_MANT_MASK | USART_BRR_FRAC_MASK);
+
+  if (usartdiv8 > 100)
+    {
+      /* Use usartdiv16 */
+
+      brr  |= (usartdiv8 + 1) >> 1;
+
+      /* Clear oversampling by 8 to enable oversampling by 16 */
+
+      cr1 &= ~USART_CR1_OVER8;
+    }
+  else
+    {
+      DEBUGASSERT(usartdiv8 >= 8);
+
+      /* Perform mysterious operations on bits 0-3 */
+
+      brr  |= ((usartdiv8 & 0xfff0) | ((usartdiv8 & 0x000f) >> 1));
+
+      /* Set oversampling by 8 */
+
+      cr1 |= USART_CR1_OVER8;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_lpuart
+ *
+ * Description:
+ *   Set the serial line baud rate (LPUART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static void stm32wb_serial_setbaud_lpuart(struct stm32wb_serial_s *priv)
+{
+  uint32_t brr;
+
+  /* The equation is:
+   *
+   *   baud = 256 * fCK / brr
+   *   brr  = 256 * fCK / baud
+   *
+   * It is forbidden to write values lower than LPUART_BRR_MIN in
+   * the LPUART_BRR register. fCK must range from 3 x baud rate to
+   * 4096 x baud rate.
+   */
+
+  brr = (((uint64_t)priv->apbclock << 8) + (priv->baud >> 1)) / priv->baud;
+  brr &= LPUART_BRR_MASK;
+
+  if (brr < LPUART_BRR_MIN)
+    {
+      brr = LPUART_BRR_MIN;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setformat
+ *
+ * Description:
+ *   Set the serial line format and speed.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Set baud rate */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  if (priv->usartbase == STM32WB_LPUART1_BASE)
+    {
+      stm32wb_serial_setbaud_lpuart(priv);
+    }
+  else
+#endif
+    {
+      stm32wb_serial_setbaud_usart(priv);
+    }
+
+  /* Configure parity mode */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0 | USART_CR1_M1);
+
+  if (priv->parity == 1)       /* Odd parity */
+    {
+      regval |= (USART_CR1_PCE | USART_CR1_PS);
+    }
+  else if (priv->parity == 2)  /* Even parity */
+    {
+      regval |= USART_CR1_PCE;
+    }
+
+  /* Configure word length (parity uses one of configured bits)
+   *
+   * Default: 1 start, 8 data (no parity), n stop, OR
+   *          1 start, 7 data + parity, n stop
+   */
+
+  if (priv->bits == 9 || (priv->bits == 8 && priv->parity != 0))
+    {
+      /* Select: 1 start, 8 data + parity, n stop, OR
+       *         1 start, 9 data (no parity), n stop.
+       */
+
+      regval |= USART_CR1_M0;
+    }
+  else if (priv->bits == 7 && priv->parity == 0)
+    {
+      /* Select: 1 start, 7 data (no parity), n stop, OR
+       */
+
+      regval |= USART_CR1_M1;
+    }
+
+  /* Else Select: 1 start, 7 data + parity, n stop, OR
+   *              1 start, 8 data (no parity), n stop.
+   */
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure STOP bits */
+
+  regval = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK);
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure hardware flow control */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSE | USART_CR3_RTSE);
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && !defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      regval |= USART_CR3_RTSE;
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->oflow && (priv->cts_gpio != 0))
+    {
+      regval |= USART_CR3_CTSE;
+    }
+#endif
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+}
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+/****************************************************************************
+ * Name: stm32wb_serial_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripheral.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#ifdef SERIAL_HAVE_RXDMA
+  bool dmarestored = false;
+#endif
+
+  if (priv->suspended == suspend)
+    {
+      return;
+    }
+
+  priv->suspended = suspend;
+
+  if (suspend)
+    {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Force RTS high to prevent further Rx. */
+
+          stm32wb_configgpio((priv->rts_gpio & ~GPIO_MODE_MASK)
+                             | (GPIO_OUTPUT | GPIO_OUTPUT_SET));
+        }
+#endif
+
+      /* Disable interrupts to prevent Tx. */
+
+      stm32wb_serial_disableusartint(priv, &priv->suspended_ie);
+
+      /* Wait last Tx to complete. */
+
+      while ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+              USART_ISR_TC) == 0);
+
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && !priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow && priv->rxdmanext == RXDMA_BUFFER_SIZE)
+            {
+              /* Rx DMA in non-circular iflow mode and already stopped
+               * at end of DMA buffer. No need to suspend.
+               */
+            }
+          else
+#endif
+            {
+              /* Suspend Rx DMA. */
+
+              stm32wb_dmastop(priv->rxdma);
+              priv->rxdmasusp = true;
+            }
+        }
+#endif
+    }
+  else
+    {
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              stm32wb_serial_dmaiflowrestart(priv);
+            }
+          else
+#endif
+            {
+              /* This USART does not have HW flow-control. Unconditionally
+               * re-enable DMA (might loss unprocessed bytes received
+               * to DMA buffer before suspending).
+               */
+
+              stm32wb_serial_dmareenable(priv);
+              priv->rxdmasusp = false;
+            }
+
+          dmarestored = true;
+        }
+#endif
+
+      /* Re-enable interrupts to resume Tx. */
+
+      stm32wb_serial_restoreusartint(priv, priv->suspended_ie);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Restore peripheral RTS control. */
+
+          stm32wb_configgpio(priv->rts_gpio);
+        }
+#endif
+    }
+
+#ifdef SERIAL_HAVE_RXDMA
+  if (dmarestored)
+    {
+      irqstate_t flags;
+
+      flags = enter_critical_section();
+
+      /* Perform initial Rx DMA buffer fetch to wake-up serial device
+       * activity.
+       */
+
+      if (priv->rxdma != NULL)
+        {
+          stm32wb_serial_dmarxcallback(priv->rxdma, 0, priv);
+        }
+
+      leave_critical_section(flags);
+    }
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_pm_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripherals for/from deep-sleep/stop modes.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_pm_setsuspend(bool suspend)
+{
+  int n;
+
+  /* Already in desired state? */
+
+  if (suspend == g_serialpm.serial_suspended)
+    return;
+
+  g_serialpm.serial_suspended = suspend;
+
+  for (n = 0; n < STM32WB_NLPUART + STM32WB_NUSART; n++)
+    {
+      struct stm32wb_serial_s *priv = g_uart_devs[n];
+
+      if (!priv || !priv->initialized)
+        {
+          continue;
+        }
+
+      stm32wb_serial_setsuspend(&priv->dev, suspend);
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setapbclock
+ *
+ * Description:
+ *   Enable or disable APB clock for the USART peripheral
+ *
+ * Input Parameters:
+ *   dev - A reference to the UART driver state structure
+ *   on  - Enable clock if 'on' is 'true' and disable if 'false'
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_setapbclock(struct uart_dev_s *dev, bool on)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t rcc_en;
+  uint32_t regaddr;
+
+  /* Determine which USART to configure */
+
+  switch (priv->usartbase)
+    {
+    default:
+      return;
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+    case STM32WB_LPUART1_BASE:
+      rcc_en = RCC_APB1ENR2_LPUART1EN;
+      regaddr = STM32WB_RCC_APB1ENR2;
+      break;
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+    case STM32WB_USART1_BASE:
+      rcc_en = RCC_APB2ENR_USART1EN;
+      regaddr = STM32WB_RCC_APB2ENR;
+      break;
+#endif

Review Comment:
   add 2 spaces for case block



##########
arch/arm/src/stm32wb/stm32wb_rtc.c:
##########
@@ -0,0 +1,1835 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_rtc.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <errno.h>
+#include <debug.h>
+#include <assert.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/time.h>
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb_rcc.h"
+#include "stm32wb_pwr.h"
+#include "stm32wb_rtc.h"
+#include "stm32wb_exti.h"
+
+#ifdef CONFIG_STM32WB_RTC
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* This RTC implementation supports
+ *  - date/time RTC hardware
+ *  - extended functions Alarm A and B
+ * */
+
+#ifndef CONFIG_RTC_DATETIME
+#  error "CONFIG_RTC_DATETIME must be set to use this driver"
+#endif
+
+#ifdef CONFIG_RTC_HIRES
+#  error "CONFIG_RTC_HIRES must NOT be set with this driver"
+#endif
+
+#ifndef CONFIG_STM32WB_PWR
+#  error "CONFIG_STM32WB_PWR must selected to use this driver"
+#endif
+
+/* Constants ****************************************************************/
+
+#define SYNCHRO_TIMEOUT               (0x00020000)
+#define INITMODE_TIMEOUT              (0x00010000)
+
+#define RTC_ALRMR_ENABLE              (0x00000000)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+typedef unsigned int rtc_alarmreg_t;
+
+struct alm_cbinfo_s
+{
+  volatile alm_callback_t ac_cb; /* Client callback function */
+  volatile void *ac_arg;         /* Argument to pass with the callback function */
+};
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+/* Callback to use when an EXTI is activated  */
+
+static struct alm_cbinfo_s g_alarmcb[RTC_ALARM_LAST];
+static bool g_alarm_enabled;  /* True: Alarm interrupts are enabled */
+#endif
+
+#ifdef CONFIG_RTC_PERIODIC
+static wakeupcb_t g_wakeupcb;
+static bool g_wakeup_enabled;  /* True: Wakeup interrupts are enabled */
+#endif
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/* g_rtc_enabled is set true after the RTC has successfully initialized */
+
+volatile bool g_rtc_enabled = false;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int rtchw_check_alrawf(void);
+static int rtchw_set_alrmar(rtc_alarmreg_t alarmreg);
+#if CONFIG_RTC_NALARMS > 1
+static int rtchw_check_alrbwf(void);
+static int rtchw_set_alrmbr(rtc_alarmreg_t alarmreg);
+#endif
+static inline void rtc_enable_alarm(void);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rtc_dumpregs
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_RTC_INFO
+static void rtc_dumpregs(const char *msg)
+{
+  rtcinfo("%s:\n", msg);
+  rtcinfo("      TR: %08x\n", getreg32(STM32WB_RTC_TR));
+  rtcinfo("      DR: %08x\n", getreg32(STM32WB_RTC_DR));
+  rtcinfo("      CR: %08x\n", getreg32(STM32WB_RTC_CR));
+  rtcinfo("     ISR: %08x\n", getreg32(STM32WB_RTC_ISR));
+  rtcinfo("    PRER: %08x\n", getreg32(STM32WB_RTC_PRER));
+  rtcinfo("    WUTR: %08x\n", getreg32(STM32WB_RTC_WUTR));
+  rtcinfo("  ALRMAR: %08x\n", getreg32(STM32WB_RTC_ALRMAR));
+  rtcinfo("  ALRMBR: %08x\n", getreg32(STM32WB_RTC_ALRMBR));
+  rtcinfo("  SHIFTR: %08x\n", getreg32(STM32WB_RTC_SHIFTR));
+  rtcinfo("    TSTR: %08x\n", getreg32(STM32WB_RTC_TSTR));
+  rtcinfo("    TSDR: %08x\n", getreg32(STM32WB_RTC_TSDR));
+  rtcinfo("   TSSSR: %08x\n", getreg32(STM32WB_RTC_TSSSR));
+  rtcinfo("    CALR: %08x\n", getreg32(STM32WB_RTC_CALR));
+  rtcinfo("  TAMPCR: %08x\n", getreg32(STM32WB_RTC_TAMPCR));
+  rtcinfo("ALRMASSR: %08x\n", getreg32(STM32WB_RTC_ALRMASSR));
+  rtcinfo("ALRMBSSR: %08x\n", getreg32(STM32WB_RTC_ALRMBSSR));
+  rtcinfo("      OR: %08x\n", getreg32(STM32WB_RTC_OR));
+  rtcinfo("MAGICREG: %08x\n", getreg32(RTC_MAGIC_REG));
+}
+#else
+#  define rtc_dumpregs(msg)
+#endif
+
+/****************************************************************************
+ * Name: rtc_dumptime
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_RTC_INFO
+static void rtc_dumptime(const struct tm *tp, const char *msg)
+{
+  rtcinfo("%s:\n", msg);
+  rtcinfo("  tm_sec: %08x\n", tp->tm_sec);
+  rtcinfo("  tm_min: %08x\n", tp->tm_min);
+  rtcinfo(" tm_hour: %08x\n", tp->tm_hour);
+  rtcinfo(" tm_mday: %08x\n", tp->tm_mday);
+  rtcinfo("  tm_mon: %08x\n", tp->tm_mon);
+  rtcinfo(" tm_year: %08x\n", tp->tm_year);
+}
+#else
+#  define rtc_dumptime(tp, msg)
+#endif
+
+/****************************************************************************
+ * Name: rtc_wprunlock
+ *
+ * Description:
+ *    Disable RTC write protection
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void rtc_wprunlock(void)
+{
+  /* Enable write access to the backup domain. */
+
+  stm32wb_pwr_enablebkp(true);
+
+  /* The following steps are required to unlock the write protection on
+   * all the RTC registers (except for RTC_ISR[13:8], RTC_TAFCR, and
+   * RTC_BKPxR).
+   *
+   * 1. Write 0xca into the RTC_WPR register.
+   * 2. Write 0x53 into the RTC_WPR register.
+   *
+   * Writing a wrong key re-activates the write protection.
+   */
+
+  putreg32(RTC_WPR_KEY1, STM32WB_RTC_WPR);
+  putreg32(RTC_WPR_KEY2, STM32WB_RTC_WPR);
+}
+
+/****************************************************************************
+ * Name: rtc_wprlock
+ *
+ * Description:
+ *    Enable RTC write protection
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void rtc_wprlock(void)
+{
+  /* Writing any wrong key re-activates the write protection. */
+
+  putreg32(0xff, STM32WB_RTC_WPR);
+
+  /* Disable write access to the backup domain. */
+
+  stm32wb_pwr_enablebkp(false);
+}
+
+/****************************************************************************
+ * Name: rtc_synchwait
+ *
+ * Description:
+ *   Waits until the RTC Time and Date registers (RTC_TR and RTC_DR) are
+ *   synchronized with RTC APB clock.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int rtc_synchwait(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret;
+
+  /* Clear Registers synchronization flag (RSF) */
+
+  regval  = getreg32(STM32WB_RTC_ISR);
+  regval &= ~RTC_ISR_RSF;
+  putreg32(regval, STM32WB_RTC_ISR);
+
+  /* Now wait the registers to become synchronised */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < SYNCHRO_TIMEOUT; timeout++)
+    {
+      regval = getreg32(STM32WB_RTC_ISR);
+      if ((regval & RTC_ISR_RSF) != 0)
+        {
+          /* Synchronized */
+
+          ret = OK;
+          break;
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: rtc_enterinit
+ *
+ * Description:
+ *   Enter RTC initialization mode.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int rtc_enterinit(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret;
+
+  /* Check if the Initialization mode is already set */
+
+  regval = getreg32(STM32WB_RTC_ISR);
+
+  ret = OK;
+  if ((regval & RTC_ISR_INITF) == 0)
+    {
+      /* Set the Initialization mode */
+
+      putreg32(RTC_ISR_INIT, STM32WB_RTC_ISR);
+
+      /* Wait until the RTC is in the INIT state (or a timeout occurs) */
+
+      ret = -ETIMEDOUT;
+      for (timeout = 0; timeout < INITMODE_TIMEOUT; timeout++)
+        {
+          regval = getreg32(STM32WB_RTC_ISR);
+          if ((regval & RTC_ISR_INITF) != 0)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: rtc_exitinit
+ *
+ * Description:
+ *   Exit RTC initialization mode.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static void rtc_exitinit(void)
+{
+  uint32_t regval;
+
+  regval = getreg32(STM32WB_RTC_ISR);
+  regval &= ~(RTC_ISR_INIT);
+  putreg32(regval, STM32WB_RTC_ISR);
+}
+
+/****************************************************************************
+ * Name: rtc_bin2bcd
+ *
+ * Description:
+ *   Converts a 2 digit binary to BCD format
+ *
+ * Input Parameters:
+ *   value - The byte to be converted.
+ *
+ * Returned Value:
+ *   The value in BCD representation
+ *
+ ****************************************************************************/
+
+static uint32_t rtc_bin2bcd(int value)
+{
+  uint32_t msbcd = 0;
+
+  while (value >= 10)
+    {
+      msbcd++;
+      value -= 10;
+    }
+
+  return (msbcd << 4) | value;
+}
+
+/****************************************************************************
+ * Name: rtc_bin2bcd
+ *
+ * Description:
+ *   Convert from 2 digit BCD to binary.
+ *
+ * Input Parameters:
+ *   value - The BCD value to be converted.
+ *
+ * Returned Value:
+ *   The value in binary representation
+ *
+ ****************************************************************************/
+
+static int rtc_bcd2bin(uint32_t value)
+{
+  uint32_t tens = (value >> 4) * 10;
+  return (int)(tens + (value & 0x0f));
+}
+
+/****************************************************************************
+ * Name: rtc_resume
+ *
+ * Description:
+ *   Called when the RTC was already initialized on a previous power cycle.
+ *   This just brings the RTC back into full operation.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static void rtc_resume(void)
+{
+#ifdef CONFIG_RTC_ALARM
+  uint32_t regval;
+
+  /* Clear the RTC alarm flags */
+
+  regval  = getreg32(STM32WB_RTC_ISR);
+  regval &= ~(RTC_ISR_ALRAF | RTC_ISR_ALRBF);
+  putreg32(regval, STM32WB_RTC_ISR);
+
+  /* Clear the EXTI Line 17 Pending bit (Connected internally to RTC Alarm) */
+
+  putreg32(EXTI_PR1_PIF(EXTI_EVT_RTCALARM), STM32WB_EXTI_PR1);
+#endif
+}
+
+/****************************************************************************
+ * Name: stm32wb_rtc_alarm_handler
+ *
+ * Description:
+ *   RTC ALARM interrupt service routine through the EXTI line
+ *
+ * Input Parameters:
+ *   irq - The IRQ number that generated the interrupt
+ *   context - Architecture specific register save information.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; A negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int stm32wb_rtc_alarm_handler(int irq, void *context,
+                                     void *rtc_handler_arg)
+{
+  struct alm_cbinfo_s *cbinfo;
+  alm_callback_t cb;
+  void *arg;
+  uint32_t isr;
+  uint32_t cr;
+  int ret = OK;
+
+  /* Enable write access to the backup domain (RTC registers, RTC
+   * backup data registers and backup SRAM).
+   */
+
+  stm32wb_pwr_enablebkp(true);
+
+  /* Check for EXTI from Alarm A or B and handle according */
+
+  cr  = getreg32(STM32WB_RTC_CR);
+  if ((cr & RTC_CR_ALRAIE) != 0)
+    {
+      isr  = getreg32(STM32WB_RTC_ISR);
+      if ((isr & RTC_ISR_ALRAF) != 0)
+        {
+          cbinfo = &g_alarmcb[RTC_ALARMA];
+          if (cbinfo->ac_cb != NULL)
+            {
+              /* Alarm A callback */
+
+              cb  = cbinfo->ac_cb;
+              arg = (void *)cbinfo->ac_arg;
+
+              cbinfo->ac_cb  = NULL;
+              cbinfo->ac_arg = NULL;
+
+              cb(arg, RTC_ALARMA);
+            }
+
+          /* note, bits 8-13 do /not/ require the write enable procedure */
+
+          isr  = getreg32(STM32WB_RTC_ISR);
+          isr &= ~RTC_ISR_ALRAF;
+          putreg32(isr, STM32WB_RTC_ISR);
+        }
+    }
+
+#if CONFIG_RTC_NALARMS > 1
+  cr  = getreg32(STM32WB_RTC_CR);
+  if ((cr & RTC_CR_ALRBIE) != 0)
+    {
+      isr  = getreg32(STM32WB_RTC_ISR);
+      if ((isr & RTC_ISR_ALRBF) != 0)
+        {
+          cbinfo = &g_alarmcb[RTC_ALARMB];
+          if (cbinfo->ac_cb != NULL)
+            {
+              /* Alarm B callback */
+
+              cb  = cbinfo->ac_cb;
+              arg = (void *)cbinfo->ac_arg;
+
+              cbinfo->ac_cb  = NULL;
+              cbinfo->ac_arg = NULL;
+
+              cb(arg, RTC_ALARMB);
+            }
+
+          /* note, bits 8-13 do /not/ require the write enable procedure */
+
+          isr  = getreg32(STM32WB_RTC_ISR);
+          isr &= ~RTC_ISR_ALRBF;
+          putreg32(isr, STM32WB_RTC_ISR);
+        }
+    }
+#endif
+
+  /* Disable write access to the backup domain (RTC registers, RTC backup
+   * data registers and backup SRAM).
+   */
+
+  stm32wb_pwr_enablebkp(false);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: rtchw_check_alrXwf X= a or B
+ *
+ * Description:
+ *   Check registers
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int rtchw_check_alrawf(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret = -ETIMEDOUT;
+
+  /* Check RTC_ISR ALRAWF for access to alarm register,
+   * Can take 2 RTCCLK cycles or timeout
+   * CubeMX use GetTick.
+   */
+
+  for (timeout = 0; timeout < INITMODE_TIMEOUT; timeout++)
+    {
+      regval = getreg32(STM32WB_RTC_ISR);
+      if ((regval & RTC_ISR_ALRAWF) != 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  return ret;
+}
+#endif
+
+#if defined(CONFIG_RTC_ALARM) && CONFIG_RTC_NALARMS > 1
+static int rtchw_check_alrbwf(void)
+{
+  volatile uint32_t timeout;
+  uint32_t regval;
+  int ret = -ETIMEDOUT;
+
+  /* Check RTC_ISR ALRBWF for access to alarm register,
+   * can take 2 RTCCLK cycles or timeout
+   * CubeMX use GetTick.
+   */
+
+  for (timeout = 0; timeout < INITMODE_TIMEOUT; timeout++)
+    {
+      regval = getreg32(STM32WB_RTC_ISR);
+      if ((regval & RTC_ISR_ALRBWF) != 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_rtchw_set_alrmXr X is a or b
+ *
+ * Description:
+ *   Set the alarm (A or B) hardware registers, using the required hardware
+ *   access protocol
+ *
+ * Input Parameters:
+ *   alarmreg - the register
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int rtchw_set_alrmar(rtc_alarmreg_t alarmreg)
+{
+  int isr;
+  int ret = -EBUSY;
+
+  /* Disable the write protection for RTC registers */
+
+  rtc_wprunlock();
+
+  /* Disable RTC alarm A & Interrupt A */
+
+  modifyreg32(STM32WB_RTC_CR, (RTC_CR_ALRAE | RTC_CR_ALRAIE), 0);
+
+  /* Ensure Alarm A flag reset; this is edge triggered */
+
+  isr  = getreg32(STM32WB_RTC_ISR) & ~RTC_ISR_ALRAF;
+  putreg32(isr, STM32WB_RTC_ISR);
+
+  /* Wait for Alarm A to be writable */
+
+  ret = rtchw_check_alrawf();
+  if (ret != OK)
+    {
+      goto errout_with_wprunlock;
+    }
+
+  /* Set the RTC Alarm A register */
+
+  putreg32(alarmreg, STM32WB_RTC_ALRMAR);
+  putreg32(0, STM32WB_RTC_ALRMASSR);
+  rtcinfo("  ALRMAR: %08" PRIx32 "\n", getreg32(STM32WB_RTC_ALRMAR));
+
+  /* Enable RTC alarm A */
+
+  modifyreg32(STM32WB_RTC_CR, 0, (RTC_CR_ALRAE | RTC_CR_ALRAIE));
+
+errout_with_wprunlock:
+  rtc_wprlock();
+  return ret;
+}
+#endif
+
+#if defined(CONFIG_RTC_ALARM) && CONFIG_RTC_NALARMS > 1
+static int rtchw_set_alrmbr(rtc_alarmreg_t alarmreg)
+{
+  int isr;
+  int ret = -EBUSY;
+
+  /* Disable the write protection for RTC registers */
+
+  rtc_wprunlock();
+
+  /* Disable RTC alarm B & Interrupt B */
+
+  modifyreg32(STM32WB_RTC_CR, (RTC_CR_ALRBE | RTC_CR_ALRBIE), 0);
+
+  /* Ensure Alarm B flag reset; this is edge triggered */
+
+  isr  = getreg32(STM32WB_RTC_ISR) & ~RTC_ISR_ALRBF;
+  putreg32(isr, STM32WB_RTC_ISR);
+
+  /* Wait for Alarm B to be writable */
+
+  ret = rtchw_check_alrbwf();
+  if (ret != OK)
+    {
+      goto rtchw_set_alrmbr_exit;
+    }
+
+  /* Set the RTC Alarm B register */
+
+  putreg32(alarmreg, STM32WB_RTC_ALRMBR);
+  putreg32(0, STM32WB_RTC_ALRMBSSR);
+  rtcinfo("  ALRMBR: %08" PRIx32 "\n", getreg32(STM32WB_RTC_ALRMBR));
+
+  /* Enable RTC alarm B */
+
+  modifyreg32(STM32WB_RTC_CR, 0, (RTC_CR_ALRBE | RTC_CR_ALRBIE));
+
+rtchw_set_alrmbr_exit:
+  rtc_wprlock();
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: rtc_enable_alarm
+ *
+ * Description:
+ *   Enable ALARM interrupts
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static inline void rtc_enable_alarm(void)
+{
+  /* Is the alarm already enabled? */
+
+  if (!g_alarm_enabled)
+    {
+      /* Configure RTC interrupt to catch alarm interrupts. All RTC
+       * interrupts are connected to the EXTI controller.  To enable the
+       * RTC Alarm interrupt, the following sequence is required:
+       *
+       * 1. Configure and enable the EXTI line 17 (RTC ALARM) in interrupt
+       *    mode and select the rising edge sensitivity.
+       *    EXTI line 18 RTC Tamper or Timestamp or CSS_LSE
+       *    EXTI line 19 RTC Wakeup
+       * 2. Configure and enable the RTC_Alarm IRQ channel in the NVIC.
+       * 3. Configure the RTC to generate RTC alarms (Alarm A or Alarm B).
+       */
+
+      stm32wb_exti_alarm(true, false, true, stm32wb_rtc_alarm_handler, NULL);
+      g_alarm_enabled = true;
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_rtc_getalarmdatetime
+ *
+ * Description:
+ *   Get the current date and time for a RTC alarm.
+ *
+ * Input Parameters:
+ *   reg - RTC alarm register
+ *   tp - The location to return the high resolution time value.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+static int stm32wb_rtc_getalarmdatetime(rtc_alarmreg_t reg,
+                                        struct tm *tp)
+{
+  uint32_t data;
+  uint32_t tmp;
+
+  DEBUGASSERT(tp != NULL);
+
+  /* Sample the data time register. */
+
+  data = getreg32(reg);
+
+  /* Convert the RTC time to fields in struct tm format.  All of the STM32WB
+   * ranges of values correspond between struct tm and the time register.
+   */
+
+  tmp = (data & (RTC_ALRMR_SU_MASK | RTC_ALRMR_ST_MASK)) >>
+        RTC_ALRMR_SU_SHIFT;
+  tp->tm_sec = rtc_bcd2bin(tmp);
+
+  tmp = (data & (RTC_ALRMR_MNU_MASK | RTC_ALRMR_MNT_MASK)) >>
+        RTC_ALRMR_MNU_SHIFT;
+  tp->tm_min = rtc_bcd2bin(tmp);
+
+  tmp = (data & (RTC_ALRMR_HU_MASK | RTC_ALRMR_HT_MASK)) >>
+        RTC_ALRMR_HU_SHIFT;
+  tp->tm_hour = rtc_bcd2bin(tmp);
+
+  tmp = (data & (RTC_ALRMR_DU_MASK | RTC_ALRMR_DT_MASK)) >>
+        RTC_ALRMR_DU_SHIFT;
+  tp->tm_mday = rtc_bcd2bin(tmp);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_rtc_is_initialized
+ *
+ * Description:
+ *    Returns 'true' if the RTC has been initialized
+ *    Returns 'false' if the RTC has never been initialized since first
+ *    time power up, and the counters are stopped until it is first
+ *    initialized.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Returns true if RTC has been initialized.
+ *
+ ****************************************************************************/
+
+bool stm32wb_rtc_is_initialized(void)
+{
+  uint32_t regval;
+
+  regval = getreg32(RTC_MAGIC_REG);
+
+  return regval == RTC_MAGIC || regval == RTC_MAGIC_TIME_SET;
+}
+
+/****************************************************************************
+ * Name: up_rtc_initialize
+ *
+ * Description:
+ *   Initialize the hardware RTC per the selected configuration.  This
+ *   function is called once during the OS initialization sequence
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int up_rtc_initialize(void)
+{
+  bool init_stat;
+  uint32_t regval;
+
+  rtc_dumpregs("Before Initialization");
+
+  /* See if the clock has already been initialized; since it is battery
+   * backed, we don't need or want to re-initialize on each reset.
+   */
+
+  init_stat = stm32wb_rtc_is_initialized();
+  if (!init_stat)
+    {
+      /* Enable write access to the backup domain (RTC registers, RTC
+       * backup data registers and backup SRAM).
+       */
+
+      stm32wb_pwr_enablebkp(true);
+
+#if defined(CONFIG_STM32WB_RTC_HSECLOCK)
+      modifyreg32(STM32WB_RCC_BDCR, RCC_BDCR_RTCSEL_MASK,
+                  RCC_BDCR_RTCSEL_HSE);
+#elif defined(CONFIG_STM32WB_RTC_LSICLOCK)
+      modifyreg32(STM32WB_RCC_BDCR, RCC_BDCR_RTCSEL_MASK,
+                  RCC_BDCR_RTCSEL_LSI);
+#elif defined(CONFIG_STM32WB_RTC_LSECLOCK)
+      modifyreg32(STM32WB_RCC_BDCR, RCC_BDCR_RTCSEL_MASK,
+                  RCC_BDCR_RTCSEL_LSE);
+#else
+#  error "No clock for RTC!"
+#endif
+
+      /* Enable the RTC Clock by setting the RTCEN bit in the RCC register */
+
+      modifyreg32(STM32WB_RCC_BDCR, 0, RCC_BDCR_RTCEN);
+
+      /* Disable the write protection for RTC registers */
+
+      rtc_wprunlock();
+
+      /* Set Initialization mode */
+
+      if (OK != rtc_enterinit())
+        {
+          /* Enable the write protection for RTC registers */
+
+          rtc_wprlock();
+
+          /* Disable write access to the backup domain (RTC registers,
+           * RTC backup data registers and backup SRAM).
+           */
+
+          stm32wb_pwr_enablebkp(false);
+
+          rtc_dumpregs("After Failed Initialization");
+
+          return ERROR;
+        }
+      else
+        {
+          /* Clear RTC_CR FMT, OSEL and POL Bits */
+
+          regval = getreg32(STM32WB_RTC_CR);
+          regval &= ~(RTC_CR_FMT | RTC_CR_OSEL_MASK | RTC_CR_POL);
+          putreg32(regval, STM32WB_RTC_CR);
+
+          /* Configure RTC pre-scaler with the required values */
+
+#ifdef CONFIG_STM32WB_RTC_HSECLOCK
+          /* The HSE is divided by 32 prior to the prescaler we set here.
+           * 1953
+           * NOTE: max HSE/32 is 4 MHz if it is to be used with RTC
+           */
+
+          /* For a 1 MHz clock this yields 0.9999360041 Hz on the second
+           * timer - which is pretty close.
+           */
+
+          putreg32(((uint32_t)7812 << RTC_PRER_PREDIV_S_SHIFT) |
+                  ((uint32_t)0x7f << RTC_PRER_PREDIV_A_SHIFT),
+                  STM32WB_RTC_PRER);
+#elif defined(CONFIG_STM32WB_RTC_LSICLOCK)
+          /* Suitable values for 32.000 KHz LSI clock (29.5 - 34 KHz,
+           * though)
+           */
+
+          putreg32(((uint32_t)0xf9 << RTC_PRER_PREDIV_S_SHIFT) |
+                  ((uint32_t)0x7f << RTC_PRER_PREDIV_A_SHIFT),
+                  STM32WB_RTC_PRER);
+#else /* defined(CONFIG_STM32WB_RTC_LSECLOCK) */
+          /* Correct values for 32.768 KHz LSE clock */
+
+          putreg32(((uint32_t)0xff << RTC_PRER_PREDIV_S_SHIFT) |
+                  ((uint32_t)0x7f << RTC_PRER_PREDIV_A_SHIFT),
+                  STM32WB_RTC_PRER);
+#endif
+
+          /* Exit Initialization mode */
+
+          rtc_exitinit();
+
+          /* Wait for the RTC Time and Date registers to be synchronized
+           * with RTC APB clock.
+           */
+
+          rtc_synchwait();
+
+          /* Keep the fact that the RTC is initialized */
+
+          putreg32(RTC_MAGIC, RTC_MAGIC_REG);
+
+          /* Enable the write protection for RTC registers */
+
+          rtc_wprlock();
+
+          /* Disable write access to the backup domain (RTC registers,
+           * RTC backup data registers and backup SRAM).
+           */
+
+          stm32wb_pwr_enablebkp(false);
+        }
+    }
+  else
+    {
+      /* Enable write access to the backup domain (RTC registers, RTC
+       * backup data registers and backup SRAM).
+       */
+
+      stm32wb_pwr_enablebkp(true);
+
+      /* Write protection for RTC registers does not need to be disabled. */
+
+      rtc_resume();
+
+      /* Disable write access to the backup domain (RTC registers, RTC backup
+       * data registers and backup SRAM).
+       */
+
+      stm32wb_pwr_enablebkp(false);
+    }
+
+  g_rtc_enabled = true;
+  rtc_dumpregs("After Initialization");
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_rtc_getdatetime_with_subseconds
+ *
+ * Description:
+ *   Get the current date and time from the date/time RTC.  This interface
+ *   is only supported by the date/time RTC hardware implementation.
+ *   It is used to replace the system timer.  It is only used by the RTOS
+ *   during initialization to set up the system time when CONFIG_RTC and
+ *   CONFIG_RTC_DATETIME are selected.
+ *
+ *   Sub-second accuracy is returned through 'nsec'.
+ *
+ * Input Parameters:
+ *   tp - The location to return the high resolution time value.
+ *   nsec - The location to return the subsecond time value.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int stm32wb_rtc_getdatetime_with_subseconds(struct tm *tp,
+                                            long *nsec)
+{
+#ifdef CONFIG_STM32WB_HAVE_RTC_SUBSECONDS
+  uint32_t ssr;
+#endif
+  uint32_t dr;
+  uint32_t tr;
+  uint32_t tmp;
+
+  /* Sample the data time registers.  There is a race condition here... If
+   * we sample the time just before midnight on December 31, the date could
+   * be wrong because the day rolled over while were sampling. Thus loop for
+   * checking overflow here is needed.  There is a race condition with
+   * subseconds too. If we sample TR register just before second rolling
+   * and subseconds are read at wrong second, we get wrong time.
+   */
+
+  do
+    {
+      dr  = getreg32(STM32WB_RTC_DR);
+      tr  = getreg32(STM32WB_RTC_TR);
+#ifdef CONFIG_STM32WB_HAVE_RTC_SUBSECONDS
+      ssr = getreg32(STM32WB_RTC_SSR);
+      tmp = getreg32(STM32WB_RTC_TR);
+      if (tmp != tr)
+        {
+          continue;
+        }
+#endif
+
+      tmp = getreg32(STM32WB_RTC_DR);
+      if (tmp == dr)
+        {
+          break;
+        }
+    }
+  while (1);
+
+  rtc_dumpregs("Reading Time");
+
+  /* Convert the RTC time to fields in struct tm format. All of the STM32WB
+   * ranges of values correspond between struct tm and the time register.
+   */
+
+  tmp = (tr & (RTC_TR_SU_MASK | RTC_TR_ST_MASK)) >> RTC_TR_SU_SHIFT;
+  tp->tm_sec = rtc_bcd2bin(tmp);
+
+  tmp = (tr & (RTC_TR_MNU_MASK | RTC_TR_MNT_MASK)) >> RTC_TR_MNU_SHIFT;
+  tp->tm_min = rtc_bcd2bin(tmp);
+
+  tmp = (tr & (RTC_TR_HU_MASK | RTC_TR_HT_MASK)) >> RTC_TR_HU_SHIFT;
+  tp->tm_hour = rtc_bcd2bin(tmp);
+
+  /* Now convert the RTC date to fields in struct tm format:
+   * Days: 1-31 match in both cases.
+   * Month: STM32 is 1-12, struct tm is 0-11.
+   * Years: STM32 is 00-99, struct tm is years since 1900.
+   * WeekDay: STM32 is 1 = Mon - 7 = Sun
+   *
+   * Issue:  I am not sure what the STM32 years mean.  Are these the
+   * years 2000-2099?  I'll assume so.
+   */
+
+  tmp = (dr & (RTC_DR_DU_MASK | RTC_DR_DT_MASK)) >> RTC_DR_DU_SHIFT;
+  tp->tm_mday = rtc_bcd2bin(tmp);
+
+  tmp = (dr & (RTC_DR_MU_MASK | RTC_DR_MT)) >> RTC_DR_MU_SHIFT;
+  tp->tm_mon = rtc_bcd2bin(tmp) - 1;
+
+  tmp = (dr & (RTC_DR_YU_MASK | RTC_DR_YT_MASK)) >> RTC_DR_YU_SHIFT;
+  tp->tm_year = rtc_bcd2bin(tmp) + 100;
+
+  tmp = (dr & RTC_DR_WDU_MASK) >> RTC_DR_WDU_SHIFT;
+  tp->tm_wday = tmp % 7;
+  tp->tm_yday = tp->tm_mday +
+                clock_daysbeforemonth(tp->tm_mon,
+                                      clock_isleapyear(tp->tm_year + 1900));
+  tp->tm_isdst = 0;
+
+  /* Return RTC sub-seconds if a non-NULL value
+   * of nsec has been provided to receive the sub-second value.
+   */
+
+#ifdef CONFIG_STM32WB_HAVE_RTC_SUBSECONDS
+  if (nsec)
+    {
+      uint32_t prediv_s;
+      uint32_t usecs;
+
+      prediv_s   = getreg32(STM32WB_RTC_PRER) & RTC_PRER_PREDIV_S_MASK;
+      prediv_s >>= RTC_PRER_PREDIV_S_SHIFT;
+
+      ssr &= RTC_SSR_MASK;
+
+      /* Maximum prediv_s is 0x7fff, thus we can multiply by 100000 and
+       * still fit 32-bit unsigned integer.
+       */
+
+      usecs = (((prediv_s - ssr) * 100000) / (prediv_s + 1)) * 10;
+      *nsec = usecs * 1000;
+    }
+#else
+  DEBUGASSERT(nsec == NULL);
+#endif
+
+  rtc_dumptime(tp, "Returning");
+  return OK;
+}
+
+/****************************************************************************
+ * Name: up_rtc_getdatetime
+ *
+ * Description:
+ *   Get the current date and time from the date/time RTC.  This interface
+ *   is only supported by the date/time RTC hardware implementation.
+ *   It is used to replace the system timer.  It is only used by the RTOS
+ *   during initialization to set up the system time when CONFIG_RTC and
+ *   CONFIG_RTC_DATETIME are selected.
+ *
+ *   NOTE: Some date/time RTC hardware is capability of sub-second accuracy.
+ *   That sub-second accuracy is lost in this interface.  However, since the
+ *   system time is reinitialized on each power-up/reset, there will be no
+ *   timing inaccuracy in the long run.
+ *
+ * Input Parameters:
+ *   tp - The location to return the high resolution time value.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int up_rtc_getdatetime(struct tm *tp)
+{
+  return stm32wb_rtc_getdatetime_with_subseconds(tp, NULL);
+}
+
+/****************************************************************************
+ * Name: up_rtc_getdatetime_with_subseconds
+ *
+ * Description:
+ *   Get the current date and time from the date/time RTC.  This interface
+ *   is only supported by the date/time RTC hardware implementation.
+ *   It is used to replace the system timer.  It is only used by the RTOS
+ *   during initialization to set up the system time when CONFIG_RTC and
+ *   CONFIG_RTC_DATETIME are selected (and CONFIG_RTC_HIRES is not).
+ *
+ *   NOTE: This interface exposes sub-second accuracy capability of RTC
+ *   hardware.  This interface allow maintaining timing accuracy when system
+ *   time needs constant resynchronization with RTC, for example with board
+ *   level power-save mode utilizing deep-sleep modes such as STOP on STM32WB
+ *   MCUs.
+ *
+ * Input Parameters:
+ *   tp - The location to return the high resolution time value.
+ *   nsec - The location to return the subsecond time value.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ARCH_HAVE_RTC_SUBSECONDS
+#  ifndef CONFIG_STM32WB_HAVE_RTC_SUBSECONDS
+#    error "Invalid config, enable CONFIG_STM32WB_HAVE_RTC_SUBSECONDS."
+#  endif
+int up_rtc_getdatetime_with_subseconds(struct tm *tp, long *nsec)
+{
+  return stm32wb_rtc_getdatetime_with_subseconds(tp, nsec);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_rtc_setdatetime
+ *
+ * Description:
+ *   Set the RTC to the provided time. RTC implementations which provide
+ *   up_rtc_getdatetime() (CONFIG_RTC_DATETIME is selected) should provide
+ *   this function.
+ *
+ * Input Parameters:
+ *   tp - the time to use
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int stm32wb_rtc_setdatetime(const struct tm *tp)
+{
+  uint32_t tr;
+  uint32_t dr;
+  int ret;
+
+  rtc_dumptime(tp, "Setting time");
+
+  /* Then write the broken out values to the RTC */
+
+  /* Convert the struct tm format to RTC time register fields.
+   * All of the ranges of values correspond between struct tm and the time
+   * register.
+   */
+
+  tr = (rtc_bin2bcd(tp->tm_sec)  << RTC_TR_SU_SHIFT) |
+       (rtc_bin2bcd(tp->tm_min)  << RTC_TR_MNU_SHIFT) |
+       (rtc_bin2bcd(tp->tm_hour) << RTC_TR_HU_SHIFT);
+  tr &= ~RTC_TR_RESERVED_BITS;
+
+  /* Now convert the fields in struct tm format to the RTC date register
+   * fields:
+   * Days: 1-31 match in both cases.
+   * Month: STM32 is 1-12, struct tm is 0-11.
+   * Years: STM32 is 00-99, struct tm is years since 1900.
+   * WeekDay: STM32 is 1 = Mon - 7 = Sun
+   * Issue:  I am not sure what the STM32 years mean.  Are these the
+   * years 2000-2099?  I'll assume so.
+   */
+
+  dr = (rtc_bin2bcd(tp->tm_mday) << RTC_DR_DU_SHIFT) |
+       ((rtc_bin2bcd(tp->tm_mon + 1))  << RTC_DR_MU_SHIFT) |
+       ((tp->tm_wday == 0 ? 7 : (tp->tm_wday & 7))  << RTC_DR_WDU_SHIFT) |
+       ((rtc_bin2bcd(tp->tm_year - 100)) << RTC_DR_YU_SHIFT);
+
+  dr &= ~RTC_DR_RESERVED_BITS;
+
+  /* Disable the write protection for RTC registers */
+
+  rtc_wprunlock();
+
+  /* Set Initialization mode */
+
+  ret = rtc_enterinit();
+  if (ret == OK)
+    {
+      /* Set the RTC TR and DR registers */
+
+      putreg32(tr, STM32WB_RTC_TR);
+      putreg32(dr, STM32WB_RTC_DR);
+
+      /* Exit Initialization mode and wait for the RTC Time and Date
+       * registers to be synchronized with RTC APB clock.
+       */
+
+      rtc_exitinit();
+      ret = rtc_synchwait();
+    }
+
+  /* Remember that the RTC is initialized and had its time set. */
+
+  if (getreg32(RTC_MAGIC_REG) != RTC_MAGIC_TIME_SET)
+    {
+      stm32wb_pwr_enablebkp(true);
+      putreg32(RTC_MAGIC_TIME_SET, RTC_MAGIC_REG);
+      stm32wb_pwr_enablebkp(false);
+    }
+
+  /* Re-enable the write protection for RTC registers */
+
+  rtc_wprlock();
+  rtc_dumpregs("New time setting");
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_rtc_havesettime
+ *
+ * Description:
+ *   Check if RTC time has been set.
+ *
+ * Returned Value:
+ *   Returns true if RTC date-time have been previously set.
+ *
+ ****************************************************************************/
+
+bool stm32wb_rtc_havesettime(void)
+{
+  return getreg32(RTC_MAGIC_REG) == RTC_MAGIC_TIME_SET;
+}
+
+/****************************************************************************
+ * Name: up_rtc_settime
+ *
+ * Description:
+ *   Set the RTC to the provided time.  All RTC implementations must be able
+ *   to set their time based on a standard timespec.
+ *
+ * Input Parameters:
+ *   tp - the time to use
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int up_rtc_settime(const struct timespec *tp)
+{
+  struct tm newtime;
+
+  /* Break out the time values (not that the time is set only to units of
+   * seconds)
+   */
+
+  gmtime_r(&tp->tv_sec, &newtime);
+  return stm32wb_rtc_setdatetime(&newtime);
+}
+
+/****************************************************************************
+ * Name: stm32wb_rtc_setalarm
+ *
+ * Description:
+ *   Set an alarm to an absolute time using associated hardware.
+ *
+ * Input Parameters:
+ *  alminfo - Information about the alarm configuration.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+int stm32wb_rtc_setalarm(struct alm_setalarm_s *alminfo)
+{
+  struct alm_cbinfo_s *cbinfo;
+  rtc_alarmreg_t alarmreg;
+  int ret = -EINVAL;
+
+  DEBUGASSERT(alminfo != NULL);
+  DEBUGASSERT(RTC_ALARM_LAST > alminfo->as_id);
+
+  /* Make sure the alarm interrupt is enabled at the NVIC */
+
+  rtc_enable_alarm();
+
+  /* REVISIT:  Should test that the time is in the future */
+
+  rtc_dumptime(&alminfo->as_time, "New alarm time");
+
+  /* Break out the values to the HW alarm register format.  The values in
+   * all STM32WB fields match the fields of struct tm in this case.  Notice
+   * that the alarm is limited to one month.
+   */
+
+  alarmreg = (rtc_bin2bcd(alminfo->as_time.tm_sec)  << RTC_ALRMR_SU_SHIFT) |
+             (rtc_bin2bcd(alminfo->as_time.tm_min)  << RTC_ALRMR_MNU_SHIFT) |
+             (rtc_bin2bcd(alminfo->as_time.tm_hour) << RTC_ALRMR_HU_SHIFT) |
+             (rtc_bin2bcd(alminfo->as_time.tm_mday) << RTC_ALRMR_DU_SHIFT);
+
+  /* Set the alarm in hardware and enable interrupts from the RTC */
+
+  switch (alminfo->as_id)
+    {
+      case RTC_ALARMA:
+        {
+          cbinfo         = &g_alarmcb[RTC_ALARMA];
+          cbinfo->ac_cb  = alminfo->as_cb;
+          cbinfo->ac_arg = alminfo->as_arg;
+
+          ret = rtchw_set_alrmar(alarmreg | RTC_ALRMR_ENABLE);
+          if (ret < 0)
+            {
+              cbinfo->ac_cb  = NULL;
+              cbinfo->ac_arg = NULL;
+            }
+        }
+        break;
+
+#if CONFIG_RTC_NALARMS > 1
+      case RTC_ALARMB:
+        {
+          cbinfo         = &g_alarmcb[RTC_ALARMB];
+          cbinfo->ac_cb  = alminfo->as_cb;
+          cbinfo->ac_arg = alminfo->as_arg;
+
+          ret = rtchw_set_alrmbr(alarmreg | RTC_ALRMR_ENABLE);
+          if (ret < 0)
+            {
+              cbinfo->ac_cb  = NULL;
+              cbinfo->ac_arg = NULL;
+            }
+        }
+        break;
+#endif
+
+      default:
+        rtcerr("ERROR: Invalid ALARM%d\n", alminfo->as_id);
+        break;
+    }
+
+  rtc_dumpregs("After alarm setting");
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_rtc_cancelalarm
+ *
+ * Description:
+ *   Cancel an alarm.
+ *
+ * Input Parameters:
+ *  alarmid - Identifies the alarm to be cancelled
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+int stm32wb_rtc_cancelalarm(enum alm_id_e alarmid)
+{
+  int ret = -EINVAL;
+
+  DEBUGASSERT(RTC_ALARM_LAST > alarmid);
+
+  /* Cancel the alarm in hardware and disable interrupts */
+
+  switch (alarmid)
+    {
+      case RTC_ALARMA:
+        {
+          /* Cancel the global callback function */
+
+           g_alarmcb[alarmid].ac_cb  = NULL;
+           g_alarmcb[alarmid].ac_arg = NULL;
+
+          /* Disable the write protection for RTC registers */
+
+          rtc_wprunlock();
+
+          /* Disable RTC alarm and interrupt */
+
+          modifyreg32(STM32WB_RTC_CR, (RTC_CR_ALRAE | RTC_CR_ALRAIE), 0);
+
+          ret = rtchw_check_alrawf();
+          if (ret < 0)
+            {
+              goto errout_with_wprunlock;
+            }
+
+          /* Unset the alarm */
+
+          putreg32(-1, STM32WB_RTC_ALRMAR);
+          modifyreg32(STM32WB_RTC_ISR, RTC_ISR_ALRAF, 0);
+          rtc_wprlock();
+          ret = OK;
+        }
+        break;
+
+#if CONFIG_RTC_NALARMS > 1
+      case RTC_ALARMB:
+        {
+          /* Cancel the global callback function */
+
+           g_alarmcb[alarmid].ac_cb  = NULL;
+           g_alarmcb[alarmid].ac_arg = NULL;
+
+          /* Disable the write protection for RTC registers */
+
+          rtc_wprunlock();
+
+          /* Disable RTC alarm and interrupt */
+
+          modifyreg32(STM32WB_RTC_CR, (RTC_CR_ALRBE | RTC_CR_ALRBIE), 0);
+
+          ret = rtchw_check_alrbwf();
+          if (ret < 0)
+            {
+              goto errout_with_wprunlock;
+            }
+
+          /* Unset the alarm */
+
+          putreg32(-1, STM32WB_RTC_ALRMBR);

Review Comment:
   ```suggestion
             putreg32(0xffffffff, STM32WB_RTC_ALRMBR);
   ```



##########
arch/arm/src/stm32wb/stm32wb_serial.c:
##########
@@ -0,0 +1,2896 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_serial.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/serial/serial.h>
+#include <nuttx/power/pm.h>
+#include <arch/board/board.h>
+
+#ifdef CONFIG_SERIAL_TERMIOS
+#  include <termios.h>
+#endif
+
+#include "arm_internal.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_uart.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Some sanity checks *******************************************************/
+
+/* DMA configuration */
+
+#ifdef SERIAL_HAVE_RXDMA
+
+/* Currently RS-485 support cannot be enabled when RXDMA is in use due to
+ * lack of testing - RS-485 support was developed on STM32F1x
+ */
+
+#  if (defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_RS485))
+#    error "RXDMA and RS-485 cannot be enabled at the same time for the same U[S]ART"
+#  endif
+
+/* For the stm32wb, there are alternate DMA channels for USART1.
+ * Logic in the board.h file make the DMA channel selection by defining
+ * the following in the board.h file.
+ */
+
+#  if defined(CONFIG_USART1_RXDMA) && !defined(DMAMAP_USART1_RX)
+#    error "USART1 DMA channel not defined (DMAMAP_USART1_RX)"
+#  endif
+
+/* The DMA buffer size when using RX DMA to emulate a FIFO.
+ *
+ * When streaming data, the generic serial layer will be called
+ * every time the FIFO receives half this number of bytes.
+ *
+ * The buffer size should be an even multiple of ARMV7M_DCACHE_LINESIZE.
+ */
+
+#  if !defined(CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE) || \
+      CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE == 0
+#    define RXDMA_BUFFER_SIZE 32
+#  else
+#    define RXDMA_BUFFER_SIZE ((CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE + 31) & ~31)
+#  endif
+
+/* DMA priority */
+
+#  ifndef CONFIG_USART_RXDMAPRIO
+#    define CONFIG_USART_RXDMAPRIO  DMA_CCR_PRIMED
+#  endif
+#  if (CONFIG_USART_RXDMAPRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_USART_RXDMAPRIO"
+#  endif
+
+/* DMA control words */
+
+#  define SERIAL_DMA_CONTROL_WORD      \
+              (DMA_CCR_CIRC          | \
+               DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  ifdef CONFIG_SERIAL_IFLOWCONTROL
+#    define SERIAL_DMA_IFLOW_CONTROL_WORD \
+              (DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  endif
+
+#endif
+
+/* Power management definitions */
+
+#if defined(CONFIG_PM) && !defined(CONFIG_STM32WB_PM_SERIAL_ACTIVITY)
+#  define CONFIG_STM32WB_PM_SERIAL_ACTIVITY  10
+#endif
+#if defined(CONFIG_PM)
+#  define PM_IDLE_DOMAIN             0 /* Revisit */
+#endif
+
+/* Keep track if a Break was set
+ *
+ * Note:
+ *
+ * 1) This value is set in the priv->ie but never written to the control
+ *    register. It must not collide with USART_CR1_USED_INTS or USART_CR3_EIE
+ * 2) USART_CR3_EIE is also carried in the up_dev_s ie member.
+ *
+ * See stm32wb_serial_restoreusartint where the masking is done.
+ */
+
+#ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+#  define USART_CR1_IE_BREAK_INPROGRESS_SHFTS 15
+#  define USART_CR1_IE_BREAK_INPROGRESS (1 << USART_CR1_IE_BREAK_INPROGRESS_SHFTS)
+#endif
+
+#ifdef USE_SERIALDRIVER
+#ifdef HAVE_UART
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_serial_s
+{
+  struct uart_dev_s dev;       /* Generic UART device */
+  uint16_t          ie;        /* Saved interrupt mask bits value */
+  uint16_t          sr;        /* Saved status bits */
+
+  /* Has been initialized and HW is setup. */
+
+  bool              initialized;
+
+#ifdef CONFIG_PM
+  bool              suspended; /* UART device has been suspended. */
+
+  /* Interrupt mask value stored before suspending for stop mode. */
+
+  uint16_t          suspended_ie;
+#endif
+
+  /* If termios are supported, then the following fields may vary at
+   * runtime.
+   */
+
+#ifdef CONFIG_SERIAL_TERMIOS
+  uint8_t           parity;    /* 0=none, 1=odd, 2=even */
+  uint8_t           bits;      /* Number of bits (7 or 8) */
+  bool              stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  bool              iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  bool              oflow;     /* output flow control (CTS) enabled */
+#endif
+  uint32_t          baud;      /* Configured baud */
+#else
+  const uint8_t     parity;    /* 0=none, 1=odd, 2=even */
+  const uint8_t     bits;      /* Number of bits (7 or 8) */
+  const bool        stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const bool        iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const bool        oflow;     /* output flow control (CTS) enabled */
+#endif
+  const uint32_t    baud;      /* Configured baud */
+#endif
+
+  const uint8_t     irq;       /* IRQ associated with this USART */
+  const uint32_t    apbclock;  /* PCLK 1 or 2 frequency */
+  const uint32_t    usartbase; /* Base address of USART registers */
+  const uint32_t    tx_gpio;   /* U[S]ART TX GPIO pin configuration */
+  const uint32_t    rx_gpio;   /* U[S]ART RX GPIO pin configuration */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const uint32_t    rts_gpio;  /* U[S]ART RTS GPIO pin configuration */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const uint32_t    cts_gpio;  /* U[S]ART CTS GPIO pin configuration */
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+  const unsigned int rxdma_channel; /* DMA channel assigned */
+#endif
+
+  /* RX DMA state */
+
+#ifdef SERIAL_HAVE_RXDMA
+  DMA_HANDLE        rxdma;     /* currently-open receive DMA stream */
+  bool              rxenable;  /* DMA-based reception en/disable */
+#ifdef CONFIG_PM
+  bool              rxdmasusp; /* Rx DMA suspended */
+#endif
+  uint32_t          rxdmanext; /* Next byte in the DMA buffer to be read */
+  char       *const rxfifo;    /* Receive DMA buffer */
+#endif
+
+#ifdef HAVE_RS485
+  const uint32_t    rs485_dir_gpio;     /* U[S]ART RS-485 DIR GPIO pin configuration */
+  const bool        rs485_dir_polarity; /* U[S]ART RS-485 DIR pin state for TX enabled */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev);
+#endif
+static int  stm32wb_serial_setup(struct uart_dev_s *dev);
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_attach(struct uart_dev_s *dev);
+static void stm32wb_serial_detach(struct uart_dev_s *dev);
+static int  up_interrupt(int irq, void *context, void *arg);
+static int  stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                 unsigned long arg);
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int  stm32wb_serial_receive(struct uart_dev_s *dev,
+                                   unsigned int *status);
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev);
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper);
+#endif
+static void stm32wb_serial_send(struct uart_dev_s *dev, int ch);
+static void stm32wb_serial_txint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_txready(struct uart_dev_s *dev);
+
+#ifdef SERIAL_HAVE_RXDMA
+static int  stm32wb_serial_dmasetup(struct uart_dev_s *dev);
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                      unsigned int *status);
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv);
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_dmaiflowrestart(struct stm32wb_serial_s *priv);
+#endif
+static void stm32wb_serial_dmarxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_dmarxavailable(struct uart_dev_s *dev);
+
+static void stm32wb_serial_dmarxcallback(DMA_HANDLE handle, uint8_t status,
+                                         void *arg);
+#endif
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend);
+static void stm32wb_serial_pm_setsuspend(bool suspend);
+static void stm32wb_serial_pmnotify(struct pm_callback_s *cb,
+                                    int domain, enum pm_state_e pmstate);
+static int  stm32wb_serial_pmprepare(struct pm_callback_s *cb,
+                                     int domain, enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static const struct uart_ops_s g_uart_ops =
+{
+  .setup          = stm32wb_serial_setup,
+  .shutdown       = stm32wb_serial_shutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_receive,
+  .rxint          = stm32wb_serial_rxint,
+  .rxavailable    = stm32wb_serial_rxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+static const struct uart_ops_s g_uart_dma_ops =
+{
+  .setup          = stm32wb_serial_dmasetup,
+  .shutdown       = stm32wb_serial_dmashutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_dmareceive,
+  .rxint          = stm32wb_serial_dmarxint,
+  .rxavailable    = stm32wb_serial_dmarxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+/* I/O buffers */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static char g_lpuart1rxbuffer[CONFIG_LPUART1_RXBUFSIZE];
+static char g_lpuart1txbuffer[CONFIG_LPUART1_TXBUFSIZE];
+# ifdef CONFIG_LPUART1_RXDMA
+static char g_lpuart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static char g_usart1rxbuffer[CONFIG_USART1_RXBUFSIZE];
+static char g_usart1txbuffer[CONFIG_USART1_TXBUFSIZE];
+# ifdef CONFIG_USART1_RXDMA
+static char g_usart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+/* This describes the state of the STM32WB LPUART1 port. */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static struct stm32wb_serial_s g_lpuart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 1
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_LPUART1_RXBUFSIZE,
+        .buffer  = g_lpuart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_LPUART1_TXBUFSIZE,
+        .buffer  = g_lpuart1txbuffer,
+      },
+#ifdef CONFIG_LPUART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_lpuart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_LPUART1,
+  .parity        = CONFIG_LPUART1_PARITY,
+  .bits          = CONFIG_LPUART1_BITS,
+  .stopbits2     = CONFIG_LPUART1_2STOP,
+  .baud          = CONFIG_LPUART1_BAUD,
+  .apbclock      = STM32WB_PCLK1_FREQUENCY,
+  .usartbase     = STM32WB_LPUART1_BASE,
+  .tx_gpio       = GPIO_LPUART1_TX,
+  .rx_gpio       = GPIO_LPUART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_LPUART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_LPUART1_RTS,
+#endif
+#ifdef CONFIG_LPUART1_RXDMA
+  .rxdma_channel = DMAMAP_LPUART1_RX,
+  .rxfifo        = g_lpuart1rxfifo,
+#endif
+
+#ifdef CONFIG_LPUART1_RS485
+  .rs485_dir_gpio = GPIO_LPUART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This describes the state of the STM32WB USART1 port. */
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static struct stm32wb_serial_s g_usart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 2
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_USART1_RXBUFSIZE,
+        .buffer  = g_usart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_USART1_TXBUFSIZE,
+        .buffer  = g_usart1txbuffer,
+      },
+#ifdef CONFIG_USART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_usart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_USART1,
+  .parity        = CONFIG_USART1_PARITY,
+  .bits          = CONFIG_USART1_BITS,
+  .stopbits2     = CONFIG_USART1_2STOP,
+  .baud          = CONFIG_USART1_BAUD,
+  .apbclock      = STM32WB_PCLK2_FREQUENCY,
+  .usartbase     = STM32WB_USART1_BASE,
+  .tx_gpio       = GPIO_USART1_TX,
+  .rx_gpio       = GPIO_USART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_USART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_USART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_USART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_USART1_RTS,
+#endif
+#ifdef CONFIG_USART1_RXDMA
+  .rxdma_channel = DMAMAP_USART1_RX,
+  .rxfifo        = g_usart1rxfifo,
+#endif
+
+#ifdef CONFIG_USART1_RS485
+  .rs485_dir_gpio = GPIO_USART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This table lets us iterate over the configured USARTs */
+
+static struct stm32wb_serial_s *
+const g_uart_devs[STM32WB_NLPUART + STM32WB_NUSART] =
+{
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  [0] = &g_lpuart1priv,
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+  [1] = &g_usart1priv,
+#endif
+};
+
+#ifdef CONFIG_PM
+static struct
+{
+  struct pm_callback_s pm_cb;
+  bool serial_suspended;
+} g_serialpm =
+  {
+    .pm_cb.notify  = stm32wb_serial_pmnotify,
+    .pm_cb.prepare = stm32wb_serial_pmprepare,
+    .serial_suspended = false
+  };
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_serial_getreg
+ ****************************************************************************/
+
+static inline
+uint32_t stm32wb_serial_getreg(struct stm32wb_serial_s *priv, int offset)
+{
+  return getreg32(priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_putreg
+ ****************************************************************************/
+
+static inline void stm32wb_serial_putreg(struct stm32wb_serial_s *priv,
+                                         int offset, uint32_t value)
+{
+  putreg32(value, priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setusartint
+ ****************************************************************************/
+
+static inline
+void stm32wb_serial_setusartint(struct stm32wb_serial_s *priv, uint16_t ie)
+{
+  uint32_t cr;
+
+  /* Save the interrupt mask */
+
+  priv->ie = ie;
+
+  /* And restore the interrupt state (see the interrupt
+   * enable/usage table above)
+   */
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  cr &= ~(USART_CR1_USED_INTS);
+  cr |= (ie & (USART_CR1_USED_INTS));
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr);
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  cr &= ~USART_CR3_EIE;
+  cr |= (ie & USART_CR3_EIE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+}
+
+/****************************************************************************
+ * Name: up_restoreusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_restoreusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  stm32wb_serial_setusartint(priv, ie);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_disableusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_disableusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t *ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  if (ie)
+    {
+      uint32_t cr1;
+      uint32_t cr3;
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning            Usage
+       * ------------------ --------------- ------------------ ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected          (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete          (used only
+       *                                                        for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag        (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag          (not used)
+       */
+
+      cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+      cr3 = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+      /* Return the current interrupt mask value for the used interrupts.
+       * Notice that this depends on the fact that none of the used interrupt
+       * enable bits overlap. This logic would fail if we needed the break
+       * interrupt!
+       */
+
+      *ie = (cr1 & (USART_CR1_USED_INTS)) | (cr3 & USART_CR3_EIE);
+    }
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_setusartint(priv, 0);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmanextrx
+ *
+ * Description:
+ *   Returns the index into the RX FIFO where the DMA will place the next
+ *   byte that it receives.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmanextrx(struct stm32wb_serial_s *priv)
+{
+  size_t dmaresidual;
+
+  dmaresidual = stm32wb_dmaresidual(priv->rxdma);
+
+  return (RXDMA_BUFFER_SIZE - (int)dmaresidual);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_usart
+ *
+ * Description:
+ *   Set the serial line baud rate (USART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setbaud_usart(struct stm32wb_serial_s *priv)
+{
+  /* This first implementation is for U[S]ARTs that support oversampling
+   * by 8 in additional to the standard oversampling by 16.
+   */
+
+  uint32_t usartdiv8;
+  uint32_t cr1;
+  uint32_t brr;
+
+  /* In case of oversampling by 8, the equation is:
+   *
+   *   baud      = 2 * fCK / usartdiv8
+   *   usartdiv8 = 2 * fCK / baud
+   */
+
+  usartdiv8 = ((priv->apbclock << 1) + (priv->baud >> 1)) / priv->baud;
+
+  /* Baud rate for standard USART (SPI mode included):
+   *
+   * In case of oversampling by 16, the equation is:
+   *   baud       = fCK / usartdiv16
+   *   usartdiv16 = fCK / baud
+   *              = 2 * usartdiv8
+   */
+
+  /* Use oversamply by 8 only if the divisor is small.  But what is small? */
+
+  cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  brr = stm32wb_serial_getreg(priv, STM32WB_USART_BRR_OFFSET);
+  brr &= ~(USART_BRR_MANT_MASK | USART_BRR_FRAC_MASK);
+
+  if (usartdiv8 > 100)
+    {
+      /* Use usartdiv16 */
+
+      brr  |= (usartdiv8 + 1) >> 1;
+
+      /* Clear oversampling by 8 to enable oversampling by 16 */
+
+      cr1 &= ~USART_CR1_OVER8;
+    }
+  else
+    {
+      DEBUGASSERT(usartdiv8 >= 8);
+
+      /* Perform mysterious operations on bits 0-3 */
+
+      brr  |= ((usartdiv8 & 0xfff0) | ((usartdiv8 & 0x000f) >> 1));
+
+      /* Set oversampling by 8 */
+
+      cr1 |= USART_CR1_OVER8;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_lpuart
+ *
+ * Description:
+ *   Set the serial line baud rate (LPUART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static void stm32wb_serial_setbaud_lpuart(struct stm32wb_serial_s *priv)
+{
+  uint32_t brr;
+
+  /* The equation is:
+   *
+   *   baud = 256 * fCK / brr
+   *   brr  = 256 * fCK / baud
+   *
+   * It is forbidden to write values lower than LPUART_BRR_MIN in
+   * the LPUART_BRR register. fCK must range from 3 x baud rate to
+   * 4096 x baud rate.
+   */
+
+  brr = (((uint64_t)priv->apbclock << 8) + (priv->baud >> 1)) / priv->baud;
+  brr &= LPUART_BRR_MASK;
+
+  if (brr < LPUART_BRR_MIN)
+    {
+      brr = LPUART_BRR_MIN;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setformat
+ *
+ * Description:
+ *   Set the serial line format and speed.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Set baud rate */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  if (priv->usartbase == STM32WB_LPUART1_BASE)
+    {
+      stm32wb_serial_setbaud_lpuart(priv);
+    }
+  else
+#endif
+    {
+      stm32wb_serial_setbaud_usart(priv);
+    }
+
+  /* Configure parity mode */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0 | USART_CR1_M1);
+
+  if (priv->parity == 1)       /* Odd parity */
+    {
+      regval |= (USART_CR1_PCE | USART_CR1_PS);
+    }
+  else if (priv->parity == 2)  /* Even parity */
+    {
+      regval |= USART_CR1_PCE;
+    }
+
+  /* Configure word length (parity uses one of configured bits)
+   *
+   * Default: 1 start, 8 data (no parity), n stop, OR
+   *          1 start, 7 data + parity, n stop
+   */
+
+  if (priv->bits == 9 || (priv->bits == 8 && priv->parity != 0))
+    {
+      /* Select: 1 start, 8 data + parity, n stop, OR
+       *         1 start, 9 data (no parity), n stop.
+       */
+
+      regval |= USART_CR1_M0;
+    }
+  else if (priv->bits == 7 && priv->parity == 0)
+    {
+      /* Select: 1 start, 7 data (no parity), n stop, OR
+       */
+
+      regval |= USART_CR1_M1;
+    }
+
+  /* Else Select: 1 start, 7 data + parity, n stop, OR
+   *              1 start, 8 data (no parity), n stop.
+   */
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure STOP bits */
+
+  regval = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK);
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure hardware flow control */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSE | USART_CR3_RTSE);
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && !defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      regval |= USART_CR3_RTSE;
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->oflow && (priv->cts_gpio != 0))
+    {
+      regval |= USART_CR3_CTSE;
+    }
+#endif
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+}
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+/****************************************************************************
+ * Name: stm32wb_serial_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripheral.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#ifdef SERIAL_HAVE_RXDMA
+  bool dmarestored = false;
+#endif
+
+  if (priv->suspended == suspend)
+    {
+      return;
+    }
+
+  priv->suspended = suspend;
+
+  if (suspend)
+    {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Force RTS high to prevent further Rx. */
+
+          stm32wb_configgpio((priv->rts_gpio & ~GPIO_MODE_MASK)
+                             | (GPIO_OUTPUT | GPIO_OUTPUT_SET));
+        }
+#endif
+
+      /* Disable interrupts to prevent Tx. */
+
+      stm32wb_serial_disableusartint(priv, &priv->suspended_ie);
+
+      /* Wait last Tx to complete. */
+
+      while ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+              USART_ISR_TC) == 0);
+
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && !priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow && priv->rxdmanext == RXDMA_BUFFER_SIZE)
+            {
+              /* Rx DMA in non-circular iflow mode and already stopped
+               * at end of DMA buffer. No need to suspend.
+               */
+            }
+          else
+#endif
+            {
+              /* Suspend Rx DMA. */
+
+              stm32wb_dmastop(priv->rxdma);
+              priv->rxdmasusp = true;
+            }
+        }
+#endif
+    }
+  else
+    {
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              stm32wb_serial_dmaiflowrestart(priv);
+            }
+          else
+#endif
+            {
+              /* This USART does not have HW flow-control. Unconditionally
+               * re-enable DMA (might loss unprocessed bytes received
+               * to DMA buffer before suspending).
+               */
+
+              stm32wb_serial_dmareenable(priv);
+              priv->rxdmasusp = false;
+            }
+
+          dmarestored = true;
+        }
+#endif
+
+      /* Re-enable interrupts to resume Tx. */
+
+      stm32wb_serial_restoreusartint(priv, priv->suspended_ie);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Restore peripheral RTS control. */
+
+          stm32wb_configgpio(priv->rts_gpio);
+        }
+#endif
+    }
+
+#ifdef SERIAL_HAVE_RXDMA
+  if (dmarestored)
+    {
+      irqstate_t flags;
+
+      flags = enter_critical_section();
+
+      /* Perform initial Rx DMA buffer fetch to wake-up serial device
+       * activity.
+       */
+
+      if (priv->rxdma != NULL)
+        {
+          stm32wb_serial_dmarxcallback(priv->rxdma, 0, priv);
+        }
+
+      leave_critical_section(flags);
+    }
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_pm_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripherals for/from deep-sleep/stop modes.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_pm_setsuspend(bool suspend)
+{
+  int n;
+
+  /* Already in desired state? */
+
+  if (suspend == g_serialpm.serial_suspended)
+    return;

Review Comment:
   ```suggestion
     if (suspend == g_serialpm.serial_suspended)
       {
         return;
       }
   ```



##########
arch/arm/src/stm32wb/stm32wb_serial.c:
##########
@@ -0,0 +1,2896 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_serial.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/serial/serial.h>
+#include <nuttx/power/pm.h>
+#include <arch/board/board.h>
+
+#ifdef CONFIG_SERIAL_TERMIOS
+#  include <termios.h>
+#endif
+
+#include "arm_internal.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_uart.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Some sanity checks *******************************************************/
+
+/* DMA configuration */
+
+#ifdef SERIAL_HAVE_RXDMA
+
+/* Currently RS-485 support cannot be enabled when RXDMA is in use due to
+ * lack of testing - RS-485 support was developed on STM32F1x
+ */
+
+#  if (defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_RS485))
+#    error "RXDMA and RS-485 cannot be enabled at the same time for the same U[S]ART"
+#  endif
+
+/* For the stm32wb, there are alternate DMA channels for USART1.
+ * Logic in the board.h file make the DMA channel selection by defining
+ * the following in the board.h file.
+ */
+
+#  if defined(CONFIG_USART1_RXDMA) && !defined(DMAMAP_USART1_RX)
+#    error "USART1 DMA channel not defined (DMAMAP_USART1_RX)"
+#  endif
+
+/* The DMA buffer size when using RX DMA to emulate a FIFO.
+ *
+ * When streaming data, the generic serial layer will be called
+ * every time the FIFO receives half this number of bytes.
+ *
+ * The buffer size should be an even multiple of ARMV7M_DCACHE_LINESIZE.
+ */
+
+#  if !defined(CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE) || \
+      CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE == 0
+#    define RXDMA_BUFFER_SIZE 32
+#  else
+#    define RXDMA_BUFFER_SIZE ((CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE + 31) & ~31)
+#  endif
+
+/* DMA priority */
+
+#  ifndef CONFIG_USART_RXDMAPRIO
+#    define CONFIG_USART_RXDMAPRIO  DMA_CCR_PRIMED
+#  endif
+#  if (CONFIG_USART_RXDMAPRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_USART_RXDMAPRIO"
+#  endif
+
+/* DMA control words */
+
+#  define SERIAL_DMA_CONTROL_WORD      \
+              (DMA_CCR_CIRC          | \
+               DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  ifdef CONFIG_SERIAL_IFLOWCONTROL
+#    define SERIAL_DMA_IFLOW_CONTROL_WORD \
+              (DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  endif
+
+#endif
+
+/* Power management definitions */
+
+#if defined(CONFIG_PM) && !defined(CONFIG_STM32WB_PM_SERIAL_ACTIVITY)
+#  define CONFIG_STM32WB_PM_SERIAL_ACTIVITY  10
+#endif
+#if defined(CONFIG_PM)
+#  define PM_IDLE_DOMAIN             0 /* Revisit */
+#endif
+
+/* Keep track if a Break was set
+ *
+ * Note:
+ *
+ * 1) This value is set in the priv->ie but never written to the control
+ *    register. It must not collide with USART_CR1_USED_INTS or USART_CR3_EIE
+ * 2) USART_CR3_EIE is also carried in the up_dev_s ie member.
+ *
+ * See stm32wb_serial_restoreusartint where the masking is done.
+ */
+
+#ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+#  define USART_CR1_IE_BREAK_INPROGRESS_SHFTS 15
+#  define USART_CR1_IE_BREAK_INPROGRESS (1 << USART_CR1_IE_BREAK_INPROGRESS_SHFTS)
+#endif
+
+#ifdef USE_SERIALDRIVER
+#ifdef HAVE_UART
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_serial_s
+{
+  struct uart_dev_s dev;       /* Generic UART device */
+  uint16_t          ie;        /* Saved interrupt mask bits value */
+  uint16_t          sr;        /* Saved status bits */
+
+  /* Has been initialized and HW is setup. */
+
+  bool              initialized;
+
+#ifdef CONFIG_PM
+  bool              suspended; /* UART device has been suspended. */
+
+  /* Interrupt mask value stored before suspending for stop mode. */
+
+  uint16_t          suspended_ie;
+#endif
+
+  /* If termios are supported, then the following fields may vary at
+   * runtime.
+   */
+
+#ifdef CONFIG_SERIAL_TERMIOS
+  uint8_t           parity;    /* 0=none, 1=odd, 2=even */
+  uint8_t           bits;      /* Number of bits (7 or 8) */
+  bool              stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  bool              iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  bool              oflow;     /* output flow control (CTS) enabled */
+#endif
+  uint32_t          baud;      /* Configured baud */
+#else
+  const uint8_t     parity;    /* 0=none, 1=odd, 2=even */
+  const uint8_t     bits;      /* Number of bits (7 or 8) */
+  const bool        stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const bool        iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const bool        oflow;     /* output flow control (CTS) enabled */
+#endif
+  const uint32_t    baud;      /* Configured baud */
+#endif
+
+  const uint8_t     irq;       /* IRQ associated with this USART */
+  const uint32_t    apbclock;  /* PCLK 1 or 2 frequency */
+  const uint32_t    usartbase; /* Base address of USART registers */
+  const uint32_t    tx_gpio;   /* U[S]ART TX GPIO pin configuration */
+  const uint32_t    rx_gpio;   /* U[S]ART RX GPIO pin configuration */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const uint32_t    rts_gpio;  /* U[S]ART RTS GPIO pin configuration */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const uint32_t    cts_gpio;  /* U[S]ART CTS GPIO pin configuration */
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+  const unsigned int rxdma_channel; /* DMA channel assigned */
+#endif
+
+  /* RX DMA state */
+
+#ifdef SERIAL_HAVE_RXDMA
+  DMA_HANDLE        rxdma;     /* currently-open receive DMA stream */
+  bool              rxenable;  /* DMA-based reception en/disable */
+#ifdef CONFIG_PM
+  bool              rxdmasusp; /* Rx DMA suspended */
+#endif
+  uint32_t          rxdmanext; /* Next byte in the DMA buffer to be read */
+  char       *const rxfifo;    /* Receive DMA buffer */
+#endif
+
+#ifdef HAVE_RS485
+  const uint32_t    rs485_dir_gpio;     /* U[S]ART RS-485 DIR GPIO pin configuration */
+  const bool        rs485_dir_polarity; /* U[S]ART RS-485 DIR pin state for TX enabled */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev);
+#endif
+static int  stm32wb_serial_setup(struct uart_dev_s *dev);
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_attach(struct uart_dev_s *dev);
+static void stm32wb_serial_detach(struct uart_dev_s *dev);
+static int  up_interrupt(int irq, void *context, void *arg);
+static int  stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                 unsigned long arg);
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int  stm32wb_serial_receive(struct uart_dev_s *dev,
+                                   unsigned int *status);
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev);
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper);
+#endif
+static void stm32wb_serial_send(struct uart_dev_s *dev, int ch);
+static void stm32wb_serial_txint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_txready(struct uart_dev_s *dev);
+
+#ifdef SERIAL_HAVE_RXDMA
+static int  stm32wb_serial_dmasetup(struct uart_dev_s *dev);
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                      unsigned int *status);
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv);
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_dmaiflowrestart(struct stm32wb_serial_s *priv);
+#endif
+static void stm32wb_serial_dmarxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_dmarxavailable(struct uart_dev_s *dev);
+
+static void stm32wb_serial_dmarxcallback(DMA_HANDLE handle, uint8_t status,
+                                         void *arg);
+#endif
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend);
+static void stm32wb_serial_pm_setsuspend(bool suspend);
+static void stm32wb_serial_pmnotify(struct pm_callback_s *cb,
+                                    int domain, enum pm_state_e pmstate);
+static int  stm32wb_serial_pmprepare(struct pm_callback_s *cb,
+                                     int domain, enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static const struct uart_ops_s g_uart_ops =
+{
+  .setup          = stm32wb_serial_setup,
+  .shutdown       = stm32wb_serial_shutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_receive,
+  .rxint          = stm32wb_serial_rxint,
+  .rxavailable    = stm32wb_serial_rxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+static const struct uart_ops_s g_uart_dma_ops =
+{
+  .setup          = stm32wb_serial_dmasetup,
+  .shutdown       = stm32wb_serial_dmashutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_dmareceive,
+  .rxint          = stm32wb_serial_dmarxint,
+  .rxavailable    = stm32wb_serial_dmarxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+/* I/O buffers */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static char g_lpuart1rxbuffer[CONFIG_LPUART1_RXBUFSIZE];
+static char g_lpuart1txbuffer[CONFIG_LPUART1_TXBUFSIZE];
+# ifdef CONFIG_LPUART1_RXDMA
+static char g_lpuart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static char g_usart1rxbuffer[CONFIG_USART1_RXBUFSIZE];
+static char g_usart1txbuffer[CONFIG_USART1_TXBUFSIZE];
+# ifdef CONFIG_USART1_RXDMA
+static char g_usart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+/* This describes the state of the STM32WB LPUART1 port. */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static struct stm32wb_serial_s g_lpuart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 1
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_LPUART1_RXBUFSIZE,
+        .buffer  = g_lpuart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_LPUART1_TXBUFSIZE,
+        .buffer  = g_lpuart1txbuffer,
+      },
+#ifdef CONFIG_LPUART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_lpuart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_LPUART1,
+  .parity        = CONFIG_LPUART1_PARITY,
+  .bits          = CONFIG_LPUART1_BITS,
+  .stopbits2     = CONFIG_LPUART1_2STOP,
+  .baud          = CONFIG_LPUART1_BAUD,
+  .apbclock      = STM32WB_PCLK1_FREQUENCY,
+  .usartbase     = STM32WB_LPUART1_BASE,
+  .tx_gpio       = GPIO_LPUART1_TX,
+  .rx_gpio       = GPIO_LPUART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_LPUART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_LPUART1_RTS,
+#endif
+#ifdef CONFIG_LPUART1_RXDMA
+  .rxdma_channel = DMAMAP_LPUART1_RX,
+  .rxfifo        = g_lpuart1rxfifo,
+#endif
+
+#ifdef CONFIG_LPUART1_RS485
+  .rs485_dir_gpio = GPIO_LPUART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This describes the state of the STM32WB USART1 port. */
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static struct stm32wb_serial_s g_usart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 2
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_USART1_RXBUFSIZE,
+        .buffer  = g_usart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_USART1_TXBUFSIZE,
+        .buffer  = g_usart1txbuffer,
+      },
+#ifdef CONFIG_USART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_usart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_USART1,
+  .parity        = CONFIG_USART1_PARITY,
+  .bits          = CONFIG_USART1_BITS,
+  .stopbits2     = CONFIG_USART1_2STOP,
+  .baud          = CONFIG_USART1_BAUD,
+  .apbclock      = STM32WB_PCLK2_FREQUENCY,
+  .usartbase     = STM32WB_USART1_BASE,
+  .tx_gpio       = GPIO_USART1_TX,
+  .rx_gpio       = GPIO_USART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_USART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_USART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_USART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_USART1_RTS,
+#endif
+#ifdef CONFIG_USART1_RXDMA
+  .rxdma_channel = DMAMAP_USART1_RX,
+  .rxfifo        = g_usart1rxfifo,
+#endif
+
+#ifdef CONFIG_USART1_RS485
+  .rs485_dir_gpio = GPIO_USART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This table lets us iterate over the configured USARTs */
+
+static struct stm32wb_serial_s *
+const g_uart_devs[STM32WB_NLPUART + STM32WB_NUSART] =
+{
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  [0] = &g_lpuart1priv,
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+  [1] = &g_usart1priv,
+#endif
+};
+
+#ifdef CONFIG_PM
+static struct
+{
+  struct pm_callback_s pm_cb;
+  bool serial_suspended;
+} g_serialpm =
+  {
+    .pm_cb.notify  = stm32wb_serial_pmnotify,
+    .pm_cb.prepare = stm32wb_serial_pmprepare,
+    .serial_suspended = false
+  };
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_serial_getreg
+ ****************************************************************************/
+
+static inline
+uint32_t stm32wb_serial_getreg(struct stm32wb_serial_s *priv, int offset)
+{
+  return getreg32(priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_putreg
+ ****************************************************************************/
+
+static inline void stm32wb_serial_putreg(struct stm32wb_serial_s *priv,
+                                         int offset, uint32_t value)
+{
+  putreg32(value, priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setusartint
+ ****************************************************************************/
+
+static inline
+void stm32wb_serial_setusartint(struct stm32wb_serial_s *priv, uint16_t ie)
+{
+  uint32_t cr;
+
+  /* Save the interrupt mask */
+
+  priv->ie = ie;
+
+  /* And restore the interrupt state (see the interrupt
+   * enable/usage table above)
+   */
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  cr &= ~(USART_CR1_USED_INTS);
+  cr |= (ie & (USART_CR1_USED_INTS));
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr);
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  cr &= ~USART_CR3_EIE;
+  cr |= (ie & USART_CR3_EIE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+}
+
+/****************************************************************************
+ * Name: up_restoreusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_restoreusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  stm32wb_serial_setusartint(priv, ie);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_disableusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_disableusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t *ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  if (ie)
+    {
+      uint32_t cr1;
+      uint32_t cr3;
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning            Usage
+       * ------------------ --------------- ------------------ ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected          (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete          (used only
+       *                                                        for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag        (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag          (not used)
+       */
+
+      cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+      cr3 = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+      /* Return the current interrupt mask value for the used interrupts.
+       * Notice that this depends on the fact that none of the used interrupt
+       * enable bits overlap. This logic would fail if we needed the break
+       * interrupt!
+       */
+
+      *ie = (cr1 & (USART_CR1_USED_INTS)) | (cr3 & USART_CR3_EIE);
+    }
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_setusartint(priv, 0);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmanextrx
+ *
+ * Description:
+ *   Returns the index into the RX FIFO where the DMA will place the next
+ *   byte that it receives.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmanextrx(struct stm32wb_serial_s *priv)
+{
+  size_t dmaresidual;
+
+  dmaresidual = stm32wb_dmaresidual(priv->rxdma);
+
+  return (RXDMA_BUFFER_SIZE - (int)dmaresidual);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_usart
+ *
+ * Description:
+ *   Set the serial line baud rate (USART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setbaud_usart(struct stm32wb_serial_s *priv)
+{
+  /* This first implementation is for U[S]ARTs that support oversampling
+   * by 8 in additional to the standard oversampling by 16.
+   */
+
+  uint32_t usartdiv8;
+  uint32_t cr1;
+  uint32_t brr;
+
+  /* In case of oversampling by 8, the equation is:
+   *
+   *   baud      = 2 * fCK / usartdiv8
+   *   usartdiv8 = 2 * fCK / baud
+   */
+
+  usartdiv8 = ((priv->apbclock << 1) + (priv->baud >> 1)) / priv->baud;
+
+  /* Baud rate for standard USART (SPI mode included):
+   *
+   * In case of oversampling by 16, the equation is:
+   *   baud       = fCK / usartdiv16
+   *   usartdiv16 = fCK / baud
+   *              = 2 * usartdiv8
+   */
+
+  /* Use oversamply by 8 only if the divisor is small.  But what is small? */
+
+  cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  brr = stm32wb_serial_getreg(priv, STM32WB_USART_BRR_OFFSET);
+  brr &= ~(USART_BRR_MANT_MASK | USART_BRR_FRAC_MASK);
+
+  if (usartdiv8 > 100)
+    {
+      /* Use usartdiv16 */
+
+      brr  |= (usartdiv8 + 1) >> 1;
+
+      /* Clear oversampling by 8 to enable oversampling by 16 */
+
+      cr1 &= ~USART_CR1_OVER8;
+    }
+  else
+    {
+      DEBUGASSERT(usartdiv8 >= 8);
+
+      /* Perform mysterious operations on bits 0-3 */
+
+      brr  |= ((usartdiv8 & 0xfff0) | ((usartdiv8 & 0x000f) >> 1));
+
+      /* Set oversampling by 8 */
+
+      cr1 |= USART_CR1_OVER8;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_lpuart
+ *
+ * Description:
+ *   Set the serial line baud rate (LPUART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static void stm32wb_serial_setbaud_lpuart(struct stm32wb_serial_s *priv)
+{
+  uint32_t brr;
+
+  /* The equation is:
+   *
+   *   baud = 256 * fCK / brr
+   *   brr  = 256 * fCK / baud
+   *
+   * It is forbidden to write values lower than LPUART_BRR_MIN in
+   * the LPUART_BRR register. fCK must range from 3 x baud rate to
+   * 4096 x baud rate.
+   */
+
+  brr = (((uint64_t)priv->apbclock << 8) + (priv->baud >> 1)) / priv->baud;
+  brr &= LPUART_BRR_MASK;
+
+  if (brr < LPUART_BRR_MIN)
+    {
+      brr = LPUART_BRR_MIN;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setformat
+ *
+ * Description:
+ *   Set the serial line format and speed.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Set baud rate */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  if (priv->usartbase == STM32WB_LPUART1_BASE)
+    {
+      stm32wb_serial_setbaud_lpuart(priv);
+    }
+  else
+#endif
+    {
+      stm32wb_serial_setbaud_usart(priv);
+    }
+
+  /* Configure parity mode */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0 | USART_CR1_M1);
+
+  if (priv->parity == 1)       /* Odd parity */
+    {
+      regval |= (USART_CR1_PCE | USART_CR1_PS);
+    }
+  else if (priv->parity == 2)  /* Even parity */
+    {
+      regval |= USART_CR1_PCE;
+    }
+
+  /* Configure word length (parity uses one of configured bits)
+   *
+   * Default: 1 start, 8 data (no parity), n stop, OR
+   *          1 start, 7 data + parity, n stop
+   */
+
+  if (priv->bits == 9 || (priv->bits == 8 && priv->parity != 0))
+    {
+      /* Select: 1 start, 8 data + parity, n stop, OR
+       *         1 start, 9 data (no parity), n stop.
+       */
+
+      regval |= USART_CR1_M0;
+    }
+  else if (priv->bits == 7 && priv->parity == 0)
+    {
+      /* Select: 1 start, 7 data (no parity), n stop, OR
+       */
+
+      regval |= USART_CR1_M1;
+    }
+
+  /* Else Select: 1 start, 7 data + parity, n stop, OR
+   *              1 start, 8 data (no parity), n stop.
+   */
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure STOP bits */
+
+  regval = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK);
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure hardware flow control */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSE | USART_CR3_RTSE);
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && !defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      regval |= USART_CR3_RTSE;
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->oflow && (priv->cts_gpio != 0))
+    {
+      regval |= USART_CR3_CTSE;
+    }
+#endif
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+}
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+/****************************************************************************
+ * Name: stm32wb_serial_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripheral.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#ifdef SERIAL_HAVE_RXDMA
+  bool dmarestored = false;
+#endif
+
+  if (priv->suspended == suspend)
+    {
+      return;
+    }
+
+  priv->suspended = suspend;
+
+  if (suspend)
+    {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Force RTS high to prevent further Rx. */
+
+          stm32wb_configgpio((priv->rts_gpio & ~GPIO_MODE_MASK)
+                             | (GPIO_OUTPUT | GPIO_OUTPUT_SET));
+        }
+#endif
+
+      /* Disable interrupts to prevent Tx. */
+
+      stm32wb_serial_disableusartint(priv, &priv->suspended_ie);
+
+      /* Wait last Tx to complete. */
+
+      while ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+              USART_ISR_TC) == 0);
+
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && !priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow && priv->rxdmanext == RXDMA_BUFFER_SIZE)
+            {
+              /* Rx DMA in non-circular iflow mode and already stopped
+               * at end of DMA buffer. No need to suspend.
+               */
+            }
+          else
+#endif
+            {
+              /* Suspend Rx DMA. */
+
+              stm32wb_dmastop(priv->rxdma);
+              priv->rxdmasusp = true;
+            }
+        }
+#endif
+    }
+  else
+    {
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              stm32wb_serial_dmaiflowrestart(priv);
+            }
+          else
+#endif
+            {
+              /* This USART does not have HW flow-control. Unconditionally
+               * re-enable DMA (might loss unprocessed bytes received
+               * to DMA buffer before suspending).
+               */
+
+              stm32wb_serial_dmareenable(priv);
+              priv->rxdmasusp = false;
+            }
+
+          dmarestored = true;
+        }
+#endif
+
+      /* Re-enable interrupts to resume Tx. */
+
+      stm32wb_serial_restoreusartint(priv, priv->suspended_ie);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Restore peripheral RTS control. */
+
+          stm32wb_configgpio(priv->rts_gpio);
+        }
+#endif
+    }
+
+#ifdef SERIAL_HAVE_RXDMA
+  if (dmarestored)
+    {
+      irqstate_t flags;
+
+      flags = enter_critical_section();
+
+      /* Perform initial Rx DMA buffer fetch to wake-up serial device
+       * activity.
+       */
+
+      if (priv->rxdma != NULL)
+        {
+          stm32wb_serial_dmarxcallback(priv->rxdma, 0, priv);
+        }
+
+      leave_critical_section(flags);
+    }
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_pm_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripherals for/from deep-sleep/stop modes.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_pm_setsuspend(bool suspend)
+{
+  int n;
+
+  /* Already in desired state? */
+
+  if (suspend == g_serialpm.serial_suspended)
+    return;
+
+  g_serialpm.serial_suspended = suspend;
+
+  for (n = 0; n < STM32WB_NLPUART + STM32WB_NUSART; n++)
+    {
+      struct stm32wb_serial_s *priv = g_uart_devs[n];
+
+      if (!priv || !priv->initialized)
+        {
+          continue;
+        }
+
+      stm32wb_serial_setsuspend(&priv->dev, suspend);

Review Comment:
   ```suggestion
         if (priv != NULL && priv->initialized)
           {
             stm32wb_serial_setsuspend(&priv->dev, suspend);
           }
   ```



##########
arch/arm/src/stm32wb/stm32wb_serial.c:
##########
@@ -0,0 +1,2896 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_serial.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/serial/serial.h>
+#include <nuttx/power/pm.h>
+#include <arch/board/board.h>
+
+#ifdef CONFIG_SERIAL_TERMIOS
+#  include <termios.h>
+#endif
+
+#include "arm_internal.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_uart.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Some sanity checks *******************************************************/
+
+/* DMA configuration */
+
+#ifdef SERIAL_HAVE_RXDMA
+
+/* Currently RS-485 support cannot be enabled when RXDMA is in use due to
+ * lack of testing - RS-485 support was developed on STM32F1x
+ */
+
+#  if (defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_RS485))
+#    error "RXDMA and RS-485 cannot be enabled at the same time for the same U[S]ART"
+#  endif
+
+/* For the stm32wb, there are alternate DMA channels for USART1.
+ * Logic in the board.h file make the DMA channel selection by defining
+ * the following in the board.h file.
+ */
+
+#  if defined(CONFIG_USART1_RXDMA) && !defined(DMAMAP_USART1_RX)
+#    error "USART1 DMA channel not defined (DMAMAP_USART1_RX)"
+#  endif
+
+/* The DMA buffer size when using RX DMA to emulate a FIFO.
+ *
+ * When streaming data, the generic serial layer will be called
+ * every time the FIFO receives half this number of bytes.
+ *
+ * The buffer size should be an even multiple of ARMV7M_DCACHE_LINESIZE.
+ */
+
+#  if !defined(CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE) || \
+      CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE == 0
+#    define RXDMA_BUFFER_SIZE 32
+#  else
+#    define RXDMA_BUFFER_SIZE ((CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE + 31) & ~31)
+#  endif
+
+/* DMA priority */
+
+#  ifndef CONFIG_USART_RXDMAPRIO
+#    define CONFIG_USART_RXDMAPRIO  DMA_CCR_PRIMED
+#  endif
+#  if (CONFIG_USART_RXDMAPRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_USART_RXDMAPRIO"
+#  endif
+
+/* DMA control words */
+
+#  define SERIAL_DMA_CONTROL_WORD      \
+              (DMA_CCR_CIRC          | \
+               DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  ifdef CONFIG_SERIAL_IFLOWCONTROL
+#    define SERIAL_DMA_IFLOW_CONTROL_WORD \
+              (DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  endif
+
+#endif
+
+/* Power management definitions */
+
+#if defined(CONFIG_PM) && !defined(CONFIG_STM32WB_PM_SERIAL_ACTIVITY)
+#  define CONFIG_STM32WB_PM_SERIAL_ACTIVITY  10
+#endif
+#if defined(CONFIG_PM)
+#  define PM_IDLE_DOMAIN             0 /* Revisit */
+#endif
+
+/* Keep track if a Break was set
+ *
+ * Note:
+ *
+ * 1) This value is set in the priv->ie but never written to the control
+ *    register. It must not collide with USART_CR1_USED_INTS or USART_CR3_EIE
+ * 2) USART_CR3_EIE is also carried in the up_dev_s ie member.
+ *
+ * See stm32wb_serial_restoreusartint where the masking is done.
+ */
+
+#ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+#  define USART_CR1_IE_BREAK_INPROGRESS_SHFTS 15
+#  define USART_CR1_IE_BREAK_INPROGRESS (1 << USART_CR1_IE_BREAK_INPROGRESS_SHFTS)
+#endif
+
+#ifdef USE_SERIALDRIVER
+#ifdef HAVE_UART
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_serial_s
+{
+  struct uart_dev_s dev;       /* Generic UART device */
+  uint16_t          ie;        /* Saved interrupt mask bits value */
+  uint16_t          sr;        /* Saved status bits */
+
+  /* Has been initialized and HW is setup. */
+
+  bool              initialized;
+
+#ifdef CONFIG_PM
+  bool              suspended; /* UART device has been suspended. */
+
+  /* Interrupt mask value stored before suspending for stop mode. */
+
+  uint16_t          suspended_ie;
+#endif
+
+  /* If termios are supported, then the following fields may vary at
+   * runtime.
+   */
+
+#ifdef CONFIG_SERIAL_TERMIOS
+  uint8_t           parity;    /* 0=none, 1=odd, 2=even */
+  uint8_t           bits;      /* Number of bits (7 or 8) */
+  bool              stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  bool              iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  bool              oflow;     /* output flow control (CTS) enabled */
+#endif
+  uint32_t          baud;      /* Configured baud */
+#else
+  const uint8_t     parity;    /* 0=none, 1=odd, 2=even */
+  const uint8_t     bits;      /* Number of bits (7 or 8) */
+  const bool        stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const bool        iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const bool        oflow;     /* output flow control (CTS) enabled */
+#endif
+  const uint32_t    baud;      /* Configured baud */
+#endif
+
+  const uint8_t     irq;       /* IRQ associated with this USART */
+  const uint32_t    apbclock;  /* PCLK 1 or 2 frequency */
+  const uint32_t    usartbase; /* Base address of USART registers */
+  const uint32_t    tx_gpio;   /* U[S]ART TX GPIO pin configuration */
+  const uint32_t    rx_gpio;   /* U[S]ART RX GPIO pin configuration */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const uint32_t    rts_gpio;  /* U[S]ART RTS GPIO pin configuration */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const uint32_t    cts_gpio;  /* U[S]ART CTS GPIO pin configuration */
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+  const unsigned int rxdma_channel; /* DMA channel assigned */
+#endif
+
+  /* RX DMA state */
+
+#ifdef SERIAL_HAVE_RXDMA
+  DMA_HANDLE        rxdma;     /* currently-open receive DMA stream */
+  bool              rxenable;  /* DMA-based reception en/disable */
+#ifdef CONFIG_PM
+  bool              rxdmasusp; /* Rx DMA suspended */
+#endif
+  uint32_t          rxdmanext; /* Next byte in the DMA buffer to be read */
+  char       *const rxfifo;    /* Receive DMA buffer */
+#endif
+
+#ifdef HAVE_RS485
+  const uint32_t    rs485_dir_gpio;     /* U[S]ART RS-485 DIR GPIO pin configuration */
+  const bool        rs485_dir_polarity; /* U[S]ART RS-485 DIR pin state for TX enabled */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev);
+#endif
+static int  stm32wb_serial_setup(struct uart_dev_s *dev);
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_attach(struct uart_dev_s *dev);
+static void stm32wb_serial_detach(struct uart_dev_s *dev);
+static int  up_interrupt(int irq, void *context, void *arg);
+static int  stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                 unsigned long arg);
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int  stm32wb_serial_receive(struct uart_dev_s *dev,
+                                   unsigned int *status);
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev);
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper);
+#endif
+static void stm32wb_serial_send(struct uart_dev_s *dev, int ch);
+static void stm32wb_serial_txint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_txready(struct uart_dev_s *dev);
+
+#ifdef SERIAL_HAVE_RXDMA
+static int  stm32wb_serial_dmasetup(struct uart_dev_s *dev);
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                      unsigned int *status);
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv);
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_dmaiflowrestart(struct stm32wb_serial_s *priv);
+#endif
+static void stm32wb_serial_dmarxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_dmarxavailable(struct uart_dev_s *dev);
+
+static void stm32wb_serial_dmarxcallback(DMA_HANDLE handle, uint8_t status,
+                                         void *arg);
+#endif
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend);
+static void stm32wb_serial_pm_setsuspend(bool suspend);
+static void stm32wb_serial_pmnotify(struct pm_callback_s *cb,
+                                    int domain, enum pm_state_e pmstate);
+static int  stm32wb_serial_pmprepare(struct pm_callback_s *cb,
+                                     int domain, enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static const struct uart_ops_s g_uart_ops =
+{
+  .setup          = stm32wb_serial_setup,
+  .shutdown       = stm32wb_serial_shutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_receive,
+  .rxint          = stm32wb_serial_rxint,
+  .rxavailable    = stm32wb_serial_rxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+static const struct uart_ops_s g_uart_dma_ops =
+{
+  .setup          = stm32wb_serial_dmasetup,
+  .shutdown       = stm32wb_serial_dmashutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_dmareceive,
+  .rxint          = stm32wb_serial_dmarxint,
+  .rxavailable    = stm32wb_serial_dmarxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+/* I/O buffers */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static char g_lpuart1rxbuffer[CONFIG_LPUART1_RXBUFSIZE];
+static char g_lpuart1txbuffer[CONFIG_LPUART1_TXBUFSIZE];
+# ifdef CONFIG_LPUART1_RXDMA
+static char g_lpuart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static char g_usart1rxbuffer[CONFIG_USART1_RXBUFSIZE];
+static char g_usart1txbuffer[CONFIG_USART1_TXBUFSIZE];
+# ifdef CONFIG_USART1_RXDMA
+static char g_usart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+/* This describes the state of the STM32WB LPUART1 port. */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static struct stm32wb_serial_s g_lpuart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 1
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_LPUART1_RXBUFSIZE,
+        .buffer  = g_lpuart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_LPUART1_TXBUFSIZE,
+        .buffer  = g_lpuart1txbuffer,
+      },
+#ifdef CONFIG_LPUART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_lpuart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_LPUART1,
+  .parity        = CONFIG_LPUART1_PARITY,
+  .bits          = CONFIG_LPUART1_BITS,
+  .stopbits2     = CONFIG_LPUART1_2STOP,
+  .baud          = CONFIG_LPUART1_BAUD,
+  .apbclock      = STM32WB_PCLK1_FREQUENCY,
+  .usartbase     = STM32WB_LPUART1_BASE,
+  .tx_gpio       = GPIO_LPUART1_TX,
+  .rx_gpio       = GPIO_LPUART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_LPUART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_LPUART1_RTS,
+#endif
+#ifdef CONFIG_LPUART1_RXDMA
+  .rxdma_channel = DMAMAP_LPUART1_RX,
+  .rxfifo        = g_lpuart1rxfifo,
+#endif
+
+#ifdef CONFIG_LPUART1_RS485
+  .rs485_dir_gpio = GPIO_LPUART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This describes the state of the STM32WB USART1 port. */
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static struct stm32wb_serial_s g_usart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 2
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_USART1_RXBUFSIZE,
+        .buffer  = g_usart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_USART1_TXBUFSIZE,
+        .buffer  = g_usart1txbuffer,
+      },
+#ifdef CONFIG_USART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_usart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_USART1,
+  .parity        = CONFIG_USART1_PARITY,
+  .bits          = CONFIG_USART1_BITS,
+  .stopbits2     = CONFIG_USART1_2STOP,
+  .baud          = CONFIG_USART1_BAUD,
+  .apbclock      = STM32WB_PCLK2_FREQUENCY,
+  .usartbase     = STM32WB_USART1_BASE,
+  .tx_gpio       = GPIO_USART1_TX,
+  .rx_gpio       = GPIO_USART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_USART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_USART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_USART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_USART1_RTS,
+#endif
+#ifdef CONFIG_USART1_RXDMA
+  .rxdma_channel = DMAMAP_USART1_RX,
+  .rxfifo        = g_usart1rxfifo,
+#endif
+
+#ifdef CONFIG_USART1_RS485
+  .rs485_dir_gpio = GPIO_USART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This table lets us iterate over the configured USARTs */
+
+static struct stm32wb_serial_s *
+const g_uart_devs[STM32WB_NLPUART + STM32WB_NUSART] =
+{
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  [0] = &g_lpuart1priv,
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+  [1] = &g_usart1priv,
+#endif
+};
+
+#ifdef CONFIG_PM
+static struct
+{
+  struct pm_callback_s pm_cb;
+  bool serial_suspended;
+} g_serialpm =
+  {
+    .pm_cb.notify  = stm32wb_serial_pmnotify,
+    .pm_cb.prepare = stm32wb_serial_pmprepare,
+    .serial_suspended = false
+  };
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_serial_getreg
+ ****************************************************************************/
+
+static inline
+uint32_t stm32wb_serial_getreg(struct stm32wb_serial_s *priv, int offset)
+{
+  return getreg32(priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_putreg
+ ****************************************************************************/
+
+static inline void stm32wb_serial_putreg(struct stm32wb_serial_s *priv,
+                                         int offset, uint32_t value)
+{
+  putreg32(value, priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setusartint
+ ****************************************************************************/
+
+static inline
+void stm32wb_serial_setusartint(struct stm32wb_serial_s *priv, uint16_t ie)
+{
+  uint32_t cr;
+
+  /* Save the interrupt mask */
+
+  priv->ie = ie;
+
+  /* And restore the interrupt state (see the interrupt
+   * enable/usage table above)
+   */
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  cr &= ~(USART_CR1_USED_INTS);
+  cr |= (ie & (USART_CR1_USED_INTS));
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr);
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  cr &= ~USART_CR3_EIE;
+  cr |= (ie & USART_CR3_EIE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+}
+
+/****************************************************************************
+ * Name: up_restoreusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_restoreusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  stm32wb_serial_setusartint(priv, ie);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_disableusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_disableusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t *ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  if (ie)
+    {
+      uint32_t cr1;
+      uint32_t cr3;
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning            Usage
+       * ------------------ --------------- ------------------ ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected          (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete          (used only
+       *                                                        for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag        (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag          (not used)
+       */
+
+      cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+      cr3 = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+      /* Return the current interrupt mask value for the used interrupts.
+       * Notice that this depends on the fact that none of the used interrupt
+       * enable bits overlap. This logic would fail if we needed the break
+       * interrupt!
+       */
+
+      *ie = (cr1 & (USART_CR1_USED_INTS)) | (cr3 & USART_CR3_EIE);
+    }
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_setusartint(priv, 0);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmanextrx
+ *
+ * Description:
+ *   Returns the index into the RX FIFO where the DMA will place the next
+ *   byte that it receives.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmanextrx(struct stm32wb_serial_s *priv)
+{
+  size_t dmaresidual;
+
+  dmaresidual = stm32wb_dmaresidual(priv->rxdma);
+
+  return (RXDMA_BUFFER_SIZE - (int)dmaresidual);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_usart
+ *
+ * Description:
+ *   Set the serial line baud rate (USART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setbaud_usart(struct stm32wb_serial_s *priv)
+{
+  /* This first implementation is for U[S]ARTs that support oversampling
+   * by 8 in additional to the standard oversampling by 16.
+   */
+
+  uint32_t usartdiv8;
+  uint32_t cr1;
+  uint32_t brr;
+
+  /* In case of oversampling by 8, the equation is:
+   *
+   *   baud      = 2 * fCK / usartdiv8
+   *   usartdiv8 = 2 * fCK / baud
+   */
+
+  usartdiv8 = ((priv->apbclock << 1) + (priv->baud >> 1)) / priv->baud;
+
+  /* Baud rate for standard USART (SPI mode included):
+   *
+   * In case of oversampling by 16, the equation is:
+   *   baud       = fCK / usartdiv16
+   *   usartdiv16 = fCK / baud
+   *              = 2 * usartdiv8
+   */
+
+  /* Use oversamply by 8 only if the divisor is small.  But what is small? */
+
+  cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  brr = stm32wb_serial_getreg(priv, STM32WB_USART_BRR_OFFSET);
+  brr &= ~(USART_BRR_MANT_MASK | USART_BRR_FRAC_MASK);
+
+  if (usartdiv8 > 100)
+    {
+      /* Use usartdiv16 */
+
+      brr  |= (usartdiv8 + 1) >> 1;
+
+      /* Clear oversampling by 8 to enable oversampling by 16 */
+
+      cr1 &= ~USART_CR1_OVER8;
+    }
+  else
+    {
+      DEBUGASSERT(usartdiv8 >= 8);
+
+      /* Perform mysterious operations on bits 0-3 */
+
+      brr  |= ((usartdiv8 & 0xfff0) | ((usartdiv8 & 0x000f) >> 1));
+
+      /* Set oversampling by 8 */
+
+      cr1 |= USART_CR1_OVER8;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_lpuart
+ *
+ * Description:
+ *   Set the serial line baud rate (LPUART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static void stm32wb_serial_setbaud_lpuart(struct stm32wb_serial_s *priv)
+{
+  uint32_t brr;
+
+  /* The equation is:
+   *
+   *   baud = 256 * fCK / brr
+   *   brr  = 256 * fCK / baud
+   *
+   * It is forbidden to write values lower than LPUART_BRR_MIN in
+   * the LPUART_BRR register. fCK must range from 3 x baud rate to
+   * 4096 x baud rate.
+   */
+
+  brr = (((uint64_t)priv->apbclock << 8) + (priv->baud >> 1)) / priv->baud;
+  brr &= LPUART_BRR_MASK;
+
+  if (brr < LPUART_BRR_MIN)
+    {
+      brr = LPUART_BRR_MIN;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setformat
+ *
+ * Description:
+ *   Set the serial line format and speed.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Set baud rate */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  if (priv->usartbase == STM32WB_LPUART1_BASE)
+    {
+      stm32wb_serial_setbaud_lpuart(priv);
+    }
+  else
+#endif
+    {
+      stm32wb_serial_setbaud_usart(priv);
+    }
+
+  /* Configure parity mode */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0 | USART_CR1_M1);
+
+  if (priv->parity == 1)       /* Odd parity */
+    {
+      regval |= (USART_CR1_PCE | USART_CR1_PS);
+    }
+  else if (priv->parity == 2)  /* Even parity */
+    {
+      regval |= USART_CR1_PCE;
+    }
+
+  /* Configure word length (parity uses one of configured bits)
+   *
+   * Default: 1 start, 8 data (no parity), n stop, OR
+   *          1 start, 7 data + parity, n stop
+   */
+
+  if (priv->bits == 9 || (priv->bits == 8 && priv->parity != 0))
+    {
+      /* Select: 1 start, 8 data + parity, n stop, OR
+       *         1 start, 9 data (no parity), n stop.
+       */
+
+      regval |= USART_CR1_M0;
+    }
+  else if (priv->bits == 7 && priv->parity == 0)
+    {
+      /* Select: 1 start, 7 data (no parity), n stop, OR
+       */
+
+      regval |= USART_CR1_M1;
+    }
+
+  /* Else Select: 1 start, 7 data + parity, n stop, OR
+   *              1 start, 8 data (no parity), n stop.
+   */
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure STOP bits */
+
+  regval = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK);
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure hardware flow control */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSE | USART_CR3_RTSE);
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && !defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      regval |= USART_CR3_RTSE;
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->oflow && (priv->cts_gpio != 0))
+    {
+      regval |= USART_CR3_CTSE;
+    }
+#endif
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+}
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+/****************************************************************************
+ * Name: stm32wb_serial_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripheral.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#ifdef SERIAL_HAVE_RXDMA
+  bool dmarestored = false;
+#endif
+
+  if (priv->suspended == suspend)
+    {
+      return;
+    }
+
+  priv->suspended = suspend;
+
+  if (suspend)
+    {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Force RTS high to prevent further Rx. */
+
+          stm32wb_configgpio((priv->rts_gpio & ~GPIO_MODE_MASK)
+                             | (GPIO_OUTPUT | GPIO_OUTPUT_SET));
+        }
+#endif
+
+      /* Disable interrupts to prevent Tx. */
+
+      stm32wb_serial_disableusartint(priv, &priv->suspended_ie);
+
+      /* Wait last Tx to complete. */
+
+      while ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+              USART_ISR_TC) == 0);
+
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && !priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow && priv->rxdmanext == RXDMA_BUFFER_SIZE)
+            {
+              /* Rx DMA in non-circular iflow mode and already stopped
+               * at end of DMA buffer. No need to suspend.
+               */
+            }
+          else
+#endif
+            {
+              /* Suspend Rx DMA. */
+
+              stm32wb_dmastop(priv->rxdma);
+              priv->rxdmasusp = true;
+            }
+        }
+#endif
+    }
+  else
+    {
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              stm32wb_serial_dmaiflowrestart(priv);
+            }
+          else
+#endif
+            {
+              /* This USART does not have HW flow-control. Unconditionally
+               * re-enable DMA (might loss unprocessed bytes received
+               * to DMA buffer before suspending).
+               */
+
+              stm32wb_serial_dmareenable(priv);
+              priv->rxdmasusp = false;
+            }
+
+          dmarestored = true;
+        }
+#endif
+
+      /* Re-enable interrupts to resume Tx. */
+
+      stm32wb_serial_restoreusartint(priv, priv->suspended_ie);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Restore peripheral RTS control. */
+
+          stm32wb_configgpio(priv->rts_gpio);
+        }
+#endif
+    }
+
+#ifdef SERIAL_HAVE_RXDMA
+  if (dmarestored)
+    {
+      irqstate_t flags;
+
+      flags = enter_critical_section();
+
+      /* Perform initial Rx DMA buffer fetch to wake-up serial device
+       * activity.
+       */
+
+      if (priv->rxdma != NULL)
+        {
+          stm32wb_serial_dmarxcallback(priv->rxdma, 0, priv);
+        }
+
+      leave_critical_section(flags);
+    }
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_pm_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripherals for/from deep-sleep/stop modes.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_pm_setsuspend(bool suspend)
+{
+  int n;
+
+  /* Already in desired state? */
+
+  if (suspend == g_serialpm.serial_suspended)
+    return;
+
+  g_serialpm.serial_suspended = suspend;
+
+  for (n = 0; n < STM32WB_NLPUART + STM32WB_NUSART; n++)
+    {
+      struct stm32wb_serial_s *priv = g_uart_devs[n];
+
+      if (!priv || !priv->initialized)
+        {
+          continue;
+        }
+
+      stm32wb_serial_setsuspend(&priv->dev, suspend);
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setapbclock
+ *
+ * Description:
+ *   Enable or disable APB clock for the USART peripheral
+ *
+ * Input Parameters:
+ *   dev - A reference to the UART driver state structure
+ *   on  - Enable clock if 'on' is 'true' and disable if 'false'
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_setapbclock(struct uart_dev_s *dev, bool on)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t rcc_en;
+  uint32_t regaddr;
+
+  /* Determine which USART to configure */
+
+  switch (priv->usartbase)
+    {
+    default:
+      return;
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+    case STM32WB_LPUART1_BASE:
+      rcc_en = RCC_APB1ENR2_LPUART1EN;
+      regaddr = STM32WB_RCC_APB1ENR2;
+      break;
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+    case STM32WB_USART1_BASE:
+      rcc_en = RCC_APB2ENR_USART1EN;
+      regaddr = STM32WB_RCC_APB2ENR;
+      break;
+#endif
+    }
+
+  /* Enable/disable APB 1/2 clock for USART */
+
+  if (on)
+    {
+      modifyreg32(regaddr, 0, rcc_en);
+    }
+  else
+    {
+      modifyreg32(regaddr, rcc_en, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setup
+ *
+ * Description:
+ *   Configure the USART baud, bits, parity, etc. This method is called the
+ *   first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_setup(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+  uint32_t regval;
+
+  /* Note: The logic here depends on the fact that that the USART module
+   * was enabled in stm32wb_lowsetup().
+   */
+
+  /* Enable USART APB1/2 clock */
+
+  stm32wb_serial_setapbclock(dev, true);
+
+  /* Configure pins for USART use */
+
+  stm32wb_configgpio(priv->tx_gpio);
+  stm32wb_configgpio(priv->rx_gpio);
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->cts_gpio != 0)
+    {
+      stm32wb_configgpio(priv->cts_gpio);
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->rts_gpio != 0)
+    {
+      uint32_t config = priv->rts_gpio;
+
+#ifdef CONFIG_STM32WB_FLOWCONTROL_BROKEN
+      /* Instead of letting hw manage this pin, we will bitbang */
+
+      config = (config & ~GPIO_MODE_MASK) | GPIO_OUTPUT;
+#endif
+      stm32wb_configgpio(config);
+    }
+#endif
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_configgpio(priv->rs485_dir_gpio);
+      stm32wb_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity);
+    }
+#endif
+
+  /* Configure CR2 */
+
+  /* Clear STOP, CLKEN, CPOL, CPHA, LBCL, and interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK | USART_CR2_CLKEN | USART_CR2_CPOL |
+              USART_CR2_CPHA | USART_CR2_LBCL | USART_CR2_LBDIE);
+
+  /* Configure STOP bits */
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure CR1 */
+
+  /* Clear TE, REm and all interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_TE | USART_CR1_RE | USART_CR1_ALLINTS);
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure CR3 */
+
+  /* Clear CTSE, RTSE, and all interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSIE | USART_CR3_CTSE | USART_CR3_RTSE |
+              USART_CR3_EIE);
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+
+  /* Configure the USART line format and speed. */
+
+  stm32wb_serial_setformat(dev);
+
+  /* Enable Rx, Tx, and the USART */
+
+  regval      = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval     |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+  /* Set up the cached interrupt enables value */
+
+  priv->ie    = 0;
+
+  /* Mark device as initialized. */
+
+  priv->initialized = true;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmasetup
+ *
+ * Description:
+ *   Configure the USART baud, bits, parity, etc. This method is called the
+ *   first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmasetup(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int result;
+  uint32_t regval;
+
+  /* Do the basic UART setup first, unless we are the console */
+
+  if (!dev->isconsole)
+    {
+      result = stm32wb_serial_setup(dev);
+      if (result != OK)
+        {
+          return result;
+        }
+    }
+
+  /* Acquire the DMA channel.  This should always succeed. */
+
+  priv->rxdma = stm32wb_dmachannel(priv->rxdma_channel);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Configure for non-circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                     priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                     (uint32_t)priv->rxfifo,
+                     RXDMA_BUFFER_SIZE,
+                     SERIAL_DMA_IFLOW_CONTROL_WORD);
+    }
+  else
+#endif
+    {
+      /* Configure for circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                     priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                     (uint32_t)priv->rxfifo,
+                     RXDMA_BUFFER_SIZE,
+                     SERIAL_DMA_CONTROL_WORD);
+    }
+
+  /* Reset our DMA shadow pointer to match the address just
+   * programmed above.
+   */
+
+  priv->rxdmanext = 0;
+
+  /* Enable receive DMA for the UART */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval |= USART_CR3_DMAR;
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Start the DMA channel, and arrange for callbacks at the full point
+       * in the FIFO. After buffer gets full, hardware flow-control kicks
+       * in and DMA transfer is stopped.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, false);
+    }
+  else
+#endif
+    {
+      /* Start the DMA channel, and arrange for callbacks at the half and
+       * full points in the FIFO.  This ensures that we have half a FIFO
+       * worth of time to claim bytes before they are overwritten.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, true);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_shutdown
+ *
+ * Description:
+ *   Disable the USART.  This method is called when the serial
+ *   port is closed
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Mark device as uninitialized. */
+
+  priv->initialized = false;
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_disableusartint(priv, NULL);
+
+  /* Disable USART APB1/2 clock */
+
+  stm32wb_serial_setapbclock(dev, false);
+
+  /* Disable Rx, Tx, and the UART */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Release pins. "If the serial-attached device is powered down, the TX
+   * pin causes back-powering, potentially confusing the device to the point
+   * of complete lock-up."
+   *
+   * REVISIT:  Is unconfiguring the pins appropriate for all device?  If not,
+   * then this may need to be a configuration option.
+   */
+
+  stm32wb_unconfiggpio(priv->tx_gpio);
+  stm32wb_unconfiggpio(priv->rx_gpio);
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->cts_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->cts_gpio);
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->rts_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->rts_gpio);
+    }
+#endif
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->rs485_dir_gpio);
+    }
+#endif
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmashutdown
+ *
+ * Description:
+ *   Disable the USART.  This method is called when the serial
+ *   port is closed
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  /* Perform the normal UART shutdown */
+
+  stm32wb_serial_shutdown(dev);
+
+  /* Stop the DMA channel */
+
+  stm32wb_dmastop(priv->rxdma);
+
+  /* Release the DMA channel */
+
+  stm32wb_dmafree(priv->rxdma);
+  priv->rxdma = NULL;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_attach
+ *
+ * Description:
+ *   Configure the USART to operation in interrupt driven mode.  This method
+ *   is called when the serial port is opened.  Normally, this is just after
+ *   the the setup() method is called, however, the serial console may
+ *   operate in a non-interrupt driven mode during the boot phase.
+ *
+ *   RX and TX interrupts are not enabled when by the attach method (unless
+ *   the hardware supports multiple levels of interrupt enabling). The RX
+ *   and TX interrupts are not enabled until the txint() and rxint()
+ *   methods are called.
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_attach(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int ret;
+
+  /* Attach and enable the IRQ */
+
+  ret = irq_attach(priv->irq, up_interrupt, priv);
+  if (ret == OK)
+    {
+      /* Enable the interrupt (RX and TX interrupts are still disabled
+       * in the USART
+       */
+
+      up_enable_irq(priv->irq);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_detach
+ *
+ * Description:
+ *   Detach USART interrupts.  This method is called when the serial port is
+ *   closed normally just before the shutdown method is called.
+ *   The exception is the serial console which is never shutdown.
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_detach(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  up_disable_irq(priv->irq);
+  irq_detach(priv->irq);
+}
+
+/****************************************************************************
+ * Name: up_interrupt
+ *
+ * Description:
+ *   This is the USART interrupt handler.  It will be invoked when an
+ *   interrupt received on the 'irq'  It should call uart_transmitchars or
+ *   uart_receivechar to perform the appropriate data transfers.  The
+ *   interrupt handling logic must be able to map the 'irq' number into the
+ *   appropriate uart_dev_s structure in order to call these functions.
+ *
+ ****************************************************************************/
+
+static int up_interrupt(int irq, void *context, void *arg)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)arg;
+  int  passes;
+  bool handled;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Report serial activity to the power management logic */
+
+#if defined(CONFIG_PM) && CONFIG_STM32WB_PM_SERIAL_ACTIVITY > 0
+  pm_activity(PM_IDLE_DOMAIN, CONFIG_STM32WB_PM_SERIAL_ACTIVITY);
+#endif
+
+  /* Loop until there are no characters to be transferred or,
+   * until we have been looping for a long time.
+   */
+
+  handled = true;
+  for (passes = 0; passes < 256 && handled; passes++)
+    {
+      handled = false;
+
+      /* Get the masked USART status word. */
+
+      priv->sr = stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET);
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning             Usage
+       * ------------------ --------------- ------------------- ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected           (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete           (used only
+       *                                                         for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag         (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag           (not used)
+       *
+       * NOTE: Some of these status bits must be cleared by explicitly
+       * writing one to the ICR register: USART_ICR_CTSCF, USART_ICR_LBDCF.
+       * Note of those are currently being used.
+       */
+
+#ifdef HAVE_RS485
+      /* Transmission of whole buffer is over - TC is set, TXEIE is cleared.
+       * Note - this should be first, to have the most recent TC bit value
+       * from SR register - sending data affects TC, but without refresh we
+       * will not know that...
+       */
+
+      if ((priv->sr & USART_ISR_TC) != 0 &&
+          (priv->ie & USART_CR1_TCIE) != 0 &&
+          (priv->ie & USART_CR1_TXEIE) == 0)
+        {
+          stm32wb_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity);
+          stm32wb_serial_restoreusartint(priv, priv->ie & ~USART_CR1_TCIE);
+        }
+#endif
+
+      /* Handle incoming, receive bytes. */
+
+      if ((priv->sr & USART_ISR_RXNE) != 0 &&
+          (priv->ie & USART_CR1_RXNEIE) != 0)
+        {
+          /* Received data ready... process incoming bytes.  NOTE the check
+           * for RXNEIE:  We cannot call uart_recvchards of RX interrupts
+           * are disabled.
+           */
+
+          uart_recvchars(&priv->dev);
+          handled = true;
+        }
+
+      /* We may still have to read from the DR register to clear any pending
+       * error conditions.
+       */
+
+      else if ((priv->sr &
+                (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE)) != 0)
+        {
+          /* These errors are cleared by writing the corresponding bit to the
+           * interrupt clear register (ICR).
+           */
+
+          stm32wb_serial_putreg(priv, STM32WB_USART_ICR_OFFSET,
+                               (USART_ICR_NCF | USART_ICR_ORECF |

Review Comment:
   ```suggestion
                                   (USART_ICR_NCF | USART_ICR_ORECF |
   ```



##########
arch/arm/src/stm32wb/stm32wb_serial.c:
##########
@@ -0,0 +1,2896 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_serial.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/serial/serial.h>
+#include <nuttx/power/pm.h>
+#include <arch/board/board.h>
+
+#ifdef CONFIG_SERIAL_TERMIOS
+#  include <termios.h>
+#endif
+
+#include "arm_internal.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_uart.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Some sanity checks *******************************************************/
+
+/* DMA configuration */
+
+#ifdef SERIAL_HAVE_RXDMA
+
+/* Currently RS-485 support cannot be enabled when RXDMA is in use due to
+ * lack of testing - RS-485 support was developed on STM32F1x
+ */
+
+#  if (defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_RS485))
+#    error "RXDMA and RS-485 cannot be enabled at the same time for the same U[S]ART"
+#  endif
+
+/* For the stm32wb, there are alternate DMA channels for USART1.
+ * Logic in the board.h file make the DMA channel selection by defining
+ * the following in the board.h file.
+ */
+
+#  if defined(CONFIG_USART1_RXDMA) && !defined(DMAMAP_USART1_RX)
+#    error "USART1 DMA channel not defined (DMAMAP_USART1_RX)"
+#  endif
+
+/* The DMA buffer size when using RX DMA to emulate a FIFO.
+ *
+ * When streaming data, the generic serial layer will be called
+ * every time the FIFO receives half this number of bytes.
+ *
+ * The buffer size should be an even multiple of ARMV7M_DCACHE_LINESIZE.
+ */
+
+#  if !defined(CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE) || \
+      CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE == 0
+#    define RXDMA_BUFFER_SIZE 32
+#  else
+#    define RXDMA_BUFFER_SIZE ((CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE + 31) & ~31)
+#  endif
+
+/* DMA priority */
+
+#  ifndef CONFIG_USART_RXDMAPRIO
+#    define CONFIG_USART_RXDMAPRIO  DMA_CCR_PRIMED
+#  endif
+#  if (CONFIG_USART_RXDMAPRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_USART_RXDMAPRIO"
+#  endif
+
+/* DMA control words */
+
+#  define SERIAL_DMA_CONTROL_WORD      \
+              (DMA_CCR_CIRC          | \
+               DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  ifdef CONFIG_SERIAL_IFLOWCONTROL
+#    define SERIAL_DMA_IFLOW_CONTROL_WORD \
+              (DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  endif
+
+#endif
+
+/* Power management definitions */
+
+#if defined(CONFIG_PM) && !defined(CONFIG_STM32WB_PM_SERIAL_ACTIVITY)
+#  define CONFIG_STM32WB_PM_SERIAL_ACTIVITY  10
+#endif
+#if defined(CONFIG_PM)
+#  define PM_IDLE_DOMAIN             0 /* Revisit */
+#endif
+
+/* Keep track if a Break was set
+ *
+ * Note:
+ *
+ * 1) This value is set in the priv->ie but never written to the control
+ *    register. It must not collide with USART_CR1_USED_INTS or USART_CR3_EIE
+ * 2) USART_CR3_EIE is also carried in the up_dev_s ie member.
+ *
+ * See stm32wb_serial_restoreusartint where the masking is done.
+ */
+
+#ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+#  define USART_CR1_IE_BREAK_INPROGRESS_SHFTS 15
+#  define USART_CR1_IE_BREAK_INPROGRESS (1 << USART_CR1_IE_BREAK_INPROGRESS_SHFTS)
+#endif
+
+#ifdef USE_SERIALDRIVER
+#ifdef HAVE_UART
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_serial_s
+{
+  struct uart_dev_s dev;       /* Generic UART device */
+  uint16_t          ie;        /* Saved interrupt mask bits value */
+  uint16_t          sr;        /* Saved status bits */
+
+  /* Has been initialized and HW is setup. */
+
+  bool              initialized;
+
+#ifdef CONFIG_PM
+  bool              suspended; /* UART device has been suspended. */
+
+  /* Interrupt mask value stored before suspending for stop mode. */
+
+  uint16_t          suspended_ie;
+#endif
+
+  /* If termios are supported, then the following fields may vary at
+   * runtime.
+   */
+
+#ifdef CONFIG_SERIAL_TERMIOS
+  uint8_t           parity;    /* 0=none, 1=odd, 2=even */
+  uint8_t           bits;      /* Number of bits (7 or 8) */
+  bool              stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  bool              iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  bool              oflow;     /* output flow control (CTS) enabled */
+#endif
+  uint32_t          baud;      /* Configured baud */
+#else
+  const uint8_t     parity;    /* 0=none, 1=odd, 2=even */
+  const uint8_t     bits;      /* Number of bits (7 or 8) */
+  const bool        stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const bool        iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const bool        oflow;     /* output flow control (CTS) enabled */
+#endif
+  const uint32_t    baud;      /* Configured baud */
+#endif
+
+  const uint8_t     irq;       /* IRQ associated with this USART */
+  const uint32_t    apbclock;  /* PCLK 1 or 2 frequency */
+  const uint32_t    usartbase; /* Base address of USART registers */
+  const uint32_t    tx_gpio;   /* U[S]ART TX GPIO pin configuration */
+  const uint32_t    rx_gpio;   /* U[S]ART RX GPIO pin configuration */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const uint32_t    rts_gpio;  /* U[S]ART RTS GPIO pin configuration */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const uint32_t    cts_gpio;  /* U[S]ART CTS GPIO pin configuration */
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+  const unsigned int rxdma_channel; /* DMA channel assigned */
+#endif
+
+  /* RX DMA state */
+
+#ifdef SERIAL_HAVE_RXDMA
+  DMA_HANDLE        rxdma;     /* currently-open receive DMA stream */
+  bool              rxenable;  /* DMA-based reception en/disable */
+#ifdef CONFIG_PM
+  bool              rxdmasusp; /* Rx DMA suspended */
+#endif
+  uint32_t          rxdmanext; /* Next byte in the DMA buffer to be read */
+  char       *const rxfifo;    /* Receive DMA buffer */
+#endif
+
+#ifdef HAVE_RS485
+  const uint32_t    rs485_dir_gpio;     /* U[S]ART RS-485 DIR GPIO pin configuration */
+  const bool        rs485_dir_polarity; /* U[S]ART RS-485 DIR pin state for TX enabled */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev);
+#endif
+static int  stm32wb_serial_setup(struct uart_dev_s *dev);
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_attach(struct uart_dev_s *dev);
+static void stm32wb_serial_detach(struct uart_dev_s *dev);
+static int  up_interrupt(int irq, void *context, void *arg);
+static int  stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                 unsigned long arg);
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int  stm32wb_serial_receive(struct uart_dev_s *dev,
+                                   unsigned int *status);
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev);
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper);
+#endif
+static void stm32wb_serial_send(struct uart_dev_s *dev, int ch);
+static void stm32wb_serial_txint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_txready(struct uart_dev_s *dev);
+
+#ifdef SERIAL_HAVE_RXDMA
+static int  stm32wb_serial_dmasetup(struct uart_dev_s *dev);
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                      unsigned int *status);
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv);
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_dmaiflowrestart(struct stm32wb_serial_s *priv);
+#endif
+static void stm32wb_serial_dmarxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_dmarxavailable(struct uart_dev_s *dev);
+
+static void stm32wb_serial_dmarxcallback(DMA_HANDLE handle, uint8_t status,
+                                         void *arg);
+#endif
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend);
+static void stm32wb_serial_pm_setsuspend(bool suspend);
+static void stm32wb_serial_pmnotify(struct pm_callback_s *cb,
+                                    int domain, enum pm_state_e pmstate);
+static int  stm32wb_serial_pmprepare(struct pm_callback_s *cb,
+                                     int domain, enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static const struct uart_ops_s g_uart_ops =
+{
+  .setup          = stm32wb_serial_setup,
+  .shutdown       = stm32wb_serial_shutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_receive,
+  .rxint          = stm32wb_serial_rxint,
+  .rxavailable    = stm32wb_serial_rxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+static const struct uart_ops_s g_uart_dma_ops =
+{
+  .setup          = stm32wb_serial_dmasetup,
+  .shutdown       = stm32wb_serial_dmashutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_dmareceive,
+  .rxint          = stm32wb_serial_dmarxint,
+  .rxavailable    = stm32wb_serial_dmarxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+/* I/O buffers */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static char g_lpuart1rxbuffer[CONFIG_LPUART1_RXBUFSIZE];
+static char g_lpuart1txbuffer[CONFIG_LPUART1_TXBUFSIZE];
+# ifdef CONFIG_LPUART1_RXDMA
+static char g_lpuart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static char g_usart1rxbuffer[CONFIG_USART1_RXBUFSIZE];
+static char g_usart1txbuffer[CONFIG_USART1_TXBUFSIZE];
+# ifdef CONFIG_USART1_RXDMA
+static char g_usart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+/* This describes the state of the STM32WB LPUART1 port. */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static struct stm32wb_serial_s g_lpuart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 1
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_LPUART1_RXBUFSIZE,
+        .buffer  = g_lpuart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_LPUART1_TXBUFSIZE,
+        .buffer  = g_lpuart1txbuffer,
+      },
+#ifdef CONFIG_LPUART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_lpuart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_LPUART1,
+  .parity        = CONFIG_LPUART1_PARITY,
+  .bits          = CONFIG_LPUART1_BITS,
+  .stopbits2     = CONFIG_LPUART1_2STOP,
+  .baud          = CONFIG_LPUART1_BAUD,
+  .apbclock      = STM32WB_PCLK1_FREQUENCY,
+  .usartbase     = STM32WB_LPUART1_BASE,
+  .tx_gpio       = GPIO_LPUART1_TX,
+  .rx_gpio       = GPIO_LPUART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_LPUART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_LPUART1_RTS,
+#endif
+#ifdef CONFIG_LPUART1_RXDMA
+  .rxdma_channel = DMAMAP_LPUART1_RX,
+  .rxfifo        = g_lpuart1rxfifo,
+#endif
+
+#ifdef CONFIG_LPUART1_RS485
+  .rs485_dir_gpio = GPIO_LPUART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This describes the state of the STM32WB USART1 port. */
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static struct stm32wb_serial_s g_usart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 2
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_USART1_RXBUFSIZE,
+        .buffer  = g_usart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_USART1_TXBUFSIZE,
+        .buffer  = g_usart1txbuffer,
+      },
+#ifdef CONFIG_USART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_usart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_USART1,
+  .parity        = CONFIG_USART1_PARITY,
+  .bits          = CONFIG_USART1_BITS,
+  .stopbits2     = CONFIG_USART1_2STOP,
+  .baud          = CONFIG_USART1_BAUD,
+  .apbclock      = STM32WB_PCLK2_FREQUENCY,
+  .usartbase     = STM32WB_USART1_BASE,
+  .tx_gpio       = GPIO_USART1_TX,
+  .rx_gpio       = GPIO_USART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_USART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_USART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_USART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_USART1_RTS,
+#endif
+#ifdef CONFIG_USART1_RXDMA
+  .rxdma_channel = DMAMAP_USART1_RX,
+  .rxfifo        = g_usart1rxfifo,
+#endif
+
+#ifdef CONFIG_USART1_RS485
+  .rs485_dir_gpio = GPIO_USART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This table lets us iterate over the configured USARTs */
+
+static struct stm32wb_serial_s *
+const g_uart_devs[STM32WB_NLPUART + STM32WB_NUSART] =
+{
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  [0] = &g_lpuart1priv,
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+  [1] = &g_usart1priv,
+#endif
+};
+
+#ifdef CONFIG_PM
+static struct
+{
+  struct pm_callback_s pm_cb;
+  bool serial_suspended;
+} g_serialpm =
+  {
+    .pm_cb.notify  = stm32wb_serial_pmnotify,
+    .pm_cb.prepare = stm32wb_serial_pmprepare,
+    .serial_suspended = false
+  };
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_serial_getreg
+ ****************************************************************************/
+
+static inline
+uint32_t stm32wb_serial_getreg(struct stm32wb_serial_s *priv, int offset)
+{
+  return getreg32(priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_putreg
+ ****************************************************************************/
+
+static inline void stm32wb_serial_putreg(struct stm32wb_serial_s *priv,
+                                         int offset, uint32_t value)
+{
+  putreg32(value, priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setusartint
+ ****************************************************************************/
+
+static inline
+void stm32wb_serial_setusartint(struct stm32wb_serial_s *priv, uint16_t ie)
+{
+  uint32_t cr;
+
+  /* Save the interrupt mask */
+
+  priv->ie = ie;
+
+  /* And restore the interrupt state (see the interrupt
+   * enable/usage table above)
+   */
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  cr &= ~(USART_CR1_USED_INTS);
+  cr |= (ie & (USART_CR1_USED_INTS));
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr);
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  cr &= ~USART_CR3_EIE;
+  cr |= (ie & USART_CR3_EIE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+}
+
+/****************************************************************************
+ * Name: up_restoreusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_restoreusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  stm32wb_serial_setusartint(priv, ie);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_disableusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_disableusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t *ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  if (ie)
+    {
+      uint32_t cr1;
+      uint32_t cr3;
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning            Usage
+       * ------------------ --------------- ------------------ ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected          (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete          (used only
+       *                                                        for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag        (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag          (not used)
+       */
+
+      cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+      cr3 = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+      /* Return the current interrupt mask value for the used interrupts.
+       * Notice that this depends on the fact that none of the used interrupt
+       * enable bits overlap. This logic would fail if we needed the break
+       * interrupt!
+       */
+
+      *ie = (cr1 & (USART_CR1_USED_INTS)) | (cr3 & USART_CR3_EIE);
+    }
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_setusartint(priv, 0);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmanextrx
+ *
+ * Description:
+ *   Returns the index into the RX FIFO where the DMA will place the next
+ *   byte that it receives.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmanextrx(struct stm32wb_serial_s *priv)
+{
+  size_t dmaresidual;
+
+  dmaresidual = stm32wb_dmaresidual(priv->rxdma);
+
+  return (RXDMA_BUFFER_SIZE - (int)dmaresidual);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_usart
+ *
+ * Description:
+ *   Set the serial line baud rate (USART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setbaud_usart(struct stm32wb_serial_s *priv)
+{
+  /* This first implementation is for U[S]ARTs that support oversampling
+   * by 8 in additional to the standard oversampling by 16.
+   */
+
+  uint32_t usartdiv8;
+  uint32_t cr1;
+  uint32_t brr;
+
+  /* In case of oversampling by 8, the equation is:
+   *
+   *   baud      = 2 * fCK / usartdiv8
+   *   usartdiv8 = 2 * fCK / baud
+   */
+
+  usartdiv8 = ((priv->apbclock << 1) + (priv->baud >> 1)) / priv->baud;
+
+  /* Baud rate for standard USART (SPI mode included):
+   *
+   * In case of oversampling by 16, the equation is:
+   *   baud       = fCK / usartdiv16
+   *   usartdiv16 = fCK / baud
+   *              = 2 * usartdiv8
+   */
+
+  /* Use oversamply by 8 only if the divisor is small.  But what is small? */
+
+  cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  brr = stm32wb_serial_getreg(priv, STM32WB_USART_BRR_OFFSET);
+  brr &= ~(USART_BRR_MANT_MASK | USART_BRR_FRAC_MASK);
+
+  if (usartdiv8 > 100)
+    {
+      /* Use usartdiv16 */
+
+      brr  |= (usartdiv8 + 1) >> 1;
+
+      /* Clear oversampling by 8 to enable oversampling by 16 */
+
+      cr1 &= ~USART_CR1_OVER8;
+    }
+  else
+    {
+      DEBUGASSERT(usartdiv8 >= 8);
+
+      /* Perform mysterious operations on bits 0-3 */
+
+      brr  |= ((usartdiv8 & 0xfff0) | ((usartdiv8 & 0x000f) >> 1));
+
+      /* Set oversampling by 8 */
+
+      cr1 |= USART_CR1_OVER8;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_lpuart
+ *
+ * Description:
+ *   Set the serial line baud rate (LPUART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static void stm32wb_serial_setbaud_lpuart(struct stm32wb_serial_s *priv)
+{
+  uint32_t brr;
+
+  /* The equation is:
+   *
+   *   baud = 256 * fCK / brr
+   *   brr  = 256 * fCK / baud
+   *
+   * It is forbidden to write values lower than LPUART_BRR_MIN in
+   * the LPUART_BRR register. fCK must range from 3 x baud rate to
+   * 4096 x baud rate.
+   */
+
+  brr = (((uint64_t)priv->apbclock << 8) + (priv->baud >> 1)) / priv->baud;
+  brr &= LPUART_BRR_MASK;
+
+  if (brr < LPUART_BRR_MIN)
+    {
+      brr = LPUART_BRR_MIN;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setformat
+ *
+ * Description:
+ *   Set the serial line format and speed.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Set baud rate */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  if (priv->usartbase == STM32WB_LPUART1_BASE)
+    {
+      stm32wb_serial_setbaud_lpuart(priv);
+    }
+  else
+#endif
+    {
+      stm32wb_serial_setbaud_usart(priv);
+    }
+
+  /* Configure parity mode */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0 | USART_CR1_M1);
+
+  if (priv->parity == 1)       /* Odd parity */
+    {
+      regval |= (USART_CR1_PCE | USART_CR1_PS);
+    }
+  else if (priv->parity == 2)  /* Even parity */
+    {
+      regval |= USART_CR1_PCE;
+    }
+
+  /* Configure word length (parity uses one of configured bits)
+   *
+   * Default: 1 start, 8 data (no parity), n stop, OR
+   *          1 start, 7 data + parity, n stop
+   */
+
+  if (priv->bits == 9 || (priv->bits == 8 && priv->parity != 0))
+    {
+      /* Select: 1 start, 8 data + parity, n stop, OR
+       *         1 start, 9 data (no parity), n stop.
+       */
+
+      regval |= USART_CR1_M0;
+    }
+  else if (priv->bits == 7 && priv->parity == 0)
+    {
+      /* Select: 1 start, 7 data (no parity), n stop, OR
+       */
+
+      regval |= USART_CR1_M1;
+    }
+
+  /* Else Select: 1 start, 7 data + parity, n stop, OR
+   *              1 start, 8 data (no parity), n stop.
+   */
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure STOP bits */
+
+  regval = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK);
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure hardware flow control */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSE | USART_CR3_RTSE);
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && !defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      regval |= USART_CR3_RTSE;
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->oflow && (priv->cts_gpio != 0))
+    {
+      regval |= USART_CR3_CTSE;
+    }
+#endif
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+}
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+/****************************************************************************
+ * Name: stm32wb_serial_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripheral.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#ifdef SERIAL_HAVE_RXDMA
+  bool dmarestored = false;
+#endif
+
+  if (priv->suspended == suspend)
+    {
+      return;
+    }
+
+  priv->suspended = suspend;
+
+  if (suspend)
+    {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Force RTS high to prevent further Rx. */
+
+          stm32wb_configgpio((priv->rts_gpio & ~GPIO_MODE_MASK)
+                             | (GPIO_OUTPUT | GPIO_OUTPUT_SET));
+        }
+#endif
+
+      /* Disable interrupts to prevent Tx. */
+
+      stm32wb_serial_disableusartint(priv, &priv->suspended_ie);
+
+      /* Wait last Tx to complete. */
+
+      while ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+              USART_ISR_TC) == 0);
+
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && !priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow && priv->rxdmanext == RXDMA_BUFFER_SIZE)
+            {
+              /* Rx DMA in non-circular iflow mode and already stopped
+               * at end of DMA buffer. No need to suspend.
+               */
+            }
+          else
+#endif
+            {
+              /* Suspend Rx DMA. */
+
+              stm32wb_dmastop(priv->rxdma);
+              priv->rxdmasusp = true;
+            }
+        }
+#endif
+    }
+  else
+    {
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              stm32wb_serial_dmaiflowrestart(priv);
+            }
+          else
+#endif
+            {
+              /* This USART does not have HW flow-control. Unconditionally
+               * re-enable DMA (might loss unprocessed bytes received
+               * to DMA buffer before suspending).
+               */
+
+              stm32wb_serial_dmareenable(priv);
+              priv->rxdmasusp = false;
+            }
+
+          dmarestored = true;
+        }
+#endif
+
+      /* Re-enable interrupts to resume Tx. */
+
+      stm32wb_serial_restoreusartint(priv, priv->suspended_ie);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Restore peripheral RTS control. */
+
+          stm32wb_configgpio(priv->rts_gpio);
+        }
+#endif
+    }
+
+#ifdef SERIAL_HAVE_RXDMA
+  if (dmarestored)
+    {
+      irqstate_t flags;
+
+      flags = enter_critical_section();
+
+      /* Perform initial Rx DMA buffer fetch to wake-up serial device
+       * activity.
+       */
+
+      if (priv->rxdma != NULL)
+        {
+          stm32wb_serial_dmarxcallback(priv->rxdma, 0, priv);
+        }
+
+      leave_critical_section(flags);
+    }
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_pm_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripherals for/from deep-sleep/stop modes.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_pm_setsuspend(bool suspend)
+{
+  int n;
+
+  /* Already in desired state? */
+
+  if (suspend == g_serialpm.serial_suspended)
+    return;
+
+  g_serialpm.serial_suspended = suspend;
+
+  for (n = 0; n < STM32WB_NLPUART + STM32WB_NUSART; n++)
+    {
+      struct stm32wb_serial_s *priv = g_uart_devs[n];
+
+      if (!priv || !priv->initialized)
+        {
+          continue;
+        }
+
+      stm32wb_serial_setsuspend(&priv->dev, suspend);
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setapbclock
+ *
+ * Description:
+ *   Enable or disable APB clock for the USART peripheral
+ *
+ * Input Parameters:
+ *   dev - A reference to the UART driver state structure
+ *   on  - Enable clock if 'on' is 'true' and disable if 'false'
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_setapbclock(struct uart_dev_s *dev, bool on)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t rcc_en;
+  uint32_t regaddr;
+
+  /* Determine which USART to configure */
+
+  switch (priv->usartbase)
+    {
+    default:
+      return;
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+    case STM32WB_LPUART1_BASE:
+      rcc_en = RCC_APB1ENR2_LPUART1EN;
+      regaddr = STM32WB_RCC_APB1ENR2;
+      break;
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+    case STM32WB_USART1_BASE:
+      rcc_en = RCC_APB2ENR_USART1EN;
+      regaddr = STM32WB_RCC_APB2ENR;
+      break;
+#endif
+    }
+
+  /* Enable/disable APB 1/2 clock for USART */
+
+  if (on)
+    {
+      modifyreg32(regaddr, 0, rcc_en);
+    }
+  else
+    {
+      modifyreg32(regaddr, rcc_en, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setup
+ *
+ * Description:
+ *   Configure the USART baud, bits, parity, etc. This method is called the
+ *   first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_setup(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+  uint32_t regval;
+
+  /* Note: The logic here depends on the fact that that the USART module
+   * was enabled in stm32wb_lowsetup().
+   */
+
+  /* Enable USART APB1/2 clock */
+
+  stm32wb_serial_setapbclock(dev, true);
+
+  /* Configure pins for USART use */
+
+  stm32wb_configgpio(priv->tx_gpio);
+  stm32wb_configgpio(priv->rx_gpio);
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->cts_gpio != 0)
+    {
+      stm32wb_configgpio(priv->cts_gpio);
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->rts_gpio != 0)
+    {
+      uint32_t config = priv->rts_gpio;
+
+#ifdef CONFIG_STM32WB_FLOWCONTROL_BROKEN
+      /* Instead of letting hw manage this pin, we will bitbang */
+
+      config = (config & ~GPIO_MODE_MASK) | GPIO_OUTPUT;
+#endif
+      stm32wb_configgpio(config);
+    }
+#endif
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_configgpio(priv->rs485_dir_gpio);
+      stm32wb_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity);
+    }
+#endif
+
+  /* Configure CR2 */
+
+  /* Clear STOP, CLKEN, CPOL, CPHA, LBCL, and interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK | USART_CR2_CLKEN | USART_CR2_CPOL |
+              USART_CR2_CPHA | USART_CR2_LBCL | USART_CR2_LBDIE);
+
+  /* Configure STOP bits */
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure CR1 */
+
+  /* Clear TE, REm and all interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_TE | USART_CR1_RE | USART_CR1_ALLINTS);
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure CR3 */
+
+  /* Clear CTSE, RTSE, and all interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSIE | USART_CR3_CTSE | USART_CR3_RTSE |
+              USART_CR3_EIE);
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+
+  /* Configure the USART line format and speed. */
+
+  stm32wb_serial_setformat(dev);
+
+  /* Enable Rx, Tx, and the USART */
+
+  regval      = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval     |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+  /* Set up the cached interrupt enables value */
+
+  priv->ie    = 0;
+
+  /* Mark device as initialized. */
+
+  priv->initialized = true;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmasetup
+ *
+ * Description:
+ *   Configure the USART baud, bits, parity, etc. This method is called the
+ *   first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmasetup(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int result;
+  uint32_t regval;
+
+  /* Do the basic UART setup first, unless we are the console */
+
+  if (!dev->isconsole)
+    {
+      result = stm32wb_serial_setup(dev);
+      if (result != OK)
+        {
+          return result;
+        }
+    }
+
+  /* Acquire the DMA channel.  This should always succeed. */
+
+  priv->rxdma = stm32wb_dmachannel(priv->rxdma_channel);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Configure for non-circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                     priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                     (uint32_t)priv->rxfifo,
+                     RXDMA_BUFFER_SIZE,
+                     SERIAL_DMA_IFLOW_CONTROL_WORD);
+    }
+  else
+#endif
+    {
+      /* Configure for circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                     priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                     (uint32_t)priv->rxfifo,
+                     RXDMA_BUFFER_SIZE,
+                     SERIAL_DMA_CONTROL_WORD);
+    }
+
+  /* Reset our DMA shadow pointer to match the address just
+   * programmed above.
+   */
+
+  priv->rxdmanext = 0;
+
+  /* Enable receive DMA for the UART */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval |= USART_CR3_DMAR;
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Start the DMA channel, and arrange for callbacks at the full point
+       * in the FIFO. After buffer gets full, hardware flow-control kicks
+       * in and DMA transfer is stopped.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, false);
+    }
+  else
+#endif
+    {
+      /* Start the DMA channel, and arrange for callbacks at the half and
+       * full points in the FIFO.  This ensures that we have half a FIFO
+       * worth of time to claim bytes before they are overwritten.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, true);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_shutdown
+ *
+ * Description:
+ *   Disable the USART.  This method is called when the serial
+ *   port is closed
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Mark device as uninitialized. */
+
+  priv->initialized = false;
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_disableusartint(priv, NULL);
+
+  /* Disable USART APB1/2 clock */
+
+  stm32wb_serial_setapbclock(dev, false);
+
+  /* Disable Rx, Tx, and the UART */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Release pins. "If the serial-attached device is powered down, the TX
+   * pin causes back-powering, potentially confusing the device to the point
+   * of complete lock-up."
+   *
+   * REVISIT:  Is unconfiguring the pins appropriate for all device?  If not,
+   * then this may need to be a configuration option.
+   */
+
+  stm32wb_unconfiggpio(priv->tx_gpio);
+  stm32wb_unconfiggpio(priv->rx_gpio);
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->cts_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->cts_gpio);
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->rts_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->rts_gpio);
+    }
+#endif
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->rs485_dir_gpio);
+    }
+#endif
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmashutdown
+ *
+ * Description:
+ *   Disable the USART.  This method is called when the serial
+ *   port is closed
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  /* Perform the normal UART shutdown */
+
+  stm32wb_serial_shutdown(dev);
+
+  /* Stop the DMA channel */
+
+  stm32wb_dmastop(priv->rxdma);
+
+  /* Release the DMA channel */
+
+  stm32wb_dmafree(priv->rxdma);
+  priv->rxdma = NULL;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_attach
+ *
+ * Description:
+ *   Configure the USART to operation in interrupt driven mode.  This method
+ *   is called when the serial port is opened.  Normally, this is just after
+ *   the the setup() method is called, however, the serial console may
+ *   operate in a non-interrupt driven mode during the boot phase.
+ *
+ *   RX and TX interrupts are not enabled when by the attach method (unless
+ *   the hardware supports multiple levels of interrupt enabling). The RX
+ *   and TX interrupts are not enabled until the txint() and rxint()
+ *   methods are called.
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_attach(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int ret;
+
+  /* Attach and enable the IRQ */
+
+  ret = irq_attach(priv->irq, up_interrupt, priv);
+  if (ret == OK)
+    {
+      /* Enable the interrupt (RX and TX interrupts are still disabled
+       * in the USART
+       */
+
+      up_enable_irq(priv->irq);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_detach
+ *
+ * Description:
+ *   Detach USART interrupts.  This method is called when the serial port is
+ *   closed normally just before the shutdown method is called.
+ *   The exception is the serial console which is never shutdown.
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_detach(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  up_disable_irq(priv->irq);
+  irq_detach(priv->irq);
+}
+
+/****************************************************************************
+ * Name: up_interrupt
+ *
+ * Description:
+ *   This is the USART interrupt handler.  It will be invoked when an
+ *   interrupt received on the 'irq'  It should call uart_transmitchars or
+ *   uart_receivechar to perform the appropriate data transfers.  The
+ *   interrupt handling logic must be able to map the 'irq' number into the
+ *   appropriate uart_dev_s structure in order to call these functions.
+ *
+ ****************************************************************************/
+
+static int up_interrupt(int irq, void *context, void *arg)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)arg;
+  int  passes;
+  bool handled;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Report serial activity to the power management logic */
+
+#if defined(CONFIG_PM) && CONFIG_STM32WB_PM_SERIAL_ACTIVITY > 0
+  pm_activity(PM_IDLE_DOMAIN, CONFIG_STM32WB_PM_SERIAL_ACTIVITY);
+#endif
+
+  /* Loop until there are no characters to be transferred or,
+   * until we have been looping for a long time.
+   */
+
+  handled = true;
+  for (passes = 0; passes < 256 && handled; passes++)
+    {
+      handled = false;
+
+      /* Get the masked USART status word. */
+
+      priv->sr = stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET);
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning             Usage
+       * ------------------ --------------- ------------------- ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected           (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete           (used only
+       *                                                         for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag         (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag           (not used)
+       *
+       * NOTE: Some of these status bits must be cleared by explicitly
+       * writing one to the ICR register: USART_ICR_CTSCF, USART_ICR_LBDCF.
+       * Note of those are currently being used.
+       */
+
+#ifdef HAVE_RS485
+      /* Transmission of whole buffer is over - TC is set, TXEIE is cleared.
+       * Note - this should be first, to have the most recent TC bit value
+       * from SR register - sending data affects TC, but without refresh we
+       * will not know that...
+       */
+
+      if ((priv->sr & USART_ISR_TC) != 0 &&
+          (priv->ie & USART_CR1_TCIE) != 0 &&
+          (priv->ie & USART_CR1_TXEIE) == 0)
+        {
+          stm32wb_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity);
+          stm32wb_serial_restoreusartint(priv, priv->ie & ~USART_CR1_TCIE);
+        }
+#endif
+
+      /* Handle incoming, receive bytes. */
+
+      if ((priv->sr & USART_ISR_RXNE) != 0 &&
+          (priv->ie & USART_CR1_RXNEIE) != 0)
+        {
+          /* Received data ready... process incoming bytes.  NOTE the check
+           * for RXNEIE:  We cannot call uart_recvchards of RX interrupts
+           * are disabled.
+           */
+
+          uart_recvchars(&priv->dev);
+          handled = true;
+        }
+
+      /* We may still have to read from the DR register to clear any pending
+       * error conditions.
+       */
+
+      else if ((priv->sr &
+                (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE)) != 0)
+        {
+          /* These errors are cleared by writing the corresponding bit to the
+           * interrupt clear register (ICR).
+           */
+
+          stm32wb_serial_putreg(priv, STM32WB_USART_ICR_OFFSET,
+                               (USART_ICR_NCF | USART_ICR_ORECF |
+                                USART_ICR_FECF));
+        }
+
+      /* Handle outgoing, transmit bytes */
+
+      if ((priv->sr & USART_ISR_TXE) != 0 &&
+          (priv->ie & USART_CR1_TXEIE) != 0)
+        {
+          /* Transmit data register empty ... process outgoing bytes */
+
+          uart_xmitchars(&priv->dev);
+          handled = true;
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_ioctl
+ *
+ * Description:
+ *   All ioctl calls will be routed through this method
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                unsigned long arg)
+{
+#if defined(CONFIG_SERIAL_TERMIOS) || defined(CONFIG_SERIAL_TIOCSERGSTRUCT)
+  struct inode      *inode = filep->f_inode;
+  struct uart_dev_s *dev   = inode->i_private;
+#endif
+#if defined(CONFIG_SERIAL_TERMIOS)
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#endif
+  int                ret    = OK;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT
+    case TIOCSERGSTRUCT:
+      {
+        struct stm32wb_serial_s *user = (struct stm32wb_serial_s *)arg;
+        if (!user)
+          {
+            ret = -EINVAL;
+          }
+        else
+          {
+            memcpy(user, dev, sizeof(struct stm32wb_serial_s));
+          }
+      }
+      break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_SINGLEWIRE
+    case TIOCSSINGLEWIRE:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, HDSEL can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Change the TX port to be open-drain/push-pull and enable/disable
+         * half-duplex mode.
+         */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+        if ((arg & SER_SINGLEWIRE_ENABLED) != 0)
+          {
+            uint32_t gpio_val = (arg & SER_SINGLEWIRE_PUSHPULL) ==
+                                 SER_SINGLEWIRE_PUSHPULL ?
+                                 GPIO_PUSHPULL : GPIO_OPENDRAIN;
+            gpio_val |=
+              (arg & SER_SINGLEWIRE_PULL_MASK) == SER_SINGLEWIRE_PULLUP ?
+                                                  GPIO_PULLUP : GPIO_FLOAT;
+
+            gpio_val |=
+              (arg & SER_SINGLEWIRE_PULL_MASK) == SER_SINGLEWIRE_PULLDOWN ?
+                                                  GPIO_PULLDOWN : GPIO_FLOAT;
+
+            stm32wb_configgpio((priv->tx_gpio & ~(GPIO_PUPD_MASK |
+                                                  GPIO_OPENDRAIN)) |
+                                                  gpio_val);
+
+            cr |= USART_CR3_HDSEL;
+          }
+        else
+          {
+            stm32wb_configgpio((priv->tx_gpio & ~(GPIO_PUPD_MASK |
+                                                  GPIO_OPENDRAIN)) |
+                                                  GPIO_PUSHPULL);
+
+            cr &= ~USART_CR3_HDSEL;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_INVERT
+    case TIOCSINVERT:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, {R,T}XINV can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Enable/disable signal inversion. */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+
+        if (arg & SER_INVERT_ENABLED_RX)
+          {
+            cr |= USART_CR2_RXINV;
+          }
+        else
+          {
+            cr &= ~USART_CR2_RXINV;
+          }
+
+        if (arg & SER_INVERT_ENABLED_TX)
+          {
+            cr |= USART_CR2_TXINV;
+          }
+        else
+          {
+            cr &= ~USART_CR2_TXINV;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_SWAP
+    case TIOCSSWAP:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, SWAP can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Enable/disable Swap mode. */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+
+        if (arg == SER_SWAP_ENABLED)
+          {
+            cr |= USART_CR2_SWAP;
+          }
+        else
+          {
+            cr &= ~USART_CR2_SWAP;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_SERIAL_TERMIOS
+    case TCGETS:
+      {
+        struct termios *termiosp = (struct termios *)arg;
+
+        if (!termiosp)
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        /* Note that since we only support 8/9 bit modes and
+         * there is no way to report 9-bit mode, we always claim 8.
+         */
+
+        termiosp->c_cflag =
+          ((priv->parity != 0) ? PARENB : 0) |
+          ((priv->parity == 1) ? PARODD : 0) |
+          ((priv->stopbits2) ? CSTOPB : 0) |
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+          ((priv->oflow) ? CCTS_OFLOW : 0) |
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          ((priv->iflow) ? CRTS_IFLOW : 0) |
+#endif
+          CS8;
+
+        cfsetispeed(termiosp, priv->baud);
+
+        /* TODO: CCTS_IFLOW, CCTS_OFLOW */
+      }
+      break;
+
+    case TCSETS:
+      {
+        struct termios *termiosp = (struct termios *)arg;
+
+        if (!termiosp)
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        /* Perform some sanity checks before accepting any changes */
+
+        if (((termiosp->c_cflag & CSIZE) != CS8)
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+            || ((termiosp->c_cflag & CCTS_OFLOW) && (priv->cts_gpio == 0))
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+            || ((termiosp->c_cflag & CRTS_IFLOW) && (priv->rts_gpio == 0))
+#endif
+           )
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        if (termiosp->c_cflag & PARENB)
+          {
+            priv->parity = (termiosp->c_cflag & PARODD) ? 1 : 2;
+          }
+        else
+          {
+            priv->parity = 0;
+          }
+
+        priv->stopbits2 = (termiosp->c_cflag & CSTOPB) != 0;
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+        priv->oflow = (termiosp->c_cflag & CCTS_OFLOW) != 0;
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+        priv->iflow = (termiosp->c_cflag & CRTS_IFLOW) != 0;
+#endif
+
+        /* Note that since there is no way to request 9-bit mode
+         * and no way to support 5/6/7-bit modes, we ignore them
+         * all here.
+         */
+
+        /* Note that only cfgetispeed is used because we have knowledge
+         * that only one speed is supported.
+         */
+
+        priv->baud = cfgetispeed(termiosp);
+
+        /* Effect the changes immediately - note that we do not implement
+         * TCSADRAIN / TCSAFLUSH
+         */
+
+        stm32wb_serial_setformat(dev);
+      }
+      break;
+#endif /* CONFIG_SERIAL_TERMIOS */
+
+#ifdef CONFIG_STM32WB_USART_BREAKS
+#  ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+    case TIOCSBRK:  /* BSD compatibility: Turn break on, unconditionally */
+      {
+        irqstate_t flags;
+        uint32_t tx_break;
+
+        flags = enter_critical_section();
+
+        /* Disable any further tx activity */
+
+        priv->ie |= USART_CR1_IE_BREAK_INPROGRESS;
+
+        stm32wb_serial_txint(dev, false);
+
+        /* Configure TX as a GPIO output pin and Send a break signal */
+
+        tx_break = GPIO_OUTPUT |
+                   (~(GPIO_MODE_MASK | GPIO_OUTPUT_SET) & priv->tx_gpio);
+        stm32wb_configgpio(tx_break);
+
+        leave_critical_section(flags);
+      }
+      break;
+
+    case TIOCCBRK:  /* BSD compatibility: Turn break off, unconditionally */
+      {
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Configure TX back to U(S)ART */
+
+        stm32wb_configgpio(priv->tx_gpio);
+
+        priv->ie &= ~USART_CR1_IE_BREAK_INPROGRESS;
+
+        /* Enable further tx activity */
+
+        stm32wb_serial_txint(dev, true);
+
+        leave_critical_section(flags);
+      }
+      break;
+#  else
+    case TIOCSBRK:  /* No BSD compatibility: Turn break on for M bit times */
+      {
+        uint32_t cr1;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+        cr1   = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET,
+                             cr1 | USART_CR1_SBK);
+        leave_critical_section(flags);
+      }
+      break;
+
+    case TIOCCBRK:  /* No BSD compatibility: May turn off break too soon */
+      {
+        uint32_t cr1;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+        cr1   = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET,
+                             cr1 & ~USART_CR1_SBK);
+        leave_critical_section(flags);
+      }
+      break;
+#  endif
+#endif
+
+    default:
+      ret = -ENOTTY;
+      break;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_receive
+ *
+ * Description:
+ *   Called (usually) from the interrupt level to receive one
+ *   character from the USART.  Error bits associated with the
+ *   receipt are provided in the return 'status'.
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int stm32wb_serial_receive(struct uart_dev_s *dev,
+                                  unsigned int *status)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t rdr;
+
+  /* Get the Rx byte */
+
+  rdr      = stm32wb_serial_getreg(priv, STM32WB_USART_RDR_OFFSET);
+
+  /* Get the Rx byte plux error information.  Return those in status */
+
+  *status  = priv->sr << 16 | rdr;
+  priv->sr = 0;
+
+  /* Then return the actual received byte */
+
+  return rdr & 0xff;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxint
+ *
+ * Description:
+ *   Call to enable or disable RX interrupts
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  irqstate_t flags;
+  uint16_t ie;
+
+  /* USART receive interrupts:
+   *
+   * Enable             Status          Meaning                Usage
+   * ------------------ --------------- ------------------     ----------
+   * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+   *                                     Detected              (not used)
+   * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+   *                                     Ready to be Read
+   * "              "   USART_ISR_ORE    Overrun Error
+   *                                     Detected
+   * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+   *
+   * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag            (not used)
+   * USART_CR3_EIE      USART_ISR_FE     Framing Error
+   * "           "      USART_ISR_NE     Noise Flag
+   * "           "      USART_ISR_ORE    Overrun Error
+   *                                     Detected
+   */
+
+  flags = enter_critical_section();
+  ie = priv->ie;
+  if (enable)
+    {
+      /* Receive an interrupt when their is anything in the Rx data register
+       * (or an Rx timeout occurs).
+       */
+
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+#ifdef CONFIG_USART_ERRINTS
+      ie |= (USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR3_EIE);
+#else
+      ie |= USART_CR1_RXNEIE;
+#endif
+#endif
+    }
+  else
+    {
+      ie &= ~(USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR3_EIE);
+    }
+
+  /* Then set the new interrupt state */
+
+  stm32wb_serial_restoreusartint(priv, ie);
+  leave_critical_section(flags);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxavailable
+ *
+ * Description:
+ *   Return true if the receive register is not empty
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  return ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+           USART_ISR_RXNE) != 0);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxflowcontrol
+ *
+ * Description:
+ *   Called when Rx buffer is full (or exceeds configured watermark levels
+ *   if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is defined).
+ *   Return true if UART activated RX flow control to block more incoming
+ *   data
+ *
+ * Input Parameters:
+ *   dev       - UART device instance
+ *   nbuffered - the number of characters currently buffered
+ *               (if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is
+ *               not defined the value will be 0 for an empty buffer or the
+ *               defined buffer size for a full buffer)
+ *   upper     - true indicates the upper watermark was crossed where
+ *               false indicates the lower watermark has been crossed
+ *
+ * Returned Value:
+ *   true if RX flow control activated.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS) && \
+    defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      /* Assert/de-assert nRTS set it high resume/stop sending */
+
+      stm32wb_gpiowrite(priv->rts_gpio, upper);
+
+      if (upper)
+        {
+          /* With heavy Rx traffic, RXNE might be set and data pending.
+           * Returning 'true' in such case would cause RXNE left unhandled
+           * and causing interrupt storm. Sending end might be also be slow
+           * to react on nRTS, and returning 'true' here would prevent
+           * processing that data.
+           *
+           * Therefore, return 'false' so input data is still being processed
+           * until sending end reacts on nRTS signal and stops sending more.
+           */
+
+          return false;
+        }
+
+      return upper;
+    }
+
+#else
+  if (priv->iflow)
+    {
+      /* Is the RX buffer full? */
+
+      if (upper)
+        {
+          /* Disable Rx interrupt to prevent more data being from
+           * peripheral.  When hardware RTS is enabled, this will
+           * prevent more data from coming in.
+           *
+           * This function is only called when UART recv buffer is full,
+           * that is: "dev->recv.head + 1 == dev->recv.tail".
+           *
+           * Logic in "uart_read" will automatically toggle Rx interrupts
+           * when buffer is read empty and thus we do not have to re-
+           * enable Rx interrupts.
+           */
+
+          uart_disablerxint(dev);
+          return true;
+        }
+
+      /* No.. The RX buffer is empty */
+
+      else
+        {
+          /* We might leave Rx interrupt disabled if full recv buffer was
+           * read empty.  Enable Rx interrupt to make sure that more input is
+           * received.
+           */
+
+          uart_enablerxint(dev);
+        }
+    }
+#endif
+
+  return false;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmareceive
+ *
+ * Description:
+ *   Called (usually) from the interrupt level to receive one
+ *   character from the USART.  Error bits associated with the
+ *   receipt are provided in the return 'status'.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                     unsigned int *status)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int c = 0;
+
+  if (stm32wb_serial_dmanextrx(priv) != priv->rxdmanext)
+    {
+      c = priv->rxfifo[priv->rxdmanext];
+
+      priv->rxdmanext++;
+      if (priv->rxdmanext == RXDMA_BUFFER_SIZE)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              /* RX DMA buffer full. RX paused, RTS line pulled up to prevent
+               * more input data from other end.
+               */
+            }
+          else
+#endif
+            {
+              priv->rxdmanext = 0;
+            }
+        }
+    }
+
+  return c;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmareenable
+ *
+ * Description:
+ *   Call to re-enable RX DMA.
+ *
+ ****************************************************************************/
+
+#if defined(SERIAL_HAVE_RXDMA)
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv)
+{
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Configure for non-circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                       priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                       (uint32_t)priv->rxfifo,
+                       RXDMA_BUFFER_SIZE,
+                       SERIAL_DMA_IFLOW_CONTROL_WORD);
+    }
+  else
+#endif
+    {
+      /* Configure for circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                       priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                       (uint32_t)priv->rxfifo,
+                       RXDMA_BUFFER_SIZE,
+                       SERIAL_DMA_CONTROL_WORD);
+    }
+
+  /* Reset our DMA shadow pointer to match the address just
+   * programmed above.
+   */
+
+  priv->rxdmanext = 0;
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Start the DMA channel, and arrange for callbacks at the full point
+       * in the FIFO. After buffer gets full, hardware flow-control kicks
+       * in and DMA transfer is stopped.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, false);

Review Comment:
   ```suggestion
                          priv, false);
   ```



##########
arch/arm/src/stm32wb/stm32wb_serial.c:
##########
@@ -0,0 +1,2896 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_serial.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/serial/serial.h>
+#include <nuttx/power/pm.h>
+#include <arch/board/board.h>
+
+#ifdef CONFIG_SERIAL_TERMIOS
+#  include <termios.h>
+#endif
+
+#include "arm_internal.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_uart.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Some sanity checks *******************************************************/
+
+/* DMA configuration */
+
+#ifdef SERIAL_HAVE_RXDMA
+
+/* Currently RS-485 support cannot be enabled when RXDMA is in use due to
+ * lack of testing - RS-485 support was developed on STM32F1x
+ */
+
+#  if (defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_RS485))
+#    error "RXDMA and RS-485 cannot be enabled at the same time for the same U[S]ART"
+#  endif
+
+/* For the stm32wb, there are alternate DMA channels for USART1.
+ * Logic in the board.h file make the DMA channel selection by defining
+ * the following in the board.h file.
+ */
+
+#  if defined(CONFIG_USART1_RXDMA) && !defined(DMAMAP_USART1_RX)
+#    error "USART1 DMA channel not defined (DMAMAP_USART1_RX)"
+#  endif
+
+/* The DMA buffer size when using RX DMA to emulate a FIFO.
+ *
+ * When streaming data, the generic serial layer will be called
+ * every time the FIFO receives half this number of bytes.
+ *
+ * The buffer size should be an even multiple of ARMV7M_DCACHE_LINESIZE.
+ */
+
+#  if !defined(CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE) || \
+      CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE == 0
+#    define RXDMA_BUFFER_SIZE 32
+#  else
+#    define RXDMA_BUFFER_SIZE ((CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE + 31) & ~31)
+#  endif
+
+/* DMA priority */
+
+#  ifndef CONFIG_USART_RXDMAPRIO
+#    define CONFIG_USART_RXDMAPRIO  DMA_CCR_PRIMED
+#  endif
+#  if (CONFIG_USART_RXDMAPRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_USART_RXDMAPRIO"
+#  endif
+
+/* DMA control words */
+
+#  define SERIAL_DMA_CONTROL_WORD      \
+              (DMA_CCR_CIRC          | \
+               DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  ifdef CONFIG_SERIAL_IFLOWCONTROL
+#    define SERIAL_DMA_IFLOW_CONTROL_WORD \
+              (DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  endif
+
+#endif
+
+/* Power management definitions */
+
+#if defined(CONFIG_PM) && !defined(CONFIG_STM32WB_PM_SERIAL_ACTIVITY)
+#  define CONFIG_STM32WB_PM_SERIAL_ACTIVITY  10
+#endif
+#if defined(CONFIG_PM)
+#  define PM_IDLE_DOMAIN             0 /* Revisit */
+#endif
+
+/* Keep track if a Break was set
+ *
+ * Note:
+ *
+ * 1) This value is set in the priv->ie but never written to the control
+ *    register. It must not collide with USART_CR1_USED_INTS or USART_CR3_EIE
+ * 2) USART_CR3_EIE is also carried in the up_dev_s ie member.
+ *
+ * See stm32wb_serial_restoreusartint where the masking is done.
+ */
+
+#ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+#  define USART_CR1_IE_BREAK_INPROGRESS_SHFTS 15
+#  define USART_CR1_IE_BREAK_INPROGRESS (1 << USART_CR1_IE_BREAK_INPROGRESS_SHFTS)
+#endif
+
+#ifdef USE_SERIALDRIVER
+#ifdef HAVE_UART
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_serial_s
+{
+  struct uart_dev_s dev;       /* Generic UART device */
+  uint16_t          ie;        /* Saved interrupt mask bits value */
+  uint16_t          sr;        /* Saved status bits */
+
+  /* Has been initialized and HW is setup. */
+
+  bool              initialized;
+
+#ifdef CONFIG_PM
+  bool              suspended; /* UART device has been suspended. */
+
+  /* Interrupt mask value stored before suspending for stop mode. */
+
+  uint16_t          suspended_ie;
+#endif
+
+  /* If termios are supported, then the following fields may vary at
+   * runtime.
+   */
+
+#ifdef CONFIG_SERIAL_TERMIOS
+  uint8_t           parity;    /* 0=none, 1=odd, 2=even */
+  uint8_t           bits;      /* Number of bits (7 or 8) */
+  bool              stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  bool              iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  bool              oflow;     /* output flow control (CTS) enabled */
+#endif
+  uint32_t          baud;      /* Configured baud */
+#else
+  const uint8_t     parity;    /* 0=none, 1=odd, 2=even */
+  const uint8_t     bits;      /* Number of bits (7 or 8) */
+  const bool        stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const bool        iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const bool        oflow;     /* output flow control (CTS) enabled */
+#endif
+  const uint32_t    baud;      /* Configured baud */
+#endif
+
+  const uint8_t     irq;       /* IRQ associated with this USART */
+  const uint32_t    apbclock;  /* PCLK 1 or 2 frequency */
+  const uint32_t    usartbase; /* Base address of USART registers */
+  const uint32_t    tx_gpio;   /* U[S]ART TX GPIO pin configuration */
+  const uint32_t    rx_gpio;   /* U[S]ART RX GPIO pin configuration */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const uint32_t    rts_gpio;  /* U[S]ART RTS GPIO pin configuration */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const uint32_t    cts_gpio;  /* U[S]ART CTS GPIO pin configuration */
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+  const unsigned int rxdma_channel; /* DMA channel assigned */
+#endif
+
+  /* RX DMA state */
+
+#ifdef SERIAL_HAVE_RXDMA
+  DMA_HANDLE        rxdma;     /* currently-open receive DMA stream */
+  bool              rxenable;  /* DMA-based reception en/disable */
+#ifdef CONFIG_PM
+  bool              rxdmasusp; /* Rx DMA suspended */
+#endif
+  uint32_t          rxdmanext; /* Next byte in the DMA buffer to be read */
+  char       *const rxfifo;    /* Receive DMA buffer */
+#endif
+
+#ifdef HAVE_RS485
+  const uint32_t    rs485_dir_gpio;     /* U[S]ART RS-485 DIR GPIO pin configuration */
+  const bool        rs485_dir_polarity; /* U[S]ART RS-485 DIR pin state for TX enabled */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev);
+#endif
+static int  stm32wb_serial_setup(struct uart_dev_s *dev);
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_attach(struct uart_dev_s *dev);
+static void stm32wb_serial_detach(struct uart_dev_s *dev);
+static int  up_interrupt(int irq, void *context, void *arg);
+static int  stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                 unsigned long arg);
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int  stm32wb_serial_receive(struct uart_dev_s *dev,
+                                   unsigned int *status);
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev);
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper);
+#endif
+static void stm32wb_serial_send(struct uart_dev_s *dev, int ch);
+static void stm32wb_serial_txint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_txready(struct uart_dev_s *dev);
+
+#ifdef SERIAL_HAVE_RXDMA
+static int  stm32wb_serial_dmasetup(struct uart_dev_s *dev);
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                      unsigned int *status);
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv);
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_dmaiflowrestart(struct stm32wb_serial_s *priv);
+#endif
+static void stm32wb_serial_dmarxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_dmarxavailable(struct uart_dev_s *dev);
+
+static void stm32wb_serial_dmarxcallback(DMA_HANDLE handle, uint8_t status,
+                                         void *arg);
+#endif
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend);
+static void stm32wb_serial_pm_setsuspend(bool suspend);
+static void stm32wb_serial_pmnotify(struct pm_callback_s *cb,
+                                    int domain, enum pm_state_e pmstate);
+static int  stm32wb_serial_pmprepare(struct pm_callback_s *cb,
+                                     int domain, enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static const struct uart_ops_s g_uart_ops =
+{
+  .setup          = stm32wb_serial_setup,
+  .shutdown       = stm32wb_serial_shutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_receive,
+  .rxint          = stm32wb_serial_rxint,
+  .rxavailable    = stm32wb_serial_rxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+static const struct uart_ops_s g_uart_dma_ops =
+{
+  .setup          = stm32wb_serial_dmasetup,
+  .shutdown       = stm32wb_serial_dmashutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_dmareceive,
+  .rxint          = stm32wb_serial_dmarxint,
+  .rxavailable    = stm32wb_serial_dmarxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+/* I/O buffers */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static char g_lpuart1rxbuffer[CONFIG_LPUART1_RXBUFSIZE];
+static char g_lpuart1txbuffer[CONFIG_LPUART1_TXBUFSIZE];
+# ifdef CONFIG_LPUART1_RXDMA
+static char g_lpuart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static char g_usart1rxbuffer[CONFIG_USART1_RXBUFSIZE];
+static char g_usart1txbuffer[CONFIG_USART1_TXBUFSIZE];
+# ifdef CONFIG_USART1_RXDMA
+static char g_usart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+/* This describes the state of the STM32WB LPUART1 port. */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static struct stm32wb_serial_s g_lpuart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 1
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_LPUART1_RXBUFSIZE,
+        .buffer  = g_lpuart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_LPUART1_TXBUFSIZE,
+        .buffer  = g_lpuart1txbuffer,
+      },
+#ifdef CONFIG_LPUART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_lpuart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_LPUART1,
+  .parity        = CONFIG_LPUART1_PARITY,
+  .bits          = CONFIG_LPUART1_BITS,
+  .stopbits2     = CONFIG_LPUART1_2STOP,
+  .baud          = CONFIG_LPUART1_BAUD,
+  .apbclock      = STM32WB_PCLK1_FREQUENCY,
+  .usartbase     = STM32WB_LPUART1_BASE,
+  .tx_gpio       = GPIO_LPUART1_TX,
+  .rx_gpio       = GPIO_LPUART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_LPUART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_LPUART1_RTS,
+#endif
+#ifdef CONFIG_LPUART1_RXDMA
+  .rxdma_channel = DMAMAP_LPUART1_RX,
+  .rxfifo        = g_lpuart1rxfifo,
+#endif
+
+#ifdef CONFIG_LPUART1_RS485
+  .rs485_dir_gpio = GPIO_LPUART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This describes the state of the STM32WB USART1 port. */
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static struct stm32wb_serial_s g_usart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 2
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_USART1_RXBUFSIZE,
+        .buffer  = g_usart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_USART1_TXBUFSIZE,
+        .buffer  = g_usart1txbuffer,
+      },
+#ifdef CONFIG_USART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_usart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_USART1,
+  .parity        = CONFIG_USART1_PARITY,
+  .bits          = CONFIG_USART1_BITS,
+  .stopbits2     = CONFIG_USART1_2STOP,
+  .baud          = CONFIG_USART1_BAUD,
+  .apbclock      = STM32WB_PCLK2_FREQUENCY,
+  .usartbase     = STM32WB_USART1_BASE,
+  .tx_gpio       = GPIO_USART1_TX,
+  .rx_gpio       = GPIO_USART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_USART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_USART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_USART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_USART1_RTS,
+#endif
+#ifdef CONFIG_USART1_RXDMA
+  .rxdma_channel = DMAMAP_USART1_RX,
+  .rxfifo        = g_usart1rxfifo,
+#endif
+
+#ifdef CONFIG_USART1_RS485
+  .rs485_dir_gpio = GPIO_USART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This table lets us iterate over the configured USARTs */
+
+static struct stm32wb_serial_s *
+const g_uart_devs[STM32WB_NLPUART + STM32WB_NUSART] =
+{
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  [0] = &g_lpuart1priv,
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+  [1] = &g_usart1priv,
+#endif
+};
+
+#ifdef CONFIG_PM
+static struct
+{
+  struct pm_callback_s pm_cb;
+  bool serial_suspended;
+} g_serialpm =
+  {
+    .pm_cb.notify  = stm32wb_serial_pmnotify,
+    .pm_cb.prepare = stm32wb_serial_pmprepare,
+    .serial_suspended = false
+  };
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_serial_getreg
+ ****************************************************************************/
+
+static inline
+uint32_t stm32wb_serial_getreg(struct stm32wb_serial_s *priv, int offset)
+{
+  return getreg32(priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_putreg
+ ****************************************************************************/
+
+static inline void stm32wb_serial_putreg(struct stm32wb_serial_s *priv,
+                                         int offset, uint32_t value)
+{
+  putreg32(value, priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setusartint
+ ****************************************************************************/
+
+static inline
+void stm32wb_serial_setusartint(struct stm32wb_serial_s *priv, uint16_t ie)
+{
+  uint32_t cr;
+
+  /* Save the interrupt mask */
+
+  priv->ie = ie;
+
+  /* And restore the interrupt state (see the interrupt
+   * enable/usage table above)
+   */
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  cr &= ~(USART_CR1_USED_INTS);
+  cr |= (ie & (USART_CR1_USED_INTS));
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr);
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  cr &= ~USART_CR3_EIE;
+  cr |= (ie & USART_CR3_EIE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+}
+
+/****************************************************************************
+ * Name: up_restoreusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_restoreusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  stm32wb_serial_setusartint(priv, ie);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_disableusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_disableusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t *ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  if (ie)
+    {
+      uint32_t cr1;
+      uint32_t cr3;
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning            Usage
+       * ------------------ --------------- ------------------ ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected          (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete          (used only
+       *                                                        for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag        (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag          (not used)
+       */
+
+      cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+      cr3 = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+      /* Return the current interrupt mask value for the used interrupts.
+       * Notice that this depends on the fact that none of the used interrupt
+       * enable bits overlap. This logic would fail if we needed the break
+       * interrupt!
+       */
+
+      *ie = (cr1 & (USART_CR1_USED_INTS)) | (cr3 & USART_CR3_EIE);
+    }
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_setusartint(priv, 0);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmanextrx
+ *
+ * Description:
+ *   Returns the index into the RX FIFO where the DMA will place the next
+ *   byte that it receives.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmanextrx(struct stm32wb_serial_s *priv)
+{
+  size_t dmaresidual;
+
+  dmaresidual = stm32wb_dmaresidual(priv->rxdma);
+
+  return (RXDMA_BUFFER_SIZE - (int)dmaresidual);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_usart
+ *
+ * Description:
+ *   Set the serial line baud rate (USART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setbaud_usart(struct stm32wb_serial_s *priv)
+{
+  /* This first implementation is for U[S]ARTs that support oversampling
+   * by 8 in additional to the standard oversampling by 16.
+   */
+
+  uint32_t usartdiv8;
+  uint32_t cr1;
+  uint32_t brr;
+
+  /* In case of oversampling by 8, the equation is:
+   *
+   *   baud      = 2 * fCK / usartdiv8
+   *   usartdiv8 = 2 * fCK / baud
+   */
+
+  usartdiv8 = ((priv->apbclock << 1) + (priv->baud >> 1)) / priv->baud;
+
+  /* Baud rate for standard USART (SPI mode included):
+   *
+   * In case of oversampling by 16, the equation is:
+   *   baud       = fCK / usartdiv16
+   *   usartdiv16 = fCK / baud
+   *              = 2 * usartdiv8
+   */
+
+  /* Use oversamply by 8 only if the divisor is small.  But what is small? */
+
+  cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  brr = stm32wb_serial_getreg(priv, STM32WB_USART_BRR_OFFSET);
+  brr &= ~(USART_BRR_MANT_MASK | USART_BRR_FRAC_MASK);
+
+  if (usartdiv8 > 100)
+    {
+      /* Use usartdiv16 */
+
+      brr  |= (usartdiv8 + 1) >> 1;
+
+      /* Clear oversampling by 8 to enable oversampling by 16 */
+
+      cr1 &= ~USART_CR1_OVER8;
+    }
+  else
+    {
+      DEBUGASSERT(usartdiv8 >= 8);
+
+      /* Perform mysterious operations on bits 0-3 */
+
+      brr  |= ((usartdiv8 & 0xfff0) | ((usartdiv8 & 0x000f) >> 1));
+
+      /* Set oversampling by 8 */
+
+      cr1 |= USART_CR1_OVER8;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_lpuart
+ *
+ * Description:
+ *   Set the serial line baud rate (LPUART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static void stm32wb_serial_setbaud_lpuart(struct stm32wb_serial_s *priv)
+{
+  uint32_t brr;
+
+  /* The equation is:
+   *
+   *   baud = 256 * fCK / brr
+   *   brr  = 256 * fCK / baud
+   *
+   * It is forbidden to write values lower than LPUART_BRR_MIN in
+   * the LPUART_BRR register. fCK must range from 3 x baud rate to
+   * 4096 x baud rate.
+   */
+
+  brr = (((uint64_t)priv->apbclock << 8) + (priv->baud >> 1)) / priv->baud;
+  brr &= LPUART_BRR_MASK;
+
+  if (brr < LPUART_BRR_MIN)
+    {
+      brr = LPUART_BRR_MIN;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setformat
+ *
+ * Description:
+ *   Set the serial line format and speed.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Set baud rate */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  if (priv->usartbase == STM32WB_LPUART1_BASE)
+    {
+      stm32wb_serial_setbaud_lpuart(priv);
+    }
+  else
+#endif
+    {
+      stm32wb_serial_setbaud_usart(priv);
+    }
+
+  /* Configure parity mode */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0 | USART_CR1_M1);
+
+  if (priv->parity == 1)       /* Odd parity */
+    {
+      regval |= (USART_CR1_PCE | USART_CR1_PS);
+    }
+  else if (priv->parity == 2)  /* Even parity */
+    {
+      regval |= USART_CR1_PCE;
+    }
+
+  /* Configure word length (parity uses one of configured bits)
+   *
+   * Default: 1 start, 8 data (no parity), n stop, OR
+   *          1 start, 7 data + parity, n stop
+   */
+
+  if (priv->bits == 9 || (priv->bits == 8 && priv->parity != 0))
+    {
+      /* Select: 1 start, 8 data + parity, n stop, OR
+       *         1 start, 9 data (no parity), n stop.
+       */
+
+      regval |= USART_CR1_M0;
+    }
+  else if (priv->bits == 7 && priv->parity == 0)
+    {
+      /* Select: 1 start, 7 data (no parity), n stop, OR
+       */
+
+      regval |= USART_CR1_M1;
+    }
+
+  /* Else Select: 1 start, 7 data + parity, n stop, OR
+   *              1 start, 8 data (no parity), n stop.
+   */
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure STOP bits */
+
+  regval = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK);
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure hardware flow control */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSE | USART_CR3_RTSE);
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && !defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      regval |= USART_CR3_RTSE;
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->oflow && (priv->cts_gpio != 0))
+    {
+      regval |= USART_CR3_CTSE;
+    }
+#endif
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+}
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+/****************************************************************************
+ * Name: stm32wb_serial_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripheral.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#ifdef SERIAL_HAVE_RXDMA
+  bool dmarestored = false;
+#endif
+
+  if (priv->suspended == suspend)
+    {
+      return;
+    }
+
+  priv->suspended = suspend;
+
+  if (suspend)
+    {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Force RTS high to prevent further Rx. */
+
+          stm32wb_configgpio((priv->rts_gpio & ~GPIO_MODE_MASK)
+                             | (GPIO_OUTPUT | GPIO_OUTPUT_SET));
+        }
+#endif
+
+      /* Disable interrupts to prevent Tx. */
+
+      stm32wb_serial_disableusartint(priv, &priv->suspended_ie);
+
+      /* Wait last Tx to complete. */
+
+      while ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+              USART_ISR_TC) == 0);
+
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && !priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow && priv->rxdmanext == RXDMA_BUFFER_SIZE)
+            {
+              /* Rx DMA in non-circular iflow mode and already stopped
+               * at end of DMA buffer. No need to suspend.
+               */
+            }
+          else
+#endif
+            {
+              /* Suspend Rx DMA. */
+
+              stm32wb_dmastop(priv->rxdma);
+              priv->rxdmasusp = true;
+            }
+        }
+#endif
+    }
+  else
+    {
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              stm32wb_serial_dmaiflowrestart(priv);
+            }
+          else
+#endif
+            {
+              /* This USART does not have HW flow-control. Unconditionally
+               * re-enable DMA (might loss unprocessed bytes received
+               * to DMA buffer before suspending).
+               */
+
+              stm32wb_serial_dmareenable(priv);
+              priv->rxdmasusp = false;
+            }
+
+          dmarestored = true;
+        }
+#endif
+
+      /* Re-enable interrupts to resume Tx. */
+
+      stm32wb_serial_restoreusartint(priv, priv->suspended_ie);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Restore peripheral RTS control. */
+
+          stm32wb_configgpio(priv->rts_gpio);
+        }
+#endif
+    }
+
+#ifdef SERIAL_HAVE_RXDMA
+  if (dmarestored)
+    {
+      irqstate_t flags;
+
+      flags = enter_critical_section();
+
+      /* Perform initial Rx DMA buffer fetch to wake-up serial device
+       * activity.
+       */
+
+      if (priv->rxdma != NULL)
+        {
+          stm32wb_serial_dmarxcallback(priv->rxdma, 0, priv);
+        }
+
+      leave_critical_section(flags);
+    }
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_pm_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripherals for/from deep-sleep/stop modes.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_pm_setsuspend(bool suspend)
+{
+  int n;
+
+  /* Already in desired state? */
+
+  if (suspend == g_serialpm.serial_suspended)
+    return;
+
+  g_serialpm.serial_suspended = suspend;
+
+  for (n = 0; n < STM32WB_NLPUART + STM32WB_NUSART; n++)
+    {
+      struct stm32wb_serial_s *priv = g_uart_devs[n];
+
+      if (!priv || !priv->initialized)
+        {
+          continue;
+        }
+
+      stm32wb_serial_setsuspend(&priv->dev, suspend);
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setapbclock
+ *
+ * Description:
+ *   Enable or disable APB clock for the USART peripheral
+ *
+ * Input Parameters:
+ *   dev - A reference to the UART driver state structure
+ *   on  - Enable clock if 'on' is 'true' and disable if 'false'
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_setapbclock(struct uart_dev_s *dev, bool on)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t rcc_en;
+  uint32_t regaddr;
+
+  /* Determine which USART to configure */
+
+  switch (priv->usartbase)
+    {
+    default:
+      return;
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+    case STM32WB_LPUART1_BASE:
+      rcc_en = RCC_APB1ENR2_LPUART1EN;
+      regaddr = STM32WB_RCC_APB1ENR2;
+      break;
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+    case STM32WB_USART1_BASE:
+      rcc_en = RCC_APB2ENR_USART1EN;
+      regaddr = STM32WB_RCC_APB2ENR;
+      break;
+#endif
+    }
+
+  /* Enable/disable APB 1/2 clock for USART */
+
+  if (on)
+    {
+      modifyreg32(regaddr, 0, rcc_en);
+    }
+  else
+    {
+      modifyreg32(regaddr, rcc_en, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setup
+ *
+ * Description:
+ *   Configure the USART baud, bits, parity, etc. This method is called the
+ *   first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_setup(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+  uint32_t regval;
+
+  /* Note: The logic here depends on the fact that that the USART module
+   * was enabled in stm32wb_lowsetup().
+   */
+
+  /* Enable USART APB1/2 clock */
+
+  stm32wb_serial_setapbclock(dev, true);
+
+  /* Configure pins for USART use */
+
+  stm32wb_configgpio(priv->tx_gpio);
+  stm32wb_configgpio(priv->rx_gpio);
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->cts_gpio != 0)
+    {
+      stm32wb_configgpio(priv->cts_gpio);
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->rts_gpio != 0)
+    {
+      uint32_t config = priv->rts_gpio;
+
+#ifdef CONFIG_STM32WB_FLOWCONTROL_BROKEN
+      /* Instead of letting hw manage this pin, we will bitbang */
+
+      config = (config & ~GPIO_MODE_MASK) | GPIO_OUTPUT;
+#endif
+      stm32wb_configgpio(config);
+    }
+#endif
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_configgpio(priv->rs485_dir_gpio);
+      stm32wb_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity);
+    }
+#endif
+
+  /* Configure CR2 */
+
+  /* Clear STOP, CLKEN, CPOL, CPHA, LBCL, and interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK | USART_CR2_CLKEN | USART_CR2_CPOL |
+              USART_CR2_CPHA | USART_CR2_LBCL | USART_CR2_LBDIE);
+
+  /* Configure STOP bits */
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure CR1 */
+
+  /* Clear TE, REm and all interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_TE | USART_CR1_RE | USART_CR1_ALLINTS);
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure CR3 */
+
+  /* Clear CTSE, RTSE, and all interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSIE | USART_CR3_CTSE | USART_CR3_RTSE |
+              USART_CR3_EIE);
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+
+  /* Configure the USART line format and speed. */
+
+  stm32wb_serial_setformat(dev);
+
+  /* Enable Rx, Tx, and the USART */
+
+  regval      = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval     |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+  /* Set up the cached interrupt enables value */
+
+  priv->ie    = 0;
+
+  /* Mark device as initialized. */
+
+  priv->initialized = true;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmasetup
+ *
+ * Description:
+ *   Configure the USART baud, bits, parity, etc. This method is called the
+ *   first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmasetup(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int result;
+  uint32_t regval;
+
+  /* Do the basic UART setup first, unless we are the console */
+
+  if (!dev->isconsole)
+    {
+      result = stm32wb_serial_setup(dev);
+      if (result != OK)
+        {
+          return result;
+        }
+    }
+
+  /* Acquire the DMA channel.  This should always succeed. */
+
+  priv->rxdma = stm32wb_dmachannel(priv->rxdma_channel);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Configure for non-circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                     priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                     (uint32_t)priv->rxfifo,
+                     RXDMA_BUFFER_SIZE,
+                     SERIAL_DMA_IFLOW_CONTROL_WORD);
+    }
+  else
+#endif
+    {
+      /* Configure for circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                     priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                     (uint32_t)priv->rxfifo,
+                     RXDMA_BUFFER_SIZE,
+                     SERIAL_DMA_CONTROL_WORD);
+    }
+
+  /* Reset our DMA shadow pointer to match the address just
+   * programmed above.
+   */
+
+  priv->rxdmanext = 0;
+
+  /* Enable receive DMA for the UART */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval |= USART_CR3_DMAR;
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Start the DMA channel, and arrange for callbacks at the full point
+       * in the FIFO. After buffer gets full, hardware flow-control kicks
+       * in and DMA transfer is stopped.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, false);
+    }
+  else
+#endif
+    {
+      /* Start the DMA channel, and arrange for callbacks at the half and
+       * full points in the FIFO.  This ensures that we have half a FIFO
+       * worth of time to claim bytes before they are overwritten.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, true);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_shutdown
+ *
+ * Description:
+ *   Disable the USART.  This method is called when the serial
+ *   port is closed
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Mark device as uninitialized. */
+
+  priv->initialized = false;
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_disableusartint(priv, NULL);
+
+  /* Disable USART APB1/2 clock */
+
+  stm32wb_serial_setapbclock(dev, false);
+
+  /* Disable Rx, Tx, and the UART */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Release pins. "If the serial-attached device is powered down, the TX
+   * pin causes back-powering, potentially confusing the device to the point
+   * of complete lock-up."
+   *
+   * REVISIT:  Is unconfiguring the pins appropriate for all device?  If not,
+   * then this may need to be a configuration option.
+   */
+
+  stm32wb_unconfiggpio(priv->tx_gpio);
+  stm32wb_unconfiggpio(priv->rx_gpio);
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->cts_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->cts_gpio);
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->rts_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->rts_gpio);
+    }
+#endif
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->rs485_dir_gpio);
+    }
+#endif
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmashutdown
+ *
+ * Description:
+ *   Disable the USART.  This method is called when the serial
+ *   port is closed
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  /* Perform the normal UART shutdown */
+
+  stm32wb_serial_shutdown(dev);
+
+  /* Stop the DMA channel */
+
+  stm32wb_dmastop(priv->rxdma);
+
+  /* Release the DMA channel */
+
+  stm32wb_dmafree(priv->rxdma);
+  priv->rxdma = NULL;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_attach
+ *
+ * Description:
+ *   Configure the USART to operation in interrupt driven mode.  This method
+ *   is called when the serial port is opened.  Normally, this is just after
+ *   the the setup() method is called, however, the serial console may
+ *   operate in a non-interrupt driven mode during the boot phase.
+ *
+ *   RX and TX interrupts are not enabled when by the attach method (unless
+ *   the hardware supports multiple levels of interrupt enabling). The RX
+ *   and TX interrupts are not enabled until the txint() and rxint()
+ *   methods are called.
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_attach(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int ret;
+
+  /* Attach and enable the IRQ */
+
+  ret = irq_attach(priv->irq, up_interrupt, priv);
+  if (ret == OK)
+    {
+      /* Enable the interrupt (RX and TX interrupts are still disabled
+       * in the USART
+       */
+
+      up_enable_irq(priv->irq);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_detach
+ *
+ * Description:
+ *   Detach USART interrupts.  This method is called when the serial port is
+ *   closed normally just before the shutdown method is called.
+ *   The exception is the serial console which is never shutdown.
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_detach(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  up_disable_irq(priv->irq);
+  irq_detach(priv->irq);
+}
+
+/****************************************************************************
+ * Name: up_interrupt
+ *
+ * Description:
+ *   This is the USART interrupt handler.  It will be invoked when an
+ *   interrupt received on the 'irq'  It should call uart_transmitchars or
+ *   uart_receivechar to perform the appropriate data transfers.  The
+ *   interrupt handling logic must be able to map the 'irq' number into the
+ *   appropriate uart_dev_s structure in order to call these functions.
+ *
+ ****************************************************************************/
+
+static int up_interrupt(int irq, void *context, void *arg)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)arg;
+  int  passes;
+  bool handled;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Report serial activity to the power management logic */
+
+#if defined(CONFIG_PM) && CONFIG_STM32WB_PM_SERIAL_ACTIVITY > 0
+  pm_activity(PM_IDLE_DOMAIN, CONFIG_STM32WB_PM_SERIAL_ACTIVITY);
+#endif
+
+  /* Loop until there are no characters to be transferred or,
+   * until we have been looping for a long time.
+   */
+
+  handled = true;
+  for (passes = 0; passes < 256 && handled; passes++)
+    {
+      handled = false;
+
+      /* Get the masked USART status word. */
+
+      priv->sr = stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET);
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning             Usage
+       * ------------------ --------------- ------------------- ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected           (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete           (used only
+       *                                                         for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag         (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag           (not used)
+       *
+       * NOTE: Some of these status bits must be cleared by explicitly
+       * writing one to the ICR register: USART_ICR_CTSCF, USART_ICR_LBDCF.
+       * Note of those are currently being used.
+       */
+
+#ifdef HAVE_RS485
+      /* Transmission of whole buffer is over - TC is set, TXEIE is cleared.
+       * Note - this should be first, to have the most recent TC bit value
+       * from SR register - sending data affects TC, but without refresh we
+       * will not know that...
+       */
+
+      if ((priv->sr & USART_ISR_TC) != 0 &&
+          (priv->ie & USART_CR1_TCIE) != 0 &&
+          (priv->ie & USART_CR1_TXEIE) == 0)
+        {
+          stm32wb_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity);
+          stm32wb_serial_restoreusartint(priv, priv->ie & ~USART_CR1_TCIE);
+        }
+#endif
+
+      /* Handle incoming, receive bytes. */
+
+      if ((priv->sr & USART_ISR_RXNE) != 0 &&
+          (priv->ie & USART_CR1_RXNEIE) != 0)
+        {
+          /* Received data ready... process incoming bytes.  NOTE the check
+           * for RXNEIE:  We cannot call uart_recvchards of RX interrupts
+           * are disabled.
+           */
+
+          uart_recvchars(&priv->dev);
+          handled = true;
+        }
+
+      /* We may still have to read from the DR register to clear any pending
+       * error conditions.
+       */
+
+      else if ((priv->sr &
+                (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE)) != 0)
+        {
+          /* These errors are cleared by writing the corresponding bit to the
+           * interrupt clear register (ICR).
+           */
+
+          stm32wb_serial_putreg(priv, STM32WB_USART_ICR_OFFSET,
+                               (USART_ICR_NCF | USART_ICR_ORECF |
+                                USART_ICR_FECF));
+        }
+
+      /* Handle outgoing, transmit bytes */
+
+      if ((priv->sr & USART_ISR_TXE) != 0 &&
+          (priv->ie & USART_CR1_TXEIE) != 0)
+        {
+          /* Transmit data register empty ... process outgoing bytes */
+
+          uart_xmitchars(&priv->dev);
+          handled = true;
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_ioctl
+ *
+ * Description:
+ *   All ioctl calls will be routed through this method
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                unsigned long arg)
+{
+#if defined(CONFIG_SERIAL_TERMIOS) || defined(CONFIG_SERIAL_TIOCSERGSTRUCT)
+  struct inode      *inode = filep->f_inode;
+  struct uart_dev_s *dev   = inode->i_private;
+#endif
+#if defined(CONFIG_SERIAL_TERMIOS)
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#endif
+  int                ret    = OK;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT
+    case TIOCSERGSTRUCT:
+      {
+        struct stm32wb_serial_s *user = (struct stm32wb_serial_s *)arg;
+        if (!user)
+          {
+            ret = -EINVAL;
+          }
+        else
+          {
+            memcpy(user, dev, sizeof(struct stm32wb_serial_s));
+          }
+      }
+      break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_SINGLEWIRE
+    case TIOCSSINGLEWIRE:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, HDSEL can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Change the TX port to be open-drain/push-pull and enable/disable
+         * half-duplex mode.
+         */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+        if ((arg & SER_SINGLEWIRE_ENABLED) != 0)
+          {
+            uint32_t gpio_val = (arg & SER_SINGLEWIRE_PUSHPULL) ==
+                                 SER_SINGLEWIRE_PUSHPULL ?
+                                 GPIO_PUSHPULL : GPIO_OPENDRAIN;
+            gpio_val |=
+              (arg & SER_SINGLEWIRE_PULL_MASK) == SER_SINGLEWIRE_PULLUP ?
+                                                  GPIO_PULLUP : GPIO_FLOAT;
+
+            gpio_val |=
+              (arg & SER_SINGLEWIRE_PULL_MASK) == SER_SINGLEWIRE_PULLDOWN ?
+                                                  GPIO_PULLDOWN : GPIO_FLOAT;
+
+            stm32wb_configgpio((priv->tx_gpio & ~(GPIO_PUPD_MASK |
+                                                  GPIO_OPENDRAIN)) |
+                                                  gpio_val);
+
+            cr |= USART_CR3_HDSEL;
+          }
+        else
+          {
+            stm32wb_configgpio((priv->tx_gpio & ~(GPIO_PUPD_MASK |
+                                                  GPIO_OPENDRAIN)) |
+                                                  GPIO_PUSHPULL);
+
+            cr &= ~USART_CR3_HDSEL;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_INVERT
+    case TIOCSINVERT:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, {R,T}XINV can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Enable/disable signal inversion. */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+
+        if (arg & SER_INVERT_ENABLED_RX)
+          {
+            cr |= USART_CR2_RXINV;
+          }
+        else
+          {
+            cr &= ~USART_CR2_RXINV;
+          }
+
+        if (arg & SER_INVERT_ENABLED_TX)
+          {
+            cr |= USART_CR2_TXINV;
+          }
+        else
+          {
+            cr &= ~USART_CR2_TXINV;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_SWAP
+    case TIOCSSWAP:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, SWAP can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Enable/disable Swap mode. */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+
+        if (arg == SER_SWAP_ENABLED)
+          {
+            cr |= USART_CR2_SWAP;
+          }
+        else
+          {
+            cr &= ~USART_CR2_SWAP;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_SERIAL_TERMIOS
+    case TCGETS:
+      {
+        struct termios *termiosp = (struct termios *)arg;
+
+        if (!termiosp)
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        /* Note that since we only support 8/9 bit modes and
+         * there is no way to report 9-bit mode, we always claim 8.
+         */
+
+        termiosp->c_cflag =
+          ((priv->parity != 0) ? PARENB : 0) |
+          ((priv->parity == 1) ? PARODD : 0) |
+          ((priv->stopbits2) ? CSTOPB : 0) |
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+          ((priv->oflow) ? CCTS_OFLOW : 0) |
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          ((priv->iflow) ? CRTS_IFLOW : 0) |
+#endif
+          CS8;
+
+        cfsetispeed(termiosp, priv->baud);
+
+        /* TODO: CCTS_IFLOW, CCTS_OFLOW */
+      }
+      break;
+
+    case TCSETS:
+      {
+        struct termios *termiosp = (struct termios *)arg;
+
+        if (!termiosp)
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        /* Perform some sanity checks before accepting any changes */
+
+        if (((termiosp->c_cflag & CSIZE) != CS8)
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+            || ((termiosp->c_cflag & CCTS_OFLOW) && (priv->cts_gpio == 0))
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+            || ((termiosp->c_cflag & CRTS_IFLOW) && (priv->rts_gpio == 0))
+#endif
+           )
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        if (termiosp->c_cflag & PARENB)
+          {
+            priv->parity = (termiosp->c_cflag & PARODD) ? 1 : 2;
+          }
+        else
+          {
+            priv->parity = 0;
+          }
+
+        priv->stopbits2 = (termiosp->c_cflag & CSTOPB) != 0;
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+        priv->oflow = (termiosp->c_cflag & CCTS_OFLOW) != 0;
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+        priv->iflow = (termiosp->c_cflag & CRTS_IFLOW) != 0;
+#endif
+
+        /* Note that since there is no way to request 9-bit mode
+         * and no way to support 5/6/7-bit modes, we ignore them
+         * all here.
+         */
+
+        /* Note that only cfgetispeed is used because we have knowledge
+         * that only one speed is supported.
+         */
+
+        priv->baud = cfgetispeed(termiosp);
+
+        /* Effect the changes immediately - note that we do not implement
+         * TCSADRAIN / TCSAFLUSH
+         */
+
+        stm32wb_serial_setformat(dev);
+      }
+      break;
+#endif /* CONFIG_SERIAL_TERMIOS */
+
+#ifdef CONFIG_STM32WB_USART_BREAKS
+#  ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+    case TIOCSBRK:  /* BSD compatibility: Turn break on, unconditionally */
+      {
+        irqstate_t flags;
+        uint32_t tx_break;
+
+        flags = enter_critical_section();
+
+        /* Disable any further tx activity */
+
+        priv->ie |= USART_CR1_IE_BREAK_INPROGRESS;
+
+        stm32wb_serial_txint(dev, false);
+
+        /* Configure TX as a GPIO output pin and Send a break signal */
+
+        tx_break = GPIO_OUTPUT |
+                   (~(GPIO_MODE_MASK | GPIO_OUTPUT_SET) & priv->tx_gpio);
+        stm32wb_configgpio(tx_break);
+
+        leave_critical_section(flags);
+      }
+      break;
+
+    case TIOCCBRK:  /* BSD compatibility: Turn break off, unconditionally */
+      {
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Configure TX back to U(S)ART */
+
+        stm32wb_configgpio(priv->tx_gpio);
+
+        priv->ie &= ~USART_CR1_IE_BREAK_INPROGRESS;
+
+        /* Enable further tx activity */
+
+        stm32wb_serial_txint(dev, true);
+
+        leave_critical_section(flags);
+      }
+      break;
+#  else
+    case TIOCSBRK:  /* No BSD compatibility: Turn break on for M bit times */
+      {
+        uint32_t cr1;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+        cr1   = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET,
+                             cr1 | USART_CR1_SBK);
+        leave_critical_section(flags);
+      }
+      break;
+
+    case TIOCCBRK:  /* No BSD compatibility: May turn off break too soon */
+      {
+        uint32_t cr1;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+        cr1   = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET,
+                             cr1 & ~USART_CR1_SBK);
+        leave_critical_section(flags);
+      }
+      break;
+#  endif
+#endif
+
+    default:
+      ret = -ENOTTY;
+      break;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_receive
+ *
+ * Description:
+ *   Called (usually) from the interrupt level to receive one
+ *   character from the USART.  Error bits associated with the
+ *   receipt are provided in the return 'status'.
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int stm32wb_serial_receive(struct uart_dev_s *dev,
+                                  unsigned int *status)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t rdr;
+
+  /* Get the Rx byte */
+
+  rdr      = stm32wb_serial_getreg(priv, STM32WB_USART_RDR_OFFSET);
+
+  /* Get the Rx byte plux error information.  Return those in status */
+
+  *status  = priv->sr << 16 | rdr;
+  priv->sr = 0;
+
+  /* Then return the actual received byte */
+
+  return rdr & 0xff;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxint
+ *
+ * Description:
+ *   Call to enable or disable RX interrupts
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  irqstate_t flags;
+  uint16_t ie;
+
+  /* USART receive interrupts:
+   *
+   * Enable             Status          Meaning                Usage
+   * ------------------ --------------- ------------------     ----------
+   * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+   *                                     Detected              (not used)
+   * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+   *                                     Ready to be Read
+   * "              "   USART_ISR_ORE    Overrun Error
+   *                                     Detected
+   * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+   *
+   * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag            (not used)
+   * USART_CR3_EIE      USART_ISR_FE     Framing Error
+   * "           "      USART_ISR_NE     Noise Flag
+   * "           "      USART_ISR_ORE    Overrun Error
+   *                                     Detected
+   */
+
+  flags = enter_critical_section();
+  ie = priv->ie;
+  if (enable)
+    {
+      /* Receive an interrupt when their is anything in the Rx data register
+       * (or an Rx timeout occurs).
+       */
+
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+#ifdef CONFIG_USART_ERRINTS
+      ie |= (USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR3_EIE);
+#else
+      ie |= USART_CR1_RXNEIE;
+#endif
+#endif
+    }
+  else
+    {
+      ie &= ~(USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR3_EIE);
+    }
+
+  /* Then set the new interrupt state */
+
+  stm32wb_serial_restoreusartint(priv, ie);
+  leave_critical_section(flags);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxavailable
+ *
+ * Description:
+ *   Return true if the receive register is not empty
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  return ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+           USART_ISR_RXNE) != 0);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxflowcontrol
+ *
+ * Description:
+ *   Called when Rx buffer is full (or exceeds configured watermark levels
+ *   if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is defined).
+ *   Return true if UART activated RX flow control to block more incoming
+ *   data
+ *
+ * Input Parameters:
+ *   dev       - UART device instance
+ *   nbuffered - the number of characters currently buffered
+ *               (if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is
+ *               not defined the value will be 0 for an empty buffer or the
+ *               defined buffer size for a full buffer)
+ *   upper     - true indicates the upper watermark was crossed where
+ *               false indicates the lower watermark has been crossed
+ *
+ * Returned Value:
+ *   true if RX flow control activated.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS) && \
+    defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      /* Assert/de-assert nRTS set it high resume/stop sending */
+
+      stm32wb_gpiowrite(priv->rts_gpio, upper);
+
+      if (upper)
+        {
+          /* With heavy Rx traffic, RXNE might be set and data pending.
+           * Returning 'true' in such case would cause RXNE left unhandled
+           * and causing interrupt storm. Sending end might be also be slow
+           * to react on nRTS, and returning 'true' here would prevent
+           * processing that data.
+           *
+           * Therefore, return 'false' so input data is still being processed
+           * until sending end reacts on nRTS signal and stops sending more.
+           */
+
+          return false;
+        }
+
+      return upper;
+    }
+
+#else
+  if (priv->iflow)
+    {
+      /* Is the RX buffer full? */
+
+      if (upper)
+        {
+          /* Disable Rx interrupt to prevent more data being from
+           * peripheral.  When hardware RTS is enabled, this will
+           * prevent more data from coming in.
+           *
+           * This function is only called when UART recv buffer is full,
+           * that is: "dev->recv.head + 1 == dev->recv.tail".
+           *
+           * Logic in "uart_read" will automatically toggle Rx interrupts
+           * when buffer is read empty and thus we do not have to re-
+           * enable Rx interrupts.
+           */
+
+          uart_disablerxint(dev);
+          return true;
+        }
+
+      /* No.. The RX buffer is empty */
+
+      else
+        {
+          /* We might leave Rx interrupt disabled if full recv buffer was
+           * read empty.  Enable Rx interrupt to make sure that more input is
+           * received.
+           */
+
+          uart_enablerxint(dev);
+        }
+    }
+#endif
+
+  return false;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmareceive
+ *
+ * Description:
+ *   Called (usually) from the interrupt level to receive one
+ *   character from the USART.  Error bits associated with the
+ *   receipt are provided in the return 'status'.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                     unsigned int *status)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int c = 0;
+
+  if (stm32wb_serial_dmanextrx(priv) != priv->rxdmanext)
+    {
+      c = priv->rxfifo[priv->rxdmanext];
+
+      priv->rxdmanext++;
+      if (priv->rxdmanext == RXDMA_BUFFER_SIZE)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              /* RX DMA buffer full. RX paused, RTS line pulled up to prevent
+               * more input data from other end.
+               */
+            }
+          else
+#endif
+            {
+              priv->rxdmanext = 0;
+            }
+        }
+    }
+
+  return c;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmareenable
+ *
+ * Description:
+ *   Call to re-enable RX DMA.
+ *
+ ****************************************************************************/
+
+#if defined(SERIAL_HAVE_RXDMA)
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv)
+{
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Configure for non-circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                       priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                       (uint32_t)priv->rxfifo,
+                       RXDMA_BUFFER_SIZE,
+                       SERIAL_DMA_IFLOW_CONTROL_WORD);
+    }
+  else
+#endif
+    {
+      /* Configure for circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                       priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                       (uint32_t)priv->rxfifo,
+                       RXDMA_BUFFER_SIZE,
+                       SERIAL_DMA_CONTROL_WORD);
+    }
+
+  /* Reset our DMA shadow pointer to match the address just
+   * programmed above.
+   */
+
+  priv->rxdmanext = 0;
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Start the DMA channel, and arrange for callbacks at the full point
+       * in the FIFO. After buffer gets full, hardware flow-control kicks
+       * in and DMA transfer is stopped.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, false);
+    }
+  else
+#endif
+    {
+      /* Start the DMA channel, and arrange for callbacks at the half and
+       * full points in the FIFO.  This ensures that we have half a FIFO
+       * worth of time to claim bytes before they are overwritten.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, true);

Review Comment:
   ```suggestion
                          priv, true);
   ```



##########
arch/arm/src/stm32wb/stm32wb_serial.c:
##########
@@ -0,0 +1,2896 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_serial.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/serial/serial.h>
+#include <nuttx/power/pm.h>
+#include <arch/board/board.h>
+
+#ifdef CONFIG_SERIAL_TERMIOS
+#  include <termios.h>
+#endif
+
+#include "arm_internal.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_uart.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Some sanity checks *******************************************************/
+
+/* DMA configuration */
+
+#ifdef SERIAL_HAVE_RXDMA
+
+/* Currently RS-485 support cannot be enabled when RXDMA is in use due to
+ * lack of testing - RS-485 support was developed on STM32F1x
+ */
+
+#  if (defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_RS485))
+#    error "RXDMA and RS-485 cannot be enabled at the same time for the same U[S]ART"
+#  endif
+
+/* For the stm32wb, there are alternate DMA channels for USART1.
+ * Logic in the board.h file make the DMA channel selection by defining
+ * the following in the board.h file.
+ */
+
+#  if defined(CONFIG_USART1_RXDMA) && !defined(DMAMAP_USART1_RX)
+#    error "USART1 DMA channel not defined (DMAMAP_USART1_RX)"
+#  endif
+
+/* The DMA buffer size when using RX DMA to emulate a FIFO.
+ *
+ * When streaming data, the generic serial layer will be called
+ * every time the FIFO receives half this number of bytes.
+ *
+ * The buffer size should be an even multiple of ARMV7M_DCACHE_LINESIZE.
+ */
+
+#  if !defined(CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE) || \
+      CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE == 0
+#    define RXDMA_BUFFER_SIZE 32
+#  else
+#    define RXDMA_BUFFER_SIZE ((CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE + 31) & ~31)
+#  endif
+
+/* DMA priority */
+
+#  ifndef CONFIG_USART_RXDMAPRIO
+#    define CONFIG_USART_RXDMAPRIO  DMA_CCR_PRIMED
+#  endif
+#  if (CONFIG_USART_RXDMAPRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_USART_RXDMAPRIO"
+#  endif
+
+/* DMA control words */
+
+#  define SERIAL_DMA_CONTROL_WORD      \
+              (DMA_CCR_CIRC          | \
+               DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  ifdef CONFIG_SERIAL_IFLOWCONTROL
+#    define SERIAL_DMA_IFLOW_CONTROL_WORD \
+              (DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  endif
+
+#endif
+
+/* Power management definitions */
+
+#if defined(CONFIG_PM) && !defined(CONFIG_STM32WB_PM_SERIAL_ACTIVITY)
+#  define CONFIG_STM32WB_PM_SERIAL_ACTIVITY  10
+#endif
+#if defined(CONFIG_PM)
+#  define PM_IDLE_DOMAIN             0 /* Revisit */
+#endif
+
+/* Keep track if a Break was set
+ *
+ * Note:
+ *
+ * 1) This value is set in the priv->ie but never written to the control
+ *    register. It must not collide with USART_CR1_USED_INTS or USART_CR3_EIE
+ * 2) USART_CR3_EIE is also carried in the up_dev_s ie member.
+ *
+ * See stm32wb_serial_restoreusartint where the masking is done.
+ */
+
+#ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+#  define USART_CR1_IE_BREAK_INPROGRESS_SHFTS 15
+#  define USART_CR1_IE_BREAK_INPROGRESS (1 << USART_CR1_IE_BREAK_INPROGRESS_SHFTS)
+#endif
+
+#ifdef USE_SERIALDRIVER
+#ifdef HAVE_UART
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_serial_s
+{
+  struct uart_dev_s dev;       /* Generic UART device */
+  uint16_t          ie;        /* Saved interrupt mask bits value */
+  uint16_t          sr;        /* Saved status bits */
+
+  /* Has been initialized and HW is setup. */
+
+  bool              initialized;
+
+#ifdef CONFIG_PM
+  bool              suspended; /* UART device has been suspended. */
+
+  /* Interrupt mask value stored before suspending for stop mode. */
+
+  uint16_t          suspended_ie;
+#endif
+
+  /* If termios are supported, then the following fields may vary at
+   * runtime.
+   */
+
+#ifdef CONFIG_SERIAL_TERMIOS
+  uint8_t           parity;    /* 0=none, 1=odd, 2=even */
+  uint8_t           bits;      /* Number of bits (7 or 8) */
+  bool              stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  bool              iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  bool              oflow;     /* output flow control (CTS) enabled */
+#endif
+  uint32_t          baud;      /* Configured baud */
+#else
+  const uint8_t     parity;    /* 0=none, 1=odd, 2=even */
+  const uint8_t     bits;      /* Number of bits (7 or 8) */
+  const bool        stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const bool        iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const bool        oflow;     /* output flow control (CTS) enabled */
+#endif
+  const uint32_t    baud;      /* Configured baud */
+#endif
+
+  const uint8_t     irq;       /* IRQ associated with this USART */
+  const uint32_t    apbclock;  /* PCLK 1 or 2 frequency */
+  const uint32_t    usartbase; /* Base address of USART registers */
+  const uint32_t    tx_gpio;   /* U[S]ART TX GPIO pin configuration */
+  const uint32_t    rx_gpio;   /* U[S]ART RX GPIO pin configuration */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const uint32_t    rts_gpio;  /* U[S]ART RTS GPIO pin configuration */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const uint32_t    cts_gpio;  /* U[S]ART CTS GPIO pin configuration */
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+  const unsigned int rxdma_channel; /* DMA channel assigned */
+#endif
+
+  /* RX DMA state */
+
+#ifdef SERIAL_HAVE_RXDMA
+  DMA_HANDLE        rxdma;     /* currently-open receive DMA stream */
+  bool              rxenable;  /* DMA-based reception en/disable */
+#ifdef CONFIG_PM
+  bool              rxdmasusp; /* Rx DMA suspended */
+#endif
+  uint32_t          rxdmanext; /* Next byte in the DMA buffer to be read */
+  char       *const rxfifo;    /* Receive DMA buffer */
+#endif
+
+#ifdef HAVE_RS485
+  const uint32_t    rs485_dir_gpio;     /* U[S]ART RS-485 DIR GPIO pin configuration */
+  const bool        rs485_dir_polarity; /* U[S]ART RS-485 DIR pin state for TX enabled */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev);
+#endif
+static int  stm32wb_serial_setup(struct uart_dev_s *dev);
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_attach(struct uart_dev_s *dev);
+static void stm32wb_serial_detach(struct uart_dev_s *dev);
+static int  up_interrupt(int irq, void *context, void *arg);
+static int  stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                 unsigned long arg);
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int  stm32wb_serial_receive(struct uart_dev_s *dev,
+                                   unsigned int *status);
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev);
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper);
+#endif
+static void stm32wb_serial_send(struct uart_dev_s *dev, int ch);
+static void stm32wb_serial_txint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_txready(struct uart_dev_s *dev);
+
+#ifdef SERIAL_HAVE_RXDMA
+static int  stm32wb_serial_dmasetup(struct uart_dev_s *dev);
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                      unsigned int *status);
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv);
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_dmaiflowrestart(struct stm32wb_serial_s *priv);
+#endif
+static void stm32wb_serial_dmarxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_dmarxavailable(struct uart_dev_s *dev);
+
+static void stm32wb_serial_dmarxcallback(DMA_HANDLE handle, uint8_t status,
+                                         void *arg);
+#endif
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend);
+static void stm32wb_serial_pm_setsuspend(bool suspend);
+static void stm32wb_serial_pmnotify(struct pm_callback_s *cb,
+                                    int domain, enum pm_state_e pmstate);
+static int  stm32wb_serial_pmprepare(struct pm_callback_s *cb,
+                                     int domain, enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static const struct uart_ops_s g_uart_ops =
+{
+  .setup          = stm32wb_serial_setup,
+  .shutdown       = stm32wb_serial_shutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_receive,
+  .rxint          = stm32wb_serial_rxint,
+  .rxavailable    = stm32wb_serial_rxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+static const struct uart_ops_s g_uart_dma_ops =
+{
+  .setup          = stm32wb_serial_dmasetup,
+  .shutdown       = stm32wb_serial_dmashutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_dmareceive,
+  .rxint          = stm32wb_serial_dmarxint,
+  .rxavailable    = stm32wb_serial_dmarxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+/* I/O buffers */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static char g_lpuart1rxbuffer[CONFIG_LPUART1_RXBUFSIZE];
+static char g_lpuart1txbuffer[CONFIG_LPUART1_TXBUFSIZE];
+# ifdef CONFIG_LPUART1_RXDMA
+static char g_lpuart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static char g_usart1rxbuffer[CONFIG_USART1_RXBUFSIZE];
+static char g_usart1txbuffer[CONFIG_USART1_TXBUFSIZE];
+# ifdef CONFIG_USART1_RXDMA
+static char g_usart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+/* This describes the state of the STM32WB LPUART1 port. */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static struct stm32wb_serial_s g_lpuart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 1
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_LPUART1_RXBUFSIZE,
+        .buffer  = g_lpuart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_LPUART1_TXBUFSIZE,
+        .buffer  = g_lpuart1txbuffer,
+      },
+#ifdef CONFIG_LPUART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_lpuart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_LPUART1,
+  .parity        = CONFIG_LPUART1_PARITY,
+  .bits          = CONFIG_LPUART1_BITS,
+  .stopbits2     = CONFIG_LPUART1_2STOP,
+  .baud          = CONFIG_LPUART1_BAUD,
+  .apbclock      = STM32WB_PCLK1_FREQUENCY,
+  .usartbase     = STM32WB_LPUART1_BASE,
+  .tx_gpio       = GPIO_LPUART1_TX,
+  .rx_gpio       = GPIO_LPUART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_LPUART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_LPUART1_RTS,
+#endif
+#ifdef CONFIG_LPUART1_RXDMA
+  .rxdma_channel = DMAMAP_LPUART1_RX,
+  .rxfifo        = g_lpuart1rxfifo,
+#endif
+
+#ifdef CONFIG_LPUART1_RS485
+  .rs485_dir_gpio = GPIO_LPUART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This describes the state of the STM32WB USART1 port. */
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static struct stm32wb_serial_s g_usart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 2
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_USART1_RXBUFSIZE,
+        .buffer  = g_usart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_USART1_TXBUFSIZE,
+        .buffer  = g_usart1txbuffer,
+      },
+#ifdef CONFIG_USART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_usart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_USART1,
+  .parity        = CONFIG_USART1_PARITY,
+  .bits          = CONFIG_USART1_BITS,
+  .stopbits2     = CONFIG_USART1_2STOP,
+  .baud          = CONFIG_USART1_BAUD,
+  .apbclock      = STM32WB_PCLK2_FREQUENCY,
+  .usartbase     = STM32WB_USART1_BASE,
+  .tx_gpio       = GPIO_USART1_TX,
+  .rx_gpio       = GPIO_USART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_USART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_USART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_USART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_USART1_RTS,
+#endif
+#ifdef CONFIG_USART1_RXDMA
+  .rxdma_channel = DMAMAP_USART1_RX,
+  .rxfifo        = g_usart1rxfifo,
+#endif
+
+#ifdef CONFIG_USART1_RS485
+  .rs485_dir_gpio = GPIO_USART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This table lets us iterate over the configured USARTs */
+
+static struct stm32wb_serial_s *
+const g_uart_devs[STM32WB_NLPUART + STM32WB_NUSART] =
+{
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  [0] = &g_lpuart1priv,
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+  [1] = &g_usart1priv,
+#endif
+};
+
+#ifdef CONFIG_PM
+static struct
+{
+  struct pm_callback_s pm_cb;
+  bool serial_suspended;
+} g_serialpm =
+  {
+    .pm_cb.notify  = stm32wb_serial_pmnotify,
+    .pm_cb.prepare = stm32wb_serial_pmprepare,
+    .serial_suspended = false
+  };
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_serial_getreg
+ ****************************************************************************/
+
+static inline
+uint32_t stm32wb_serial_getreg(struct stm32wb_serial_s *priv, int offset)
+{
+  return getreg32(priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_putreg
+ ****************************************************************************/
+
+static inline void stm32wb_serial_putreg(struct stm32wb_serial_s *priv,
+                                         int offset, uint32_t value)
+{
+  putreg32(value, priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setusartint
+ ****************************************************************************/
+
+static inline
+void stm32wb_serial_setusartint(struct stm32wb_serial_s *priv, uint16_t ie)
+{
+  uint32_t cr;
+
+  /* Save the interrupt mask */
+
+  priv->ie = ie;
+
+  /* And restore the interrupt state (see the interrupt
+   * enable/usage table above)
+   */
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  cr &= ~(USART_CR1_USED_INTS);
+  cr |= (ie & (USART_CR1_USED_INTS));
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr);
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  cr &= ~USART_CR3_EIE;
+  cr |= (ie & USART_CR3_EIE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+}
+
+/****************************************************************************
+ * Name: up_restoreusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_restoreusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  stm32wb_serial_setusartint(priv, ie);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_disableusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_disableusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t *ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  if (ie)
+    {
+      uint32_t cr1;
+      uint32_t cr3;
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning            Usage
+       * ------------------ --------------- ------------------ ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected          (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete          (used only
+       *                                                        for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag        (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag          (not used)
+       */
+
+      cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+      cr3 = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+      /* Return the current interrupt mask value for the used interrupts.
+       * Notice that this depends on the fact that none of the used interrupt
+       * enable bits overlap. This logic would fail if we needed the break
+       * interrupt!
+       */
+
+      *ie = (cr1 & (USART_CR1_USED_INTS)) | (cr3 & USART_CR3_EIE);
+    }
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_setusartint(priv, 0);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmanextrx
+ *
+ * Description:
+ *   Returns the index into the RX FIFO where the DMA will place the next
+ *   byte that it receives.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmanextrx(struct stm32wb_serial_s *priv)
+{
+  size_t dmaresidual;
+
+  dmaresidual = stm32wb_dmaresidual(priv->rxdma);
+
+  return (RXDMA_BUFFER_SIZE - (int)dmaresidual);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_usart
+ *
+ * Description:
+ *   Set the serial line baud rate (USART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setbaud_usart(struct stm32wb_serial_s *priv)
+{
+  /* This first implementation is for U[S]ARTs that support oversampling
+   * by 8 in additional to the standard oversampling by 16.
+   */
+
+  uint32_t usartdiv8;
+  uint32_t cr1;
+  uint32_t brr;
+
+  /* In case of oversampling by 8, the equation is:
+   *
+   *   baud      = 2 * fCK / usartdiv8
+   *   usartdiv8 = 2 * fCK / baud
+   */
+
+  usartdiv8 = ((priv->apbclock << 1) + (priv->baud >> 1)) / priv->baud;
+
+  /* Baud rate for standard USART (SPI mode included):
+   *
+   * In case of oversampling by 16, the equation is:
+   *   baud       = fCK / usartdiv16
+   *   usartdiv16 = fCK / baud
+   *              = 2 * usartdiv8
+   */
+
+  /* Use oversamply by 8 only if the divisor is small.  But what is small? */
+
+  cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  brr = stm32wb_serial_getreg(priv, STM32WB_USART_BRR_OFFSET);
+  brr &= ~(USART_BRR_MANT_MASK | USART_BRR_FRAC_MASK);
+
+  if (usartdiv8 > 100)
+    {
+      /* Use usartdiv16 */
+
+      brr  |= (usartdiv8 + 1) >> 1;
+
+      /* Clear oversampling by 8 to enable oversampling by 16 */
+
+      cr1 &= ~USART_CR1_OVER8;
+    }
+  else
+    {
+      DEBUGASSERT(usartdiv8 >= 8);
+
+      /* Perform mysterious operations on bits 0-3 */
+
+      brr  |= ((usartdiv8 & 0xfff0) | ((usartdiv8 & 0x000f) >> 1));
+
+      /* Set oversampling by 8 */
+
+      cr1 |= USART_CR1_OVER8;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_lpuart
+ *
+ * Description:
+ *   Set the serial line baud rate (LPUART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static void stm32wb_serial_setbaud_lpuart(struct stm32wb_serial_s *priv)
+{
+  uint32_t brr;
+
+  /* The equation is:
+   *
+   *   baud = 256 * fCK / brr
+   *   brr  = 256 * fCK / baud
+   *
+   * It is forbidden to write values lower than LPUART_BRR_MIN in
+   * the LPUART_BRR register. fCK must range from 3 x baud rate to
+   * 4096 x baud rate.
+   */
+
+  brr = (((uint64_t)priv->apbclock << 8) + (priv->baud >> 1)) / priv->baud;
+  brr &= LPUART_BRR_MASK;
+
+  if (brr < LPUART_BRR_MIN)
+    {
+      brr = LPUART_BRR_MIN;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setformat
+ *
+ * Description:
+ *   Set the serial line format and speed.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Set baud rate */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  if (priv->usartbase == STM32WB_LPUART1_BASE)
+    {
+      stm32wb_serial_setbaud_lpuart(priv);
+    }
+  else
+#endif
+    {
+      stm32wb_serial_setbaud_usart(priv);
+    }
+
+  /* Configure parity mode */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0 | USART_CR1_M1);
+
+  if (priv->parity == 1)       /* Odd parity */
+    {
+      regval |= (USART_CR1_PCE | USART_CR1_PS);
+    }
+  else if (priv->parity == 2)  /* Even parity */
+    {
+      regval |= USART_CR1_PCE;
+    }
+
+  /* Configure word length (parity uses one of configured bits)
+   *
+   * Default: 1 start, 8 data (no parity), n stop, OR
+   *          1 start, 7 data + parity, n stop
+   */
+
+  if (priv->bits == 9 || (priv->bits == 8 && priv->parity != 0))
+    {
+      /* Select: 1 start, 8 data + parity, n stop, OR
+       *         1 start, 9 data (no parity), n stop.
+       */
+
+      regval |= USART_CR1_M0;
+    }
+  else if (priv->bits == 7 && priv->parity == 0)
+    {
+      /* Select: 1 start, 7 data (no parity), n stop, OR
+       */
+
+      regval |= USART_CR1_M1;
+    }
+
+  /* Else Select: 1 start, 7 data + parity, n stop, OR
+   *              1 start, 8 data (no parity), n stop.
+   */
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure STOP bits */
+
+  regval = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK);
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure hardware flow control */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSE | USART_CR3_RTSE);
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && !defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      regval |= USART_CR3_RTSE;
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->oflow && (priv->cts_gpio != 0))
+    {
+      regval |= USART_CR3_CTSE;
+    }
+#endif
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+}
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+/****************************************************************************
+ * Name: stm32wb_serial_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripheral.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#ifdef SERIAL_HAVE_RXDMA
+  bool dmarestored = false;
+#endif
+
+  if (priv->suspended == suspend)
+    {
+      return;
+    }
+
+  priv->suspended = suspend;
+
+  if (suspend)
+    {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Force RTS high to prevent further Rx. */
+
+          stm32wb_configgpio((priv->rts_gpio & ~GPIO_MODE_MASK)
+                             | (GPIO_OUTPUT | GPIO_OUTPUT_SET));
+        }
+#endif
+
+      /* Disable interrupts to prevent Tx. */
+
+      stm32wb_serial_disableusartint(priv, &priv->suspended_ie);
+
+      /* Wait last Tx to complete. */
+
+      while ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+              USART_ISR_TC) == 0);
+
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && !priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow && priv->rxdmanext == RXDMA_BUFFER_SIZE)
+            {
+              /* Rx DMA in non-circular iflow mode and already stopped
+               * at end of DMA buffer. No need to suspend.
+               */
+            }
+          else
+#endif
+            {
+              /* Suspend Rx DMA. */
+
+              stm32wb_dmastop(priv->rxdma);
+              priv->rxdmasusp = true;
+            }
+        }
+#endif
+    }
+  else
+    {
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              stm32wb_serial_dmaiflowrestart(priv);
+            }
+          else
+#endif
+            {
+              /* This USART does not have HW flow-control. Unconditionally
+               * re-enable DMA (might loss unprocessed bytes received
+               * to DMA buffer before suspending).
+               */
+
+              stm32wb_serial_dmareenable(priv);
+              priv->rxdmasusp = false;
+            }
+
+          dmarestored = true;
+        }
+#endif
+
+      /* Re-enable interrupts to resume Tx. */
+
+      stm32wb_serial_restoreusartint(priv, priv->suspended_ie);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Restore peripheral RTS control. */
+
+          stm32wb_configgpio(priv->rts_gpio);
+        }
+#endif
+    }
+
+#ifdef SERIAL_HAVE_RXDMA
+  if (dmarestored)
+    {
+      irqstate_t flags;
+
+      flags = enter_critical_section();
+
+      /* Perform initial Rx DMA buffer fetch to wake-up serial device
+       * activity.
+       */
+
+      if (priv->rxdma != NULL)
+        {
+          stm32wb_serial_dmarxcallback(priv->rxdma, 0, priv);
+        }
+
+      leave_critical_section(flags);
+    }
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_pm_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripherals for/from deep-sleep/stop modes.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_pm_setsuspend(bool suspend)
+{
+  int n;
+
+  /* Already in desired state? */
+
+  if (suspend == g_serialpm.serial_suspended)
+    return;
+
+  g_serialpm.serial_suspended = suspend;
+
+  for (n = 0; n < STM32WB_NLPUART + STM32WB_NUSART; n++)
+    {
+      struct stm32wb_serial_s *priv = g_uart_devs[n];
+
+      if (!priv || !priv->initialized)
+        {
+          continue;
+        }
+
+      stm32wb_serial_setsuspend(&priv->dev, suspend);
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setapbclock
+ *
+ * Description:
+ *   Enable or disable APB clock for the USART peripheral
+ *
+ * Input Parameters:
+ *   dev - A reference to the UART driver state structure
+ *   on  - Enable clock if 'on' is 'true' and disable if 'false'
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_setapbclock(struct uart_dev_s *dev, bool on)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t rcc_en;
+  uint32_t regaddr;
+
+  /* Determine which USART to configure */
+
+  switch (priv->usartbase)
+    {
+    default:
+      return;
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+    case STM32WB_LPUART1_BASE:
+      rcc_en = RCC_APB1ENR2_LPUART1EN;
+      regaddr = STM32WB_RCC_APB1ENR2;
+      break;
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+    case STM32WB_USART1_BASE:
+      rcc_en = RCC_APB2ENR_USART1EN;
+      regaddr = STM32WB_RCC_APB2ENR;
+      break;
+#endif
+    }
+
+  /* Enable/disable APB 1/2 clock for USART */
+
+  if (on)
+    {
+      modifyreg32(regaddr, 0, rcc_en);
+    }
+  else
+    {
+      modifyreg32(regaddr, rcc_en, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setup
+ *
+ * Description:
+ *   Configure the USART baud, bits, parity, etc. This method is called the
+ *   first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_setup(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+  uint32_t regval;
+
+  /* Note: The logic here depends on the fact that that the USART module
+   * was enabled in stm32wb_lowsetup().
+   */
+
+  /* Enable USART APB1/2 clock */
+
+  stm32wb_serial_setapbclock(dev, true);
+
+  /* Configure pins for USART use */
+
+  stm32wb_configgpio(priv->tx_gpio);
+  stm32wb_configgpio(priv->rx_gpio);
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->cts_gpio != 0)
+    {
+      stm32wb_configgpio(priv->cts_gpio);
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->rts_gpio != 0)
+    {
+      uint32_t config = priv->rts_gpio;
+
+#ifdef CONFIG_STM32WB_FLOWCONTROL_BROKEN
+      /* Instead of letting hw manage this pin, we will bitbang */
+
+      config = (config & ~GPIO_MODE_MASK) | GPIO_OUTPUT;
+#endif
+      stm32wb_configgpio(config);
+    }
+#endif
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_configgpio(priv->rs485_dir_gpio);
+      stm32wb_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity);
+    }
+#endif
+
+  /* Configure CR2 */
+
+  /* Clear STOP, CLKEN, CPOL, CPHA, LBCL, and interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK | USART_CR2_CLKEN | USART_CR2_CPOL |
+              USART_CR2_CPHA | USART_CR2_LBCL | USART_CR2_LBDIE);
+
+  /* Configure STOP bits */
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure CR1 */
+
+  /* Clear TE, REm and all interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_TE | USART_CR1_RE | USART_CR1_ALLINTS);
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure CR3 */
+
+  /* Clear CTSE, RTSE, and all interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSIE | USART_CR3_CTSE | USART_CR3_RTSE |
+              USART_CR3_EIE);
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+
+  /* Configure the USART line format and speed. */
+
+  stm32wb_serial_setformat(dev);
+
+  /* Enable Rx, Tx, and the USART */
+
+  regval      = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval     |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+  /* Set up the cached interrupt enables value */
+
+  priv->ie    = 0;
+
+  /* Mark device as initialized. */
+
+  priv->initialized = true;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmasetup
+ *
+ * Description:
+ *   Configure the USART baud, bits, parity, etc. This method is called the
+ *   first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmasetup(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int result;
+  uint32_t regval;
+
+  /* Do the basic UART setup first, unless we are the console */
+
+  if (!dev->isconsole)
+    {
+      result = stm32wb_serial_setup(dev);
+      if (result != OK)
+        {
+          return result;
+        }
+    }
+
+  /* Acquire the DMA channel.  This should always succeed. */
+
+  priv->rxdma = stm32wb_dmachannel(priv->rxdma_channel);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Configure for non-circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                     priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                     (uint32_t)priv->rxfifo,
+                     RXDMA_BUFFER_SIZE,
+                     SERIAL_DMA_IFLOW_CONTROL_WORD);
+    }
+  else
+#endif
+    {
+      /* Configure for circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                     priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                     (uint32_t)priv->rxfifo,
+                     RXDMA_BUFFER_SIZE,
+                     SERIAL_DMA_CONTROL_WORD);
+    }
+
+  /* Reset our DMA shadow pointer to match the address just
+   * programmed above.
+   */
+
+  priv->rxdmanext = 0;
+
+  /* Enable receive DMA for the UART */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval |= USART_CR3_DMAR;
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Start the DMA channel, and arrange for callbacks at the full point
+       * in the FIFO. After buffer gets full, hardware flow-control kicks
+       * in and DMA transfer is stopped.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, false);
+    }
+  else
+#endif
+    {
+      /* Start the DMA channel, and arrange for callbacks at the half and
+       * full points in the FIFO.  This ensures that we have half a FIFO
+       * worth of time to claim bytes before they are overwritten.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, true);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_shutdown
+ *
+ * Description:
+ *   Disable the USART.  This method is called when the serial
+ *   port is closed
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Mark device as uninitialized. */
+
+  priv->initialized = false;
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_disableusartint(priv, NULL);
+
+  /* Disable USART APB1/2 clock */
+
+  stm32wb_serial_setapbclock(dev, false);
+
+  /* Disable Rx, Tx, and the UART */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Release pins. "If the serial-attached device is powered down, the TX
+   * pin causes back-powering, potentially confusing the device to the point
+   * of complete lock-up."
+   *
+   * REVISIT:  Is unconfiguring the pins appropriate for all device?  If not,
+   * then this may need to be a configuration option.
+   */
+
+  stm32wb_unconfiggpio(priv->tx_gpio);
+  stm32wb_unconfiggpio(priv->rx_gpio);
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->cts_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->cts_gpio);
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->rts_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->rts_gpio);
+    }
+#endif
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->rs485_dir_gpio);
+    }
+#endif
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmashutdown
+ *
+ * Description:
+ *   Disable the USART.  This method is called when the serial
+ *   port is closed
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  /* Perform the normal UART shutdown */
+
+  stm32wb_serial_shutdown(dev);
+
+  /* Stop the DMA channel */
+
+  stm32wb_dmastop(priv->rxdma);
+
+  /* Release the DMA channel */
+
+  stm32wb_dmafree(priv->rxdma);
+  priv->rxdma = NULL;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_attach
+ *
+ * Description:
+ *   Configure the USART to operation in interrupt driven mode.  This method
+ *   is called when the serial port is opened.  Normally, this is just after
+ *   the the setup() method is called, however, the serial console may
+ *   operate in a non-interrupt driven mode during the boot phase.
+ *
+ *   RX and TX interrupts are not enabled when by the attach method (unless
+ *   the hardware supports multiple levels of interrupt enabling). The RX
+ *   and TX interrupts are not enabled until the txint() and rxint()
+ *   methods are called.
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_attach(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int ret;
+
+  /* Attach and enable the IRQ */
+
+  ret = irq_attach(priv->irq, up_interrupt, priv);
+  if (ret == OK)
+    {
+      /* Enable the interrupt (RX and TX interrupts are still disabled
+       * in the USART
+       */
+
+      up_enable_irq(priv->irq);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_detach
+ *
+ * Description:
+ *   Detach USART interrupts.  This method is called when the serial port is
+ *   closed normally just before the shutdown method is called.
+ *   The exception is the serial console which is never shutdown.
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_detach(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  up_disable_irq(priv->irq);
+  irq_detach(priv->irq);
+}
+
+/****************************************************************************
+ * Name: up_interrupt
+ *
+ * Description:
+ *   This is the USART interrupt handler.  It will be invoked when an
+ *   interrupt received on the 'irq'  It should call uart_transmitchars or
+ *   uart_receivechar to perform the appropriate data transfers.  The
+ *   interrupt handling logic must be able to map the 'irq' number into the
+ *   appropriate uart_dev_s structure in order to call these functions.
+ *
+ ****************************************************************************/
+
+static int up_interrupt(int irq, void *context, void *arg)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)arg;
+  int  passes;
+  bool handled;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Report serial activity to the power management logic */
+
+#if defined(CONFIG_PM) && CONFIG_STM32WB_PM_SERIAL_ACTIVITY > 0
+  pm_activity(PM_IDLE_DOMAIN, CONFIG_STM32WB_PM_SERIAL_ACTIVITY);
+#endif
+
+  /* Loop until there are no characters to be transferred or,
+   * until we have been looping for a long time.
+   */
+
+  handled = true;
+  for (passes = 0; passes < 256 && handled; passes++)
+    {
+      handled = false;
+
+      /* Get the masked USART status word. */
+
+      priv->sr = stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET);
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning             Usage
+       * ------------------ --------------- ------------------- ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected           (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete           (used only
+       *                                                         for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag         (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag           (not used)
+       *
+       * NOTE: Some of these status bits must be cleared by explicitly
+       * writing one to the ICR register: USART_ICR_CTSCF, USART_ICR_LBDCF.
+       * Note of those are currently being used.
+       */
+
+#ifdef HAVE_RS485
+      /* Transmission of whole buffer is over - TC is set, TXEIE is cleared.
+       * Note - this should be first, to have the most recent TC bit value
+       * from SR register - sending data affects TC, but without refresh we
+       * will not know that...
+       */
+
+      if ((priv->sr & USART_ISR_TC) != 0 &&
+          (priv->ie & USART_CR1_TCIE) != 0 &&
+          (priv->ie & USART_CR1_TXEIE) == 0)
+        {
+          stm32wb_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity);
+          stm32wb_serial_restoreusartint(priv, priv->ie & ~USART_CR1_TCIE);
+        }
+#endif
+
+      /* Handle incoming, receive bytes. */
+
+      if ((priv->sr & USART_ISR_RXNE) != 0 &&
+          (priv->ie & USART_CR1_RXNEIE) != 0)
+        {
+          /* Received data ready... process incoming bytes.  NOTE the check
+           * for RXNEIE:  We cannot call uart_recvchards of RX interrupts
+           * are disabled.
+           */
+
+          uart_recvchars(&priv->dev);
+          handled = true;
+        }
+
+      /* We may still have to read from the DR register to clear any pending
+       * error conditions.
+       */
+
+      else if ((priv->sr &
+                (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE)) != 0)
+        {
+          /* These errors are cleared by writing the corresponding bit to the
+           * interrupt clear register (ICR).
+           */
+
+          stm32wb_serial_putreg(priv, STM32WB_USART_ICR_OFFSET,
+                               (USART_ICR_NCF | USART_ICR_ORECF |
+                                USART_ICR_FECF));
+        }
+
+      /* Handle outgoing, transmit bytes */
+
+      if ((priv->sr & USART_ISR_TXE) != 0 &&
+          (priv->ie & USART_CR1_TXEIE) != 0)
+        {
+          /* Transmit data register empty ... process outgoing bytes */
+
+          uart_xmitchars(&priv->dev);
+          handled = true;
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_ioctl
+ *
+ * Description:
+ *   All ioctl calls will be routed through this method
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                unsigned long arg)
+{
+#if defined(CONFIG_SERIAL_TERMIOS) || defined(CONFIG_SERIAL_TIOCSERGSTRUCT)
+  struct inode      *inode = filep->f_inode;
+  struct uart_dev_s *dev   = inode->i_private;
+#endif
+#if defined(CONFIG_SERIAL_TERMIOS)
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#endif
+  int                ret    = OK;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT
+    case TIOCSERGSTRUCT:
+      {
+        struct stm32wb_serial_s *user = (struct stm32wb_serial_s *)arg;
+        if (!user)
+          {
+            ret = -EINVAL;
+          }
+        else
+          {
+            memcpy(user, dev, sizeof(struct stm32wb_serial_s));
+          }
+      }
+      break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_SINGLEWIRE
+    case TIOCSSINGLEWIRE:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, HDSEL can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Change the TX port to be open-drain/push-pull and enable/disable
+         * half-duplex mode.
+         */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+        if ((arg & SER_SINGLEWIRE_ENABLED) != 0)
+          {
+            uint32_t gpio_val = (arg & SER_SINGLEWIRE_PUSHPULL) ==
+                                 SER_SINGLEWIRE_PUSHPULL ?
+                                 GPIO_PUSHPULL : GPIO_OPENDRAIN;
+            gpio_val |=
+              (arg & SER_SINGLEWIRE_PULL_MASK) == SER_SINGLEWIRE_PULLUP ?
+                                                  GPIO_PULLUP : GPIO_FLOAT;
+
+            gpio_val |=
+              (arg & SER_SINGLEWIRE_PULL_MASK) == SER_SINGLEWIRE_PULLDOWN ?
+                                                  GPIO_PULLDOWN : GPIO_FLOAT;
+
+            stm32wb_configgpio((priv->tx_gpio & ~(GPIO_PUPD_MASK |
+                                                  GPIO_OPENDRAIN)) |
+                                                  gpio_val);
+
+            cr |= USART_CR3_HDSEL;
+          }
+        else
+          {
+            stm32wb_configgpio((priv->tx_gpio & ~(GPIO_PUPD_MASK |
+                                                  GPIO_OPENDRAIN)) |
+                                                  GPIO_PUSHPULL);
+
+            cr &= ~USART_CR3_HDSEL;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_INVERT
+    case TIOCSINVERT:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, {R,T}XINV can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Enable/disable signal inversion. */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+
+        if (arg & SER_INVERT_ENABLED_RX)
+          {
+            cr |= USART_CR2_RXINV;
+          }
+        else
+          {
+            cr &= ~USART_CR2_RXINV;
+          }
+
+        if (arg & SER_INVERT_ENABLED_TX)
+          {
+            cr |= USART_CR2_TXINV;
+          }
+        else
+          {
+            cr &= ~USART_CR2_TXINV;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_SWAP
+    case TIOCSSWAP:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, SWAP can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Enable/disable Swap mode. */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+
+        if (arg == SER_SWAP_ENABLED)
+          {
+            cr |= USART_CR2_SWAP;
+          }
+        else
+          {
+            cr &= ~USART_CR2_SWAP;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_SERIAL_TERMIOS
+    case TCGETS:
+      {
+        struct termios *termiosp = (struct termios *)arg;
+
+        if (!termiosp)
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        /* Note that since we only support 8/9 bit modes and
+         * there is no way to report 9-bit mode, we always claim 8.
+         */
+
+        termiosp->c_cflag =
+          ((priv->parity != 0) ? PARENB : 0) |
+          ((priv->parity == 1) ? PARODD : 0) |
+          ((priv->stopbits2) ? CSTOPB : 0) |
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+          ((priv->oflow) ? CCTS_OFLOW : 0) |
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          ((priv->iflow) ? CRTS_IFLOW : 0) |
+#endif
+          CS8;
+
+        cfsetispeed(termiosp, priv->baud);
+
+        /* TODO: CCTS_IFLOW, CCTS_OFLOW */
+      }
+      break;
+
+    case TCSETS:
+      {
+        struct termios *termiosp = (struct termios *)arg;
+
+        if (!termiosp)
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        /* Perform some sanity checks before accepting any changes */
+
+        if (((termiosp->c_cflag & CSIZE) != CS8)
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+            || ((termiosp->c_cflag & CCTS_OFLOW) && (priv->cts_gpio == 0))
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+            || ((termiosp->c_cflag & CRTS_IFLOW) && (priv->rts_gpio == 0))
+#endif
+           )
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        if (termiosp->c_cflag & PARENB)
+          {
+            priv->parity = (termiosp->c_cflag & PARODD) ? 1 : 2;
+          }
+        else
+          {
+            priv->parity = 0;
+          }
+
+        priv->stopbits2 = (termiosp->c_cflag & CSTOPB) != 0;
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+        priv->oflow = (termiosp->c_cflag & CCTS_OFLOW) != 0;
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+        priv->iflow = (termiosp->c_cflag & CRTS_IFLOW) != 0;
+#endif
+
+        /* Note that since there is no way to request 9-bit mode
+         * and no way to support 5/6/7-bit modes, we ignore them
+         * all here.
+         */
+
+        /* Note that only cfgetispeed is used because we have knowledge
+         * that only one speed is supported.
+         */
+
+        priv->baud = cfgetispeed(termiosp);
+
+        /* Effect the changes immediately - note that we do not implement
+         * TCSADRAIN / TCSAFLUSH
+         */
+
+        stm32wb_serial_setformat(dev);
+      }
+      break;
+#endif /* CONFIG_SERIAL_TERMIOS */
+
+#ifdef CONFIG_STM32WB_USART_BREAKS
+#  ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+    case TIOCSBRK:  /* BSD compatibility: Turn break on, unconditionally */
+      {
+        irqstate_t flags;
+        uint32_t tx_break;
+
+        flags = enter_critical_section();
+
+        /* Disable any further tx activity */
+
+        priv->ie |= USART_CR1_IE_BREAK_INPROGRESS;
+
+        stm32wb_serial_txint(dev, false);
+
+        /* Configure TX as a GPIO output pin and Send a break signal */
+
+        tx_break = GPIO_OUTPUT |
+                   (~(GPIO_MODE_MASK | GPIO_OUTPUT_SET) & priv->tx_gpio);
+        stm32wb_configgpio(tx_break);
+
+        leave_critical_section(flags);
+      }
+      break;
+
+    case TIOCCBRK:  /* BSD compatibility: Turn break off, unconditionally */
+      {
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Configure TX back to U(S)ART */
+
+        stm32wb_configgpio(priv->tx_gpio);
+
+        priv->ie &= ~USART_CR1_IE_BREAK_INPROGRESS;
+
+        /* Enable further tx activity */
+
+        stm32wb_serial_txint(dev, true);
+
+        leave_critical_section(flags);
+      }
+      break;
+#  else
+    case TIOCSBRK:  /* No BSD compatibility: Turn break on for M bit times */
+      {
+        uint32_t cr1;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+        cr1   = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET,
+                             cr1 | USART_CR1_SBK);
+        leave_critical_section(flags);
+      }
+      break;
+
+    case TIOCCBRK:  /* No BSD compatibility: May turn off break too soon */
+      {
+        uint32_t cr1;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+        cr1   = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET,
+                             cr1 & ~USART_CR1_SBK);
+        leave_critical_section(flags);
+      }
+      break;
+#  endif
+#endif
+
+    default:
+      ret = -ENOTTY;
+      break;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_receive
+ *
+ * Description:
+ *   Called (usually) from the interrupt level to receive one
+ *   character from the USART.  Error bits associated with the
+ *   receipt are provided in the return 'status'.
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int stm32wb_serial_receive(struct uart_dev_s *dev,
+                                  unsigned int *status)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t rdr;
+
+  /* Get the Rx byte */
+
+  rdr      = stm32wb_serial_getreg(priv, STM32WB_USART_RDR_OFFSET);
+
+  /* Get the Rx byte plux error information.  Return those in status */
+
+  *status  = priv->sr << 16 | rdr;
+  priv->sr = 0;
+
+  /* Then return the actual received byte */
+
+  return rdr & 0xff;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxint
+ *
+ * Description:
+ *   Call to enable or disable RX interrupts
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  irqstate_t flags;
+  uint16_t ie;
+
+  /* USART receive interrupts:
+   *
+   * Enable             Status          Meaning                Usage
+   * ------------------ --------------- ------------------     ----------
+   * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+   *                                     Detected              (not used)
+   * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+   *                                     Ready to be Read
+   * "              "   USART_ISR_ORE    Overrun Error
+   *                                     Detected
+   * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+   *
+   * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag            (not used)
+   * USART_CR3_EIE      USART_ISR_FE     Framing Error
+   * "           "      USART_ISR_NE     Noise Flag
+   * "           "      USART_ISR_ORE    Overrun Error
+   *                                     Detected
+   */
+
+  flags = enter_critical_section();
+  ie = priv->ie;
+  if (enable)
+    {
+      /* Receive an interrupt when their is anything in the Rx data register
+       * (or an Rx timeout occurs).
+       */
+
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+#ifdef CONFIG_USART_ERRINTS
+      ie |= (USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR3_EIE);
+#else
+      ie |= USART_CR1_RXNEIE;
+#endif
+#endif
+    }
+  else
+    {
+      ie &= ~(USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR3_EIE);
+    }
+
+  /* Then set the new interrupt state */
+
+  stm32wb_serial_restoreusartint(priv, ie);
+  leave_critical_section(flags);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxavailable
+ *
+ * Description:
+ *   Return true if the receive register is not empty
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  return ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+           USART_ISR_RXNE) != 0);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxflowcontrol
+ *
+ * Description:
+ *   Called when Rx buffer is full (or exceeds configured watermark levels
+ *   if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is defined).
+ *   Return true if UART activated RX flow control to block more incoming
+ *   data
+ *
+ * Input Parameters:
+ *   dev       - UART device instance
+ *   nbuffered - the number of characters currently buffered
+ *               (if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is
+ *               not defined the value will be 0 for an empty buffer or the
+ *               defined buffer size for a full buffer)
+ *   upper     - true indicates the upper watermark was crossed where
+ *               false indicates the lower watermark has been crossed
+ *
+ * Returned Value:
+ *   true if RX flow control activated.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS) && \
+    defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      /* Assert/de-assert nRTS set it high resume/stop sending */
+
+      stm32wb_gpiowrite(priv->rts_gpio, upper);
+
+      if (upper)
+        {
+          /* With heavy Rx traffic, RXNE might be set and data pending.
+           * Returning 'true' in such case would cause RXNE left unhandled
+           * and causing interrupt storm. Sending end might be also be slow
+           * to react on nRTS, and returning 'true' here would prevent
+           * processing that data.
+           *
+           * Therefore, return 'false' so input data is still being processed
+           * until sending end reacts on nRTS signal and stops sending more.
+           */
+
+          return false;
+        }
+
+      return upper;
+    }
+
+#else
+  if (priv->iflow)
+    {
+      /* Is the RX buffer full? */
+
+      if (upper)
+        {
+          /* Disable Rx interrupt to prevent more data being from
+           * peripheral.  When hardware RTS is enabled, this will
+           * prevent more data from coming in.
+           *
+           * This function is only called when UART recv buffer is full,
+           * that is: "dev->recv.head + 1 == dev->recv.tail".
+           *
+           * Logic in "uart_read" will automatically toggle Rx interrupts
+           * when buffer is read empty and thus we do not have to re-
+           * enable Rx interrupts.
+           */
+
+          uart_disablerxint(dev);
+          return true;
+        }
+
+      /* No.. The RX buffer is empty */
+
+      else
+        {
+          /* We might leave Rx interrupt disabled if full recv buffer was
+           * read empty.  Enable Rx interrupt to make sure that more input is
+           * received.
+           */
+
+          uart_enablerxint(dev);
+        }
+    }
+#endif
+
+  return false;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmareceive
+ *
+ * Description:
+ *   Called (usually) from the interrupt level to receive one
+ *   character from the USART.  Error bits associated with the
+ *   receipt are provided in the return 'status'.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                     unsigned int *status)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int c = 0;
+
+  if (stm32wb_serial_dmanextrx(priv) != priv->rxdmanext)
+    {
+      c = priv->rxfifo[priv->rxdmanext];
+
+      priv->rxdmanext++;
+      if (priv->rxdmanext == RXDMA_BUFFER_SIZE)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              /* RX DMA buffer full. RX paused, RTS line pulled up to prevent
+               * more input data from other end.
+               */
+            }
+          else
+#endif
+            {
+              priv->rxdmanext = 0;
+            }
+        }
+    }
+
+  return c;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmareenable
+ *
+ * Description:
+ *   Call to re-enable RX DMA.
+ *
+ ****************************************************************************/
+
+#if defined(SERIAL_HAVE_RXDMA)
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv)
+{
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Configure for non-circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                       priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                       (uint32_t)priv->rxfifo,
+                       RXDMA_BUFFER_SIZE,
+                       SERIAL_DMA_IFLOW_CONTROL_WORD);
+    }
+  else
+#endif
+    {
+      /* Configure for circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                       priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                       (uint32_t)priv->rxfifo,
+                       RXDMA_BUFFER_SIZE,
+                       SERIAL_DMA_CONTROL_WORD);
+    }
+
+  /* Reset our DMA shadow pointer to match the address just
+   * programmed above.
+   */
+
+  priv->rxdmanext = 0;
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Start the DMA channel, and arrange for callbacks at the full point
+       * in the FIFO. After buffer gets full, hardware flow-control kicks
+       * in and DMA transfer is stopped.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, false);
+    }
+  else
+#endif
+    {
+      /* Start the DMA channel, and arrange for callbacks at the half and
+       * full points in the FIFO.  This ensures that we have half a FIFO
+       * worth of time to claim bytes before they are overwritten.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, true);
+    }
+
+#ifdef CONFIG_PM
+  /* Clear DMA suspended flag. */
+
+  priv->rxdmasusp = false;
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmaiflowrestart
+ *
+ * Description:
+ *   Call to restart RX DMA for input flow-controlled USART
+ *
+ ****************************************************************************/
+
+#if defined(SERIAL_HAVE_RXDMA) && defined(CONFIG_SERIAL_IFLOWCONTROL)
+static bool stm32wb_serial_dmaiflowrestart(struct stm32wb_serial_s *priv)
+{
+  if (!priv->rxenable)
+    {
+      /* Rx not enabled by upper layer. */
+
+      return false;
+    }
+
+  if (priv->rxdmanext != RXDMA_BUFFER_SIZE)
+    {
+#ifdef CONFIG_PM
+      if (priv->rxdmasusp)
+        {
+          /* Rx DMA in suspended state. */
+
+          if (stm32wb_serial_dmarxavailable(&priv->dev))
+            {
+              /* DMA buffer has unprocessed data, do not re-enable yet. */
+
+              return false;
+            }
+        }
+      else
+#endif
+        {
+          return false;
+        }
+    }
+
+  /* DMA is stopped or suspended and DMA buffer does not have pending data,
+   * re-enabling without data loss is now safe.
+   */
+
+  stm32wb_serial_dmareenable(priv);
+
+  return true;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmarxint
+ *
+ * Description:
+ *   Call to enable or disable RX interrupts
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void stm32wb_serial_dmarxint(struct uart_dev_s *dev, bool enable)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  /* En/disable DMA reception.
+   *
+   * Note that it is not safe to check for available bytes and immediately
+   * pass them to uart_recvchars as that could potentially recurse back
+   * to us again.  Instead, bytes must wait until the next up_dma_poll or
+   * DMA event.
+   */
+
+  priv->rxenable = enable;
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Re-enable RX DMA. */
+
+      stm32wb_serial_dmaiflowrestart(priv);
+    }
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmarxavailable
+ *
+ * Description:
+ *   Return true if the receive register is not empty
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static bool stm32wb_serial_dmarxavailable(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  /* Compare our receive pointer to the current DMA pointer, if they
+   * do not match, then there are bytes to be received.
+   */
+
+  return (stm32wb_serial_dmanextrx(priv) != priv->rxdmanext);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_send
+ *
+ * Description:
+ *   This method will send one byte on the USART
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_send(struct uart_dev_s *dev, int ch)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_gpiowrite(priv->rs485_dir_gpio, priv->rs485_dir_polarity);
+    }
+#endif
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_TDR_OFFSET, (uint32_t)ch);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_txint
+ *
+ * Description:
+ *   Call to enable or disable TX interrupts
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_txint(struct uart_dev_s *dev, bool enable)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  irqstate_t flags;
+
+  /* USART transmit interrupts:
+   *
+   * Enable             Status          Meaning            Usage
+   * ------------------ --------------- ----------------   ----------
+   * USART_CR1_TCIE     USART_ISR_TC     Transmission
+   *                                     Complete          (used only
+   *                                                       for RS-485)
+   * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+   *                                     Register Empty
+   * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag          (not used)
+   */
+
+  flags = enter_critical_section();
+  if (enable)
+    {
+      /* Set to receive an interrupt when the TX data register is empty */
+
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+      uint16_t ie = priv->ie | USART_CR1_TXEIE;
+
+      /* If RS-485 is supported on this U[S]ART, then also enable the
+       * transmission complete interrupt.
+       */
+
+#  ifdef HAVE_RS485
+      if (priv->rs485_dir_gpio != 0)
+        {
+          ie |= USART_CR1_TCIE;
+        }
+#  endif
+
+#  ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+      if (priv->ie & USART_CR1_IE_BREAK_INPROGRESS)
+        {
+          leave_critical_section(flags);
+          return;
+        }
+#  endif
+
+      stm32wb_serial_restoreusartint(priv, ie);
+
+      /* Fake a TX interrupt here by just calling uart_xmitchars() with
+       * interrupts disabled (note this may recurse).
+       */
+
+      uart_xmitchars(dev);
+#endif
+    }
+  else
+    {
+      /* Disable the TX interrupt */
+
+      stm32wb_serial_restoreusartint(priv, priv->ie & ~USART_CR1_TXEIE);
+    }
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_txready
+ *
+ * Description:
+ *   Return true if the transmit data register is empty
+ *
+ ****************************************************************************/
+
+static bool stm32wb_serial_txready(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  return ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+           USART_ISR_TXE) != 0);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmarxcallback
+ *
+ * Description:
+ *   This function checks the current DMA state and calls the generic
+ *   serial stack when bytes appear to be available.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void stm32wb_serial_dmarxcallback(DMA_HANDLE handle, uint8_t status,
+                                         void *arg)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)arg;
+
+  if (priv->rxenable && stm32wb_serial_dmarxavailable(&priv->dev))
+    {
+      uart_recvchars(&priv->dev);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Re-enable RX DMA. */
+
+          stm32wb_serial_dmaiflowrestart(priv);
+        }
+#endif
+    }
+
+  /* Get the masked USART status word to check and clear error flags.
+   *
+   * When wake-up from low power mode was not fast enough, UART is resumed
+   * too late and sometimes exactly when character was coming over UART,
+   * resulting to frame error.
+
+   * If error flag is not cleared, Rx DMA will be stuck. Clearing errors
+   * will release Rx DMA.
+   */
+
+  priv->sr = stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET);
+
+  if ((priv->sr & (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE)) != 0)
+    {
+      stm32wb_serial_putreg(priv, STM32WB_USART_ICR_OFFSET,
+                           (USART_ICR_NCF | USART_ICR_ORECF |

Review Comment:
   ```suggestion
                               (USART_ICR_NCF | USART_ICR_ORECF |
   ```



##########
arch/arm/src/stm32wb/stm32wb_serial.c:
##########
@@ -0,0 +1,2896 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_serial.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/serial/serial.h>
+#include <nuttx/power/pm.h>
+#include <arch/board/board.h>
+
+#ifdef CONFIG_SERIAL_TERMIOS
+#  include <termios.h>
+#endif
+
+#include "arm_internal.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_uart.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Some sanity checks *******************************************************/
+
+/* DMA configuration */
+
+#ifdef SERIAL_HAVE_RXDMA
+
+/* Currently RS-485 support cannot be enabled when RXDMA is in use due to
+ * lack of testing - RS-485 support was developed on STM32F1x
+ */
+
+#  if (defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_RS485))
+#    error "RXDMA and RS-485 cannot be enabled at the same time for the same U[S]ART"
+#  endif
+
+/* For the stm32wb, there are alternate DMA channels for USART1.
+ * Logic in the board.h file make the DMA channel selection by defining
+ * the following in the board.h file.
+ */
+
+#  if defined(CONFIG_USART1_RXDMA) && !defined(DMAMAP_USART1_RX)
+#    error "USART1 DMA channel not defined (DMAMAP_USART1_RX)"
+#  endif
+
+/* The DMA buffer size when using RX DMA to emulate a FIFO.
+ *
+ * When streaming data, the generic serial layer will be called
+ * every time the FIFO receives half this number of bytes.
+ *
+ * The buffer size should be an even multiple of ARMV7M_DCACHE_LINESIZE.
+ */
+
+#  if !defined(CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE) || \
+      CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE == 0
+#    define RXDMA_BUFFER_SIZE 32
+#  else
+#    define RXDMA_BUFFER_SIZE ((CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE + 31) & ~31)
+#  endif
+
+/* DMA priority */
+
+#  ifndef CONFIG_USART_RXDMAPRIO
+#    define CONFIG_USART_RXDMAPRIO  DMA_CCR_PRIMED
+#  endif
+#  if (CONFIG_USART_RXDMAPRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_USART_RXDMAPRIO"
+#  endif
+
+/* DMA control words */
+
+#  define SERIAL_DMA_CONTROL_WORD      \
+              (DMA_CCR_CIRC          | \
+               DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  ifdef CONFIG_SERIAL_IFLOWCONTROL
+#    define SERIAL_DMA_IFLOW_CONTROL_WORD \
+              (DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  endif
+
+#endif
+
+/* Power management definitions */
+
+#if defined(CONFIG_PM) && !defined(CONFIG_STM32WB_PM_SERIAL_ACTIVITY)
+#  define CONFIG_STM32WB_PM_SERIAL_ACTIVITY  10
+#endif
+#if defined(CONFIG_PM)
+#  define PM_IDLE_DOMAIN             0 /* Revisit */
+#endif
+
+/* Keep track if a Break was set
+ *
+ * Note:
+ *
+ * 1) This value is set in the priv->ie but never written to the control
+ *    register. It must not collide with USART_CR1_USED_INTS or USART_CR3_EIE
+ * 2) USART_CR3_EIE is also carried in the up_dev_s ie member.
+ *
+ * See stm32wb_serial_restoreusartint where the masking is done.
+ */
+
+#ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+#  define USART_CR1_IE_BREAK_INPROGRESS_SHFTS 15
+#  define USART_CR1_IE_BREAK_INPROGRESS (1 << USART_CR1_IE_BREAK_INPROGRESS_SHFTS)
+#endif
+
+#ifdef USE_SERIALDRIVER
+#ifdef HAVE_UART
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_serial_s
+{
+  struct uart_dev_s dev;       /* Generic UART device */
+  uint16_t          ie;        /* Saved interrupt mask bits value */
+  uint16_t          sr;        /* Saved status bits */
+
+  /* Has been initialized and HW is setup. */
+
+  bool              initialized;
+
+#ifdef CONFIG_PM
+  bool              suspended; /* UART device has been suspended. */
+
+  /* Interrupt mask value stored before suspending for stop mode. */
+
+  uint16_t          suspended_ie;
+#endif
+
+  /* If termios are supported, then the following fields may vary at
+   * runtime.
+   */
+
+#ifdef CONFIG_SERIAL_TERMIOS
+  uint8_t           parity;    /* 0=none, 1=odd, 2=even */
+  uint8_t           bits;      /* Number of bits (7 or 8) */
+  bool              stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  bool              iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  bool              oflow;     /* output flow control (CTS) enabled */
+#endif
+  uint32_t          baud;      /* Configured baud */
+#else
+  const uint8_t     parity;    /* 0=none, 1=odd, 2=even */
+  const uint8_t     bits;      /* Number of bits (7 or 8) */
+  const bool        stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const bool        iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const bool        oflow;     /* output flow control (CTS) enabled */
+#endif
+  const uint32_t    baud;      /* Configured baud */
+#endif
+
+  const uint8_t     irq;       /* IRQ associated with this USART */
+  const uint32_t    apbclock;  /* PCLK 1 or 2 frequency */
+  const uint32_t    usartbase; /* Base address of USART registers */
+  const uint32_t    tx_gpio;   /* U[S]ART TX GPIO pin configuration */
+  const uint32_t    rx_gpio;   /* U[S]ART RX GPIO pin configuration */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const uint32_t    rts_gpio;  /* U[S]ART RTS GPIO pin configuration */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const uint32_t    cts_gpio;  /* U[S]ART CTS GPIO pin configuration */
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+  const unsigned int rxdma_channel; /* DMA channel assigned */
+#endif
+
+  /* RX DMA state */
+
+#ifdef SERIAL_HAVE_RXDMA
+  DMA_HANDLE        rxdma;     /* currently-open receive DMA stream */
+  bool              rxenable;  /* DMA-based reception en/disable */
+#ifdef CONFIG_PM
+  bool              rxdmasusp; /* Rx DMA suspended */
+#endif
+  uint32_t          rxdmanext; /* Next byte in the DMA buffer to be read */
+  char       *const rxfifo;    /* Receive DMA buffer */
+#endif
+
+#ifdef HAVE_RS485
+  const uint32_t    rs485_dir_gpio;     /* U[S]ART RS-485 DIR GPIO pin configuration */
+  const bool        rs485_dir_polarity; /* U[S]ART RS-485 DIR pin state for TX enabled */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev);
+#endif
+static int  stm32wb_serial_setup(struct uart_dev_s *dev);
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_attach(struct uart_dev_s *dev);
+static void stm32wb_serial_detach(struct uart_dev_s *dev);
+static int  up_interrupt(int irq, void *context, void *arg);
+static int  stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                 unsigned long arg);
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int  stm32wb_serial_receive(struct uart_dev_s *dev,
+                                   unsigned int *status);
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev);
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper);
+#endif
+static void stm32wb_serial_send(struct uart_dev_s *dev, int ch);
+static void stm32wb_serial_txint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_txready(struct uart_dev_s *dev);
+
+#ifdef SERIAL_HAVE_RXDMA
+static int  stm32wb_serial_dmasetup(struct uart_dev_s *dev);
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                      unsigned int *status);
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv);
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_dmaiflowrestart(struct stm32wb_serial_s *priv);
+#endif
+static void stm32wb_serial_dmarxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_dmarxavailable(struct uart_dev_s *dev);
+
+static void stm32wb_serial_dmarxcallback(DMA_HANDLE handle, uint8_t status,
+                                         void *arg);
+#endif
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend);
+static void stm32wb_serial_pm_setsuspend(bool suspend);
+static void stm32wb_serial_pmnotify(struct pm_callback_s *cb,
+                                    int domain, enum pm_state_e pmstate);
+static int  stm32wb_serial_pmprepare(struct pm_callback_s *cb,
+                                     int domain, enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static const struct uart_ops_s g_uart_ops =
+{
+  .setup          = stm32wb_serial_setup,
+  .shutdown       = stm32wb_serial_shutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_receive,
+  .rxint          = stm32wb_serial_rxint,
+  .rxavailable    = stm32wb_serial_rxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+static const struct uart_ops_s g_uart_dma_ops =
+{
+  .setup          = stm32wb_serial_dmasetup,
+  .shutdown       = stm32wb_serial_dmashutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_dmareceive,
+  .rxint          = stm32wb_serial_dmarxint,
+  .rxavailable    = stm32wb_serial_dmarxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+/* I/O buffers */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static char g_lpuart1rxbuffer[CONFIG_LPUART1_RXBUFSIZE];
+static char g_lpuart1txbuffer[CONFIG_LPUART1_TXBUFSIZE];
+# ifdef CONFIG_LPUART1_RXDMA
+static char g_lpuart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static char g_usart1rxbuffer[CONFIG_USART1_RXBUFSIZE];
+static char g_usart1txbuffer[CONFIG_USART1_TXBUFSIZE];
+# ifdef CONFIG_USART1_RXDMA
+static char g_usart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+/* This describes the state of the STM32WB LPUART1 port. */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static struct stm32wb_serial_s g_lpuart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 1
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_LPUART1_RXBUFSIZE,
+        .buffer  = g_lpuart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_LPUART1_TXBUFSIZE,
+        .buffer  = g_lpuart1txbuffer,
+      },
+#ifdef CONFIG_LPUART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_lpuart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_LPUART1,
+  .parity        = CONFIG_LPUART1_PARITY,
+  .bits          = CONFIG_LPUART1_BITS,
+  .stopbits2     = CONFIG_LPUART1_2STOP,
+  .baud          = CONFIG_LPUART1_BAUD,
+  .apbclock      = STM32WB_PCLK1_FREQUENCY,
+  .usartbase     = STM32WB_LPUART1_BASE,
+  .tx_gpio       = GPIO_LPUART1_TX,
+  .rx_gpio       = GPIO_LPUART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_LPUART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_LPUART1_RTS,
+#endif
+#ifdef CONFIG_LPUART1_RXDMA
+  .rxdma_channel = DMAMAP_LPUART1_RX,
+  .rxfifo        = g_lpuart1rxfifo,
+#endif
+
+#ifdef CONFIG_LPUART1_RS485
+  .rs485_dir_gpio = GPIO_LPUART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This describes the state of the STM32WB USART1 port. */
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static struct stm32wb_serial_s g_usart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 2
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_USART1_RXBUFSIZE,
+        .buffer  = g_usart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_USART1_TXBUFSIZE,
+        .buffer  = g_usart1txbuffer,
+      },
+#ifdef CONFIG_USART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_usart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_USART1,
+  .parity        = CONFIG_USART1_PARITY,
+  .bits          = CONFIG_USART1_BITS,
+  .stopbits2     = CONFIG_USART1_2STOP,
+  .baud          = CONFIG_USART1_BAUD,
+  .apbclock      = STM32WB_PCLK2_FREQUENCY,
+  .usartbase     = STM32WB_USART1_BASE,
+  .tx_gpio       = GPIO_USART1_TX,
+  .rx_gpio       = GPIO_USART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_USART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_USART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_USART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_USART1_RTS,
+#endif
+#ifdef CONFIG_USART1_RXDMA
+  .rxdma_channel = DMAMAP_USART1_RX,
+  .rxfifo        = g_usart1rxfifo,
+#endif
+
+#ifdef CONFIG_USART1_RS485
+  .rs485_dir_gpio = GPIO_USART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This table lets us iterate over the configured USARTs */
+
+static struct stm32wb_serial_s *
+const g_uart_devs[STM32WB_NLPUART + STM32WB_NUSART] =
+{
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  [0] = &g_lpuart1priv,
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+  [1] = &g_usart1priv,
+#endif
+};
+
+#ifdef CONFIG_PM
+static struct
+{
+  struct pm_callback_s pm_cb;
+  bool serial_suspended;
+} g_serialpm =
+  {
+    .pm_cb.notify  = stm32wb_serial_pmnotify,
+    .pm_cb.prepare = stm32wb_serial_pmprepare,
+    .serial_suspended = false
+  };
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_serial_getreg
+ ****************************************************************************/
+
+static inline
+uint32_t stm32wb_serial_getreg(struct stm32wb_serial_s *priv, int offset)
+{
+  return getreg32(priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_putreg
+ ****************************************************************************/
+
+static inline void stm32wb_serial_putreg(struct stm32wb_serial_s *priv,
+                                         int offset, uint32_t value)
+{
+  putreg32(value, priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setusartint
+ ****************************************************************************/
+
+static inline
+void stm32wb_serial_setusartint(struct stm32wb_serial_s *priv, uint16_t ie)
+{
+  uint32_t cr;
+
+  /* Save the interrupt mask */
+
+  priv->ie = ie;
+
+  /* And restore the interrupt state (see the interrupt
+   * enable/usage table above)
+   */
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  cr &= ~(USART_CR1_USED_INTS);
+  cr |= (ie & (USART_CR1_USED_INTS));
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr);
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  cr &= ~USART_CR3_EIE;
+  cr |= (ie & USART_CR3_EIE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+}
+
+/****************************************************************************
+ * Name: up_restoreusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_restoreusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  stm32wb_serial_setusartint(priv, ie);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_disableusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_disableusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t *ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  if (ie)
+    {
+      uint32_t cr1;
+      uint32_t cr3;
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning            Usage
+       * ------------------ --------------- ------------------ ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected          (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete          (used only
+       *                                                        for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag        (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag          (not used)
+       */
+
+      cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+      cr3 = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+      /* Return the current interrupt mask value for the used interrupts.
+       * Notice that this depends on the fact that none of the used interrupt
+       * enable bits overlap. This logic would fail if we needed the break
+       * interrupt!
+       */
+
+      *ie = (cr1 & (USART_CR1_USED_INTS)) | (cr3 & USART_CR3_EIE);
+    }
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_setusartint(priv, 0);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmanextrx
+ *
+ * Description:
+ *   Returns the index into the RX FIFO where the DMA will place the next
+ *   byte that it receives.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmanextrx(struct stm32wb_serial_s *priv)
+{
+  size_t dmaresidual;
+
+  dmaresidual = stm32wb_dmaresidual(priv->rxdma);
+
+  return (RXDMA_BUFFER_SIZE - (int)dmaresidual);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_usart
+ *
+ * Description:
+ *   Set the serial line baud rate (USART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setbaud_usart(struct stm32wb_serial_s *priv)
+{
+  /* This first implementation is for U[S]ARTs that support oversampling
+   * by 8 in additional to the standard oversampling by 16.
+   */
+
+  uint32_t usartdiv8;
+  uint32_t cr1;
+  uint32_t brr;
+
+  /* In case of oversampling by 8, the equation is:
+   *
+   *   baud      = 2 * fCK / usartdiv8
+   *   usartdiv8 = 2 * fCK / baud
+   */
+
+  usartdiv8 = ((priv->apbclock << 1) + (priv->baud >> 1)) / priv->baud;
+
+  /* Baud rate for standard USART (SPI mode included):
+   *
+   * In case of oversampling by 16, the equation is:
+   *   baud       = fCK / usartdiv16
+   *   usartdiv16 = fCK / baud
+   *              = 2 * usartdiv8
+   */
+
+  /* Use oversamply by 8 only if the divisor is small.  But what is small? */
+
+  cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  brr = stm32wb_serial_getreg(priv, STM32WB_USART_BRR_OFFSET);
+  brr &= ~(USART_BRR_MANT_MASK | USART_BRR_FRAC_MASK);
+
+  if (usartdiv8 > 100)
+    {
+      /* Use usartdiv16 */
+
+      brr  |= (usartdiv8 + 1) >> 1;
+
+      /* Clear oversampling by 8 to enable oversampling by 16 */
+
+      cr1 &= ~USART_CR1_OVER8;
+    }
+  else
+    {
+      DEBUGASSERT(usartdiv8 >= 8);
+
+      /* Perform mysterious operations on bits 0-3 */
+
+      brr  |= ((usartdiv8 & 0xfff0) | ((usartdiv8 & 0x000f) >> 1));
+
+      /* Set oversampling by 8 */
+
+      cr1 |= USART_CR1_OVER8;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_lpuart
+ *
+ * Description:
+ *   Set the serial line baud rate (LPUART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static void stm32wb_serial_setbaud_lpuart(struct stm32wb_serial_s *priv)
+{
+  uint32_t brr;
+
+  /* The equation is:
+   *
+   *   baud = 256 * fCK / brr
+   *   brr  = 256 * fCK / baud
+   *
+   * It is forbidden to write values lower than LPUART_BRR_MIN in
+   * the LPUART_BRR register. fCK must range from 3 x baud rate to
+   * 4096 x baud rate.
+   */
+
+  brr = (((uint64_t)priv->apbclock << 8) + (priv->baud >> 1)) / priv->baud;
+  brr &= LPUART_BRR_MASK;
+
+  if (brr < LPUART_BRR_MIN)
+    {
+      brr = LPUART_BRR_MIN;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setformat
+ *
+ * Description:
+ *   Set the serial line format and speed.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Set baud rate */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  if (priv->usartbase == STM32WB_LPUART1_BASE)
+    {
+      stm32wb_serial_setbaud_lpuart(priv);
+    }
+  else
+#endif
+    {
+      stm32wb_serial_setbaud_usart(priv);
+    }
+
+  /* Configure parity mode */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0 | USART_CR1_M1);
+
+  if (priv->parity == 1)       /* Odd parity */
+    {
+      regval |= (USART_CR1_PCE | USART_CR1_PS);
+    }
+  else if (priv->parity == 2)  /* Even parity */
+    {
+      regval |= USART_CR1_PCE;
+    }
+
+  /* Configure word length (parity uses one of configured bits)
+   *
+   * Default: 1 start, 8 data (no parity), n stop, OR
+   *          1 start, 7 data + parity, n stop
+   */
+
+  if (priv->bits == 9 || (priv->bits == 8 && priv->parity != 0))
+    {
+      /* Select: 1 start, 8 data + parity, n stop, OR
+       *         1 start, 9 data (no parity), n stop.
+       */
+
+      regval |= USART_CR1_M0;
+    }
+  else if (priv->bits == 7 && priv->parity == 0)
+    {
+      /* Select: 1 start, 7 data (no parity), n stop, OR
+       */
+
+      regval |= USART_CR1_M1;
+    }
+
+  /* Else Select: 1 start, 7 data + parity, n stop, OR
+   *              1 start, 8 data (no parity), n stop.
+   */
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure STOP bits */
+
+  regval = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK);
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure hardware flow control */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSE | USART_CR3_RTSE);
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && !defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      regval |= USART_CR3_RTSE;
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->oflow && (priv->cts_gpio != 0))
+    {
+      regval |= USART_CR3_CTSE;
+    }
+#endif
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+}
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+/****************************************************************************
+ * Name: stm32wb_serial_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripheral.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#ifdef SERIAL_HAVE_RXDMA
+  bool dmarestored = false;
+#endif
+
+  if (priv->suspended == suspend)
+    {
+      return;
+    }
+
+  priv->suspended = suspend;
+
+  if (suspend)
+    {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Force RTS high to prevent further Rx. */
+
+          stm32wb_configgpio((priv->rts_gpio & ~GPIO_MODE_MASK)
+                             | (GPIO_OUTPUT | GPIO_OUTPUT_SET));
+        }
+#endif
+
+      /* Disable interrupts to prevent Tx. */
+
+      stm32wb_serial_disableusartint(priv, &priv->suspended_ie);
+
+      /* Wait last Tx to complete. */
+
+      while ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+              USART_ISR_TC) == 0);
+
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && !priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow && priv->rxdmanext == RXDMA_BUFFER_SIZE)
+            {
+              /* Rx DMA in non-circular iflow mode and already stopped
+               * at end of DMA buffer. No need to suspend.
+               */
+            }
+          else
+#endif
+            {
+              /* Suspend Rx DMA. */
+
+              stm32wb_dmastop(priv->rxdma);
+              priv->rxdmasusp = true;
+            }
+        }
+#endif
+    }
+  else
+    {
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              stm32wb_serial_dmaiflowrestart(priv);
+            }
+          else
+#endif
+            {
+              /* This USART does not have HW flow-control. Unconditionally
+               * re-enable DMA (might loss unprocessed bytes received
+               * to DMA buffer before suspending).
+               */
+
+              stm32wb_serial_dmareenable(priv);
+              priv->rxdmasusp = false;
+            }
+
+          dmarestored = true;
+        }
+#endif
+
+      /* Re-enable interrupts to resume Tx. */
+
+      stm32wb_serial_restoreusartint(priv, priv->suspended_ie);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Restore peripheral RTS control. */
+
+          stm32wb_configgpio(priv->rts_gpio);
+        }
+#endif
+    }
+
+#ifdef SERIAL_HAVE_RXDMA
+  if (dmarestored)
+    {
+      irqstate_t flags;
+
+      flags = enter_critical_section();
+
+      /* Perform initial Rx DMA buffer fetch to wake-up serial device
+       * activity.
+       */
+
+      if (priv->rxdma != NULL)
+        {
+          stm32wb_serial_dmarxcallback(priv->rxdma, 0, priv);
+        }
+
+      leave_critical_section(flags);
+    }
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_pm_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripherals for/from deep-sleep/stop modes.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_pm_setsuspend(bool suspend)
+{
+  int n;
+
+  /* Already in desired state? */
+
+  if (suspend == g_serialpm.serial_suspended)
+    return;
+
+  g_serialpm.serial_suspended = suspend;
+
+  for (n = 0; n < STM32WB_NLPUART + STM32WB_NUSART; n++)
+    {
+      struct stm32wb_serial_s *priv = g_uart_devs[n];
+
+      if (!priv || !priv->initialized)
+        {
+          continue;
+        }
+
+      stm32wb_serial_setsuspend(&priv->dev, suspend);
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setapbclock
+ *
+ * Description:
+ *   Enable or disable APB clock for the USART peripheral
+ *
+ * Input Parameters:
+ *   dev - A reference to the UART driver state structure
+ *   on  - Enable clock if 'on' is 'true' and disable if 'false'
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_setapbclock(struct uart_dev_s *dev, bool on)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t rcc_en;
+  uint32_t regaddr;
+
+  /* Determine which USART to configure */
+
+  switch (priv->usartbase)
+    {
+    default:
+      return;
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+    case STM32WB_LPUART1_BASE:
+      rcc_en = RCC_APB1ENR2_LPUART1EN;
+      regaddr = STM32WB_RCC_APB1ENR2;
+      break;
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+    case STM32WB_USART1_BASE:
+      rcc_en = RCC_APB2ENR_USART1EN;
+      regaddr = STM32WB_RCC_APB2ENR;
+      break;
+#endif
+    }
+
+  /* Enable/disable APB 1/2 clock for USART */
+
+  if (on)
+    {
+      modifyreg32(regaddr, 0, rcc_en);
+    }
+  else
+    {
+      modifyreg32(regaddr, rcc_en, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setup
+ *
+ * Description:
+ *   Configure the USART baud, bits, parity, etc. This method is called the
+ *   first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_setup(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+  uint32_t regval;
+
+  /* Note: The logic here depends on the fact that that the USART module
+   * was enabled in stm32wb_lowsetup().
+   */
+
+  /* Enable USART APB1/2 clock */
+
+  stm32wb_serial_setapbclock(dev, true);
+
+  /* Configure pins for USART use */
+
+  stm32wb_configgpio(priv->tx_gpio);
+  stm32wb_configgpio(priv->rx_gpio);
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->cts_gpio != 0)
+    {
+      stm32wb_configgpio(priv->cts_gpio);
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->rts_gpio != 0)
+    {
+      uint32_t config = priv->rts_gpio;
+
+#ifdef CONFIG_STM32WB_FLOWCONTROL_BROKEN
+      /* Instead of letting hw manage this pin, we will bitbang */
+
+      config = (config & ~GPIO_MODE_MASK) | GPIO_OUTPUT;
+#endif
+      stm32wb_configgpio(config);
+    }
+#endif
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_configgpio(priv->rs485_dir_gpio);
+      stm32wb_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity);
+    }
+#endif
+
+  /* Configure CR2 */
+
+  /* Clear STOP, CLKEN, CPOL, CPHA, LBCL, and interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK | USART_CR2_CLKEN | USART_CR2_CPOL |
+              USART_CR2_CPHA | USART_CR2_LBCL | USART_CR2_LBDIE);
+
+  /* Configure STOP bits */
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure CR1 */
+
+  /* Clear TE, REm and all interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_TE | USART_CR1_RE | USART_CR1_ALLINTS);
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure CR3 */
+
+  /* Clear CTSE, RTSE, and all interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSIE | USART_CR3_CTSE | USART_CR3_RTSE |
+              USART_CR3_EIE);
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+
+  /* Configure the USART line format and speed. */
+
+  stm32wb_serial_setformat(dev);
+
+  /* Enable Rx, Tx, and the USART */
+
+  regval      = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval     |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+  /* Set up the cached interrupt enables value */
+
+  priv->ie    = 0;
+
+  /* Mark device as initialized. */
+
+  priv->initialized = true;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmasetup
+ *
+ * Description:
+ *   Configure the USART baud, bits, parity, etc. This method is called the
+ *   first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmasetup(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int result;
+  uint32_t regval;
+
+  /* Do the basic UART setup first, unless we are the console */
+
+  if (!dev->isconsole)
+    {
+      result = stm32wb_serial_setup(dev);
+      if (result != OK)
+        {
+          return result;
+        }
+    }
+
+  /* Acquire the DMA channel.  This should always succeed. */
+
+  priv->rxdma = stm32wb_dmachannel(priv->rxdma_channel);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Configure for non-circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                     priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                     (uint32_t)priv->rxfifo,
+                     RXDMA_BUFFER_SIZE,
+                     SERIAL_DMA_IFLOW_CONTROL_WORD);
+    }
+  else
+#endif
+    {
+      /* Configure for circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                     priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                     (uint32_t)priv->rxfifo,
+                     RXDMA_BUFFER_SIZE,
+                     SERIAL_DMA_CONTROL_WORD);
+    }
+
+  /* Reset our DMA shadow pointer to match the address just
+   * programmed above.
+   */
+
+  priv->rxdmanext = 0;
+
+  /* Enable receive DMA for the UART */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval |= USART_CR3_DMAR;
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Start the DMA channel, and arrange for callbacks at the full point
+       * in the FIFO. After buffer gets full, hardware flow-control kicks
+       * in and DMA transfer is stopped.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, false);
+    }
+  else
+#endif
+    {
+      /* Start the DMA channel, and arrange for callbacks at the half and
+       * full points in the FIFO.  This ensures that we have half a FIFO
+       * worth of time to claim bytes before they are overwritten.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, true);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_shutdown
+ *
+ * Description:
+ *   Disable the USART.  This method is called when the serial
+ *   port is closed
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Mark device as uninitialized. */
+
+  priv->initialized = false;
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_disableusartint(priv, NULL);
+
+  /* Disable USART APB1/2 clock */
+
+  stm32wb_serial_setapbclock(dev, false);
+
+  /* Disable Rx, Tx, and the UART */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Release pins. "If the serial-attached device is powered down, the TX
+   * pin causes back-powering, potentially confusing the device to the point
+   * of complete lock-up."
+   *
+   * REVISIT:  Is unconfiguring the pins appropriate for all device?  If not,
+   * then this may need to be a configuration option.
+   */
+
+  stm32wb_unconfiggpio(priv->tx_gpio);
+  stm32wb_unconfiggpio(priv->rx_gpio);
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->cts_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->cts_gpio);
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->rts_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->rts_gpio);
+    }
+#endif
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->rs485_dir_gpio);
+    }
+#endif
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmashutdown
+ *
+ * Description:
+ *   Disable the USART.  This method is called when the serial
+ *   port is closed
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  /* Perform the normal UART shutdown */
+
+  stm32wb_serial_shutdown(dev);
+
+  /* Stop the DMA channel */
+
+  stm32wb_dmastop(priv->rxdma);
+
+  /* Release the DMA channel */
+
+  stm32wb_dmafree(priv->rxdma);
+  priv->rxdma = NULL;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_attach
+ *
+ * Description:
+ *   Configure the USART to operation in interrupt driven mode.  This method
+ *   is called when the serial port is opened.  Normally, this is just after
+ *   the the setup() method is called, however, the serial console may
+ *   operate in a non-interrupt driven mode during the boot phase.
+ *
+ *   RX and TX interrupts are not enabled when by the attach method (unless
+ *   the hardware supports multiple levels of interrupt enabling). The RX
+ *   and TX interrupts are not enabled until the txint() and rxint()
+ *   methods are called.
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_attach(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int ret;
+
+  /* Attach and enable the IRQ */
+
+  ret = irq_attach(priv->irq, up_interrupt, priv);
+  if (ret == OK)
+    {
+      /* Enable the interrupt (RX and TX interrupts are still disabled
+       * in the USART
+       */
+
+      up_enable_irq(priv->irq);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_detach
+ *
+ * Description:
+ *   Detach USART interrupts.  This method is called when the serial port is
+ *   closed normally just before the shutdown method is called.
+ *   The exception is the serial console which is never shutdown.
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_detach(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  up_disable_irq(priv->irq);
+  irq_detach(priv->irq);
+}
+
+/****************************************************************************
+ * Name: up_interrupt
+ *
+ * Description:
+ *   This is the USART interrupt handler.  It will be invoked when an
+ *   interrupt received on the 'irq'  It should call uart_transmitchars or
+ *   uart_receivechar to perform the appropriate data transfers.  The
+ *   interrupt handling logic must be able to map the 'irq' number into the
+ *   appropriate uart_dev_s structure in order to call these functions.
+ *
+ ****************************************************************************/
+
+static int up_interrupt(int irq, void *context, void *arg)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)arg;
+  int  passes;
+  bool handled;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Report serial activity to the power management logic */
+
+#if defined(CONFIG_PM) && CONFIG_STM32WB_PM_SERIAL_ACTIVITY > 0
+  pm_activity(PM_IDLE_DOMAIN, CONFIG_STM32WB_PM_SERIAL_ACTIVITY);
+#endif
+
+  /* Loop until there are no characters to be transferred or,
+   * until we have been looping for a long time.
+   */
+
+  handled = true;
+  for (passes = 0; passes < 256 && handled; passes++)
+    {
+      handled = false;
+
+      /* Get the masked USART status word. */
+
+      priv->sr = stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET);
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning             Usage
+       * ------------------ --------------- ------------------- ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected           (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete           (used only
+       *                                                         for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag         (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag           (not used)
+       *
+       * NOTE: Some of these status bits must be cleared by explicitly
+       * writing one to the ICR register: USART_ICR_CTSCF, USART_ICR_LBDCF.
+       * Note of those are currently being used.
+       */
+
+#ifdef HAVE_RS485
+      /* Transmission of whole buffer is over - TC is set, TXEIE is cleared.
+       * Note - this should be first, to have the most recent TC bit value
+       * from SR register - sending data affects TC, but without refresh we
+       * will not know that...
+       */
+
+      if ((priv->sr & USART_ISR_TC) != 0 &&
+          (priv->ie & USART_CR1_TCIE) != 0 &&
+          (priv->ie & USART_CR1_TXEIE) == 0)
+        {
+          stm32wb_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity);
+          stm32wb_serial_restoreusartint(priv, priv->ie & ~USART_CR1_TCIE);
+        }
+#endif
+
+      /* Handle incoming, receive bytes. */
+
+      if ((priv->sr & USART_ISR_RXNE) != 0 &&
+          (priv->ie & USART_CR1_RXNEIE) != 0)
+        {
+          /* Received data ready... process incoming bytes.  NOTE the check
+           * for RXNEIE:  We cannot call uart_recvchards of RX interrupts
+           * are disabled.
+           */
+
+          uart_recvchars(&priv->dev);
+          handled = true;
+        }
+
+      /* We may still have to read from the DR register to clear any pending
+       * error conditions.
+       */
+
+      else if ((priv->sr &
+                (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE)) != 0)
+        {
+          /* These errors are cleared by writing the corresponding bit to the
+           * interrupt clear register (ICR).
+           */
+
+          stm32wb_serial_putreg(priv, STM32WB_USART_ICR_OFFSET,
+                               (USART_ICR_NCF | USART_ICR_ORECF |
+                                USART_ICR_FECF));
+        }
+
+      /* Handle outgoing, transmit bytes */
+
+      if ((priv->sr & USART_ISR_TXE) != 0 &&
+          (priv->ie & USART_CR1_TXEIE) != 0)
+        {
+          /* Transmit data register empty ... process outgoing bytes */
+
+          uart_xmitchars(&priv->dev);
+          handled = true;
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_ioctl
+ *
+ * Description:
+ *   All ioctl calls will be routed through this method
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                unsigned long arg)
+{
+#if defined(CONFIG_SERIAL_TERMIOS) || defined(CONFIG_SERIAL_TIOCSERGSTRUCT)
+  struct inode      *inode = filep->f_inode;
+  struct uart_dev_s *dev   = inode->i_private;
+#endif
+#if defined(CONFIG_SERIAL_TERMIOS)
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#endif
+  int                ret    = OK;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT
+    case TIOCSERGSTRUCT:
+      {
+        struct stm32wb_serial_s *user = (struct stm32wb_serial_s *)arg;
+        if (!user)
+          {
+            ret = -EINVAL;
+          }
+        else
+          {
+            memcpy(user, dev, sizeof(struct stm32wb_serial_s));
+          }
+      }
+      break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_SINGLEWIRE
+    case TIOCSSINGLEWIRE:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, HDSEL can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Change the TX port to be open-drain/push-pull and enable/disable
+         * half-duplex mode.
+         */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+        if ((arg & SER_SINGLEWIRE_ENABLED) != 0)
+          {
+            uint32_t gpio_val = (arg & SER_SINGLEWIRE_PUSHPULL) ==
+                                 SER_SINGLEWIRE_PUSHPULL ?
+                                 GPIO_PUSHPULL : GPIO_OPENDRAIN;
+            gpio_val |=
+              (arg & SER_SINGLEWIRE_PULL_MASK) == SER_SINGLEWIRE_PULLUP ?
+                                                  GPIO_PULLUP : GPIO_FLOAT;
+
+            gpio_val |=
+              (arg & SER_SINGLEWIRE_PULL_MASK) == SER_SINGLEWIRE_PULLDOWN ?
+                                                  GPIO_PULLDOWN : GPIO_FLOAT;
+
+            stm32wb_configgpio((priv->tx_gpio & ~(GPIO_PUPD_MASK |
+                                                  GPIO_OPENDRAIN)) |
+                                                  gpio_val);
+
+            cr |= USART_CR3_HDSEL;
+          }
+        else
+          {
+            stm32wb_configgpio((priv->tx_gpio & ~(GPIO_PUPD_MASK |
+                                                  GPIO_OPENDRAIN)) |
+                                                  GPIO_PUSHPULL);
+
+            cr &= ~USART_CR3_HDSEL;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_INVERT
+    case TIOCSINVERT:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, {R,T}XINV can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Enable/disable signal inversion. */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+
+        if (arg & SER_INVERT_ENABLED_RX)
+          {
+            cr |= USART_CR2_RXINV;
+          }
+        else
+          {
+            cr &= ~USART_CR2_RXINV;
+          }
+
+        if (arg & SER_INVERT_ENABLED_TX)
+          {
+            cr |= USART_CR2_TXINV;
+          }
+        else
+          {
+            cr &= ~USART_CR2_TXINV;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_SWAP
+    case TIOCSSWAP:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, SWAP can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Enable/disable Swap mode. */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+
+        if (arg == SER_SWAP_ENABLED)
+          {
+            cr |= USART_CR2_SWAP;
+          }
+        else
+          {
+            cr &= ~USART_CR2_SWAP;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_SERIAL_TERMIOS
+    case TCGETS:
+      {
+        struct termios *termiosp = (struct termios *)arg;
+
+        if (!termiosp)
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        /* Note that since we only support 8/9 bit modes and
+         * there is no way to report 9-bit mode, we always claim 8.
+         */
+
+        termiosp->c_cflag =
+          ((priv->parity != 0) ? PARENB : 0) |
+          ((priv->parity == 1) ? PARODD : 0) |
+          ((priv->stopbits2) ? CSTOPB : 0) |
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+          ((priv->oflow) ? CCTS_OFLOW : 0) |
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          ((priv->iflow) ? CRTS_IFLOW : 0) |
+#endif
+          CS8;
+
+        cfsetispeed(termiosp, priv->baud);
+
+        /* TODO: CCTS_IFLOW, CCTS_OFLOW */
+      }
+      break;
+
+    case TCSETS:
+      {
+        struct termios *termiosp = (struct termios *)arg;
+
+        if (!termiosp)
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        /* Perform some sanity checks before accepting any changes */
+
+        if (((termiosp->c_cflag & CSIZE) != CS8)
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+            || ((termiosp->c_cflag & CCTS_OFLOW) && (priv->cts_gpio == 0))
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+            || ((termiosp->c_cflag & CRTS_IFLOW) && (priv->rts_gpio == 0))
+#endif
+           )
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        if (termiosp->c_cflag & PARENB)
+          {
+            priv->parity = (termiosp->c_cflag & PARODD) ? 1 : 2;
+          }
+        else
+          {
+            priv->parity = 0;
+          }
+
+        priv->stopbits2 = (termiosp->c_cflag & CSTOPB) != 0;
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+        priv->oflow = (termiosp->c_cflag & CCTS_OFLOW) != 0;
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+        priv->iflow = (termiosp->c_cflag & CRTS_IFLOW) != 0;
+#endif
+
+        /* Note that since there is no way to request 9-bit mode
+         * and no way to support 5/6/7-bit modes, we ignore them
+         * all here.
+         */
+
+        /* Note that only cfgetispeed is used because we have knowledge
+         * that only one speed is supported.
+         */
+
+        priv->baud = cfgetispeed(termiosp);
+
+        /* Effect the changes immediately - note that we do not implement
+         * TCSADRAIN / TCSAFLUSH
+         */
+
+        stm32wb_serial_setformat(dev);
+      }
+      break;
+#endif /* CONFIG_SERIAL_TERMIOS */
+
+#ifdef CONFIG_STM32WB_USART_BREAKS
+#  ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+    case TIOCSBRK:  /* BSD compatibility: Turn break on, unconditionally */
+      {
+        irqstate_t flags;
+        uint32_t tx_break;
+
+        flags = enter_critical_section();
+
+        /* Disable any further tx activity */
+
+        priv->ie |= USART_CR1_IE_BREAK_INPROGRESS;
+
+        stm32wb_serial_txint(dev, false);
+
+        /* Configure TX as a GPIO output pin and Send a break signal */
+
+        tx_break = GPIO_OUTPUT |
+                   (~(GPIO_MODE_MASK | GPIO_OUTPUT_SET) & priv->tx_gpio);
+        stm32wb_configgpio(tx_break);
+
+        leave_critical_section(flags);
+      }
+      break;
+
+    case TIOCCBRK:  /* BSD compatibility: Turn break off, unconditionally */
+      {
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Configure TX back to U(S)ART */
+
+        stm32wb_configgpio(priv->tx_gpio);
+
+        priv->ie &= ~USART_CR1_IE_BREAK_INPROGRESS;
+
+        /* Enable further tx activity */
+
+        stm32wb_serial_txint(dev, true);
+
+        leave_critical_section(flags);
+      }
+      break;
+#  else
+    case TIOCSBRK:  /* No BSD compatibility: Turn break on for M bit times */
+      {
+        uint32_t cr1;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+        cr1   = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET,
+                             cr1 | USART_CR1_SBK);
+        leave_critical_section(flags);
+      }
+      break;
+
+    case TIOCCBRK:  /* No BSD compatibility: May turn off break too soon */
+      {
+        uint32_t cr1;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+        cr1   = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET,
+                             cr1 & ~USART_CR1_SBK);
+        leave_critical_section(flags);
+      }
+      break;
+#  endif
+#endif
+
+    default:
+      ret = -ENOTTY;
+      break;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_receive
+ *
+ * Description:
+ *   Called (usually) from the interrupt level to receive one
+ *   character from the USART.  Error bits associated with the
+ *   receipt are provided in the return 'status'.
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int stm32wb_serial_receive(struct uart_dev_s *dev,
+                                  unsigned int *status)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t rdr;
+
+  /* Get the Rx byte */
+
+  rdr      = stm32wb_serial_getreg(priv, STM32WB_USART_RDR_OFFSET);
+
+  /* Get the Rx byte plux error information.  Return those in status */
+
+  *status  = priv->sr << 16 | rdr;
+  priv->sr = 0;
+
+  /* Then return the actual received byte */
+
+  return rdr & 0xff;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxint
+ *
+ * Description:
+ *   Call to enable or disable RX interrupts
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  irqstate_t flags;
+  uint16_t ie;
+
+  /* USART receive interrupts:
+   *
+   * Enable             Status          Meaning                Usage
+   * ------------------ --------------- ------------------     ----------
+   * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+   *                                     Detected              (not used)
+   * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+   *                                     Ready to be Read
+   * "              "   USART_ISR_ORE    Overrun Error
+   *                                     Detected
+   * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+   *
+   * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag            (not used)
+   * USART_CR3_EIE      USART_ISR_FE     Framing Error
+   * "           "      USART_ISR_NE     Noise Flag
+   * "           "      USART_ISR_ORE    Overrun Error
+   *                                     Detected
+   */
+
+  flags = enter_critical_section();
+  ie = priv->ie;
+  if (enable)
+    {
+      /* Receive an interrupt when their is anything in the Rx data register
+       * (or an Rx timeout occurs).
+       */
+
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+#ifdef CONFIG_USART_ERRINTS
+      ie |= (USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR3_EIE);
+#else
+      ie |= USART_CR1_RXNEIE;
+#endif
+#endif
+    }
+  else
+    {
+      ie &= ~(USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR3_EIE);
+    }
+
+  /* Then set the new interrupt state */
+
+  stm32wb_serial_restoreusartint(priv, ie);
+  leave_critical_section(flags);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxavailable
+ *
+ * Description:
+ *   Return true if the receive register is not empty
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  return ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+           USART_ISR_RXNE) != 0);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxflowcontrol
+ *
+ * Description:
+ *   Called when Rx buffer is full (or exceeds configured watermark levels
+ *   if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is defined).
+ *   Return true if UART activated RX flow control to block more incoming
+ *   data
+ *
+ * Input Parameters:
+ *   dev       - UART device instance
+ *   nbuffered - the number of characters currently buffered
+ *               (if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is
+ *               not defined the value will be 0 for an empty buffer or the
+ *               defined buffer size for a full buffer)
+ *   upper     - true indicates the upper watermark was crossed where
+ *               false indicates the lower watermark has been crossed
+ *
+ * Returned Value:
+ *   true if RX flow control activated.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS) && \
+    defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      /* Assert/de-assert nRTS set it high resume/stop sending */
+
+      stm32wb_gpiowrite(priv->rts_gpio, upper);
+
+      if (upper)
+        {
+          /* With heavy Rx traffic, RXNE might be set and data pending.
+           * Returning 'true' in such case would cause RXNE left unhandled
+           * and causing interrupt storm. Sending end might be also be slow
+           * to react on nRTS, and returning 'true' here would prevent
+           * processing that data.
+           *
+           * Therefore, return 'false' so input data is still being processed
+           * until sending end reacts on nRTS signal and stops sending more.
+           */
+
+          return false;
+        }
+
+      return upper;
+    }
+
+#else
+  if (priv->iflow)
+    {
+      /* Is the RX buffer full? */
+
+      if (upper)
+        {
+          /* Disable Rx interrupt to prevent more data being from
+           * peripheral.  When hardware RTS is enabled, this will
+           * prevent more data from coming in.
+           *
+           * This function is only called when UART recv buffer is full,
+           * that is: "dev->recv.head + 1 == dev->recv.tail".
+           *
+           * Logic in "uart_read" will automatically toggle Rx interrupts
+           * when buffer is read empty and thus we do not have to re-
+           * enable Rx interrupts.
+           */
+
+          uart_disablerxint(dev);
+          return true;
+        }
+
+      /* No.. The RX buffer is empty */
+
+      else
+        {
+          /* We might leave Rx interrupt disabled if full recv buffer was
+           * read empty.  Enable Rx interrupt to make sure that more input is
+           * received.
+           */
+
+          uart_enablerxint(dev);
+        }
+    }
+#endif
+
+  return false;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmareceive
+ *
+ * Description:
+ *   Called (usually) from the interrupt level to receive one
+ *   character from the USART.  Error bits associated with the
+ *   receipt are provided in the return 'status'.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                     unsigned int *status)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int c = 0;
+
+  if (stm32wb_serial_dmanextrx(priv) != priv->rxdmanext)
+    {
+      c = priv->rxfifo[priv->rxdmanext];
+
+      priv->rxdmanext++;
+      if (priv->rxdmanext == RXDMA_BUFFER_SIZE)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              /* RX DMA buffer full. RX paused, RTS line pulled up to prevent
+               * more input data from other end.
+               */
+            }
+          else
+#endif
+            {
+              priv->rxdmanext = 0;
+            }
+        }
+    }
+
+  return c;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmareenable
+ *
+ * Description:
+ *   Call to re-enable RX DMA.
+ *
+ ****************************************************************************/
+
+#if defined(SERIAL_HAVE_RXDMA)
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv)
+{
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Configure for non-circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                       priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                       (uint32_t)priv->rxfifo,
+                       RXDMA_BUFFER_SIZE,
+                       SERIAL_DMA_IFLOW_CONTROL_WORD);
+    }
+  else
+#endif
+    {
+      /* Configure for circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                       priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                       (uint32_t)priv->rxfifo,
+                       RXDMA_BUFFER_SIZE,
+                       SERIAL_DMA_CONTROL_WORD);
+    }
+
+  /* Reset our DMA shadow pointer to match the address just
+   * programmed above.
+   */
+
+  priv->rxdmanext = 0;
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Start the DMA channel, and arrange for callbacks at the full point
+       * in the FIFO. After buffer gets full, hardware flow-control kicks
+       * in and DMA transfer is stopped.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, false);
+    }
+  else
+#endif
+    {
+      /* Start the DMA channel, and arrange for callbacks at the half and
+       * full points in the FIFO.  This ensures that we have half a FIFO
+       * worth of time to claim bytes before they are overwritten.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, true);
+    }
+
+#ifdef CONFIG_PM
+  /* Clear DMA suspended flag. */
+
+  priv->rxdmasusp = false;
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmaiflowrestart
+ *
+ * Description:
+ *   Call to restart RX DMA for input flow-controlled USART
+ *
+ ****************************************************************************/
+
+#if defined(SERIAL_HAVE_RXDMA) && defined(CONFIG_SERIAL_IFLOWCONTROL)
+static bool stm32wb_serial_dmaiflowrestart(struct stm32wb_serial_s *priv)
+{
+  if (!priv->rxenable)
+    {
+      /* Rx not enabled by upper layer. */
+
+      return false;
+    }
+
+  if (priv->rxdmanext != RXDMA_BUFFER_SIZE)
+    {
+#ifdef CONFIG_PM
+      if (priv->rxdmasusp)
+        {
+          /* Rx DMA in suspended state. */
+
+          if (stm32wb_serial_dmarxavailable(&priv->dev))
+            {
+              /* DMA buffer has unprocessed data, do not re-enable yet. */
+
+              return false;
+            }
+        }
+      else
+#endif
+        {
+          return false;
+        }
+    }
+
+  /* DMA is stopped or suspended and DMA buffer does not have pending data,
+   * re-enabling without data loss is now safe.
+   */
+
+  stm32wb_serial_dmareenable(priv);
+
+  return true;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmarxint
+ *
+ * Description:
+ *   Call to enable or disable RX interrupts
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void stm32wb_serial_dmarxint(struct uart_dev_s *dev, bool enable)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  /* En/disable DMA reception.
+   *
+   * Note that it is not safe to check for available bytes and immediately
+   * pass them to uart_recvchars as that could potentially recurse back
+   * to us again.  Instead, bytes must wait until the next up_dma_poll or
+   * DMA event.
+   */
+
+  priv->rxenable = enable;
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Re-enable RX DMA. */
+
+      stm32wb_serial_dmaiflowrestart(priv);
+    }
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmarxavailable
+ *
+ * Description:
+ *   Return true if the receive register is not empty
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static bool stm32wb_serial_dmarxavailable(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  /* Compare our receive pointer to the current DMA pointer, if they
+   * do not match, then there are bytes to be received.
+   */
+
+  return (stm32wb_serial_dmanextrx(priv) != priv->rxdmanext);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_send
+ *
+ * Description:
+ *   This method will send one byte on the USART
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_send(struct uart_dev_s *dev, int ch)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_gpiowrite(priv->rs485_dir_gpio, priv->rs485_dir_polarity);
+    }
+#endif
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_TDR_OFFSET, (uint32_t)ch);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_txint
+ *
+ * Description:
+ *   Call to enable or disable TX interrupts
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_txint(struct uart_dev_s *dev, bool enable)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  irqstate_t flags;
+
+  /* USART transmit interrupts:
+   *
+   * Enable             Status          Meaning            Usage
+   * ------------------ --------------- ----------------   ----------
+   * USART_CR1_TCIE     USART_ISR_TC     Transmission
+   *                                     Complete          (used only
+   *                                                       for RS-485)
+   * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+   *                                     Register Empty
+   * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag          (not used)
+   */
+
+  flags = enter_critical_section();
+  if (enable)
+    {
+      /* Set to receive an interrupt when the TX data register is empty */
+
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+      uint16_t ie = priv->ie | USART_CR1_TXEIE;
+
+      /* If RS-485 is supported on this U[S]ART, then also enable the
+       * transmission complete interrupt.
+       */
+
+#  ifdef HAVE_RS485
+      if (priv->rs485_dir_gpio != 0)
+        {
+          ie |= USART_CR1_TCIE;
+        }
+#  endif
+
+#  ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+      if (priv->ie & USART_CR1_IE_BREAK_INPROGRESS)
+        {
+          leave_critical_section(flags);
+          return;
+        }
+#  endif
+
+      stm32wb_serial_restoreusartint(priv, ie);
+
+      /* Fake a TX interrupt here by just calling uart_xmitchars() with
+       * interrupts disabled (note this may recurse).
+       */
+
+      uart_xmitchars(dev);
+#endif
+    }
+  else
+    {
+      /* Disable the TX interrupt */
+
+      stm32wb_serial_restoreusartint(priv, priv->ie & ~USART_CR1_TXEIE);
+    }
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_txready
+ *
+ * Description:
+ *   Return true if the transmit data register is empty
+ *
+ ****************************************************************************/
+
+static bool stm32wb_serial_txready(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  return ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+           USART_ISR_TXE) != 0);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmarxcallback
+ *
+ * Description:
+ *   This function checks the current DMA state and calls the generic
+ *   serial stack when bytes appear to be available.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void stm32wb_serial_dmarxcallback(DMA_HANDLE handle, uint8_t status,
+                                         void *arg)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)arg;
+
+  if (priv->rxenable && stm32wb_serial_dmarxavailable(&priv->dev))
+    {
+      uart_recvchars(&priv->dev);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Re-enable RX DMA. */
+
+          stm32wb_serial_dmaiflowrestart(priv);
+        }
+#endif
+    }
+
+  /* Get the masked USART status word to check and clear error flags.
+   *
+   * When wake-up from low power mode was not fast enough, UART is resumed
+   * too late and sometimes exactly when character was coming over UART,
+   * resulting to frame error.
+
+   * If error flag is not cleared, Rx DMA will be stuck. Clearing errors
+   * will release Rx DMA.
+   */
+
+  priv->sr = stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET);
+
+  if ((priv->sr & (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE)) != 0)
+    {
+      stm32wb_serial_putreg(priv, STM32WB_USART_ICR_OFFSET,
+                           (USART_ICR_NCF | USART_ICR_ORECF |
+                            USART_ICR_FECF));
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_pmnotify
+ *
+ * Description:
+ *   Notify the driver of new power state. This callback is  called after
+ *   all drivers have had the opportunity to prepare for the new power state.
+ *
+ * Input Parameters:
+ *
+ *    cb - Returned to the driver. The driver version of the callback
+ *         structure may include additional, driver-specific state data at
+ *         the end of the structure.
+ *
+ *    pmstate - Identifies the new PM state
+ *
+ * Returned Value:
+ *   None - The driver already agreed to transition to the low power
+ *   consumption state when when it returned OK to the prepare() call.
+ *
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_pmnotify(struct pm_callback_s *cb, int domain,
+                                    enum pm_state_e pmstate)
+{
+  switch (pmstate)
+    {
+      case PM_NORMAL:
+        {
+          stm32wb_serial_pm_setsuspend(false);
+        }
+        break;
+
+      case PM_IDLE:
+        {
+          stm32wb_serial_pm_setsuspend(false);
+        }
+        break;
+
+      case PM_STANDBY:
+        {
+          /* TODO: Alternative configuration and logic for enabling serial in
+           *       Stop 1 mode with HSI16 missing. Current logic allows
+           *       suspending serial peripherals for Stop 0/1/2 when serial
+           *       Rx/Tx buffers are empty (checked in pmprepare).
+           */
+
+          stm32wb_serial_pm_setsuspend(true);
+        }
+        break;
+
+      case PM_SLEEP:
+        {
+          stm32wb_serial_pm_setsuspend(true);
+        }
+        break;
+
+      default:
+
+        /* Should not get here */

Review Comment:
   `DEBUGASSERT(0);`?



##########
arch/arm/src/stm32wb/stm32wb_serial.c:
##########
@@ -0,0 +1,2896 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_serial.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/serial/serial.h>
+#include <nuttx/power/pm.h>
+#include <arch/board/board.h>
+
+#ifdef CONFIG_SERIAL_TERMIOS
+#  include <termios.h>
+#endif
+
+#include "arm_internal.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_uart.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Some sanity checks *******************************************************/
+
+/* DMA configuration */
+
+#ifdef SERIAL_HAVE_RXDMA
+
+/* Currently RS-485 support cannot be enabled when RXDMA is in use due to
+ * lack of testing - RS-485 support was developed on STM32F1x
+ */
+
+#  if (defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_RS485))
+#    error "RXDMA and RS-485 cannot be enabled at the same time for the same U[S]ART"
+#  endif
+
+/* For the stm32wb, there are alternate DMA channels for USART1.
+ * Logic in the board.h file make the DMA channel selection by defining
+ * the following in the board.h file.
+ */
+
+#  if defined(CONFIG_USART1_RXDMA) && !defined(DMAMAP_USART1_RX)
+#    error "USART1 DMA channel not defined (DMAMAP_USART1_RX)"
+#  endif
+
+/* The DMA buffer size when using RX DMA to emulate a FIFO.
+ *
+ * When streaming data, the generic serial layer will be called
+ * every time the FIFO receives half this number of bytes.
+ *
+ * The buffer size should be an even multiple of ARMV7M_DCACHE_LINESIZE.
+ */
+
+#  if !defined(CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE) || \
+      CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE == 0
+#    define RXDMA_BUFFER_SIZE 32
+#  else
+#    define RXDMA_BUFFER_SIZE ((CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE + 31) & ~31)
+#  endif
+
+/* DMA priority */
+
+#  ifndef CONFIG_USART_RXDMAPRIO
+#    define CONFIG_USART_RXDMAPRIO  DMA_CCR_PRIMED
+#  endif
+#  if (CONFIG_USART_RXDMAPRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_USART_RXDMAPRIO"
+#  endif
+
+/* DMA control words */
+
+#  define SERIAL_DMA_CONTROL_WORD      \
+              (DMA_CCR_CIRC          | \
+               DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  ifdef CONFIG_SERIAL_IFLOWCONTROL
+#    define SERIAL_DMA_IFLOW_CONTROL_WORD \
+              (DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  endif
+
+#endif
+
+/* Power management definitions */
+
+#if defined(CONFIG_PM) && !defined(CONFIG_STM32WB_PM_SERIAL_ACTIVITY)
+#  define CONFIG_STM32WB_PM_SERIAL_ACTIVITY  10
+#endif
+#if defined(CONFIG_PM)
+#  define PM_IDLE_DOMAIN             0 /* Revisit */
+#endif
+
+/* Keep track if a Break was set
+ *
+ * Note:
+ *
+ * 1) This value is set in the priv->ie but never written to the control
+ *    register. It must not collide with USART_CR1_USED_INTS or USART_CR3_EIE
+ * 2) USART_CR3_EIE is also carried in the up_dev_s ie member.
+ *
+ * See stm32wb_serial_restoreusartint where the masking is done.
+ */
+
+#ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+#  define USART_CR1_IE_BREAK_INPROGRESS_SHFTS 15
+#  define USART_CR1_IE_BREAK_INPROGRESS (1 << USART_CR1_IE_BREAK_INPROGRESS_SHFTS)
+#endif
+
+#ifdef USE_SERIALDRIVER
+#ifdef HAVE_UART
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_serial_s
+{
+  struct uart_dev_s dev;       /* Generic UART device */
+  uint16_t          ie;        /* Saved interrupt mask bits value */
+  uint16_t          sr;        /* Saved status bits */
+
+  /* Has been initialized and HW is setup. */
+
+  bool              initialized;
+
+#ifdef CONFIG_PM
+  bool              suspended; /* UART device has been suspended. */
+
+  /* Interrupt mask value stored before suspending for stop mode. */
+
+  uint16_t          suspended_ie;
+#endif
+
+  /* If termios are supported, then the following fields may vary at
+   * runtime.
+   */
+
+#ifdef CONFIG_SERIAL_TERMIOS
+  uint8_t           parity;    /* 0=none, 1=odd, 2=even */
+  uint8_t           bits;      /* Number of bits (7 or 8) */
+  bool              stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  bool              iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  bool              oflow;     /* output flow control (CTS) enabled */
+#endif
+  uint32_t          baud;      /* Configured baud */
+#else
+  const uint8_t     parity;    /* 0=none, 1=odd, 2=even */
+  const uint8_t     bits;      /* Number of bits (7 or 8) */
+  const bool        stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const bool        iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const bool        oflow;     /* output flow control (CTS) enabled */
+#endif
+  const uint32_t    baud;      /* Configured baud */
+#endif
+
+  const uint8_t     irq;       /* IRQ associated with this USART */
+  const uint32_t    apbclock;  /* PCLK 1 or 2 frequency */
+  const uint32_t    usartbase; /* Base address of USART registers */
+  const uint32_t    tx_gpio;   /* U[S]ART TX GPIO pin configuration */
+  const uint32_t    rx_gpio;   /* U[S]ART RX GPIO pin configuration */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const uint32_t    rts_gpio;  /* U[S]ART RTS GPIO pin configuration */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const uint32_t    cts_gpio;  /* U[S]ART CTS GPIO pin configuration */
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+  const unsigned int rxdma_channel; /* DMA channel assigned */
+#endif
+
+  /* RX DMA state */
+
+#ifdef SERIAL_HAVE_RXDMA
+  DMA_HANDLE        rxdma;     /* currently-open receive DMA stream */
+  bool              rxenable;  /* DMA-based reception en/disable */
+#ifdef CONFIG_PM
+  bool              rxdmasusp; /* Rx DMA suspended */
+#endif
+  uint32_t          rxdmanext; /* Next byte in the DMA buffer to be read */
+  char       *const rxfifo;    /* Receive DMA buffer */
+#endif
+
+#ifdef HAVE_RS485
+  const uint32_t    rs485_dir_gpio;     /* U[S]ART RS-485 DIR GPIO pin configuration */
+  const bool        rs485_dir_polarity; /* U[S]ART RS-485 DIR pin state for TX enabled */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev);
+#endif
+static int  stm32wb_serial_setup(struct uart_dev_s *dev);
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_attach(struct uart_dev_s *dev);
+static void stm32wb_serial_detach(struct uart_dev_s *dev);
+static int  up_interrupt(int irq, void *context, void *arg);
+static int  stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                 unsigned long arg);
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int  stm32wb_serial_receive(struct uart_dev_s *dev,
+                                   unsigned int *status);
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev);
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper);
+#endif
+static void stm32wb_serial_send(struct uart_dev_s *dev, int ch);
+static void stm32wb_serial_txint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_txready(struct uart_dev_s *dev);
+
+#ifdef SERIAL_HAVE_RXDMA
+static int  stm32wb_serial_dmasetup(struct uart_dev_s *dev);
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                      unsigned int *status);
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv);
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_dmaiflowrestart(struct stm32wb_serial_s *priv);
+#endif
+static void stm32wb_serial_dmarxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_dmarxavailable(struct uart_dev_s *dev);
+
+static void stm32wb_serial_dmarxcallback(DMA_HANDLE handle, uint8_t status,
+                                         void *arg);
+#endif
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend);
+static void stm32wb_serial_pm_setsuspend(bool suspend);
+static void stm32wb_serial_pmnotify(struct pm_callback_s *cb,
+                                    int domain, enum pm_state_e pmstate);
+static int  stm32wb_serial_pmprepare(struct pm_callback_s *cb,
+                                     int domain, enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static const struct uart_ops_s g_uart_ops =
+{
+  .setup          = stm32wb_serial_setup,
+  .shutdown       = stm32wb_serial_shutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_receive,
+  .rxint          = stm32wb_serial_rxint,
+  .rxavailable    = stm32wb_serial_rxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+static const struct uart_ops_s g_uart_dma_ops =
+{
+  .setup          = stm32wb_serial_dmasetup,
+  .shutdown       = stm32wb_serial_dmashutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_dmareceive,
+  .rxint          = stm32wb_serial_dmarxint,
+  .rxavailable    = stm32wb_serial_dmarxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+/* I/O buffers */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static char g_lpuart1rxbuffer[CONFIG_LPUART1_RXBUFSIZE];
+static char g_lpuart1txbuffer[CONFIG_LPUART1_TXBUFSIZE];
+# ifdef CONFIG_LPUART1_RXDMA
+static char g_lpuart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static char g_usart1rxbuffer[CONFIG_USART1_RXBUFSIZE];
+static char g_usart1txbuffer[CONFIG_USART1_TXBUFSIZE];
+# ifdef CONFIG_USART1_RXDMA
+static char g_usart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+/* This describes the state of the STM32WB LPUART1 port. */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static struct stm32wb_serial_s g_lpuart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 1
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_LPUART1_RXBUFSIZE,
+        .buffer  = g_lpuart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_LPUART1_TXBUFSIZE,
+        .buffer  = g_lpuart1txbuffer,
+      },
+#ifdef CONFIG_LPUART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_lpuart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_LPUART1,
+  .parity        = CONFIG_LPUART1_PARITY,
+  .bits          = CONFIG_LPUART1_BITS,
+  .stopbits2     = CONFIG_LPUART1_2STOP,
+  .baud          = CONFIG_LPUART1_BAUD,
+  .apbclock      = STM32WB_PCLK1_FREQUENCY,
+  .usartbase     = STM32WB_LPUART1_BASE,
+  .tx_gpio       = GPIO_LPUART1_TX,
+  .rx_gpio       = GPIO_LPUART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_LPUART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_LPUART1_RTS,
+#endif
+#ifdef CONFIG_LPUART1_RXDMA
+  .rxdma_channel = DMAMAP_LPUART1_RX,
+  .rxfifo        = g_lpuart1rxfifo,
+#endif
+
+#ifdef CONFIG_LPUART1_RS485
+  .rs485_dir_gpio = GPIO_LPUART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This describes the state of the STM32WB USART1 port. */
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static struct stm32wb_serial_s g_usart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 2
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_USART1_RXBUFSIZE,
+        .buffer  = g_usart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_USART1_TXBUFSIZE,
+        .buffer  = g_usart1txbuffer,
+      },
+#ifdef CONFIG_USART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_usart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_USART1,
+  .parity        = CONFIG_USART1_PARITY,
+  .bits          = CONFIG_USART1_BITS,
+  .stopbits2     = CONFIG_USART1_2STOP,
+  .baud          = CONFIG_USART1_BAUD,
+  .apbclock      = STM32WB_PCLK2_FREQUENCY,
+  .usartbase     = STM32WB_USART1_BASE,
+  .tx_gpio       = GPIO_USART1_TX,
+  .rx_gpio       = GPIO_USART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_USART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_USART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_USART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_USART1_RTS,
+#endif
+#ifdef CONFIG_USART1_RXDMA
+  .rxdma_channel = DMAMAP_USART1_RX,
+  .rxfifo        = g_usart1rxfifo,
+#endif
+
+#ifdef CONFIG_USART1_RS485
+  .rs485_dir_gpio = GPIO_USART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This table lets us iterate over the configured USARTs */
+
+static struct stm32wb_serial_s *
+const g_uart_devs[STM32WB_NLPUART + STM32WB_NUSART] =
+{
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  [0] = &g_lpuart1priv,
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+  [1] = &g_usart1priv,
+#endif
+};
+
+#ifdef CONFIG_PM
+static struct
+{
+  struct pm_callback_s pm_cb;
+  bool serial_suspended;
+} g_serialpm =
+  {
+    .pm_cb.notify  = stm32wb_serial_pmnotify,
+    .pm_cb.prepare = stm32wb_serial_pmprepare,
+    .serial_suspended = false
+  };
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_serial_getreg
+ ****************************************************************************/
+
+static inline
+uint32_t stm32wb_serial_getreg(struct stm32wb_serial_s *priv, int offset)
+{
+  return getreg32(priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_putreg
+ ****************************************************************************/
+
+static inline void stm32wb_serial_putreg(struct stm32wb_serial_s *priv,
+                                         int offset, uint32_t value)
+{
+  putreg32(value, priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setusartint
+ ****************************************************************************/
+
+static inline
+void stm32wb_serial_setusartint(struct stm32wb_serial_s *priv, uint16_t ie)
+{
+  uint32_t cr;
+
+  /* Save the interrupt mask */
+
+  priv->ie = ie;
+
+  /* And restore the interrupt state (see the interrupt
+   * enable/usage table above)
+   */
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  cr &= ~(USART_CR1_USED_INTS);
+  cr |= (ie & (USART_CR1_USED_INTS));
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr);
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  cr &= ~USART_CR3_EIE;
+  cr |= (ie & USART_CR3_EIE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+}
+
+/****************************************************************************
+ * Name: up_restoreusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_restoreusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  stm32wb_serial_setusartint(priv, ie);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_disableusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_disableusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t *ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  if (ie)
+    {
+      uint32_t cr1;
+      uint32_t cr3;
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning            Usage
+       * ------------------ --------------- ------------------ ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected          (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete          (used only
+       *                                                        for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag        (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag          (not used)
+       */
+
+      cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+      cr3 = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+      /* Return the current interrupt mask value for the used interrupts.
+       * Notice that this depends on the fact that none of the used interrupt
+       * enable bits overlap. This logic would fail if we needed the break
+       * interrupt!
+       */
+
+      *ie = (cr1 & (USART_CR1_USED_INTS)) | (cr3 & USART_CR3_EIE);
+    }
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_setusartint(priv, 0);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmanextrx
+ *
+ * Description:
+ *   Returns the index into the RX FIFO where the DMA will place the next
+ *   byte that it receives.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmanextrx(struct stm32wb_serial_s *priv)
+{
+  size_t dmaresidual;
+
+  dmaresidual = stm32wb_dmaresidual(priv->rxdma);
+
+  return (RXDMA_BUFFER_SIZE - (int)dmaresidual);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_usart
+ *
+ * Description:
+ *   Set the serial line baud rate (USART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setbaud_usart(struct stm32wb_serial_s *priv)
+{
+  /* This first implementation is for U[S]ARTs that support oversampling
+   * by 8 in additional to the standard oversampling by 16.
+   */
+
+  uint32_t usartdiv8;
+  uint32_t cr1;
+  uint32_t brr;
+
+  /* In case of oversampling by 8, the equation is:
+   *
+   *   baud      = 2 * fCK / usartdiv8
+   *   usartdiv8 = 2 * fCK / baud
+   */
+
+  usartdiv8 = ((priv->apbclock << 1) + (priv->baud >> 1)) / priv->baud;
+
+  /* Baud rate for standard USART (SPI mode included):
+   *
+   * In case of oversampling by 16, the equation is:
+   *   baud       = fCK / usartdiv16
+   *   usartdiv16 = fCK / baud
+   *              = 2 * usartdiv8
+   */
+
+  /* Use oversamply by 8 only if the divisor is small.  But what is small? */
+
+  cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  brr = stm32wb_serial_getreg(priv, STM32WB_USART_BRR_OFFSET);
+  brr &= ~(USART_BRR_MANT_MASK | USART_BRR_FRAC_MASK);
+
+  if (usartdiv8 > 100)
+    {
+      /* Use usartdiv16 */
+
+      brr  |= (usartdiv8 + 1) >> 1;
+
+      /* Clear oversampling by 8 to enable oversampling by 16 */
+
+      cr1 &= ~USART_CR1_OVER8;
+    }
+  else
+    {
+      DEBUGASSERT(usartdiv8 >= 8);
+
+      /* Perform mysterious operations on bits 0-3 */
+
+      brr  |= ((usartdiv8 & 0xfff0) | ((usartdiv8 & 0x000f) >> 1));
+
+      /* Set oversampling by 8 */
+
+      cr1 |= USART_CR1_OVER8;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_lpuart
+ *
+ * Description:
+ *   Set the serial line baud rate (LPUART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static void stm32wb_serial_setbaud_lpuart(struct stm32wb_serial_s *priv)
+{
+  uint32_t brr;
+
+  /* The equation is:
+   *
+   *   baud = 256 * fCK / brr
+   *   brr  = 256 * fCK / baud
+   *
+   * It is forbidden to write values lower than LPUART_BRR_MIN in
+   * the LPUART_BRR register. fCK must range from 3 x baud rate to
+   * 4096 x baud rate.
+   */
+
+  brr = (((uint64_t)priv->apbclock << 8) + (priv->baud >> 1)) / priv->baud;
+  brr &= LPUART_BRR_MASK;
+
+  if (brr < LPUART_BRR_MIN)
+    {
+      brr = LPUART_BRR_MIN;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setformat
+ *
+ * Description:
+ *   Set the serial line format and speed.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Set baud rate */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  if (priv->usartbase == STM32WB_LPUART1_BASE)
+    {
+      stm32wb_serial_setbaud_lpuart(priv);
+    }
+  else
+#endif
+    {
+      stm32wb_serial_setbaud_usart(priv);
+    }
+
+  /* Configure parity mode */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0 | USART_CR1_M1);
+
+  if (priv->parity == 1)       /* Odd parity */
+    {
+      regval |= (USART_CR1_PCE | USART_CR1_PS);
+    }
+  else if (priv->parity == 2)  /* Even parity */
+    {
+      regval |= USART_CR1_PCE;
+    }
+
+  /* Configure word length (parity uses one of configured bits)
+   *
+   * Default: 1 start, 8 data (no parity), n stop, OR
+   *          1 start, 7 data + parity, n stop
+   */
+
+  if (priv->bits == 9 || (priv->bits == 8 && priv->parity != 0))
+    {
+      /* Select: 1 start, 8 data + parity, n stop, OR
+       *         1 start, 9 data (no parity), n stop.
+       */
+
+      regval |= USART_CR1_M0;
+    }
+  else if (priv->bits == 7 && priv->parity == 0)
+    {
+      /* Select: 1 start, 7 data (no parity), n stop, OR
+       */
+
+      regval |= USART_CR1_M1;
+    }
+
+  /* Else Select: 1 start, 7 data + parity, n stop, OR
+   *              1 start, 8 data (no parity), n stop.
+   */
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure STOP bits */
+
+  regval = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK);
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure hardware flow control */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSE | USART_CR3_RTSE);
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && !defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      regval |= USART_CR3_RTSE;
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->oflow && (priv->cts_gpio != 0))
+    {
+      regval |= USART_CR3_CTSE;
+    }
+#endif
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+}
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+/****************************************************************************
+ * Name: stm32wb_serial_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripheral.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#ifdef SERIAL_HAVE_RXDMA
+  bool dmarestored = false;
+#endif
+
+  if (priv->suspended == suspend)
+    {
+      return;
+    }
+
+  priv->suspended = suspend;
+
+  if (suspend)
+    {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Force RTS high to prevent further Rx. */
+
+          stm32wb_configgpio((priv->rts_gpio & ~GPIO_MODE_MASK)
+                             | (GPIO_OUTPUT | GPIO_OUTPUT_SET));
+        }
+#endif
+
+      /* Disable interrupts to prevent Tx. */
+
+      stm32wb_serial_disableusartint(priv, &priv->suspended_ie);
+
+      /* Wait last Tx to complete. */
+
+      while ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+              USART_ISR_TC) == 0);
+
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && !priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow && priv->rxdmanext == RXDMA_BUFFER_SIZE)
+            {
+              /* Rx DMA in non-circular iflow mode and already stopped
+               * at end of DMA buffer. No need to suspend.
+               */
+            }
+          else
+#endif
+            {
+              /* Suspend Rx DMA. */
+
+              stm32wb_dmastop(priv->rxdma);
+              priv->rxdmasusp = true;
+            }
+        }
+#endif
+    }
+  else
+    {
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              stm32wb_serial_dmaiflowrestart(priv);
+            }
+          else
+#endif
+            {
+              /* This USART does not have HW flow-control. Unconditionally
+               * re-enable DMA (might loss unprocessed bytes received
+               * to DMA buffer before suspending).
+               */
+
+              stm32wb_serial_dmareenable(priv);
+              priv->rxdmasusp = false;
+            }
+
+          dmarestored = true;
+        }
+#endif
+
+      /* Re-enable interrupts to resume Tx. */
+
+      stm32wb_serial_restoreusartint(priv, priv->suspended_ie);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Restore peripheral RTS control. */
+
+          stm32wb_configgpio(priv->rts_gpio);
+        }
+#endif
+    }
+
+#ifdef SERIAL_HAVE_RXDMA
+  if (dmarestored)
+    {
+      irqstate_t flags;
+
+      flags = enter_critical_section();
+
+      /* Perform initial Rx DMA buffer fetch to wake-up serial device
+       * activity.
+       */
+
+      if (priv->rxdma != NULL)
+        {
+          stm32wb_serial_dmarxcallback(priv->rxdma, 0, priv);
+        }
+
+      leave_critical_section(flags);
+    }
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_pm_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripherals for/from deep-sleep/stop modes.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_pm_setsuspend(bool suspend)
+{
+  int n;
+
+  /* Already in desired state? */
+
+  if (suspend == g_serialpm.serial_suspended)
+    return;
+
+  g_serialpm.serial_suspended = suspend;
+
+  for (n = 0; n < STM32WB_NLPUART + STM32WB_NUSART; n++)
+    {
+      struct stm32wb_serial_s *priv = g_uart_devs[n];
+
+      if (!priv || !priv->initialized)
+        {
+          continue;
+        }
+
+      stm32wb_serial_setsuspend(&priv->dev, suspend);
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setapbclock
+ *
+ * Description:
+ *   Enable or disable APB clock for the USART peripheral
+ *
+ * Input Parameters:
+ *   dev - A reference to the UART driver state structure
+ *   on  - Enable clock if 'on' is 'true' and disable if 'false'
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_setapbclock(struct uart_dev_s *dev, bool on)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t rcc_en;
+  uint32_t regaddr;
+
+  /* Determine which USART to configure */
+
+  switch (priv->usartbase)
+    {
+    default:
+      return;
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+    case STM32WB_LPUART1_BASE:
+      rcc_en = RCC_APB1ENR2_LPUART1EN;
+      regaddr = STM32WB_RCC_APB1ENR2;
+      break;
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+    case STM32WB_USART1_BASE:
+      rcc_en = RCC_APB2ENR_USART1EN;
+      regaddr = STM32WB_RCC_APB2ENR;
+      break;
+#endif
+    }
+
+  /* Enable/disable APB 1/2 clock for USART */
+
+  if (on)
+    {
+      modifyreg32(regaddr, 0, rcc_en);
+    }
+  else
+    {
+      modifyreg32(regaddr, rcc_en, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setup
+ *
+ * Description:
+ *   Configure the USART baud, bits, parity, etc. This method is called the
+ *   first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_setup(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+  uint32_t regval;
+
+  /* Note: The logic here depends on the fact that that the USART module
+   * was enabled in stm32wb_lowsetup().
+   */
+
+  /* Enable USART APB1/2 clock */
+
+  stm32wb_serial_setapbclock(dev, true);
+
+  /* Configure pins for USART use */
+
+  stm32wb_configgpio(priv->tx_gpio);
+  stm32wb_configgpio(priv->rx_gpio);
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->cts_gpio != 0)
+    {
+      stm32wb_configgpio(priv->cts_gpio);
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->rts_gpio != 0)
+    {
+      uint32_t config = priv->rts_gpio;
+
+#ifdef CONFIG_STM32WB_FLOWCONTROL_BROKEN
+      /* Instead of letting hw manage this pin, we will bitbang */
+
+      config = (config & ~GPIO_MODE_MASK) | GPIO_OUTPUT;
+#endif
+      stm32wb_configgpio(config);
+    }
+#endif
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_configgpio(priv->rs485_dir_gpio);
+      stm32wb_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity);
+    }
+#endif
+
+  /* Configure CR2 */
+
+  /* Clear STOP, CLKEN, CPOL, CPHA, LBCL, and interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK | USART_CR2_CLKEN | USART_CR2_CPOL |
+              USART_CR2_CPHA | USART_CR2_LBCL | USART_CR2_LBDIE);
+
+  /* Configure STOP bits */
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure CR1 */
+
+  /* Clear TE, REm and all interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_TE | USART_CR1_RE | USART_CR1_ALLINTS);
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure CR3 */
+
+  /* Clear CTSE, RTSE, and all interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSIE | USART_CR3_CTSE | USART_CR3_RTSE |
+              USART_CR3_EIE);
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+
+  /* Configure the USART line format and speed. */
+
+  stm32wb_serial_setformat(dev);
+
+  /* Enable Rx, Tx, and the USART */
+
+  regval      = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval     |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+  /* Set up the cached interrupt enables value */
+
+  priv->ie    = 0;
+
+  /* Mark device as initialized. */
+
+  priv->initialized = true;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmasetup
+ *
+ * Description:
+ *   Configure the USART baud, bits, parity, etc. This method is called the
+ *   first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmasetup(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int result;
+  uint32_t regval;
+
+  /* Do the basic UART setup first, unless we are the console */
+
+  if (!dev->isconsole)
+    {
+      result = stm32wb_serial_setup(dev);
+      if (result != OK)
+        {
+          return result;
+        }
+    }
+
+  /* Acquire the DMA channel.  This should always succeed. */
+
+  priv->rxdma = stm32wb_dmachannel(priv->rxdma_channel);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Configure for non-circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                     priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                     (uint32_t)priv->rxfifo,
+                     RXDMA_BUFFER_SIZE,
+                     SERIAL_DMA_IFLOW_CONTROL_WORD);
+    }
+  else
+#endif
+    {
+      /* Configure for circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                     priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                     (uint32_t)priv->rxfifo,
+                     RXDMA_BUFFER_SIZE,
+                     SERIAL_DMA_CONTROL_WORD);
+    }
+
+  /* Reset our DMA shadow pointer to match the address just
+   * programmed above.
+   */
+
+  priv->rxdmanext = 0;
+
+  /* Enable receive DMA for the UART */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval |= USART_CR3_DMAR;
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Start the DMA channel, and arrange for callbacks at the full point
+       * in the FIFO. After buffer gets full, hardware flow-control kicks
+       * in and DMA transfer is stopped.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, false);
+    }
+  else
+#endif
+    {
+      /* Start the DMA channel, and arrange for callbacks at the half and
+       * full points in the FIFO.  This ensures that we have half a FIFO
+       * worth of time to claim bytes before they are overwritten.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, true);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_shutdown
+ *
+ * Description:
+ *   Disable the USART.  This method is called when the serial
+ *   port is closed
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Mark device as uninitialized. */
+
+  priv->initialized = false;
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_disableusartint(priv, NULL);
+
+  /* Disable USART APB1/2 clock */
+
+  stm32wb_serial_setapbclock(dev, false);
+
+  /* Disable Rx, Tx, and the UART */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Release pins. "If the serial-attached device is powered down, the TX
+   * pin causes back-powering, potentially confusing the device to the point
+   * of complete lock-up."
+   *
+   * REVISIT:  Is unconfiguring the pins appropriate for all device?  If not,
+   * then this may need to be a configuration option.
+   */
+
+  stm32wb_unconfiggpio(priv->tx_gpio);
+  stm32wb_unconfiggpio(priv->rx_gpio);
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->cts_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->cts_gpio);
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->rts_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->rts_gpio);
+    }
+#endif
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->rs485_dir_gpio);
+    }
+#endif
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmashutdown
+ *
+ * Description:
+ *   Disable the USART.  This method is called when the serial
+ *   port is closed
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  /* Perform the normal UART shutdown */
+
+  stm32wb_serial_shutdown(dev);
+
+  /* Stop the DMA channel */
+
+  stm32wb_dmastop(priv->rxdma);
+
+  /* Release the DMA channel */
+
+  stm32wb_dmafree(priv->rxdma);
+  priv->rxdma = NULL;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_attach
+ *
+ * Description:
+ *   Configure the USART to operation in interrupt driven mode.  This method
+ *   is called when the serial port is opened.  Normally, this is just after
+ *   the the setup() method is called, however, the serial console may
+ *   operate in a non-interrupt driven mode during the boot phase.
+ *
+ *   RX and TX interrupts are not enabled when by the attach method (unless
+ *   the hardware supports multiple levels of interrupt enabling). The RX
+ *   and TX interrupts are not enabled until the txint() and rxint()
+ *   methods are called.
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_attach(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int ret;
+
+  /* Attach and enable the IRQ */
+
+  ret = irq_attach(priv->irq, up_interrupt, priv);
+  if (ret == OK)
+    {
+      /* Enable the interrupt (RX and TX interrupts are still disabled
+       * in the USART
+       */
+
+      up_enable_irq(priv->irq);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_detach
+ *
+ * Description:
+ *   Detach USART interrupts.  This method is called when the serial port is
+ *   closed normally just before the shutdown method is called.
+ *   The exception is the serial console which is never shutdown.
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_detach(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  up_disable_irq(priv->irq);
+  irq_detach(priv->irq);
+}
+
+/****************************************************************************
+ * Name: up_interrupt
+ *
+ * Description:
+ *   This is the USART interrupt handler.  It will be invoked when an
+ *   interrupt received on the 'irq'  It should call uart_transmitchars or
+ *   uart_receivechar to perform the appropriate data transfers.  The
+ *   interrupt handling logic must be able to map the 'irq' number into the
+ *   appropriate uart_dev_s structure in order to call these functions.
+ *
+ ****************************************************************************/
+
+static int up_interrupt(int irq, void *context, void *arg)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)arg;
+  int  passes;
+  bool handled;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Report serial activity to the power management logic */
+
+#if defined(CONFIG_PM) && CONFIG_STM32WB_PM_SERIAL_ACTIVITY > 0
+  pm_activity(PM_IDLE_DOMAIN, CONFIG_STM32WB_PM_SERIAL_ACTIVITY);
+#endif
+
+  /* Loop until there are no characters to be transferred or,
+   * until we have been looping for a long time.
+   */
+
+  handled = true;
+  for (passes = 0; passes < 256 && handled; passes++)
+    {
+      handled = false;
+
+      /* Get the masked USART status word. */
+
+      priv->sr = stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET);
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning             Usage
+       * ------------------ --------------- ------------------- ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected           (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete           (used only
+       *                                                         for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag         (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag           (not used)
+       *
+       * NOTE: Some of these status bits must be cleared by explicitly
+       * writing one to the ICR register: USART_ICR_CTSCF, USART_ICR_LBDCF.
+       * Note of those are currently being used.
+       */
+
+#ifdef HAVE_RS485
+      /* Transmission of whole buffer is over - TC is set, TXEIE is cleared.
+       * Note - this should be first, to have the most recent TC bit value
+       * from SR register - sending data affects TC, but without refresh we
+       * will not know that...
+       */
+
+      if ((priv->sr & USART_ISR_TC) != 0 &&
+          (priv->ie & USART_CR1_TCIE) != 0 &&
+          (priv->ie & USART_CR1_TXEIE) == 0)
+        {
+          stm32wb_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity);
+          stm32wb_serial_restoreusartint(priv, priv->ie & ~USART_CR1_TCIE);
+        }
+#endif
+
+      /* Handle incoming, receive bytes. */
+
+      if ((priv->sr & USART_ISR_RXNE) != 0 &&
+          (priv->ie & USART_CR1_RXNEIE) != 0)
+        {
+          /* Received data ready... process incoming bytes.  NOTE the check
+           * for RXNEIE:  We cannot call uart_recvchards of RX interrupts
+           * are disabled.
+           */
+
+          uart_recvchars(&priv->dev);
+          handled = true;
+        }
+
+      /* We may still have to read from the DR register to clear any pending
+       * error conditions.
+       */
+
+      else if ((priv->sr &
+                (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE)) != 0)
+        {
+          /* These errors are cleared by writing the corresponding bit to the
+           * interrupt clear register (ICR).
+           */
+
+          stm32wb_serial_putreg(priv, STM32WB_USART_ICR_OFFSET,
+                               (USART_ICR_NCF | USART_ICR_ORECF |
+                                USART_ICR_FECF));
+        }
+
+      /* Handle outgoing, transmit bytes */
+
+      if ((priv->sr & USART_ISR_TXE) != 0 &&
+          (priv->ie & USART_CR1_TXEIE) != 0)
+        {
+          /* Transmit data register empty ... process outgoing bytes */
+
+          uart_xmitchars(&priv->dev);
+          handled = true;
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_ioctl
+ *
+ * Description:
+ *   All ioctl calls will be routed through this method
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                unsigned long arg)
+{
+#if defined(CONFIG_SERIAL_TERMIOS) || defined(CONFIG_SERIAL_TIOCSERGSTRUCT)
+  struct inode      *inode = filep->f_inode;
+  struct uart_dev_s *dev   = inode->i_private;
+#endif
+#if defined(CONFIG_SERIAL_TERMIOS)
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#endif
+  int                ret    = OK;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT
+    case TIOCSERGSTRUCT:
+      {
+        struct stm32wb_serial_s *user = (struct stm32wb_serial_s *)arg;
+        if (!user)
+          {
+            ret = -EINVAL;
+          }
+        else
+          {
+            memcpy(user, dev, sizeof(struct stm32wb_serial_s));
+          }
+      }
+      break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_SINGLEWIRE
+    case TIOCSSINGLEWIRE:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, HDSEL can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Change the TX port to be open-drain/push-pull and enable/disable
+         * half-duplex mode.
+         */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+        if ((arg & SER_SINGLEWIRE_ENABLED) != 0)
+          {
+            uint32_t gpio_val = (arg & SER_SINGLEWIRE_PUSHPULL) ==
+                                 SER_SINGLEWIRE_PUSHPULL ?
+                                 GPIO_PUSHPULL : GPIO_OPENDRAIN;
+            gpio_val |=
+              (arg & SER_SINGLEWIRE_PULL_MASK) == SER_SINGLEWIRE_PULLUP ?
+                                                  GPIO_PULLUP : GPIO_FLOAT;
+
+            gpio_val |=
+              (arg & SER_SINGLEWIRE_PULL_MASK) == SER_SINGLEWIRE_PULLDOWN ?
+                                                  GPIO_PULLDOWN : GPIO_FLOAT;
+
+            stm32wb_configgpio((priv->tx_gpio & ~(GPIO_PUPD_MASK |
+                                                  GPIO_OPENDRAIN)) |
+                                                  gpio_val);
+
+            cr |= USART_CR3_HDSEL;
+          }
+        else
+          {
+            stm32wb_configgpio((priv->tx_gpio & ~(GPIO_PUPD_MASK |
+                                                  GPIO_OPENDRAIN)) |
+                                                  GPIO_PUSHPULL);
+
+            cr &= ~USART_CR3_HDSEL;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_INVERT
+    case TIOCSINVERT:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, {R,T}XINV can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Enable/disable signal inversion. */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+
+        if (arg & SER_INVERT_ENABLED_RX)
+          {
+            cr |= USART_CR2_RXINV;
+          }
+        else
+          {
+            cr &= ~USART_CR2_RXINV;
+          }
+
+        if (arg & SER_INVERT_ENABLED_TX)
+          {
+            cr |= USART_CR2_TXINV;
+          }
+        else
+          {
+            cr &= ~USART_CR2_TXINV;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_SWAP
+    case TIOCSSWAP:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, SWAP can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Enable/disable Swap mode. */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+
+        if (arg == SER_SWAP_ENABLED)
+          {
+            cr |= USART_CR2_SWAP;
+          }
+        else
+          {
+            cr &= ~USART_CR2_SWAP;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_SERIAL_TERMIOS
+    case TCGETS:
+      {
+        struct termios *termiosp = (struct termios *)arg;
+
+        if (!termiosp)
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        /* Note that since we only support 8/9 bit modes and
+         * there is no way to report 9-bit mode, we always claim 8.
+         */
+
+        termiosp->c_cflag =
+          ((priv->parity != 0) ? PARENB : 0) |
+          ((priv->parity == 1) ? PARODD : 0) |
+          ((priv->stopbits2) ? CSTOPB : 0) |
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+          ((priv->oflow) ? CCTS_OFLOW : 0) |
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          ((priv->iflow) ? CRTS_IFLOW : 0) |
+#endif
+          CS8;
+
+        cfsetispeed(termiosp, priv->baud);
+
+        /* TODO: CCTS_IFLOW, CCTS_OFLOW */
+      }
+      break;
+
+    case TCSETS:
+      {
+        struct termios *termiosp = (struct termios *)arg;
+
+        if (!termiosp)
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        /* Perform some sanity checks before accepting any changes */
+
+        if (((termiosp->c_cflag & CSIZE) != CS8)
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+            || ((termiosp->c_cflag & CCTS_OFLOW) && (priv->cts_gpio == 0))
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+            || ((termiosp->c_cflag & CRTS_IFLOW) && (priv->rts_gpio == 0))
+#endif
+           )
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        if (termiosp->c_cflag & PARENB)
+          {
+            priv->parity = (termiosp->c_cflag & PARODD) ? 1 : 2;
+          }
+        else
+          {
+            priv->parity = 0;
+          }
+
+        priv->stopbits2 = (termiosp->c_cflag & CSTOPB) != 0;
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+        priv->oflow = (termiosp->c_cflag & CCTS_OFLOW) != 0;
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+        priv->iflow = (termiosp->c_cflag & CRTS_IFLOW) != 0;
+#endif
+
+        /* Note that since there is no way to request 9-bit mode
+         * and no way to support 5/6/7-bit modes, we ignore them
+         * all here.
+         */
+
+        /* Note that only cfgetispeed is used because we have knowledge
+         * that only one speed is supported.
+         */
+
+        priv->baud = cfgetispeed(termiosp);
+
+        /* Effect the changes immediately - note that we do not implement
+         * TCSADRAIN / TCSAFLUSH
+         */
+
+        stm32wb_serial_setformat(dev);
+      }
+      break;
+#endif /* CONFIG_SERIAL_TERMIOS */
+
+#ifdef CONFIG_STM32WB_USART_BREAKS
+#  ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+    case TIOCSBRK:  /* BSD compatibility: Turn break on, unconditionally */
+      {
+        irqstate_t flags;
+        uint32_t tx_break;
+
+        flags = enter_critical_section();
+
+        /* Disable any further tx activity */
+
+        priv->ie |= USART_CR1_IE_BREAK_INPROGRESS;
+
+        stm32wb_serial_txint(dev, false);
+
+        /* Configure TX as a GPIO output pin and Send a break signal */
+
+        tx_break = GPIO_OUTPUT |
+                   (~(GPIO_MODE_MASK | GPIO_OUTPUT_SET) & priv->tx_gpio);
+        stm32wb_configgpio(tx_break);
+
+        leave_critical_section(flags);
+      }
+      break;
+
+    case TIOCCBRK:  /* BSD compatibility: Turn break off, unconditionally */
+      {
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Configure TX back to U(S)ART */
+
+        stm32wb_configgpio(priv->tx_gpio);
+
+        priv->ie &= ~USART_CR1_IE_BREAK_INPROGRESS;
+
+        /* Enable further tx activity */
+
+        stm32wb_serial_txint(dev, true);
+
+        leave_critical_section(flags);
+      }
+      break;
+#  else
+    case TIOCSBRK:  /* No BSD compatibility: Turn break on for M bit times */
+      {
+        uint32_t cr1;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+        cr1   = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET,
+                             cr1 | USART_CR1_SBK);
+        leave_critical_section(flags);
+      }
+      break;
+
+    case TIOCCBRK:  /* No BSD compatibility: May turn off break too soon */
+      {
+        uint32_t cr1;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+        cr1   = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET,
+                             cr1 & ~USART_CR1_SBK);
+        leave_critical_section(flags);
+      }
+      break;
+#  endif
+#endif
+
+    default:
+      ret = -ENOTTY;
+      break;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_receive
+ *
+ * Description:
+ *   Called (usually) from the interrupt level to receive one
+ *   character from the USART.  Error bits associated with the
+ *   receipt are provided in the return 'status'.
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int stm32wb_serial_receive(struct uart_dev_s *dev,
+                                  unsigned int *status)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t rdr;
+
+  /* Get the Rx byte */
+
+  rdr      = stm32wb_serial_getreg(priv, STM32WB_USART_RDR_OFFSET);
+
+  /* Get the Rx byte plux error information.  Return those in status */
+
+  *status  = priv->sr << 16 | rdr;
+  priv->sr = 0;
+
+  /* Then return the actual received byte */
+
+  return rdr & 0xff;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxint
+ *
+ * Description:
+ *   Call to enable or disable RX interrupts
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  irqstate_t flags;
+  uint16_t ie;
+
+  /* USART receive interrupts:
+   *
+   * Enable             Status          Meaning                Usage
+   * ------------------ --------------- ------------------     ----------
+   * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+   *                                     Detected              (not used)
+   * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+   *                                     Ready to be Read
+   * "              "   USART_ISR_ORE    Overrun Error
+   *                                     Detected
+   * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+   *
+   * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag            (not used)
+   * USART_CR3_EIE      USART_ISR_FE     Framing Error
+   * "           "      USART_ISR_NE     Noise Flag
+   * "           "      USART_ISR_ORE    Overrun Error
+   *                                     Detected
+   */
+
+  flags = enter_critical_section();
+  ie = priv->ie;
+  if (enable)
+    {
+      /* Receive an interrupt when their is anything in the Rx data register
+       * (or an Rx timeout occurs).
+       */
+
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+#ifdef CONFIG_USART_ERRINTS
+      ie |= (USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR3_EIE);
+#else
+      ie |= USART_CR1_RXNEIE;
+#endif
+#endif
+    }
+  else
+    {
+      ie &= ~(USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR3_EIE);
+    }
+
+  /* Then set the new interrupt state */
+
+  stm32wb_serial_restoreusartint(priv, ie);
+  leave_critical_section(flags);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxavailable
+ *
+ * Description:
+ *   Return true if the receive register is not empty
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  return ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+           USART_ISR_RXNE) != 0);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxflowcontrol
+ *
+ * Description:
+ *   Called when Rx buffer is full (or exceeds configured watermark levels
+ *   if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is defined).
+ *   Return true if UART activated RX flow control to block more incoming
+ *   data
+ *
+ * Input Parameters:
+ *   dev       - UART device instance
+ *   nbuffered - the number of characters currently buffered
+ *               (if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is
+ *               not defined the value will be 0 for an empty buffer or the
+ *               defined buffer size for a full buffer)
+ *   upper     - true indicates the upper watermark was crossed where
+ *               false indicates the lower watermark has been crossed
+ *
+ * Returned Value:
+ *   true if RX flow control activated.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS) && \
+    defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      /* Assert/de-assert nRTS set it high resume/stop sending */
+
+      stm32wb_gpiowrite(priv->rts_gpio, upper);
+
+      if (upper)
+        {
+          /* With heavy Rx traffic, RXNE might be set and data pending.
+           * Returning 'true' in such case would cause RXNE left unhandled
+           * and causing interrupt storm. Sending end might be also be slow
+           * to react on nRTS, and returning 'true' here would prevent
+           * processing that data.
+           *
+           * Therefore, return 'false' so input data is still being processed
+           * until sending end reacts on nRTS signal and stops sending more.
+           */
+
+          return false;
+        }
+
+      return upper;
+    }
+
+#else
+  if (priv->iflow)
+    {
+      /* Is the RX buffer full? */
+
+      if (upper)
+        {
+          /* Disable Rx interrupt to prevent more data being from
+           * peripheral.  When hardware RTS is enabled, this will
+           * prevent more data from coming in.
+           *
+           * This function is only called when UART recv buffer is full,
+           * that is: "dev->recv.head + 1 == dev->recv.tail".
+           *
+           * Logic in "uart_read" will automatically toggle Rx interrupts
+           * when buffer is read empty and thus we do not have to re-
+           * enable Rx interrupts.
+           */
+
+          uart_disablerxint(dev);
+          return true;
+        }
+
+      /* No.. The RX buffer is empty */
+
+      else
+        {
+          /* We might leave Rx interrupt disabled if full recv buffer was
+           * read empty.  Enable Rx interrupt to make sure that more input is
+           * received.
+           */
+
+          uart_enablerxint(dev);
+        }
+    }
+#endif
+
+  return false;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmareceive
+ *
+ * Description:
+ *   Called (usually) from the interrupt level to receive one
+ *   character from the USART.  Error bits associated with the
+ *   receipt are provided in the return 'status'.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                     unsigned int *status)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int c = 0;
+
+  if (stm32wb_serial_dmanextrx(priv) != priv->rxdmanext)
+    {
+      c = priv->rxfifo[priv->rxdmanext];
+
+      priv->rxdmanext++;
+      if (priv->rxdmanext == RXDMA_BUFFER_SIZE)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              /* RX DMA buffer full. RX paused, RTS line pulled up to prevent
+               * more input data from other end.
+               */
+            }
+          else
+#endif
+            {
+              priv->rxdmanext = 0;
+            }
+        }
+    }
+
+  return c;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmareenable
+ *
+ * Description:
+ *   Call to re-enable RX DMA.
+ *
+ ****************************************************************************/
+
+#if defined(SERIAL_HAVE_RXDMA)
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv)
+{
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Configure for non-circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                       priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                       (uint32_t)priv->rxfifo,
+                       RXDMA_BUFFER_SIZE,
+                       SERIAL_DMA_IFLOW_CONTROL_WORD);
+    }
+  else
+#endif
+    {
+      /* Configure for circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                       priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                       (uint32_t)priv->rxfifo,
+                       RXDMA_BUFFER_SIZE,
+                       SERIAL_DMA_CONTROL_WORD);
+    }
+
+  /* Reset our DMA shadow pointer to match the address just
+   * programmed above.
+   */
+
+  priv->rxdmanext = 0;
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Start the DMA channel, and arrange for callbacks at the full point
+       * in the FIFO. After buffer gets full, hardware flow-control kicks
+       * in and DMA transfer is stopped.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, false);
+    }
+  else
+#endif
+    {
+      /* Start the DMA channel, and arrange for callbacks at the half and
+       * full points in the FIFO.  This ensures that we have half a FIFO
+       * worth of time to claim bytes before they are overwritten.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback,
+                       (void *)priv, true);
+    }
+
+#ifdef CONFIG_PM
+  /* Clear DMA suspended flag. */
+
+  priv->rxdmasusp = false;
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmaiflowrestart
+ *
+ * Description:
+ *   Call to restart RX DMA for input flow-controlled USART
+ *
+ ****************************************************************************/
+
+#if defined(SERIAL_HAVE_RXDMA) && defined(CONFIG_SERIAL_IFLOWCONTROL)
+static bool stm32wb_serial_dmaiflowrestart(struct stm32wb_serial_s *priv)
+{
+  if (!priv->rxenable)
+    {
+      /* Rx not enabled by upper layer. */
+
+      return false;
+    }
+
+  if (priv->rxdmanext != RXDMA_BUFFER_SIZE)
+    {
+#ifdef CONFIG_PM
+      if (priv->rxdmasusp)
+        {
+          /* Rx DMA in suspended state. */
+
+          if (stm32wb_serial_dmarxavailable(&priv->dev))
+            {
+              /* DMA buffer has unprocessed data, do not re-enable yet. */
+
+              return false;
+            }
+        }
+      else
+#endif
+        {
+          return false;
+        }
+    }
+
+  /* DMA is stopped or suspended and DMA buffer does not have pending data,
+   * re-enabling without data loss is now safe.
+   */
+
+  stm32wb_serial_dmareenable(priv);
+
+  return true;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmarxint
+ *
+ * Description:
+ *   Call to enable or disable RX interrupts
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void stm32wb_serial_dmarxint(struct uart_dev_s *dev, bool enable)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  /* En/disable DMA reception.
+   *
+   * Note that it is not safe to check for available bytes and immediately
+   * pass them to uart_recvchars as that could potentially recurse back
+   * to us again.  Instead, bytes must wait until the next up_dma_poll or
+   * DMA event.
+   */
+
+  priv->rxenable = enable;
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Re-enable RX DMA. */
+
+      stm32wb_serial_dmaiflowrestart(priv);
+    }
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmarxavailable
+ *
+ * Description:
+ *   Return true if the receive register is not empty
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static bool stm32wb_serial_dmarxavailable(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  /* Compare our receive pointer to the current DMA pointer, if they
+   * do not match, then there are bytes to be received.
+   */
+
+  return (stm32wb_serial_dmanextrx(priv) != priv->rxdmanext);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_send
+ *
+ * Description:
+ *   This method will send one byte on the USART
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_send(struct uart_dev_s *dev, int ch)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_gpiowrite(priv->rs485_dir_gpio, priv->rs485_dir_polarity);
+    }
+#endif
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_TDR_OFFSET, (uint32_t)ch);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_txint
+ *
+ * Description:
+ *   Call to enable or disable TX interrupts
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_txint(struct uart_dev_s *dev, bool enable)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  irqstate_t flags;
+
+  /* USART transmit interrupts:
+   *
+   * Enable             Status          Meaning            Usage
+   * ------------------ --------------- ----------------   ----------
+   * USART_CR1_TCIE     USART_ISR_TC     Transmission
+   *                                     Complete          (used only
+   *                                                       for RS-485)
+   * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+   *                                     Register Empty
+   * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag          (not used)
+   */
+
+  flags = enter_critical_section();
+  if (enable)
+    {
+      /* Set to receive an interrupt when the TX data register is empty */
+
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+      uint16_t ie = priv->ie | USART_CR1_TXEIE;
+
+      /* If RS-485 is supported on this U[S]ART, then also enable the
+       * transmission complete interrupt.
+       */
+
+#  ifdef HAVE_RS485
+      if (priv->rs485_dir_gpio != 0)
+        {
+          ie |= USART_CR1_TCIE;
+        }
+#  endif
+
+#  ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+      if (priv->ie & USART_CR1_IE_BREAK_INPROGRESS)
+        {
+          leave_critical_section(flags);
+          return;
+        }
+#  endif
+
+      stm32wb_serial_restoreusartint(priv, ie);
+
+      /* Fake a TX interrupt here by just calling uart_xmitchars() with
+       * interrupts disabled (note this may recurse).
+       */
+
+      uart_xmitchars(dev);
+#endif
+    }
+  else
+    {
+      /* Disable the TX interrupt */
+
+      stm32wb_serial_restoreusartint(priv, priv->ie & ~USART_CR1_TXEIE);
+    }
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_txready
+ *
+ * Description:
+ *   Return true if the transmit data register is empty
+ *
+ ****************************************************************************/
+
+static bool stm32wb_serial_txready(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  return ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+           USART_ISR_TXE) != 0);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmarxcallback
+ *
+ * Description:
+ *   This function checks the current DMA state and calls the generic
+ *   serial stack when bytes appear to be available.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void stm32wb_serial_dmarxcallback(DMA_HANDLE handle, uint8_t status,
+                                         void *arg)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)arg;
+
+  if (priv->rxenable && stm32wb_serial_dmarxavailable(&priv->dev))
+    {
+      uart_recvchars(&priv->dev);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Re-enable RX DMA. */
+
+          stm32wb_serial_dmaiflowrestart(priv);
+        }
+#endif
+    }
+
+  /* Get the masked USART status word to check and clear error flags.
+   *
+   * When wake-up from low power mode was not fast enough, UART is resumed
+   * too late and sometimes exactly when character was coming over UART,
+   * resulting to frame error.
+
+   * If error flag is not cleared, Rx DMA will be stuck. Clearing errors
+   * will release Rx DMA.
+   */
+
+  priv->sr = stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET);
+
+  if ((priv->sr & (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE)) != 0)
+    {
+      stm32wb_serial_putreg(priv, STM32WB_USART_ICR_OFFSET,
+                           (USART_ICR_NCF | USART_ICR_ORECF |
+                            USART_ICR_FECF));
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_pmnotify
+ *
+ * Description:
+ *   Notify the driver of new power state. This callback is  called after
+ *   all drivers have had the opportunity to prepare for the new power state.
+ *
+ * Input Parameters:
+ *
+ *    cb - Returned to the driver. The driver version of the callback
+ *         structure may include additional, driver-specific state data at
+ *         the end of the structure.
+ *
+ *    pmstate - Identifies the new PM state
+ *
+ * Returned Value:
+ *   None - The driver already agreed to transition to the low power
+ *   consumption state when when it returned OK to the prepare() call.
+ *
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_pmnotify(struct pm_callback_s *cb, int domain,
+                                    enum pm_state_e pmstate)
+{
+  switch (pmstate)
+    {
+      case PM_NORMAL:
+        {
+          stm32wb_serial_pm_setsuspend(false);
+        }
+        break;
+
+      case PM_IDLE:
+        {
+          stm32wb_serial_pm_setsuspend(false);
+        }
+        break;
+
+      case PM_STANDBY:
+        {
+          /* TODO: Alternative configuration and logic for enabling serial in
+           *       Stop 1 mode with HSI16 missing. Current logic allows
+           *       suspending serial peripherals for Stop 0/1/2 when serial
+           *       Rx/Tx buffers are empty (checked in pmprepare).
+           */
+
+          stm32wb_serial_pm_setsuspend(true);
+        }
+        break;
+
+      case PM_SLEEP:
+        {
+          stm32wb_serial_pm_setsuspend(true);
+        }
+        break;
+
+      default:
+
+        /* Should not get here */
+
+        break;
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_pmprepare
+ *
+ * Description:
+ *   Request the driver to prepare for a new power state. This is a warning
+ *   that the system is about to enter into a new power state. The driver
+ *   should begin whatever operations that may be required to enter power
+ *   state. The driver may abort the state change mode by returning a
+ *   non-zero value from the callback function.
+ *
+ * Input Parameters:
+ *
+ *    cb - Returned to the driver. The driver version of the callback
+ *         structure may include additional, driver-specific state data at
+ *         the end of the structure.
+ *
+ *    pmstate - Identifies the new PM state
+ *
+ * Returned Value:
+ *   Zero - (OK) means the event was successfully processed and that the
+ *          driver is prepared for the PM state change.
+ *
+ *   Non-zero - means that the driver is not prepared to perform the tasks
+ *              needed achieve this power setting and will cause the state
+ *              change to be aborted. NOTE: The prepare() method will also
+ *              be called when reverting from lower back to higher power
+ *              consumption modes (say because another driver refused a
+ *              lower power state change). Drivers are not permitted to
+ *              return non-zero values when reverting back to higher power
+ *              consumption modes!
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static int stm32wb_serial_pmprepare(struct pm_callback_s *cb, int domain,
+                                    enum pm_state_e pmstate)
+{
+  int n;
+
+  /* Logic to prepare for a reduced power state goes here. */
+
+  switch (pmstate)
+    {
+    case PM_NORMAL:
+    case PM_IDLE:
+      break;
+
+    case PM_STANDBY:
+    case PM_SLEEP:
+
+#ifdef SERIAL_HAVE_RXDMA
+      /* Flush Rx DMA buffers before checking state of serial device
+       * buffers.
+       */
+
+      stm32wb_serial_dma_poll();
+#endif
+
+      /* Check if any of the active ports have data pending on Tx/Rx
+       * buffers.
+       */
+
+      for (n = 0; n < STM32WB_NLPUART + STM32WB_NUSART; n++)
+        {
+          struct stm32wb_serial_s *priv = g_uart_devs[n];
+
+          if (!priv || !priv->initialized)
+            {
+              /* Not active, skip. */
+
+              continue;
+            }
+
+          if (priv->suspended)
+            {
+              /* Port already suspended, skip. */
+
+              continue;
+            }
+
+          /* Check if port has data pending (Rx & Tx). */
+
+          if (priv->dev.xmit.head != priv->dev.xmit.tail)
+            {
+              return ERROR;
+            }
+
+          if (priv->dev.recv.head != priv->dev.recv.tail)
+            {
+              return ERROR;
+            }
+        }
+      break;
+
+    default:
+
+      /* Should not get here */
+
+      break;

Review Comment:
   add 2 spaces for case block



##########
arch/arm/src/stm32wb/stm32wb_spi.c:
##########
@@ -0,0 +1,1819 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * The external functions, stm32wb_spi1/2select and stm32wb_spi1/2status
+ * must be provided by board-specific logic.  They are implementations of the
+ * select and status methods of the SPI interface defined by struct spi_ops_s
+ * (see include/nuttx/spi/spi.h).  All other methods (including
+ * stm32wb_spibus_initialize()) are provided by common STM32 logic.  To use
+ * this common SPI logic on your board:
+ *
+ *   1. Provide logic in stm32wb_board_initialize() to configure SPI chip
+ *      select pins.
+ *   2. Provide stm32wb_spi1/2select() and stm32wb_spi1/2status()
+ *      functions in your board-specific logic.  These functions will perform
+ *      chip selection and status operations using GPIOs in the way your
+ *      board is configured.
+ *   3. Add a calls to stm32wb_spibus_initialize() in your low level
+ *      application initialization logic
+ *   4. The handle returned by stm32wb_spibus_initialize() may then be used
+ *      to bind the SPI driver to higher level logic (e.g., calling
+ *      mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ *      the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+/* This driver is ported from the stm32 one, which only supports 8 and 16
+ * bits transfers. The STM32WB family supports frame size from 4 to 16 bits,
+ * but we do not support that yet. For the moment, we replace uses of the
+ * CR1_DFF bit with a check of the CR2_DS[0..3] bits. If the value is
+ * SPI_CR2_DS_16BIT it means 16 bits, else 8 bits.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/power/pm.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_spi.h"
+
+#include <arch/board/board.h>
+
+#if defined(CONFIG_STM32WB_SPI1) || defined(CONFIG_STM32WB_SPI2)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* SPI interrupts */
+
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+#  error "Interrupt driven SPI not yet supported"
+#endif
+
+/* Can't have both interrupt driven SPI and SPI DMA */
+
+#if defined(CONFIG_STM32WB_SPI_INTERRUPTS) && defined(CONFIG_STM32WB_SPI_DMA)
+#  error "Cannot enable both interrupt mode and DMA mode for SPI"
+#endif
+
+/* SPI DMA priority */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+
+#  if defined(CONFIG_SPI_DMAPRIO)
+#    define SPI_DMA_PRIO  CONFIG_SPI_DMAPRIO
+#  else
+#    define SPI_DMA_PRIO  DMA_CCR_PRIMED
+#  endif
+
+#  if (SPI_DMA_PRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_SPI_DMAPRIO"
+#  endif
+
+#endif
+
+/* DMA channel configuration */
+
+#define SPI_RXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC            )
+#define SPI_RXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC            )
+#define SPI_RXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS                         )
+#define SPI_RXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS                          )
+#define SPI_TXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS             |DMA_CCR_DIR)
+#define SPI_TXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS              |DMA_CCR_DIR)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_spidev_s
+{
+  struct spi_dev_s spidev;       /* Externally visible part of the SPI interface */
+  uint32_t         spibase;      /* SPIn base address */
+  uint32_t         spiclock;     /* Clocking for the SPI module */
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  uint8_t          spiirq;       /* SPI IRQ number */
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  volatile uint8_t rxresult;     /* Result of the RX DMA */
+  volatile uint8_t txresult;     /* Result of the RX DMA */
+#ifdef CONFIG_SPI_TRIGGER
+  bool             defertrig;    /* Flag indicating that trigger should be deferred */
+  bool             trigarmed;    /* Flag indicating that the trigger is armed */
+#endif
+  uint16_t         rxch;         /* The RX DMA channel number */
+  uint16_t         txch;         /* The TX DMA channel number */
+  DMA_HANDLE       rxdma;        /* DMA channel handle for RX transfers */
+  DMA_HANDLE       txdma;        /* DMA channel handle for TX transfers */
+  sem_t            rxsem;        /* Wait for RX DMA to complete */
+  sem_t            txsem;        /* Wait for TX DMA to complete */
+  uint32_t         txccr;        /* DMA control register for TX transfers */
+  uint32_t         rxccr;        /* DMA control register for RX transfers */
+#endif
+  bool             initialized;  /* Has SPI interface been initialized */
+  sem_t            exclsem;      /* Held while chip is selected for mutual exclusion */
+  uint32_t         frequency;    /* Requested clock frequency */
+  uint32_t         actual;       /* Actual clock frequency */
+  uint8_t          nbits;        /* Width of word in bits (4 through 16) */
+  uint8_t          mode;         /* Mode 0,1,2,3 */
+#ifdef CONFIG_PM
+  struct pm_callback_s pm_cb;    /* PM callbacks */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset);
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value);
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv);
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t byte);
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv);
+
+/* DMA support */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int         spi_dmarxwait(struct stm32wb_spidev_s *priv);
+static int         spi_dmatxwait(struct stm32wb_spidev_s *priv);
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv);
+static void        spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                                  void *rxbuffer, void *rxdummy,
+                                  size_t nwords);
+static void        spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                                  const void *txbuffer,
+                                  const void *txdummy, size_t nwords);
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv);
+#endif
+
+/* SPI methods */
+
+static int         spi_lock(struct spi_dev_s *dev, bool lock);
+static uint32_t    spi_setfrequency(struct spi_dev_s *dev,
+                                    uint32_t frequency);
+static void        spi_setmode(struct spi_dev_s *dev,
+                               enum spi_mode_e mode);
+static void        spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int         spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t    spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void        spi_exchange(struct spi_dev_s *dev,
+                                const void *txbuffer,
+                                void *rxbuffer, size_t nwords);
+#ifdef CONFIG_SPI_TRIGGER
+static int         spi_trigger(struct spi_dev_s *dev);
+#endif
+#ifndef CONFIG_SPI_EXCHANGE
+static void        spi_sndblock(struct spi_dev_s *dev,
+                                const void *txbuffer, size_t nwords);
+static void        spi_recvblock(struct spi_dev_s *dev,
+                                 void *rxbuffer, size_t nwords);
+#endif
+
+/* Initialization */
+
+static void        spi_bus_initialize(struct stm32wb_spidev_s *priv);
+
+/* PM interface */
+
+#ifdef CONFIG_PM
+static int         spi_pm_prepare(struct pm_callback_s *cb, int domain,
+                                  enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI1
+static const struct spi_ops_s g_spi1ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi1select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi1status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi1cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi1register,  /* Provided externally */
+#else
+  .registercallback  = 0,                   /* Not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi1dev =
+{
+  .spidev   =
+    {
+      &g_spi1ops
+    },
+  .spibase  = STM32WB_SPI1_BASE,
+  .spiclock = STM32WB_PCLK2_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI1,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  /* lines must be configured in board.h */
+
+  .rxch     = DMAMAP_SPI1_RX,
+  .txch     = DMAMAP_SPI1_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+#ifdef CONFIG_STM32WB_SPI2
+static const struct spi_ops_s g_spi2ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi2select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi2status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi2cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi2register,  /* provided externally */
+#else
+  .registercallback  = 0,  /* not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi2dev =
+{
+  .spidev   =
+    {
+      &g_spi2ops
+    },
+  .spibase  = STM32WB_SPI2_BASE,
+  .spiclock = STM32WB_PCLK1_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI2,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  .rxch     = DMACHAN_SPI2_RX,
+  .txch     = DMACHAN_SPI2_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spi_getreg
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg16(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg
+ *
+ * Description:
+ *   Write a 16-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 16-bit value to be written
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value)
+{
+  putreg16(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_getreg8
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 8-bit register
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_getreg8(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg8(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg8
+ *
+ * Description:
+ *   Write a 8-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 8-bit value to be written
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg8(struct stm32wb_spidev_s *priv,
+                               uint8_t offset, uint8_t value)
+{
+  putreg8(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_readword
+ *
+ * Description:
+ *   Read one word (TWO bytes!) from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Word as read
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_readbyte
+ *
+ * Description:
+ *   Read one byte from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Byte as read
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_readbyte(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg8(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_writeword
+ *
+ * Description:
+ *   Write one 16-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Word to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t word)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg(priv, STM32WB_SPI_DR_OFFSET, word);
+}
+
+/****************************************************************************
+ * Name: spi_writebyte
+ *
+ * Description:
+ *   Write one 8-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Byte to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writebyte(struct stm32wb_spidev_s *priv,
+                                 uint8_t byte)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg8(priv, STM32WB_SPI_DR_OFFSET, byte);
+}
+
+/****************************************************************************
+ * Name: spi_16bitmode
+ *
+ * Description:
+ *   Check if the SPI is operating in 16-bit mode
+ *
+ * Input Parameters:
+ *   priv     - Device-specific state data
+ *
+ * Returned Value:
+ *   true: 16-bit mode, false: 8-bit mode
+ *
+ ****************************************************************************/
+
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv)
+{
+  return (priv->nbits > 8);
+}
+
+/****************************************************************************
+ * Name: spi_dmarxwaitw
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmarxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->rxsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->rxresult == 0 && ret == OK);

Review Comment:
   Let's use this PR to clarify what is the expected behavior. I already reviewed similar code in https://github.com/apache/incubator-nuttx/pull/6492#discussion_r902578883
   Do we need to apply similar logic here or I misunderstood the logic in original PR?



##########
arch/arm/src/stm32wb/stm32wb_spi.c:
##########
@@ -0,0 +1,1819 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * The external functions, stm32wb_spi1/2select and stm32wb_spi1/2status
+ * must be provided by board-specific logic.  They are implementations of the
+ * select and status methods of the SPI interface defined by struct spi_ops_s
+ * (see include/nuttx/spi/spi.h).  All other methods (including
+ * stm32wb_spibus_initialize()) are provided by common STM32 logic.  To use
+ * this common SPI logic on your board:
+ *
+ *   1. Provide logic in stm32wb_board_initialize() to configure SPI chip
+ *      select pins.
+ *   2. Provide stm32wb_spi1/2select() and stm32wb_spi1/2status()
+ *      functions in your board-specific logic.  These functions will perform
+ *      chip selection and status operations using GPIOs in the way your
+ *      board is configured.
+ *   3. Add a calls to stm32wb_spibus_initialize() in your low level
+ *      application initialization logic
+ *   4. The handle returned by stm32wb_spibus_initialize() may then be used
+ *      to bind the SPI driver to higher level logic (e.g., calling
+ *      mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ *      the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+/* This driver is ported from the stm32 one, which only supports 8 and 16
+ * bits transfers. The STM32WB family supports frame size from 4 to 16 bits,
+ * but we do not support that yet. For the moment, we replace uses of the
+ * CR1_DFF bit with a check of the CR2_DS[0..3] bits. If the value is
+ * SPI_CR2_DS_16BIT it means 16 bits, else 8 bits.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/power/pm.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_spi.h"
+
+#include <arch/board/board.h>
+
+#if defined(CONFIG_STM32WB_SPI1) || defined(CONFIG_STM32WB_SPI2)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* SPI interrupts */
+
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+#  error "Interrupt driven SPI not yet supported"
+#endif
+
+/* Can't have both interrupt driven SPI and SPI DMA */
+
+#if defined(CONFIG_STM32WB_SPI_INTERRUPTS) && defined(CONFIG_STM32WB_SPI_DMA)
+#  error "Cannot enable both interrupt mode and DMA mode for SPI"
+#endif
+
+/* SPI DMA priority */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+
+#  if defined(CONFIG_SPI_DMAPRIO)
+#    define SPI_DMA_PRIO  CONFIG_SPI_DMAPRIO
+#  else
+#    define SPI_DMA_PRIO  DMA_CCR_PRIMED
+#  endif
+
+#  if (SPI_DMA_PRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_SPI_DMAPRIO"
+#  endif
+
+#endif
+
+/* DMA channel configuration */
+
+#define SPI_RXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC            )
+#define SPI_RXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC            )
+#define SPI_RXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS                         )
+#define SPI_RXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS                          )
+#define SPI_TXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS             |DMA_CCR_DIR)
+#define SPI_TXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS              |DMA_CCR_DIR)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_spidev_s
+{
+  struct spi_dev_s spidev;       /* Externally visible part of the SPI interface */
+  uint32_t         spibase;      /* SPIn base address */
+  uint32_t         spiclock;     /* Clocking for the SPI module */
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  uint8_t          spiirq;       /* SPI IRQ number */
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  volatile uint8_t rxresult;     /* Result of the RX DMA */
+  volatile uint8_t txresult;     /* Result of the RX DMA */
+#ifdef CONFIG_SPI_TRIGGER
+  bool             defertrig;    /* Flag indicating that trigger should be deferred */
+  bool             trigarmed;    /* Flag indicating that the trigger is armed */
+#endif
+  uint16_t         rxch;         /* The RX DMA channel number */
+  uint16_t         txch;         /* The TX DMA channel number */
+  DMA_HANDLE       rxdma;        /* DMA channel handle for RX transfers */
+  DMA_HANDLE       txdma;        /* DMA channel handle for TX transfers */
+  sem_t            rxsem;        /* Wait for RX DMA to complete */
+  sem_t            txsem;        /* Wait for TX DMA to complete */
+  uint32_t         txccr;        /* DMA control register for TX transfers */
+  uint32_t         rxccr;        /* DMA control register for RX transfers */
+#endif
+  bool             initialized;  /* Has SPI interface been initialized */
+  sem_t            exclsem;      /* Held while chip is selected for mutual exclusion */
+  uint32_t         frequency;    /* Requested clock frequency */
+  uint32_t         actual;       /* Actual clock frequency */
+  uint8_t          nbits;        /* Width of word in bits (4 through 16) */
+  uint8_t          mode;         /* Mode 0,1,2,3 */
+#ifdef CONFIG_PM
+  struct pm_callback_s pm_cb;    /* PM callbacks */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset);
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value);
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv);
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t byte);
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv);
+
+/* DMA support */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int         spi_dmarxwait(struct stm32wb_spidev_s *priv);
+static int         spi_dmatxwait(struct stm32wb_spidev_s *priv);
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv);
+static void        spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                                  void *rxbuffer, void *rxdummy,
+                                  size_t nwords);
+static void        spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                                  const void *txbuffer,
+                                  const void *txdummy, size_t nwords);
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv);
+#endif
+
+/* SPI methods */
+
+static int         spi_lock(struct spi_dev_s *dev, bool lock);
+static uint32_t    spi_setfrequency(struct spi_dev_s *dev,
+                                    uint32_t frequency);
+static void        spi_setmode(struct spi_dev_s *dev,
+                               enum spi_mode_e mode);
+static void        spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int         spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t    spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void        spi_exchange(struct spi_dev_s *dev,
+                                const void *txbuffer,
+                                void *rxbuffer, size_t nwords);
+#ifdef CONFIG_SPI_TRIGGER
+static int         spi_trigger(struct spi_dev_s *dev);
+#endif
+#ifndef CONFIG_SPI_EXCHANGE
+static void        spi_sndblock(struct spi_dev_s *dev,
+                                const void *txbuffer, size_t nwords);
+static void        spi_recvblock(struct spi_dev_s *dev,
+                                 void *rxbuffer, size_t nwords);
+#endif
+
+/* Initialization */
+
+static void        spi_bus_initialize(struct stm32wb_spidev_s *priv);
+
+/* PM interface */
+
+#ifdef CONFIG_PM
+static int         spi_pm_prepare(struct pm_callback_s *cb, int domain,
+                                  enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI1
+static const struct spi_ops_s g_spi1ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi1select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi1status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi1cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi1register,  /* Provided externally */
+#else
+  .registercallback  = 0,                   /* Not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi1dev =
+{
+  .spidev   =
+    {
+      &g_spi1ops
+    },
+  .spibase  = STM32WB_SPI1_BASE,
+  .spiclock = STM32WB_PCLK2_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI1,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  /* lines must be configured in board.h */
+
+  .rxch     = DMAMAP_SPI1_RX,
+  .txch     = DMAMAP_SPI1_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+#ifdef CONFIG_STM32WB_SPI2
+static const struct spi_ops_s g_spi2ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi2select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi2status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi2cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi2register,  /* provided externally */
+#else
+  .registercallback  = 0,  /* not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi2dev =
+{
+  .spidev   =
+    {
+      &g_spi2ops
+    },
+  .spibase  = STM32WB_SPI2_BASE,
+  .spiclock = STM32WB_PCLK1_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI2,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  .rxch     = DMACHAN_SPI2_RX,
+  .txch     = DMACHAN_SPI2_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spi_getreg
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg16(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg
+ *
+ * Description:
+ *   Write a 16-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 16-bit value to be written
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value)
+{
+  putreg16(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_getreg8
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 8-bit register
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_getreg8(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg8(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg8
+ *
+ * Description:
+ *   Write a 8-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 8-bit value to be written
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg8(struct stm32wb_spidev_s *priv,
+                               uint8_t offset, uint8_t value)
+{
+  putreg8(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_readword
+ *
+ * Description:
+ *   Read one word (TWO bytes!) from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Word as read
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_readbyte
+ *
+ * Description:
+ *   Read one byte from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Byte as read
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_readbyte(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg8(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_writeword
+ *
+ * Description:
+ *   Write one 16-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Word to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t word)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg(priv, STM32WB_SPI_DR_OFFSET, word);
+}
+
+/****************************************************************************
+ * Name: spi_writebyte
+ *
+ * Description:
+ *   Write one 8-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Byte to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writebyte(struct stm32wb_spidev_s *priv,
+                                 uint8_t byte)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg8(priv, STM32WB_SPI_DR_OFFSET, byte);
+}
+
+/****************************************************************************
+ * Name: spi_16bitmode
+ *
+ * Description:
+ *   Check if the SPI is operating in 16-bit mode
+ *
+ * Input Parameters:
+ *   priv     - Device-specific state data
+ *
+ * Returned Value:
+ *   true: 16-bit mode, false: 8-bit mode
+ *
+ ****************************************************************************/
+
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv)
+{
+  return (priv->nbits > 8);
+}
+
+/****************************************************************************
+ * Name: spi_dmarxwaitw
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmarxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->rxsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->rxresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwait
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmatxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->txsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);

Review Comment:
   ditto



##########
arch/arm/src/stm32wb/stm32wb_spi.c:
##########
@@ -0,0 +1,1819 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * The external functions, stm32wb_spi1/2select and stm32wb_spi1/2status
+ * must be provided by board-specific logic.  They are implementations of the
+ * select and status methods of the SPI interface defined by struct spi_ops_s
+ * (see include/nuttx/spi/spi.h).  All other methods (including
+ * stm32wb_spibus_initialize()) are provided by common STM32 logic.  To use
+ * this common SPI logic on your board:
+ *
+ *   1. Provide logic in stm32wb_board_initialize() to configure SPI chip
+ *      select pins.
+ *   2. Provide stm32wb_spi1/2select() and stm32wb_spi1/2status()
+ *      functions in your board-specific logic.  These functions will perform
+ *      chip selection and status operations using GPIOs in the way your
+ *      board is configured.
+ *   3. Add a calls to stm32wb_spibus_initialize() in your low level
+ *      application initialization logic
+ *   4. The handle returned by stm32wb_spibus_initialize() may then be used
+ *      to bind the SPI driver to higher level logic (e.g., calling
+ *      mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ *      the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+/* This driver is ported from the stm32 one, which only supports 8 and 16
+ * bits transfers. The STM32WB family supports frame size from 4 to 16 bits,
+ * but we do not support that yet. For the moment, we replace uses of the
+ * CR1_DFF bit with a check of the CR2_DS[0..3] bits. If the value is
+ * SPI_CR2_DS_16BIT it means 16 bits, else 8 bits.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/power/pm.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_spi.h"
+
+#include <arch/board/board.h>
+
+#if defined(CONFIG_STM32WB_SPI1) || defined(CONFIG_STM32WB_SPI2)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* SPI interrupts */
+
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+#  error "Interrupt driven SPI not yet supported"
+#endif
+
+/* Can't have both interrupt driven SPI and SPI DMA */
+
+#if defined(CONFIG_STM32WB_SPI_INTERRUPTS) && defined(CONFIG_STM32WB_SPI_DMA)
+#  error "Cannot enable both interrupt mode and DMA mode for SPI"
+#endif
+
+/* SPI DMA priority */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+
+#  if defined(CONFIG_SPI_DMAPRIO)
+#    define SPI_DMA_PRIO  CONFIG_SPI_DMAPRIO
+#  else
+#    define SPI_DMA_PRIO  DMA_CCR_PRIMED
+#  endif
+
+#  if (SPI_DMA_PRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_SPI_DMAPRIO"
+#  endif
+
+#endif
+
+/* DMA channel configuration */
+
+#define SPI_RXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC            )
+#define SPI_RXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC            )
+#define SPI_RXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS                         )
+#define SPI_RXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS                          )
+#define SPI_TXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS             |DMA_CCR_DIR)
+#define SPI_TXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS              |DMA_CCR_DIR)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_spidev_s
+{
+  struct spi_dev_s spidev;       /* Externally visible part of the SPI interface */
+  uint32_t         spibase;      /* SPIn base address */
+  uint32_t         spiclock;     /* Clocking for the SPI module */
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  uint8_t          spiirq;       /* SPI IRQ number */
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  volatile uint8_t rxresult;     /* Result of the RX DMA */
+  volatile uint8_t txresult;     /* Result of the RX DMA */
+#ifdef CONFIG_SPI_TRIGGER
+  bool             defertrig;    /* Flag indicating that trigger should be deferred */
+  bool             trigarmed;    /* Flag indicating that the trigger is armed */
+#endif
+  uint16_t         rxch;         /* The RX DMA channel number */
+  uint16_t         txch;         /* The TX DMA channel number */
+  DMA_HANDLE       rxdma;        /* DMA channel handle for RX transfers */
+  DMA_HANDLE       txdma;        /* DMA channel handle for TX transfers */
+  sem_t            rxsem;        /* Wait for RX DMA to complete */
+  sem_t            txsem;        /* Wait for TX DMA to complete */
+  uint32_t         txccr;        /* DMA control register for TX transfers */
+  uint32_t         rxccr;        /* DMA control register for RX transfers */
+#endif
+  bool             initialized;  /* Has SPI interface been initialized */
+  sem_t            exclsem;      /* Held while chip is selected for mutual exclusion */
+  uint32_t         frequency;    /* Requested clock frequency */
+  uint32_t         actual;       /* Actual clock frequency */
+  uint8_t          nbits;        /* Width of word in bits (4 through 16) */
+  uint8_t          mode;         /* Mode 0,1,2,3 */
+#ifdef CONFIG_PM
+  struct pm_callback_s pm_cb;    /* PM callbacks */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset);
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value);
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv);
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t byte);
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv);
+
+/* DMA support */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int         spi_dmarxwait(struct stm32wb_spidev_s *priv);
+static int         spi_dmatxwait(struct stm32wb_spidev_s *priv);
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv);
+static void        spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                                  void *rxbuffer, void *rxdummy,
+                                  size_t nwords);
+static void        spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                                  const void *txbuffer,
+                                  const void *txdummy, size_t nwords);
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv);
+#endif
+
+/* SPI methods */
+
+static int         spi_lock(struct spi_dev_s *dev, bool lock);
+static uint32_t    spi_setfrequency(struct spi_dev_s *dev,
+                                    uint32_t frequency);
+static void        spi_setmode(struct spi_dev_s *dev,
+                               enum spi_mode_e mode);
+static void        spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int         spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t    spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void        spi_exchange(struct spi_dev_s *dev,
+                                const void *txbuffer,
+                                void *rxbuffer, size_t nwords);
+#ifdef CONFIG_SPI_TRIGGER
+static int         spi_trigger(struct spi_dev_s *dev);
+#endif
+#ifndef CONFIG_SPI_EXCHANGE
+static void        spi_sndblock(struct spi_dev_s *dev,
+                                const void *txbuffer, size_t nwords);
+static void        spi_recvblock(struct spi_dev_s *dev,
+                                 void *rxbuffer, size_t nwords);
+#endif
+
+/* Initialization */
+
+static void        spi_bus_initialize(struct stm32wb_spidev_s *priv);
+
+/* PM interface */
+
+#ifdef CONFIG_PM
+static int         spi_pm_prepare(struct pm_callback_s *cb, int domain,
+                                  enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI1
+static const struct spi_ops_s g_spi1ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi1select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi1status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi1cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi1register,  /* Provided externally */
+#else
+  .registercallback  = 0,                   /* Not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi1dev =
+{
+  .spidev   =
+    {
+      &g_spi1ops
+    },
+  .spibase  = STM32WB_SPI1_BASE,
+  .spiclock = STM32WB_PCLK2_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI1,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  /* lines must be configured in board.h */
+
+  .rxch     = DMAMAP_SPI1_RX,
+  .txch     = DMAMAP_SPI1_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+#ifdef CONFIG_STM32WB_SPI2
+static const struct spi_ops_s g_spi2ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi2select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi2status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi2cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi2register,  /* provided externally */
+#else
+  .registercallback  = 0,  /* not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi2dev =
+{
+  .spidev   =
+    {
+      &g_spi2ops
+    },
+  .spibase  = STM32WB_SPI2_BASE,
+  .spiclock = STM32WB_PCLK1_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI2,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  .rxch     = DMACHAN_SPI2_RX,
+  .txch     = DMACHAN_SPI2_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spi_getreg
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg16(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg
+ *
+ * Description:
+ *   Write a 16-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 16-bit value to be written
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value)
+{
+  putreg16(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_getreg8
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 8-bit register
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_getreg8(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg8(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg8
+ *
+ * Description:
+ *   Write a 8-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 8-bit value to be written
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg8(struct stm32wb_spidev_s *priv,
+                               uint8_t offset, uint8_t value)
+{
+  putreg8(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_readword
+ *
+ * Description:
+ *   Read one word (TWO bytes!) from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Word as read
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_readbyte
+ *
+ * Description:
+ *   Read one byte from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Byte as read
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_readbyte(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg8(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_writeword
+ *
+ * Description:
+ *   Write one 16-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Word to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t word)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg(priv, STM32WB_SPI_DR_OFFSET, word);
+}
+
+/****************************************************************************
+ * Name: spi_writebyte
+ *
+ * Description:
+ *   Write one 8-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Byte to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writebyte(struct stm32wb_spidev_s *priv,
+                                 uint8_t byte)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg8(priv, STM32WB_SPI_DR_OFFSET, byte);
+}
+
+/****************************************************************************
+ * Name: spi_16bitmode
+ *
+ * Description:
+ *   Check if the SPI is operating in 16-bit mode
+ *
+ * Input Parameters:
+ *   priv     - Device-specific state data
+ *
+ * Returned Value:
+ *   true: 16-bit mode, false: 8-bit mode
+ *
+ ****************************************************************************/
+
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv)
+{
+  return (priv->nbits > 8);
+}
+
+/****************************************************************************
+ * Name: spi_dmarxwaitw
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmarxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->rxsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->rxresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwait
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmatxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->txsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->txresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv)
+{
+  nxsem_post(&priv->rxsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv)
+{
+  nxsem_post(&priv->txsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->rxresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmarxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->txresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmatxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxsetup
+ *
+ * Description:
+ *   Setup to perform RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                           void *rxbuffer, void *rxdummy, size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA16_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA8_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Configure the RX DMA */
+
+  stm32wb_dmasetup(priv->rxdma, priv->spibase + STM32WB_SPI_DR_OFFSET,
+                 (uint32_t)rxbuffer, nwords, priv->rxccr);

Review Comment:
   ```suggestion
     stm32wb_dmasetup(priv->rxdma, priv->spibase + STM32WB_SPI_DR_OFFSET,
                      (uint32_t)rxbuffer, nwords, priv->rxccr);
   ```



##########
arch/arm/src/stm32wb/stm32wb_spi.c:
##########
@@ -0,0 +1,1819 @@
+/****************************************************************************

Review Comment:
   Let's compare this file with https://github.com/apache/incubator-nuttx/pull/6492 variant and discuss items that are different



##########
arch/arm/src/stm32wb/stm32wb_spi.c:
##########
@@ -0,0 +1,1819 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * The external functions, stm32wb_spi1/2select and stm32wb_spi1/2status
+ * must be provided by board-specific logic.  They are implementations of the
+ * select and status methods of the SPI interface defined by struct spi_ops_s
+ * (see include/nuttx/spi/spi.h).  All other methods (including
+ * stm32wb_spibus_initialize()) are provided by common STM32 logic.  To use
+ * this common SPI logic on your board:
+ *
+ *   1. Provide logic in stm32wb_board_initialize() to configure SPI chip
+ *      select pins.
+ *   2. Provide stm32wb_spi1/2select() and stm32wb_spi1/2status()
+ *      functions in your board-specific logic.  These functions will perform
+ *      chip selection and status operations using GPIOs in the way your
+ *      board is configured.
+ *   3. Add a calls to stm32wb_spibus_initialize() in your low level
+ *      application initialization logic
+ *   4. The handle returned by stm32wb_spibus_initialize() may then be used
+ *      to bind the SPI driver to higher level logic (e.g., calling
+ *      mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ *      the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+/* This driver is ported from the stm32 one, which only supports 8 and 16
+ * bits transfers. The STM32WB family supports frame size from 4 to 16 bits,
+ * but we do not support that yet. For the moment, we replace uses of the
+ * CR1_DFF bit with a check of the CR2_DS[0..3] bits. If the value is
+ * SPI_CR2_DS_16BIT it means 16 bits, else 8 bits.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/power/pm.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_spi.h"
+
+#include <arch/board/board.h>
+
+#if defined(CONFIG_STM32WB_SPI1) || defined(CONFIG_STM32WB_SPI2)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* SPI interrupts */
+
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+#  error "Interrupt driven SPI not yet supported"
+#endif
+
+/* Can't have both interrupt driven SPI and SPI DMA */
+
+#if defined(CONFIG_STM32WB_SPI_INTERRUPTS) && defined(CONFIG_STM32WB_SPI_DMA)
+#  error "Cannot enable both interrupt mode and DMA mode for SPI"
+#endif
+
+/* SPI DMA priority */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+
+#  if defined(CONFIG_SPI_DMAPRIO)
+#    define SPI_DMA_PRIO  CONFIG_SPI_DMAPRIO
+#  else
+#    define SPI_DMA_PRIO  DMA_CCR_PRIMED
+#  endif
+
+#  if (SPI_DMA_PRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_SPI_DMAPRIO"
+#  endif
+
+#endif
+
+/* DMA channel configuration */
+
+#define SPI_RXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC            )
+#define SPI_RXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC            )
+#define SPI_RXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS                         )
+#define SPI_RXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS                          )
+#define SPI_TXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS             |DMA_CCR_DIR)
+#define SPI_TXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS              |DMA_CCR_DIR)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_spidev_s
+{
+  struct spi_dev_s spidev;       /* Externally visible part of the SPI interface */
+  uint32_t         spibase;      /* SPIn base address */
+  uint32_t         spiclock;     /* Clocking for the SPI module */
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  uint8_t          spiirq;       /* SPI IRQ number */
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  volatile uint8_t rxresult;     /* Result of the RX DMA */
+  volatile uint8_t txresult;     /* Result of the RX DMA */
+#ifdef CONFIG_SPI_TRIGGER
+  bool             defertrig;    /* Flag indicating that trigger should be deferred */
+  bool             trigarmed;    /* Flag indicating that the trigger is armed */
+#endif
+  uint16_t         rxch;         /* The RX DMA channel number */
+  uint16_t         txch;         /* The TX DMA channel number */
+  DMA_HANDLE       rxdma;        /* DMA channel handle for RX transfers */
+  DMA_HANDLE       txdma;        /* DMA channel handle for TX transfers */
+  sem_t            rxsem;        /* Wait for RX DMA to complete */
+  sem_t            txsem;        /* Wait for TX DMA to complete */
+  uint32_t         txccr;        /* DMA control register for TX transfers */
+  uint32_t         rxccr;        /* DMA control register for RX transfers */
+#endif
+  bool             initialized;  /* Has SPI interface been initialized */
+  sem_t            exclsem;      /* Held while chip is selected for mutual exclusion */
+  uint32_t         frequency;    /* Requested clock frequency */
+  uint32_t         actual;       /* Actual clock frequency */
+  uint8_t          nbits;        /* Width of word in bits (4 through 16) */
+  uint8_t          mode;         /* Mode 0,1,2,3 */
+#ifdef CONFIG_PM
+  struct pm_callback_s pm_cb;    /* PM callbacks */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset);
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value);
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv);
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t byte);
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv);
+
+/* DMA support */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int         spi_dmarxwait(struct stm32wb_spidev_s *priv);
+static int         spi_dmatxwait(struct stm32wb_spidev_s *priv);
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv);
+static void        spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                                  void *rxbuffer, void *rxdummy,
+                                  size_t nwords);
+static void        spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                                  const void *txbuffer,
+                                  const void *txdummy, size_t nwords);
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv);
+#endif
+
+/* SPI methods */
+
+static int         spi_lock(struct spi_dev_s *dev, bool lock);
+static uint32_t    spi_setfrequency(struct spi_dev_s *dev,
+                                    uint32_t frequency);
+static void        spi_setmode(struct spi_dev_s *dev,
+                               enum spi_mode_e mode);
+static void        spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int         spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t    spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void        spi_exchange(struct spi_dev_s *dev,
+                                const void *txbuffer,
+                                void *rxbuffer, size_t nwords);
+#ifdef CONFIG_SPI_TRIGGER
+static int         spi_trigger(struct spi_dev_s *dev);
+#endif
+#ifndef CONFIG_SPI_EXCHANGE
+static void        spi_sndblock(struct spi_dev_s *dev,
+                                const void *txbuffer, size_t nwords);
+static void        spi_recvblock(struct spi_dev_s *dev,
+                                 void *rxbuffer, size_t nwords);
+#endif
+
+/* Initialization */
+
+static void        spi_bus_initialize(struct stm32wb_spidev_s *priv);
+
+/* PM interface */
+
+#ifdef CONFIG_PM
+static int         spi_pm_prepare(struct pm_callback_s *cb, int domain,
+                                  enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI1
+static const struct spi_ops_s g_spi1ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi1select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi1status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi1cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi1register,  /* Provided externally */
+#else
+  .registercallback  = 0,                   /* Not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi1dev =
+{
+  .spidev   =
+    {
+      &g_spi1ops
+    },
+  .spibase  = STM32WB_SPI1_BASE,
+  .spiclock = STM32WB_PCLK2_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI1,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  /* lines must be configured in board.h */
+
+  .rxch     = DMAMAP_SPI1_RX,
+  .txch     = DMAMAP_SPI1_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+#ifdef CONFIG_STM32WB_SPI2
+static const struct spi_ops_s g_spi2ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi2select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi2status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi2cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi2register,  /* provided externally */
+#else
+  .registercallback  = 0,  /* not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi2dev =
+{
+  .spidev   =
+    {
+      &g_spi2ops
+    },
+  .spibase  = STM32WB_SPI2_BASE,
+  .spiclock = STM32WB_PCLK1_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI2,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  .rxch     = DMACHAN_SPI2_RX,
+  .txch     = DMACHAN_SPI2_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spi_getreg
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg16(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg
+ *
+ * Description:
+ *   Write a 16-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 16-bit value to be written
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value)
+{
+  putreg16(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_getreg8
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 8-bit register
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_getreg8(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg8(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg8
+ *
+ * Description:
+ *   Write a 8-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 8-bit value to be written
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg8(struct stm32wb_spidev_s *priv,
+                               uint8_t offset, uint8_t value)
+{
+  putreg8(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_readword
+ *
+ * Description:
+ *   Read one word (TWO bytes!) from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Word as read
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_readbyte
+ *
+ * Description:
+ *   Read one byte from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Byte as read
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_readbyte(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg8(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_writeword
+ *
+ * Description:
+ *   Write one 16-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Word to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t word)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg(priv, STM32WB_SPI_DR_OFFSET, word);
+}
+
+/****************************************************************************
+ * Name: spi_writebyte
+ *
+ * Description:
+ *   Write one 8-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Byte to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writebyte(struct stm32wb_spidev_s *priv,
+                                 uint8_t byte)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg8(priv, STM32WB_SPI_DR_OFFSET, byte);
+}
+
+/****************************************************************************
+ * Name: spi_16bitmode
+ *
+ * Description:
+ *   Check if the SPI is operating in 16-bit mode
+ *
+ * Input Parameters:
+ *   priv     - Device-specific state data
+ *
+ * Returned Value:
+ *   true: 16-bit mode, false: 8-bit mode
+ *
+ ****************************************************************************/
+
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv)
+{
+  return (priv->nbits > 8);
+}
+
+/****************************************************************************
+ * Name: spi_dmarxwaitw
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmarxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->rxsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->rxresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwait
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmatxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->txsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->txresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv)
+{
+  nxsem_post(&priv->rxsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv)
+{
+  nxsem_post(&priv->txsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->rxresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmarxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->txresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmatxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxsetup
+ *
+ * Description:
+ *   Setup to perform RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                           void *rxbuffer, void *rxdummy, size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA16_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA8_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Configure the RX DMA */
+
+  stm32wb_dmasetup(priv->rxdma, priv->spibase + STM32WB_SPI_DR_OFFSET,
+                 (uint32_t)rxbuffer, nwords, priv->rxccr);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxsetup
+ *
+ * Description:
+ *   Setup to perform TX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                           const void *txbuffer, const void *txdummy,
+                           size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA16_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA8_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Setup the TX DMA */
+
+  stm32wb_dmasetup(priv->txdma, priv->spibase + STM32WB_SPI_DR_OFFSET,
+                 (uint32_t)txbuffer, nwords, priv->txccr);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxstart
+ *
+ * Description:
+ *   Start RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv)
+{
+  priv->rxresult = 0;
+  stm32wb_dmastart(priv->rxdma, spi_dmarxcallback, priv, false);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxstart
+ *
+ * Description:
+ *   Start TX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv)
+{
+  priv->txresult = 0;
+  stm32wb_dmastart(priv->txdma, spi_dmatxcallback, priv, false);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_modifycr
+ *
+ * Description:
+ *   Clear and set bits in the CR1 or CR2 register
+ *
+ * Input Parameters:
+ *   priv    - Device-specific state data
+ *   clrbits - The bits to clear
+ *   setbits - The bits to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_modifycr(uint32_t addr, struct stm32wb_spidev_s *priv,
+                         uint16_t setbits, uint16_t clrbits)
+{
+  uint16_t cr;
+
+  cr  = spi_getreg(priv, addr);
+  cr &= ~clrbits;
+  cr |= setbits;
+  spi_putreg(priv, addr, cr);
+}
+
+/****************************************************************************
+ * Name: spi_lock
+ *
+ * Description:
+ *   On SPI buses where there are multiple devices, it will be necessary to
+ *   lock SPI to have exclusive access to the buses for a sequence of
+ *   transfers.  The bus should be locked before the chip is selected. After
+ *   locking the SPI bus, the caller should then also call the setfrequency,
+ *   setbits, and setmode methods to make sure that the SPI is properly
+ *   configured for the device.  If the SPI bus is being shared, then it
+ *   may have been left in an incompatible state.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   lock - true: Lock spi bus, false: unlock SPI bus
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int spi_lock(struct spi_dev_s *dev, bool lock)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  int ret;
+
+  if (lock)
+    {
+      ret = nxsem_wait_uninterruptible(&priv->exclsem);
+    }
+  else
+    {
+      ret = nxsem_post(&priv->exclsem);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: spi_setfrequency
+ *
+ * Description:
+ *   Set the SPI frequency.
+ *
+ * Input Parameters:
+ *   dev -       Device-specific state data
+ *   frequency - The SPI frequency requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint16_t setbits;
+  uint32_t actual;
+
+  /* Limit to max possible (if STM32WB_SPI_CLK_MAX is defined in board.h) */
+
+  if (frequency > STM32WB_SPI_CLK_MAX)
+    {
+      frequency = STM32WB_SPI_CLK_MAX;
+    }
+
+  /* Has the frequency changed? */
+
+  if (frequency != priv->frequency)
+    {
+      /* Choices are limited by PCLK frequency with a set of divisors */
+
+      if (frequency >= priv->spiclock >> 1)
+        {
+          /* More than fPCLK/2.  This is as fast as we can go */
+
+          setbits = SPI_CR1_FPCLCKd2; /* 000: fPCLK/2 */
+          actual = priv->spiclock >> 1;
+        }
+      else if (frequency >= priv->spiclock >> 2)
+        {
+          /* Between fPCLCK/2 and fPCLCK/4, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd4; /* 001: fPCLK/4 */
+          actual = priv->spiclock >> 2;
+        }
+      else if (frequency >= priv->spiclock >> 3)
+        {
+          /* Between fPCLCK/4 and fPCLCK/8, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd8; /* 010: fPCLK/8 */
+          actual = priv->spiclock >> 3;
+        }
+      else if (frequency >= priv->spiclock >> 4)
+        {
+          /* Between fPCLCK/8 and fPCLCK/16, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd16; /* 011: fPCLK/16 */
+          actual = priv->spiclock >> 4;
+        }
+      else if (frequency >= priv->spiclock >> 5)
+        {
+          /* Between fPCLCK/16 and fPCLCK/32, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd32; /* 100: fPCLK/32 */
+          actual = priv->spiclock >> 5;
+        }
+      else if (frequency >= priv->spiclock >> 6)
+        {
+          /* Between fPCLCK/32 and fPCLCK/64, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd64; /*  101: fPCLK/64 */
+          actual = priv->spiclock >> 6;
+        }
+      else if (frequency >= priv->spiclock >> 7)
+        {
+          /* Between fPCLCK/64 and fPCLCK/128, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd128; /* 110: fPCLK/128 */
+          actual = priv->spiclock >> 7;
+        }
+      else
+        {
+          /* Less than fPCLK/128.  This is as slow as we can go */
+
+          setbits = SPI_CR1_FPCLCKd256; /* 111: fPCLK/256 */
+          actual = priv->spiclock >> 8;
+        }
+
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, setbits, SPI_CR1_BR_MASK);
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+      /* Save the frequency selection so that subsequent reconfigurations
+       * will be faster.
+       */
+
+      spiinfo("Frequency %" PRId32 "->%" PRId32 "\n", frequency, actual);
+
+      priv->frequency = frequency;
+      priv->actual    = actual;
+    }
+
+  return priv->actual;
+}
+
+/****************************************************************************
+ * Name: spi_setmode
+ *
+ * Description:
+ *   Set the SPI mode.  see enum spi_mode_e for mode definitions
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   mode - The SPI mode requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("mode=%d\n", mode);
+
+  /* Has the mode changed? */
+
+  if (mode != priv->mode)
+    {
+      /* Yes... Set CR1 appropriately */
+
+      switch (mode)
+        {
+        case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
+          setbits = 0;
+          clrbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          break;
+
+        case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
+          setbits = SPI_CR1_CPHA;
+          clrbits = SPI_CR1_CPOL;
+          break;
+
+        case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
+          setbits = SPI_CR1_CPOL;
+          clrbits = SPI_CR1_CPHA;
+          break;
+
+        case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
+          setbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          clrbits = 0;
+          break;
+
+        default:
+          return;

Review Comment:
   apply 2 spaces for case block



##########
arch/arm/src/stm32wb/stm32wb_spi.c:
##########
@@ -0,0 +1,1819 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * The external functions, stm32wb_spi1/2select and stm32wb_spi1/2status
+ * must be provided by board-specific logic.  They are implementations of the
+ * select and status methods of the SPI interface defined by struct spi_ops_s
+ * (see include/nuttx/spi/spi.h).  All other methods (including
+ * stm32wb_spibus_initialize()) are provided by common STM32 logic.  To use
+ * this common SPI logic on your board:
+ *
+ *   1. Provide logic in stm32wb_board_initialize() to configure SPI chip
+ *      select pins.
+ *   2. Provide stm32wb_spi1/2select() and stm32wb_spi1/2status()
+ *      functions in your board-specific logic.  These functions will perform
+ *      chip selection and status operations using GPIOs in the way your
+ *      board is configured.
+ *   3. Add a calls to stm32wb_spibus_initialize() in your low level
+ *      application initialization logic
+ *   4. The handle returned by stm32wb_spibus_initialize() may then be used
+ *      to bind the SPI driver to higher level logic (e.g., calling
+ *      mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ *      the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+/* This driver is ported from the stm32 one, which only supports 8 and 16
+ * bits transfers. The STM32WB family supports frame size from 4 to 16 bits,
+ * but we do not support that yet. For the moment, we replace uses of the
+ * CR1_DFF bit with a check of the CR2_DS[0..3] bits. If the value is
+ * SPI_CR2_DS_16BIT it means 16 bits, else 8 bits.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/power/pm.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_spi.h"
+
+#include <arch/board/board.h>
+
+#if defined(CONFIG_STM32WB_SPI1) || defined(CONFIG_STM32WB_SPI2)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* SPI interrupts */
+
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+#  error "Interrupt driven SPI not yet supported"
+#endif
+
+/* Can't have both interrupt driven SPI and SPI DMA */
+
+#if defined(CONFIG_STM32WB_SPI_INTERRUPTS) && defined(CONFIG_STM32WB_SPI_DMA)
+#  error "Cannot enable both interrupt mode and DMA mode for SPI"
+#endif
+
+/* SPI DMA priority */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+
+#  if defined(CONFIG_SPI_DMAPRIO)
+#    define SPI_DMA_PRIO  CONFIG_SPI_DMAPRIO
+#  else
+#    define SPI_DMA_PRIO  DMA_CCR_PRIMED
+#  endif
+
+#  if (SPI_DMA_PRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_SPI_DMAPRIO"
+#  endif
+
+#endif
+
+/* DMA channel configuration */
+
+#define SPI_RXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC            )
+#define SPI_RXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC            )
+#define SPI_RXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS                         )
+#define SPI_RXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS                          )
+#define SPI_TXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS             |DMA_CCR_DIR)
+#define SPI_TXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS              |DMA_CCR_DIR)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_spidev_s
+{
+  struct spi_dev_s spidev;       /* Externally visible part of the SPI interface */
+  uint32_t         spibase;      /* SPIn base address */
+  uint32_t         spiclock;     /* Clocking for the SPI module */
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  uint8_t          spiirq;       /* SPI IRQ number */
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  volatile uint8_t rxresult;     /* Result of the RX DMA */
+  volatile uint8_t txresult;     /* Result of the RX DMA */
+#ifdef CONFIG_SPI_TRIGGER
+  bool             defertrig;    /* Flag indicating that trigger should be deferred */
+  bool             trigarmed;    /* Flag indicating that the trigger is armed */
+#endif
+  uint16_t         rxch;         /* The RX DMA channel number */
+  uint16_t         txch;         /* The TX DMA channel number */
+  DMA_HANDLE       rxdma;        /* DMA channel handle for RX transfers */
+  DMA_HANDLE       txdma;        /* DMA channel handle for TX transfers */
+  sem_t            rxsem;        /* Wait for RX DMA to complete */
+  sem_t            txsem;        /* Wait for TX DMA to complete */
+  uint32_t         txccr;        /* DMA control register for TX transfers */
+  uint32_t         rxccr;        /* DMA control register for RX transfers */
+#endif
+  bool             initialized;  /* Has SPI interface been initialized */
+  sem_t            exclsem;      /* Held while chip is selected for mutual exclusion */
+  uint32_t         frequency;    /* Requested clock frequency */
+  uint32_t         actual;       /* Actual clock frequency */
+  uint8_t          nbits;        /* Width of word in bits (4 through 16) */
+  uint8_t          mode;         /* Mode 0,1,2,3 */
+#ifdef CONFIG_PM
+  struct pm_callback_s pm_cb;    /* PM callbacks */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset);
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value);
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv);
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t byte);
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv);
+
+/* DMA support */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int         spi_dmarxwait(struct stm32wb_spidev_s *priv);
+static int         spi_dmatxwait(struct stm32wb_spidev_s *priv);
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv);
+static void        spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                                  void *rxbuffer, void *rxdummy,
+                                  size_t nwords);
+static void        spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                                  const void *txbuffer,
+                                  const void *txdummy, size_t nwords);
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv);
+#endif
+
+/* SPI methods */
+
+static int         spi_lock(struct spi_dev_s *dev, bool lock);
+static uint32_t    spi_setfrequency(struct spi_dev_s *dev,
+                                    uint32_t frequency);
+static void        spi_setmode(struct spi_dev_s *dev,
+                               enum spi_mode_e mode);
+static void        spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int         spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t    spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void        spi_exchange(struct spi_dev_s *dev,
+                                const void *txbuffer,
+                                void *rxbuffer, size_t nwords);
+#ifdef CONFIG_SPI_TRIGGER
+static int         spi_trigger(struct spi_dev_s *dev);
+#endif
+#ifndef CONFIG_SPI_EXCHANGE
+static void        spi_sndblock(struct spi_dev_s *dev,
+                                const void *txbuffer, size_t nwords);
+static void        spi_recvblock(struct spi_dev_s *dev,
+                                 void *rxbuffer, size_t nwords);
+#endif
+
+/* Initialization */
+
+static void        spi_bus_initialize(struct stm32wb_spidev_s *priv);
+
+/* PM interface */
+
+#ifdef CONFIG_PM
+static int         spi_pm_prepare(struct pm_callback_s *cb, int domain,
+                                  enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI1
+static const struct spi_ops_s g_spi1ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi1select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi1status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi1cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi1register,  /* Provided externally */
+#else
+  .registercallback  = 0,                   /* Not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi1dev =
+{
+  .spidev   =
+    {
+      &g_spi1ops
+    },
+  .spibase  = STM32WB_SPI1_BASE,
+  .spiclock = STM32WB_PCLK2_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI1,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  /* lines must be configured in board.h */
+
+  .rxch     = DMAMAP_SPI1_RX,
+  .txch     = DMAMAP_SPI1_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+#ifdef CONFIG_STM32WB_SPI2
+static const struct spi_ops_s g_spi2ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi2select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi2status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi2cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi2register,  /* provided externally */
+#else
+  .registercallback  = 0,  /* not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi2dev =
+{
+  .spidev   =
+    {
+      &g_spi2ops
+    },
+  .spibase  = STM32WB_SPI2_BASE,
+  .spiclock = STM32WB_PCLK1_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI2,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  .rxch     = DMACHAN_SPI2_RX,
+  .txch     = DMACHAN_SPI2_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spi_getreg
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg16(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg
+ *
+ * Description:
+ *   Write a 16-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 16-bit value to be written
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value)
+{
+  putreg16(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_getreg8
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 8-bit register
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_getreg8(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg8(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg8
+ *
+ * Description:
+ *   Write a 8-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 8-bit value to be written
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg8(struct stm32wb_spidev_s *priv,
+                               uint8_t offset, uint8_t value)
+{
+  putreg8(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_readword
+ *
+ * Description:
+ *   Read one word (TWO bytes!) from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Word as read
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_readbyte
+ *
+ * Description:
+ *   Read one byte from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Byte as read
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_readbyte(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg8(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_writeword
+ *
+ * Description:
+ *   Write one 16-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Word to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t word)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg(priv, STM32WB_SPI_DR_OFFSET, word);
+}
+
+/****************************************************************************
+ * Name: spi_writebyte
+ *
+ * Description:
+ *   Write one 8-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Byte to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writebyte(struct stm32wb_spidev_s *priv,
+                                 uint8_t byte)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg8(priv, STM32WB_SPI_DR_OFFSET, byte);
+}
+
+/****************************************************************************
+ * Name: spi_16bitmode
+ *
+ * Description:
+ *   Check if the SPI is operating in 16-bit mode
+ *
+ * Input Parameters:
+ *   priv     - Device-specific state data
+ *
+ * Returned Value:
+ *   true: 16-bit mode, false: 8-bit mode
+ *
+ ****************************************************************************/
+
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv)
+{
+  return (priv->nbits > 8);
+}
+
+/****************************************************************************
+ * Name: spi_dmarxwaitw
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmarxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->rxsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->rxresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwait
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmatxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->txsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->txresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv)
+{
+  nxsem_post(&priv->rxsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv)
+{
+  nxsem_post(&priv->txsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->rxresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmarxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->txresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmatxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxsetup
+ *
+ * Description:
+ *   Setup to perform RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                           void *rxbuffer, void *rxdummy, size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA16_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA8_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Configure the RX DMA */
+
+  stm32wb_dmasetup(priv->rxdma, priv->spibase + STM32WB_SPI_DR_OFFSET,
+                 (uint32_t)rxbuffer, nwords, priv->rxccr);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxsetup
+ *
+ * Description:
+ *   Setup to perform TX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                           const void *txbuffer, const void *txdummy,
+                           size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA16_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA8_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Setup the TX DMA */
+
+  stm32wb_dmasetup(priv->txdma, priv->spibase + STM32WB_SPI_DR_OFFSET,
+                 (uint32_t)txbuffer, nwords, priv->txccr);

Review Comment:
   ```suggestion
     stm32wb_dmasetup(priv->txdma, priv->spibase + STM32WB_SPI_DR_OFFSET,
                      (uint32_t)txbuffer, nwords, priv->txccr);
   ```



##########
arch/arm/src/stm32wb/stm32wb_uid.c:
##########
@@ -0,0 +1,46 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_uid.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "hardware/stm32wb_memorymap.h"
+#include "stm32wb_uid.h"
+
+#ifdef STM32WB_SYSMEM_UID
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+void stm32wb_get_uniqueid(uint8_t uniqueid[12])
+{
+  int i;
+
+  for (i = 0; i < 12; i++)
+    {
+      uniqueid[i] = *((uint8_t *)(STM32WB_SYSMEM_UID)+i);

Review Comment:
   ```suggestion
         uniqueid[i] = *((uint8_t *)(STM32WB_SYSMEM_UID) + i);
   ```



##########
arch/arm/src/stm32wb/stm32wb_spi.c:
##########
@@ -0,0 +1,1819 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * The external functions, stm32wb_spi1/2select and stm32wb_spi1/2status
+ * must be provided by board-specific logic.  They are implementations of the
+ * select and status methods of the SPI interface defined by struct spi_ops_s
+ * (see include/nuttx/spi/spi.h).  All other methods (including
+ * stm32wb_spibus_initialize()) are provided by common STM32 logic.  To use
+ * this common SPI logic on your board:
+ *
+ *   1. Provide logic in stm32wb_board_initialize() to configure SPI chip
+ *      select pins.
+ *   2. Provide stm32wb_spi1/2select() and stm32wb_spi1/2status()
+ *      functions in your board-specific logic.  These functions will perform
+ *      chip selection and status operations using GPIOs in the way your
+ *      board is configured.
+ *   3. Add a calls to stm32wb_spibus_initialize() in your low level
+ *      application initialization logic
+ *   4. The handle returned by stm32wb_spibus_initialize() may then be used
+ *      to bind the SPI driver to higher level logic (e.g., calling
+ *      mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ *      the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+/* This driver is ported from the stm32 one, which only supports 8 and 16
+ * bits transfers. The STM32WB family supports frame size from 4 to 16 bits,
+ * but we do not support that yet. For the moment, we replace uses of the
+ * CR1_DFF bit with a check of the CR2_DS[0..3] bits. If the value is
+ * SPI_CR2_DS_16BIT it means 16 bits, else 8 bits.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/power/pm.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_spi.h"
+
+#include <arch/board/board.h>
+
+#if defined(CONFIG_STM32WB_SPI1) || defined(CONFIG_STM32WB_SPI2)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* SPI interrupts */
+
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+#  error "Interrupt driven SPI not yet supported"
+#endif
+
+/* Can't have both interrupt driven SPI and SPI DMA */
+
+#if defined(CONFIG_STM32WB_SPI_INTERRUPTS) && defined(CONFIG_STM32WB_SPI_DMA)
+#  error "Cannot enable both interrupt mode and DMA mode for SPI"
+#endif
+
+/* SPI DMA priority */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+
+#  if defined(CONFIG_SPI_DMAPRIO)
+#    define SPI_DMA_PRIO  CONFIG_SPI_DMAPRIO
+#  else
+#    define SPI_DMA_PRIO  DMA_CCR_PRIMED
+#  endif
+
+#  if (SPI_DMA_PRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_SPI_DMAPRIO"
+#  endif
+
+#endif
+
+/* DMA channel configuration */
+
+#define SPI_RXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC            )
+#define SPI_RXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC            )
+#define SPI_RXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS                         )
+#define SPI_RXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS                          )
+#define SPI_TXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS             |DMA_CCR_DIR)
+#define SPI_TXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS              |DMA_CCR_DIR)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_spidev_s
+{
+  struct spi_dev_s spidev;       /* Externally visible part of the SPI interface */
+  uint32_t         spibase;      /* SPIn base address */
+  uint32_t         spiclock;     /* Clocking for the SPI module */
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  uint8_t          spiirq;       /* SPI IRQ number */
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  volatile uint8_t rxresult;     /* Result of the RX DMA */
+  volatile uint8_t txresult;     /* Result of the RX DMA */
+#ifdef CONFIG_SPI_TRIGGER
+  bool             defertrig;    /* Flag indicating that trigger should be deferred */
+  bool             trigarmed;    /* Flag indicating that the trigger is armed */
+#endif
+  uint16_t         rxch;         /* The RX DMA channel number */
+  uint16_t         txch;         /* The TX DMA channel number */
+  DMA_HANDLE       rxdma;        /* DMA channel handle for RX transfers */
+  DMA_HANDLE       txdma;        /* DMA channel handle for TX transfers */
+  sem_t            rxsem;        /* Wait for RX DMA to complete */
+  sem_t            txsem;        /* Wait for TX DMA to complete */
+  uint32_t         txccr;        /* DMA control register for TX transfers */
+  uint32_t         rxccr;        /* DMA control register for RX transfers */
+#endif
+  bool             initialized;  /* Has SPI interface been initialized */
+  sem_t            exclsem;      /* Held while chip is selected for mutual exclusion */
+  uint32_t         frequency;    /* Requested clock frequency */
+  uint32_t         actual;       /* Actual clock frequency */
+  uint8_t          nbits;        /* Width of word in bits (4 through 16) */
+  uint8_t          mode;         /* Mode 0,1,2,3 */
+#ifdef CONFIG_PM
+  struct pm_callback_s pm_cb;    /* PM callbacks */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset);
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value);
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv);
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t byte);
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv);
+
+/* DMA support */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int         spi_dmarxwait(struct stm32wb_spidev_s *priv);
+static int         spi_dmatxwait(struct stm32wb_spidev_s *priv);
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv);
+static void        spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                                  void *rxbuffer, void *rxdummy,
+                                  size_t nwords);
+static void        spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                                  const void *txbuffer,
+                                  const void *txdummy, size_t nwords);
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv);
+#endif
+
+/* SPI methods */
+
+static int         spi_lock(struct spi_dev_s *dev, bool lock);
+static uint32_t    spi_setfrequency(struct spi_dev_s *dev,
+                                    uint32_t frequency);
+static void        spi_setmode(struct spi_dev_s *dev,
+                               enum spi_mode_e mode);
+static void        spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int         spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t    spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void        spi_exchange(struct spi_dev_s *dev,
+                                const void *txbuffer,
+                                void *rxbuffer, size_t nwords);
+#ifdef CONFIG_SPI_TRIGGER
+static int         spi_trigger(struct spi_dev_s *dev);
+#endif
+#ifndef CONFIG_SPI_EXCHANGE
+static void        spi_sndblock(struct spi_dev_s *dev,
+                                const void *txbuffer, size_t nwords);
+static void        spi_recvblock(struct spi_dev_s *dev,
+                                 void *rxbuffer, size_t nwords);
+#endif
+
+/* Initialization */
+
+static void        spi_bus_initialize(struct stm32wb_spidev_s *priv);
+
+/* PM interface */
+
+#ifdef CONFIG_PM
+static int         spi_pm_prepare(struct pm_callback_s *cb, int domain,
+                                  enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI1
+static const struct spi_ops_s g_spi1ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi1select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi1status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi1cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi1register,  /* Provided externally */
+#else
+  .registercallback  = 0,                   /* Not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi1dev =
+{
+  .spidev   =
+    {
+      &g_spi1ops
+    },
+  .spibase  = STM32WB_SPI1_BASE,
+  .spiclock = STM32WB_PCLK2_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI1,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  /* lines must be configured in board.h */
+
+  .rxch     = DMAMAP_SPI1_RX,
+  .txch     = DMAMAP_SPI1_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+#ifdef CONFIG_STM32WB_SPI2
+static const struct spi_ops_s g_spi2ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi2select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi2status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi2cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi2register,  /* provided externally */
+#else
+  .registercallback  = 0,  /* not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi2dev =
+{
+  .spidev   =
+    {
+      &g_spi2ops
+    },
+  .spibase  = STM32WB_SPI2_BASE,
+  .spiclock = STM32WB_PCLK1_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI2,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  .rxch     = DMACHAN_SPI2_RX,
+  .txch     = DMACHAN_SPI2_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spi_getreg
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg16(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg
+ *
+ * Description:
+ *   Write a 16-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 16-bit value to be written
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value)
+{
+  putreg16(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_getreg8
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 8-bit register
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_getreg8(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg8(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg8
+ *
+ * Description:
+ *   Write a 8-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 8-bit value to be written
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg8(struct stm32wb_spidev_s *priv,
+                               uint8_t offset, uint8_t value)
+{
+  putreg8(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_readword
+ *
+ * Description:
+ *   Read one word (TWO bytes!) from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Word as read
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_readbyte
+ *
+ * Description:
+ *   Read one byte from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Byte as read
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_readbyte(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg8(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_writeword
+ *
+ * Description:
+ *   Write one 16-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Word to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t word)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg(priv, STM32WB_SPI_DR_OFFSET, word);
+}
+
+/****************************************************************************
+ * Name: spi_writebyte
+ *
+ * Description:
+ *   Write one 8-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Byte to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writebyte(struct stm32wb_spidev_s *priv,
+                                 uint8_t byte)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg8(priv, STM32WB_SPI_DR_OFFSET, byte);
+}
+
+/****************************************************************************
+ * Name: spi_16bitmode
+ *
+ * Description:
+ *   Check if the SPI is operating in 16-bit mode
+ *
+ * Input Parameters:
+ *   priv     - Device-specific state data
+ *
+ * Returned Value:
+ *   true: 16-bit mode, false: 8-bit mode
+ *
+ ****************************************************************************/
+
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv)
+{
+  return (priv->nbits > 8);
+}
+
+/****************************************************************************
+ * Name: spi_dmarxwaitw
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmarxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->rxsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->rxresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwait
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmatxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->txsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->txresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv)
+{
+  nxsem_post(&priv->rxsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv)
+{
+  nxsem_post(&priv->txsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->rxresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmarxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->txresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmatxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxsetup
+ *
+ * Description:
+ *   Setup to perform RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                           void *rxbuffer, void *rxdummy, size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA16_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA8_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Configure the RX DMA */
+
+  stm32wb_dmasetup(priv->rxdma, priv->spibase + STM32WB_SPI_DR_OFFSET,
+                 (uint32_t)rxbuffer, nwords, priv->rxccr);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxsetup
+ *
+ * Description:
+ *   Setup to perform TX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                           const void *txbuffer, const void *txdummy,
+                           size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA16_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA8_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Setup the TX DMA */
+
+  stm32wb_dmasetup(priv->txdma, priv->spibase + STM32WB_SPI_DR_OFFSET,
+                 (uint32_t)txbuffer, nwords, priv->txccr);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxstart
+ *
+ * Description:
+ *   Start RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv)
+{
+  priv->rxresult = 0;
+  stm32wb_dmastart(priv->rxdma, spi_dmarxcallback, priv, false);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxstart
+ *
+ * Description:
+ *   Start TX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv)
+{
+  priv->txresult = 0;
+  stm32wb_dmastart(priv->txdma, spi_dmatxcallback, priv, false);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_modifycr
+ *
+ * Description:
+ *   Clear and set bits in the CR1 or CR2 register
+ *
+ * Input Parameters:
+ *   priv    - Device-specific state data
+ *   clrbits - The bits to clear
+ *   setbits - The bits to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_modifycr(uint32_t addr, struct stm32wb_spidev_s *priv,
+                         uint16_t setbits, uint16_t clrbits)
+{
+  uint16_t cr;
+
+  cr  = spi_getreg(priv, addr);
+  cr &= ~clrbits;
+  cr |= setbits;
+  spi_putreg(priv, addr, cr);
+}
+
+/****************************************************************************
+ * Name: spi_lock
+ *
+ * Description:
+ *   On SPI buses where there are multiple devices, it will be necessary to
+ *   lock SPI to have exclusive access to the buses for a sequence of
+ *   transfers.  The bus should be locked before the chip is selected. After
+ *   locking the SPI bus, the caller should then also call the setfrequency,
+ *   setbits, and setmode methods to make sure that the SPI is properly
+ *   configured for the device.  If the SPI bus is being shared, then it
+ *   may have been left in an incompatible state.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   lock - true: Lock spi bus, false: unlock SPI bus
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int spi_lock(struct spi_dev_s *dev, bool lock)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  int ret;
+
+  if (lock)
+    {
+      ret = nxsem_wait_uninterruptible(&priv->exclsem);
+    }
+  else
+    {
+      ret = nxsem_post(&priv->exclsem);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: spi_setfrequency
+ *
+ * Description:
+ *   Set the SPI frequency.
+ *
+ * Input Parameters:
+ *   dev -       Device-specific state data
+ *   frequency - The SPI frequency requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint16_t setbits;
+  uint32_t actual;
+
+  /* Limit to max possible (if STM32WB_SPI_CLK_MAX is defined in board.h) */
+
+  if (frequency > STM32WB_SPI_CLK_MAX)
+    {
+      frequency = STM32WB_SPI_CLK_MAX;
+    }
+
+  /* Has the frequency changed? */
+
+  if (frequency != priv->frequency)
+    {
+      /* Choices are limited by PCLK frequency with a set of divisors */
+
+      if (frequency >= priv->spiclock >> 1)
+        {
+          /* More than fPCLK/2.  This is as fast as we can go */
+
+          setbits = SPI_CR1_FPCLCKd2; /* 000: fPCLK/2 */
+          actual = priv->spiclock >> 1;
+        }
+      else if (frequency >= priv->spiclock >> 2)
+        {
+          /* Between fPCLCK/2 and fPCLCK/4, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd4; /* 001: fPCLK/4 */
+          actual = priv->spiclock >> 2;
+        }
+      else if (frequency >= priv->spiclock >> 3)
+        {
+          /* Between fPCLCK/4 and fPCLCK/8, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd8; /* 010: fPCLK/8 */
+          actual = priv->spiclock >> 3;
+        }
+      else if (frequency >= priv->spiclock >> 4)
+        {
+          /* Between fPCLCK/8 and fPCLCK/16, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd16; /* 011: fPCLK/16 */
+          actual = priv->spiclock >> 4;
+        }
+      else if (frequency >= priv->spiclock >> 5)
+        {
+          /* Between fPCLCK/16 and fPCLCK/32, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd32; /* 100: fPCLK/32 */
+          actual = priv->spiclock >> 5;
+        }
+      else if (frequency >= priv->spiclock >> 6)
+        {
+          /* Between fPCLCK/32 and fPCLCK/64, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd64; /*  101: fPCLK/64 */
+          actual = priv->spiclock >> 6;
+        }
+      else if (frequency >= priv->spiclock >> 7)
+        {
+          /* Between fPCLCK/64 and fPCLCK/128, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd128; /* 110: fPCLK/128 */
+          actual = priv->spiclock >> 7;
+        }
+      else
+        {
+          /* Less than fPCLK/128.  This is as slow as we can go */
+
+          setbits = SPI_CR1_FPCLCKd256; /* 111: fPCLK/256 */
+          actual = priv->spiclock >> 8;
+        }
+
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, setbits, SPI_CR1_BR_MASK);
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+      /* Save the frequency selection so that subsequent reconfigurations
+       * will be faster.
+       */
+
+      spiinfo("Frequency %" PRId32 "->%" PRId32 "\n", frequency, actual);
+
+      priv->frequency = frequency;
+      priv->actual    = actual;
+    }
+
+  return priv->actual;
+}
+
+/****************************************************************************
+ * Name: spi_setmode
+ *
+ * Description:
+ *   Set the SPI mode.  see enum spi_mode_e for mode definitions
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   mode - The SPI mode requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("mode=%d\n", mode);
+
+  /* Has the mode changed? */
+
+  if (mode != priv->mode)
+    {
+      /* Yes... Set CR1 appropriately */
+
+      switch (mode)
+        {
+        case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
+          setbits = 0;
+          clrbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          break;
+
+        case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
+          setbits = SPI_CR1_CPHA;
+          clrbits = SPI_CR1_CPOL;
+          break;
+
+        case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
+          setbits = SPI_CR1_CPOL;
+          clrbits = SPI_CR1_CPHA;
+          break;
+
+        case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
+          setbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          clrbits = 0;
+          break;
+
+        default:
+          return;
+        }
+
+        spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+        spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, setbits, clrbits);
+        spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+        /* Save the mode so that subsequent re-configurations will be
+         * faster.
+         */
+
+        priv->mode = mode;
+    }
+}
+
+/****************************************************************************
+ * Name: spi_setbits
+ *
+ * Description:
+ *   Set the number of bits per word. With STM32WB, this is not restricted to
+ *   8 or 16, but can be any value between 4 and 16.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *   nbits - The number of bits requested, negative value means LSB first.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_setbits(struct spi_dev_s *dev, int nbits)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("nbits=%d\n", nbits);
+
+  /* Has the number of bits changed? */
+
+  if (nbits != priv->nbits)
+    {
+      /* Yes... Set CR2 appropriately */
+
+      /* Set the number of bits (valid range 4-16) */
+
+      if (nbits < 4 || nbits > 16)
+        {
+          spierr("ERROR: nbits out of range: %d\n", nbits);
+          return;
+        }
+
+      clrbits = SPI_CR2_DS_MASK;
+      setbits = SPI_CR2_DS_VAL(nbits);
+
+      /* If nbits is <=8, then we are in byte mode and FRXTH shall be set
+       * (else, transaction will not complete).
+       */
+
+      if (nbits < 9)
+        {
+          setbits |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 1 byte */
+        }
+      else
+        {
+          clrbits |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 2 bytes */
+        }
+
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+      spi_modifycr(STM32WB_SPI_CR2_OFFSET, priv, setbits, clrbits);
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+      /* Save the selection so that subsequent re-configurations will be
+       * faster.
+       */
+
+      priv->nbits = nbits;
+    }
+}
+
+/****************************************************************************
+ * Name: spi_hwfeatures
+ *
+ * Description:
+ *   Set hardware-specific feature flags.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   features - H/W feature flags
+ *
+ * Returned Value:
+ *   Zero (OK) if the selected H/W features are enabled; A negated errno
+ *   value if any H/W feature is not supportable.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_HWFEATURES
+static int spi_hwfeatures(struct spi_dev_s *dev, spi_hwfeatures_t features)
+{
+#if defined(CONFIG_SPI_BITORDER) || defined(CONFIG_SPI_TRIGGER)
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+#endif
+
+#ifdef CONFIG_SPI_BITORDER
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("features=%08x\n", features);
+
+  /* Transfer data LSB first? */
+
+  if ((features & HWFEAT_LSBFIRST) != 0)
+    {
+      setbits = SPI_CR1_LSBFIRST;
+      clrbits = 0;
+    }
+  else
+    {
+      setbits = 0;
+      clrbits = SPI_CR1_LSBFIRST;
+    }
+
+  spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+  spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, setbits, clrbits);
+  spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+  features &= ~HWFEAT_LSBFIRST;
+#endif
+
+#ifdef CONFIG_SPI_TRIGGER
+/* Turn deferred trigger mode on or off.  Only applicable for DMA mode. If a
+ * transfer is deferred then the DMA will not actually be triggered until a
+ * subsequent call to SPI_TRIGGER to set it off. The thread will be waiting
+ * on the transfer completing as normal.
+ */
+
+  priv->defertrig = ((features & HWFEAT_TRIGGER) != 0);
+  features &= ~HWFEAT_TRIGGER;
+#endif
+
+  /* Other H/W features are not supported */
+
+  return (features == 0) ? OK : -ENOSYS;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_send
+ *
+ * Description:
+ *   Exchange one word on SPI
+ *
+ * Input Parameters:
+ *   dev - Device-specific state data
+ *   wd  - The word to send.  the size of the data is determined by the
+ *         number of bits selected for the SPI interface.
+ *
+ * Returned Value:
+ *   response
+ *
+ ****************************************************************************/
+
+static uint32_t spi_send(struct spi_dev_s *dev, uint32_t wd)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint32_t regval;
+  uint32_t ret;
+
+  DEBUGASSERT(priv && priv->spibase);
+
+  /* According to the number of bits, access data register as word or byte
+   * This is absolutely required because of packing. With <=8 bit frames,
+   * two bytes are received by a 16-bit read of the data register!
+   */
+
+  if (spi_16bitmode(priv))
+    {
+      spi_writeword(priv, (uint16_t)(wd & 0xffff));
+      ret = (uint32_t)spi_readword(priv);
+    }
+  else
+    {
+      spi_writebyte(priv, (uint8_t)(wd & 0xff));
+      ret = (uint32_t)spi_readbyte(priv);
+    }
+
+  /* Check and clear any error flags (Reading from the SR clears the error
+   * flags).
+   */
+
+  regval = spi_getreg(priv, STM32WB_SPI_SR_OFFSET);
+
+  if (spi_16bitmode(priv))
+    {
+      spiinfo("Sent: %04" PRIx32 " Return: %04" PRIx32
+              " Status: %02" PRIx32 "\n", wd, ret, regval);
+    }
+  else
+    {
+      spiinfo("Sent: %02" PRIx32 " Return: %02" PRIx32
+              " Status: %02" PRIx32 "\n", wd, ret, regval);
+    }
+
+  UNUSED(regval);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: spi_exchange (no DMA).  aka spi_exchange_nodma
+ *
+ * Description:
+ *   Exchange a block of data on SPI without using DMA
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   txbuffer - A pointer to the buffer of data to be sent
+ *   rxbuffer - A pointer to a buffer in which to receive data
+ *   nwords   - the length of data to be exchanged in units of words.
+ *              The wordsize is determined by the number of bits-per-word
+ *              selected for the SPI interface.  If nbits <= 8, the data is
+ *              packed into uint8_t's; if nbits >8, the data is packed into
+ *              uint16_t's
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if !defined(CONFIG_STM32WB_SPI_DMA) || defined(CONFIG_STM32WB_DMACAPABLE)
+#if !defined(CONFIG_STM32WB_SPI_DMA)
+static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+                         void *rxbuffer, size_t nwords)
+#else
+static void spi_exchange_nodma(struct spi_dev_s *dev,
+                               const void *txbuffer, void *rxbuffer,
+                               size_t nwords)
+#endif
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  DEBUGASSERT(priv && priv->spibase);
+
+  spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);
+
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode */
+
+      const uint16_t *src  = (const uint16_t *)txbuffer;
+            uint16_t *dest = (uint16_t *)rxbuffer;
+            uint16_t  word;
+
+      while (nwords-- > 0)
+        {
+          /* Get the next word to write.  Is there a source buffer? */
+
+          if (src)
+            {
+              word = *src++;
+            }
+          else
+            {
+              word = 0xffff;
+            }
+
+          /* Exchange one word */
+
+          word = (uint16_t)spi_send(dev, (uint32_t)word);
+
+          /* Is there a buffer to receive the return value? */
+
+          if (dest)
+            {
+              *dest++ = word;
+            }
+        }
+    }
+  else
+    {
+      /* 8-bit mode */
+
+      const uint8_t *src  = (const uint8_t *)txbuffer;
+            uint8_t *dest = (uint8_t *)rxbuffer;
+            uint8_t  word;
+
+      while (nwords-- > 0)
+        {
+          /* Get the next word to write.  Is there a source buffer? */
+
+          if (src)
+            {
+              word = *src++;
+            }
+          else
+            {
+              word = 0xff;
+            }
+
+          /* Exchange one word */
+
+          word = (uint8_t)spi_send(dev, (uint32_t)word);
+
+          /* Is there a buffer to receive the return value? */
+
+          if (dest)
+            {
+              *dest++ = word;
+            }
+        }
+    }
+}
+#endif /* !CONFIG_STM32WB_SPI_DMA || CONFIG_STM32WB_DMACAPABLE */
+
+/****************************************************************************
+ * Name: spi_exchange (with DMA capability)
+ *
+ * Description:
+ *   Exchange a block of data on SPI using DMA
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   txbuffer - A pointer to the buffer of data to be sent
+ *   rxbuffer - A pointer to a buffer in which to receive data
+ *   nwords   - the length of data to be exchanged in units of words.
+ *              The wordsize is determined by the number of bits-per-word
+ *              selected for the SPI interface.  If nbits <= 8, the data is
+ *              packed into uint8_t's; if nbits >8, the data is packed into
+ *              uint16_t's
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+                         void *rxbuffer, size_t nwords)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  int ret;
+
+#ifdef CONFIG_STM32WB_DMACAPABLE
+  if ((txbuffer &&
+       !stm32wb_dmacapable((uint32_t)txbuffer, nwords, priv->txccr)) ||
+      (rxbuffer &&
+       !stm32wb_dmacapable((uint32_t)rxbuffer, nwords, priv->rxccr)))
+    {
+      /* Unsupported memory region, fall back to non-DMA method. */
+
+      spi_exchange_nodma(dev, txbuffer, rxbuffer, nwords);
+    }
+  else
+#endif
+    {
+      static uint16_t rxdummy = 0xffff;
+      static const uint16_t txdummy = 0xffff;
+
+      spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer,
+              nwords);
+      DEBUGASSERT(priv && priv->spibase);
+
+      /* Setup DMAs */
+
+      spi_dmarxsetup(priv, rxbuffer, &rxdummy, nwords);
+      spi_dmatxsetup(priv, txbuffer, &txdummy, nwords);
+
+#ifdef CONFIG_SPI_TRIGGER
+      /* Is deferred triggering in effect? */
+
+      if (!priv->defertrig)
+        {
+          /* No.. Start the DMAs */
+
+          spi_dmarxstart(priv);
+          spi_dmatxstart(priv);
+        }
+      else
+        {
+          /* Yes.. indicated that we are ready to be started */
+
+          priv->trigarmed = true;
+        }
+#else
+      /* Start the DMAs */
+
+      spi_dmarxstart(priv);
+      spi_dmatxstart(priv);
+#endif
+
+      /* Then wait for each to complete */
+
+      ret = spi_dmarxwait(priv);
+      if (ret >= 0)
+        {
+          ret = spi_dmatxwait(priv);
+          UNUSED(ret);
+        }
+
+#ifdef CONFIG_SPI_TRIGGER
+      priv->trigarmed = false;
+#endif
+    }
+}
+#endif /* CONFIG_STM32WB_SPI_DMA */
+
+/****************************************************************************
+ * Name: spi_trigger
+ *
+ * Description:
+ *   Trigger a previously configured DMA transfer.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *
+ * Returned Value:
+ *   OK       - Trigger was fired
+ *   ENOTSUP  - Trigger not fired due to lack of DMA support
+ *   EIO      - Trigger not fired because not previously primed
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_TRIGGER
+static int spi_trigger(struct spi_dev_s *dev)
+{
+#ifdef CONFIG_STM32WB_SPI_DMA
+  struct stm32_spidev_s *priv = (struct stm32_spidev_s *)dev;
+
+  if (!priv->trigarmed)
+    {
+      return -EIO;
+    }
+
+  spi_dmarxstart(priv);
+  spi_dmatxstart(priv);
+
+  return OK;
+#else
+  return -ENOSYS;
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_sndblock
+ *
+ * Description:
+ *   Send a block of data on SPI
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   txbuffer - A pointer to the buffer of data to be sent
+ *   nwords   - the length of data to send from the buffer in number of
+ *              words.  The wordsize is determined by the number of
+ *              bits-per-word selected for the SPI interface.  If nbits <= 8,
+ *              the data is packed into uint8_t's; if nbits >8, the data is
+ *              packed into uint16_t's
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SPI_EXCHANGE
+static void spi_sndblock(struct spi_dev_s *dev, const void *txbuffer,
+                         size_t nwords)
+{
+  spiinfo("txbuffer=%p nwords=%d\n", txbuffer, nwords);
+  return spi_exchange(dev, txbuffer, NULL, nwords);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_recvblock
+ *
+ * Description:
+ *   Receive a block of data from SPI
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   rxbuffer - A pointer to the buffer in which to receive data
+ *   nwords   - the length of data that can be received in the buffer in
+ *              number of words.  The wordsize is determined by the number of
+ *              bits-per-word selected for the SPI interface.  If nbits <= 8,
+ *              the data is packed into uint8_t's; if nbits >8, the data is
+ *              packed into uint16_t's
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SPI_EXCHANGE
+static void spi_recvblock(struct spi_dev_s *dev, void *rxbuffer,
+                          size_t nwords)
+{
+  spiinfo("rxbuffer=%p nwords=%d\n", rxbuffer, nwords);
+  return spi_exchange(dev, NULL, rxbuffer, nwords);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_pm_prepare
+ *
+ * Description:
+ *   Request the driver to prepare for a new power state. This is a
+ *   warning that the system is about to enter into a new power state.  The
+ *   driver should begin whatever operations that may be required to enter
+ *   power state.  The driver may abort the state change mode by returning
+ *   a non-zero value from the callback function.
+ *
+ * Input Parameters:
+ *   cb      - Returned to the driver.  The driver version of the callback
+ *             structure may include additional, driver-specific state
+ *             data at the end of the structure.
+ *   domain  - Identifies the activity domain of the state change
+ *   pmstate - Identifies the new PM state
+ *
+ * Returned Value:
+ *   0 (OK) means the event was successfully processed and that the driver
+ *   is prepared for the PM state change.  Non-zero means that the driver
+ *   is not prepared to perform the tasks needed achieve this power setting
+ *   and will cause the state change to be aborted.  NOTE:  The prepare
+ *   method will also be recalled when reverting from lower back to higher
+ *   power consumption modes (say because another driver refused a lower
+ *   power state change).  Drivers are not permitted to return non-zero
+ *   values when reverting back to higher power consumption modes!
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static int spi_pm_prepare(struct pm_callback_s *cb, int domain,
+                          enum pm_state_e pmstate)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)((char *)cb -
+                                  offsetof(struct stm32wb_spidev_s, pm_cb));
+  int sval;
+
+  /* Logic to prepare for a reduced power state goes here. */
+
+  switch (pmstate)
+    {
+    case PM_NORMAL:
+    case PM_IDLE:
+      break;
+
+    case PM_STANDBY:
+    case PM_SLEEP:
+
+      /* Check if exclusive lock for SPI bus is held. */
+
+      if (nxsem_get_value(&priv->exclsem, &sval) < 0)
+        {
+          DEBUGASSERT(false);
+          return -EINVAL;
+        }
+
+      if (sval <= 0)
+        {
+          /* Exclusive lock is held, do not allow entry to deeper PM states.
+           */
+
+          return -EBUSY;
+        }
+
+      break;
+
+    default:
+
+      /* Should not get here */
+
+      break;

Review Comment:
   Apply 2 spaces for case block



##########
arch/arm/src/stm32wb/stm32wb_spi.c:
##########
@@ -0,0 +1,1819 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * The external functions, stm32wb_spi1/2select and stm32wb_spi1/2status
+ * must be provided by board-specific logic.  They are implementations of the
+ * select and status methods of the SPI interface defined by struct spi_ops_s
+ * (see include/nuttx/spi/spi.h).  All other methods (including
+ * stm32wb_spibus_initialize()) are provided by common STM32 logic.  To use
+ * this common SPI logic on your board:
+ *
+ *   1. Provide logic in stm32wb_board_initialize() to configure SPI chip
+ *      select pins.
+ *   2. Provide stm32wb_spi1/2select() and stm32wb_spi1/2status()
+ *      functions in your board-specific logic.  These functions will perform
+ *      chip selection and status operations using GPIOs in the way your
+ *      board is configured.
+ *   3. Add a calls to stm32wb_spibus_initialize() in your low level
+ *      application initialization logic
+ *   4. The handle returned by stm32wb_spibus_initialize() may then be used
+ *      to bind the SPI driver to higher level logic (e.g., calling
+ *      mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ *      the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+/* This driver is ported from the stm32 one, which only supports 8 and 16
+ * bits transfers. The STM32WB family supports frame size from 4 to 16 bits,
+ * but we do not support that yet. For the moment, we replace uses of the
+ * CR1_DFF bit with a check of the CR2_DS[0..3] bits. If the value is
+ * SPI_CR2_DS_16BIT it means 16 bits, else 8 bits.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/power/pm.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_spi.h"
+
+#include <arch/board/board.h>
+
+#if defined(CONFIG_STM32WB_SPI1) || defined(CONFIG_STM32WB_SPI2)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* SPI interrupts */
+
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+#  error "Interrupt driven SPI not yet supported"
+#endif
+
+/* Can't have both interrupt driven SPI and SPI DMA */
+
+#if defined(CONFIG_STM32WB_SPI_INTERRUPTS) && defined(CONFIG_STM32WB_SPI_DMA)
+#  error "Cannot enable both interrupt mode and DMA mode for SPI"
+#endif
+
+/* SPI DMA priority */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+
+#  if defined(CONFIG_SPI_DMAPRIO)
+#    define SPI_DMA_PRIO  CONFIG_SPI_DMAPRIO
+#  else
+#    define SPI_DMA_PRIO  DMA_CCR_PRIMED
+#  endif
+
+#  if (SPI_DMA_PRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_SPI_DMAPRIO"
+#  endif
+
+#endif
+
+/* DMA channel configuration */
+
+#define SPI_RXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC            )
+#define SPI_RXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC            )
+#define SPI_RXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS                         )
+#define SPI_RXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS                          )
+#define SPI_TXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS             |DMA_CCR_DIR)
+#define SPI_TXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS              |DMA_CCR_DIR)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_spidev_s
+{
+  struct spi_dev_s spidev;       /* Externally visible part of the SPI interface */
+  uint32_t         spibase;      /* SPIn base address */
+  uint32_t         spiclock;     /* Clocking for the SPI module */
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  uint8_t          spiirq;       /* SPI IRQ number */
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  volatile uint8_t rxresult;     /* Result of the RX DMA */
+  volatile uint8_t txresult;     /* Result of the RX DMA */
+#ifdef CONFIG_SPI_TRIGGER
+  bool             defertrig;    /* Flag indicating that trigger should be deferred */
+  bool             trigarmed;    /* Flag indicating that the trigger is armed */
+#endif
+  uint16_t         rxch;         /* The RX DMA channel number */
+  uint16_t         txch;         /* The TX DMA channel number */
+  DMA_HANDLE       rxdma;        /* DMA channel handle for RX transfers */
+  DMA_HANDLE       txdma;        /* DMA channel handle for TX transfers */
+  sem_t            rxsem;        /* Wait for RX DMA to complete */
+  sem_t            txsem;        /* Wait for TX DMA to complete */
+  uint32_t         txccr;        /* DMA control register for TX transfers */
+  uint32_t         rxccr;        /* DMA control register for RX transfers */
+#endif
+  bool             initialized;  /* Has SPI interface been initialized */
+  sem_t            exclsem;      /* Held while chip is selected for mutual exclusion */
+  uint32_t         frequency;    /* Requested clock frequency */
+  uint32_t         actual;       /* Actual clock frequency */
+  uint8_t          nbits;        /* Width of word in bits (4 through 16) */
+  uint8_t          mode;         /* Mode 0,1,2,3 */
+#ifdef CONFIG_PM
+  struct pm_callback_s pm_cb;    /* PM callbacks */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset);
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value);
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv);
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t byte);
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv);
+
+/* DMA support */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int         spi_dmarxwait(struct stm32wb_spidev_s *priv);
+static int         spi_dmatxwait(struct stm32wb_spidev_s *priv);
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv);
+static void        spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                                  void *rxbuffer, void *rxdummy,
+                                  size_t nwords);
+static void        spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                                  const void *txbuffer,
+                                  const void *txdummy, size_t nwords);
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv);
+#endif
+
+/* SPI methods */
+
+static int         spi_lock(struct spi_dev_s *dev, bool lock);
+static uint32_t    spi_setfrequency(struct spi_dev_s *dev,
+                                    uint32_t frequency);
+static void        spi_setmode(struct spi_dev_s *dev,
+                               enum spi_mode_e mode);
+static void        spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int         spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t    spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void        spi_exchange(struct spi_dev_s *dev,
+                                const void *txbuffer,
+                                void *rxbuffer, size_t nwords);
+#ifdef CONFIG_SPI_TRIGGER
+static int         spi_trigger(struct spi_dev_s *dev);
+#endif
+#ifndef CONFIG_SPI_EXCHANGE
+static void        spi_sndblock(struct spi_dev_s *dev,
+                                const void *txbuffer, size_t nwords);
+static void        spi_recvblock(struct spi_dev_s *dev,
+                                 void *rxbuffer, size_t nwords);
+#endif
+
+/* Initialization */
+
+static void        spi_bus_initialize(struct stm32wb_spidev_s *priv);
+
+/* PM interface */
+
+#ifdef CONFIG_PM
+static int         spi_pm_prepare(struct pm_callback_s *cb, int domain,
+                                  enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI1
+static const struct spi_ops_s g_spi1ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi1select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi1status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi1cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi1register,  /* Provided externally */
+#else
+  .registercallback  = 0,                   /* Not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi1dev =
+{
+  .spidev   =
+    {
+      &g_spi1ops
+    },
+  .spibase  = STM32WB_SPI1_BASE,
+  .spiclock = STM32WB_PCLK2_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI1,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  /* lines must be configured in board.h */
+
+  .rxch     = DMAMAP_SPI1_RX,
+  .txch     = DMAMAP_SPI1_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+#ifdef CONFIG_STM32WB_SPI2
+static const struct spi_ops_s g_spi2ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi2select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi2status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi2cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi2register,  /* provided externally */
+#else
+  .registercallback  = 0,  /* not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi2dev =
+{
+  .spidev   =
+    {
+      &g_spi2ops
+    },
+  .spibase  = STM32WB_SPI2_BASE,
+  .spiclock = STM32WB_PCLK1_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI2,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  .rxch     = DMACHAN_SPI2_RX,
+  .txch     = DMACHAN_SPI2_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spi_getreg
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg16(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg
+ *
+ * Description:
+ *   Write a 16-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 16-bit value to be written
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value)
+{
+  putreg16(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_getreg8
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 8-bit register
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_getreg8(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg8(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg8
+ *
+ * Description:
+ *   Write a 8-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 8-bit value to be written
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg8(struct stm32wb_spidev_s *priv,
+                               uint8_t offset, uint8_t value)
+{
+  putreg8(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_readword
+ *
+ * Description:
+ *   Read one word (TWO bytes!) from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Word as read
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_readbyte
+ *
+ * Description:
+ *   Read one byte from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Byte as read
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_readbyte(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg8(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_writeword
+ *
+ * Description:
+ *   Write one 16-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Word to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t word)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg(priv, STM32WB_SPI_DR_OFFSET, word);
+}
+
+/****************************************************************************
+ * Name: spi_writebyte
+ *
+ * Description:
+ *   Write one 8-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Byte to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writebyte(struct stm32wb_spidev_s *priv,
+                                 uint8_t byte)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg8(priv, STM32WB_SPI_DR_OFFSET, byte);
+}
+
+/****************************************************************************
+ * Name: spi_16bitmode
+ *
+ * Description:
+ *   Check if the SPI is operating in 16-bit mode
+ *
+ * Input Parameters:
+ *   priv     - Device-specific state data
+ *
+ * Returned Value:
+ *   true: 16-bit mode, false: 8-bit mode
+ *
+ ****************************************************************************/
+
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv)
+{
+  return (priv->nbits > 8);
+}
+
+/****************************************************************************
+ * Name: spi_dmarxwaitw
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmarxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->rxsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->rxresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwait
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmatxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->txsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->txresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv)
+{
+  nxsem_post(&priv->rxsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv)
+{
+  nxsem_post(&priv->txsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->rxresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmarxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->txresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmatxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxsetup
+ *
+ * Description:
+ *   Setup to perform RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                           void *rxbuffer, void *rxdummy, size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA16_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA8_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Configure the RX DMA */
+
+  stm32wb_dmasetup(priv->rxdma, priv->spibase + STM32WB_SPI_DR_OFFSET,
+                 (uint32_t)rxbuffer, nwords, priv->rxccr);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxsetup
+ *
+ * Description:
+ *   Setup to perform TX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                           const void *txbuffer, const void *txdummy,
+                           size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA16_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA8_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Setup the TX DMA */
+
+  stm32wb_dmasetup(priv->txdma, priv->spibase + STM32WB_SPI_DR_OFFSET,
+                 (uint32_t)txbuffer, nwords, priv->txccr);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxstart
+ *
+ * Description:
+ *   Start RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv)
+{
+  priv->rxresult = 0;
+  stm32wb_dmastart(priv->rxdma, spi_dmarxcallback, priv, false);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxstart
+ *
+ * Description:
+ *   Start TX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv)
+{
+  priv->txresult = 0;
+  stm32wb_dmastart(priv->txdma, spi_dmatxcallback, priv, false);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_modifycr
+ *
+ * Description:
+ *   Clear and set bits in the CR1 or CR2 register
+ *
+ * Input Parameters:
+ *   priv    - Device-specific state data
+ *   clrbits - The bits to clear
+ *   setbits - The bits to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_modifycr(uint32_t addr, struct stm32wb_spidev_s *priv,
+                         uint16_t setbits, uint16_t clrbits)
+{
+  uint16_t cr;
+
+  cr  = spi_getreg(priv, addr);
+  cr &= ~clrbits;
+  cr |= setbits;
+  spi_putreg(priv, addr, cr);
+}
+
+/****************************************************************************
+ * Name: spi_lock
+ *
+ * Description:
+ *   On SPI buses where there are multiple devices, it will be necessary to
+ *   lock SPI to have exclusive access to the buses for a sequence of
+ *   transfers.  The bus should be locked before the chip is selected. After
+ *   locking the SPI bus, the caller should then also call the setfrequency,
+ *   setbits, and setmode methods to make sure that the SPI is properly
+ *   configured for the device.  If the SPI bus is being shared, then it
+ *   may have been left in an incompatible state.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   lock - true: Lock spi bus, false: unlock SPI bus
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int spi_lock(struct spi_dev_s *dev, bool lock)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  int ret;
+
+  if (lock)
+    {
+      ret = nxsem_wait_uninterruptible(&priv->exclsem);
+    }
+  else
+    {
+      ret = nxsem_post(&priv->exclsem);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: spi_setfrequency
+ *
+ * Description:
+ *   Set the SPI frequency.
+ *
+ * Input Parameters:
+ *   dev -       Device-specific state data
+ *   frequency - The SPI frequency requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint16_t setbits;
+  uint32_t actual;
+
+  /* Limit to max possible (if STM32WB_SPI_CLK_MAX is defined in board.h) */
+
+  if (frequency > STM32WB_SPI_CLK_MAX)
+    {
+      frequency = STM32WB_SPI_CLK_MAX;
+    }
+
+  /* Has the frequency changed? */
+
+  if (frequency != priv->frequency)
+    {
+      /* Choices are limited by PCLK frequency with a set of divisors */
+
+      if (frequency >= priv->spiclock >> 1)
+        {
+          /* More than fPCLK/2.  This is as fast as we can go */
+
+          setbits = SPI_CR1_FPCLCKd2; /* 000: fPCLK/2 */
+          actual = priv->spiclock >> 1;
+        }
+      else if (frequency >= priv->spiclock >> 2)
+        {
+          /* Between fPCLCK/2 and fPCLCK/4, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd4; /* 001: fPCLK/4 */
+          actual = priv->spiclock >> 2;
+        }
+      else if (frequency >= priv->spiclock >> 3)
+        {
+          /* Between fPCLCK/4 and fPCLCK/8, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd8; /* 010: fPCLK/8 */
+          actual = priv->spiclock >> 3;
+        }
+      else if (frequency >= priv->spiclock >> 4)
+        {
+          /* Between fPCLCK/8 and fPCLCK/16, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd16; /* 011: fPCLK/16 */
+          actual = priv->spiclock >> 4;
+        }
+      else if (frequency >= priv->spiclock >> 5)
+        {
+          /* Between fPCLCK/16 and fPCLCK/32, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd32; /* 100: fPCLK/32 */
+          actual = priv->spiclock >> 5;
+        }
+      else if (frequency >= priv->spiclock >> 6)
+        {
+          /* Between fPCLCK/32 and fPCLCK/64, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd64; /*  101: fPCLK/64 */
+          actual = priv->spiclock >> 6;
+        }
+      else if (frequency >= priv->spiclock >> 7)
+        {
+          /* Between fPCLCK/64 and fPCLCK/128, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd128; /* 110: fPCLK/128 */
+          actual = priv->spiclock >> 7;
+        }
+      else
+        {
+          /* Less than fPCLK/128.  This is as slow as we can go */
+
+          setbits = SPI_CR1_FPCLCKd256; /* 111: fPCLK/256 */
+          actual = priv->spiclock >> 8;
+        }
+
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, setbits, SPI_CR1_BR_MASK);
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+      /* Save the frequency selection so that subsequent reconfigurations
+       * will be faster.
+       */
+
+      spiinfo("Frequency %" PRId32 "->%" PRId32 "\n", frequency, actual);
+
+      priv->frequency = frequency;
+      priv->actual    = actual;
+    }
+
+  return priv->actual;
+}
+
+/****************************************************************************
+ * Name: spi_setmode
+ *
+ * Description:
+ *   Set the SPI mode.  see enum spi_mode_e for mode definitions
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   mode - The SPI mode requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("mode=%d\n", mode);
+
+  /* Has the mode changed? */
+
+  if (mode != priv->mode)
+    {
+      /* Yes... Set CR1 appropriately */
+
+      switch (mode)
+        {
+        case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
+          setbits = 0;
+          clrbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          break;
+
+        case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
+          setbits = SPI_CR1_CPHA;
+          clrbits = SPI_CR1_CPOL;
+          break;
+
+        case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
+          setbits = SPI_CR1_CPOL;
+          clrbits = SPI_CR1_CPHA;
+          break;
+
+        case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
+          setbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          clrbits = 0;
+          break;
+
+        default:
+          return;
+        }
+
+        spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+        spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, setbits, clrbits);
+        spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+        /* Save the mode so that subsequent re-configurations will be
+         * faster.
+         */
+
+        priv->mode = mode;
+    }
+}
+
+/****************************************************************************
+ * Name: spi_setbits
+ *
+ * Description:
+ *   Set the number of bits per word. With STM32WB, this is not restricted to
+ *   8 or 16, but can be any value between 4 and 16.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *   nbits - The number of bits requested, negative value means LSB first.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_setbits(struct spi_dev_s *dev, int nbits)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("nbits=%d\n", nbits);
+
+  /* Has the number of bits changed? */
+
+  if (nbits != priv->nbits)
+    {
+      /* Yes... Set CR2 appropriately */
+
+      /* Set the number of bits (valid range 4-16) */
+
+      if (nbits < 4 || nbits > 16)
+        {
+          spierr("ERROR: nbits out of range: %d\n", nbits);
+          return;
+        }
+
+      clrbits = SPI_CR2_DS_MASK;
+      setbits = SPI_CR2_DS_VAL(nbits);
+
+      /* If nbits is <=8, then we are in byte mode and FRXTH shall be set
+       * (else, transaction will not complete).
+       */
+
+      if (nbits < 9)
+        {
+          setbits |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 1 byte */
+        }
+      else
+        {
+          clrbits |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 2 bytes */
+        }
+
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+      spi_modifycr(STM32WB_SPI_CR2_OFFSET, priv, setbits, clrbits);
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+      /* Save the selection so that subsequent re-configurations will be
+       * faster.
+       */
+
+      priv->nbits = nbits;
+    }
+}
+
+/****************************************************************************
+ * Name: spi_hwfeatures
+ *
+ * Description:
+ *   Set hardware-specific feature flags.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   features - H/W feature flags
+ *
+ * Returned Value:
+ *   Zero (OK) if the selected H/W features are enabled; A negated errno
+ *   value if any H/W feature is not supportable.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_HWFEATURES
+static int spi_hwfeatures(struct spi_dev_s *dev, spi_hwfeatures_t features)
+{
+#if defined(CONFIG_SPI_BITORDER) || defined(CONFIG_SPI_TRIGGER)
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+#endif
+
+#ifdef CONFIG_SPI_BITORDER
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("features=%08x\n", features);
+
+  /* Transfer data LSB first? */
+
+  if ((features & HWFEAT_LSBFIRST) != 0)
+    {
+      setbits = SPI_CR1_LSBFIRST;
+      clrbits = 0;
+    }
+  else
+    {
+      setbits = 0;
+      clrbits = SPI_CR1_LSBFIRST;
+    }
+
+  spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+  spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, setbits, clrbits);
+  spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+  features &= ~HWFEAT_LSBFIRST;
+#endif
+
+#ifdef CONFIG_SPI_TRIGGER
+/* Turn deferred trigger mode on or off.  Only applicable for DMA mode. If a
+ * transfer is deferred then the DMA will not actually be triggered until a
+ * subsequent call to SPI_TRIGGER to set it off. The thread will be waiting
+ * on the transfer completing as normal.
+ */
+
+  priv->defertrig = ((features & HWFEAT_TRIGGER) != 0);
+  features &= ~HWFEAT_TRIGGER;
+#endif
+
+  /* Other H/W features are not supported */
+
+  return (features == 0) ? OK : -ENOSYS;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_send
+ *
+ * Description:
+ *   Exchange one word on SPI
+ *
+ * Input Parameters:
+ *   dev - Device-specific state data
+ *   wd  - The word to send.  the size of the data is determined by the
+ *         number of bits selected for the SPI interface.
+ *
+ * Returned Value:
+ *   response
+ *
+ ****************************************************************************/
+
+static uint32_t spi_send(struct spi_dev_s *dev, uint32_t wd)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint32_t regval;
+  uint32_t ret;
+
+  DEBUGASSERT(priv && priv->spibase);
+
+  /* According to the number of bits, access data register as word or byte
+   * This is absolutely required because of packing. With <=8 bit frames,
+   * two bytes are received by a 16-bit read of the data register!
+   */
+
+  if (spi_16bitmode(priv))
+    {
+      spi_writeword(priv, (uint16_t)(wd & 0xffff));
+      ret = (uint32_t)spi_readword(priv);
+    }
+  else
+    {
+      spi_writebyte(priv, (uint8_t)(wd & 0xff));
+      ret = (uint32_t)spi_readbyte(priv);
+    }
+
+  /* Check and clear any error flags (Reading from the SR clears the error
+   * flags).
+   */
+
+  regval = spi_getreg(priv, STM32WB_SPI_SR_OFFSET);
+
+  if (spi_16bitmode(priv))
+    {
+      spiinfo("Sent: %04" PRIx32 " Return: %04" PRIx32
+              " Status: %02" PRIx32 "\n", wd, ret, regval);
+    }
+  else
+    {
+      spiinfo("Sent: %02" PRIx32 " Return: %02" PRIx32
+              " Status: %02" PRIx32 "\n", wd, ret, regval);
+    }
+
+  UNUSED(regval);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: spi_exchange (no DMA).  aka spi_exchange_nodma
+ *
+ * Description:
+ *   Exchange a block of data on SPI without using DMA
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   txbuffer - A pointer to the buffer of data to be sent
+ *   rxbuffer - A pointer to a buffer in which to receive data
+ *   nwords   - the length of data to be exchanged in units of words.
+ *              The wordsize is determined by the number of bits-per-word
+ *              selected for the SPI interface.  If nbits <= 8, the data is
+ *              packed into uint8_t's; if nbits >8, the data is packed into
+ *              uint16_t's
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if !defined(CONFIG_STM32WB_SPI_DMA) || defined(CONFIG_STM32WB_DMACAPABLE)
+#if !defined(CONFIG_STM32WB_SPI_DMA)
+static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+                         void *rxbuffer, size_t nwords)
+#else
+static void spi_exchange_nodma(struct spi_dev_s *dev,
+                               const void *txbuffer, void *rxbuffer,
+                               size_t nwords)
+#endif
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  DEBUGASSERT(priv && priv->spibase);
+
+  spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);
+
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode */
+
+      const uint16_t *src  = (const uint16_t *)txbuffer;
+            uint16_t *dest = (uint16_t *)rxbuffer;
+            uint16_t  word;
+
+      while (nwords-- > 0)
+        {
+          /* Get the next word to write.  Is there a source buffer? */
+
+          if (src)
+            {
+              word = *src++;
+            }
+          else
+            {
+              word = 0xffff;
+            }
+
+          /* Exchange one word */
+
+          word = (uint16_t)spi_send(dev, (uint32_t)word);
+
+          /* Is there a buffer to receive the return value? */
+
+          if (dest)
+            {
+              *dest++ = word;
+            }
+        }
+    }
+  else
+    {
+      /* 8-bit mode */
+
+      const uint8_t *src  = (const uint8_t *)txbuffer;
+            uint8_t *dest = (uint8_t *)rxbuffer;
+            uint8_t  word;

Review Comment:
   ```suggestion
         const uint8_t *src  = (const uint8_t *)txbuffer;
         uint8_t *dest = (uint8_t *)rxbuffer;
         uint8_t  word;
   ```



##########
arch/arm/src/stm32wb/stm32wb_tickless.c:
##########
@@ -0,0 +1,837 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_tickless.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Tickless OS Support.
+ *
+ * When CONFIG_SCHED_TICKLESS is enabled, all support for timer interrupts
+ * is suppressed and the platform specific code is expected to provide the
+ * following custom functions.
+ *
+ *   void up_timer_initialize(void): Initializes the timer facilities.
+ *     Called early in the initialization sequence (by up_initialize()).
+ *   int up_timer_gettime(struct timespec *ts):  Returns the current
+ *     time from the platform specific time source.
+ *   int up_timer_cancel(void):  Cancels the interval timer.
+ *   int up_timer_start(const struct timespec *ts): Start (or re-starts)
+ *     the interval timer.
+ *
+ * The RTOS will provide the following interfaces for use by the platform-
+ * specific interval timer implementation:
+ *
+ *   void nxsched_timer_expiration(void):  Called by the platform-specific
+ *     logic when the interval timer expires.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * STM32WB Timer Usage
+ *
+ * This implementation uses one timer:  A free running timer to provide
+ * the current time and a capture/compare channel for timed-events.
+ *
+ * There are two interrupts generated from our timer, the overflow interrupt
+ * which drives the timing handler and the capture/compare interrupt which
+ * drives the interval handler.  There are some low level timer control
+ * functions implemented here because the API of stm32wb_tim.c does not
+ * provide adequate control over capture/compare interrupts.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <nuttx/arch.h>
+#include <debug.h>
+
+#include "arm_internal.h"
+#include "stm32wb_tim.h"
+
+#ifdef CONFIG_SCHED_TICKLESS
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Only TIM2 is a 32-bit timer */
+
+#undef HAVE_32BIT_TICKLESS
+
+#ifdef CONFIG_STM32WB_TICKLESS_TIMER
+#  if CONFIG_STM32WB_TICKLESS_TIMER == 2
+#    define HAVE_32BIT_TICKLESS 1
+#  endif
+#else
+#  error "STM32WB_TICKLESS_TIMER must be defined for tickless configuration"
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_tickless_s
+{
+  uint8_t timer;                     /* The timer/counter in use */
+  uint8_t channel;                   /* The timer channel to use
+                                      * for intervals */
+  struct stm32wb_tim_dev_s *tch;     /* Pointer returned by
+                                      * stm32wb_tim_init() */
+  uint32_t frequency;
+  uint32_t overflow;                 /* Timer counter overflow */
+  volatile bool pending;             /* True: pending task */
+  uint32_t period;                   /* Interval period */
+  uint32_t base;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct stm32wb_tickless_s g_tickless;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_getreg16
+ *
+ * Description:
+ *   Get a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline uint16_t stm32wb_getreg16(uint8_t offset)
+{
+  return getreg16(g_tickless.base + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_putreg16
+ *
+ * Description:
+ *   Put a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_putreg16(uint8_t offset, uint16_t value)
+{
+  putreg16(value, g_tickless.base + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_modifyreg16
+ *
+ * Description:
+ *   Modify a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_modifyreg16(uint8_t offset, uint16_t clearbits,
+                                     uint16_t setbits)
+{
+  modifyreg16(g_tickless.base + offset, clearbits, setbits);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_enableint
+ ****************************************************************************/
+
+static inline void stm32wb_tickless_enableint(int channel)
+{
+  stm32wb_modifyreg16(STM32WB_TIM_DIER_OFFSET, 0, 1 << channel);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_disableint
+ ****************************************************************************/
+
+static inline void stm32wb_tickless_disableint(int channel)
+{
+  stm32wb_modifyreg16(STM32WB_TIM_DIER_OFFSET, 1 << channel, 0);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_ackint
+ ****************************************************************************/
+
+static inline void stm32wb_tickless_ackint(int channel)
+{
+  stm32wb_putreg16(STM32WB_TIM_SR_OFFSET, ~(1 << channel));
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_getint
+ ****************************************************************************/
+
+static inline uint16_t stm32wb_tickless_getint(void)
+{
+  return stm32wb_getreg16(STM32WB_TIM_SR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_setchannel
+ ****************************************************************************/
+
+static int stm32wb_tickless_setchannel(uint8_t channel)
+{
+  uint16_t ccmr_orig = 0;
+  uint16_t ccmr_val = 0;
+  uint16_t ccer_val;
+  uint8_t ccmr_offset = STM32WB_TIM_CCMR1_OFFSET;
+
+  /* Further we use range as 0..3; if channel=0 it will also overflow here */
+
+  if (--channel > 3)
+    {
+      return -EINVAL;
+    }
+
+  /* Assume that channel is disabled and polarity is active high */
+
+  ccer_val = stm32wb_getreg16(STM32WB_TIM_CCER_OFFSET);
+  ccer_val &= ~(GTIM_CCER_CCXE(channel) | GTIM_CCER_CCXP(channel));
+
+  /* Frozen mode because we don't want to change the GPIO, preload register
+   * disabled.
+   */
+
+  ccmr_val = GTIM_CCMR_OCXM_FRZN(channel);
+
+  /* Set polarity */
+
+  ccer_val |= GTIM_CCER_CCXP(channel);
+
+  if (channel > 1)
+    {
+      ccmr_offset = STM32WB_TIM_CCMR2_OFFSET;
+    }
+
+  ccmr_orig  = stm32wb_getreg16(ccmr_offset);
+  ccmr_orig &= ~(GTIM_CCMR_OCXM_MASK(channel) | GTIM_CCMR_OCXPE(channel));
+  ccmr_orig |= ccmr_val;
+  stm32wb_putreg16(ccmr_offset, ccmr_orig);
+  stm32wb_putreg16(STM32WB_TIM_CCER_OFFSET, ccer_val);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_interval_handler
+ *
+ * Description:
+ *   Called when the timer counter matches the compare register
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called early in the initialization sequence before any special
+ *   concurrency protections are required.
+ *
+ ****************************************************************************/
+
+static void stm32wb_interval_handler(void)
+{
+  tmrinfo("Expired...\n");
+
+  /* Disable the compare interrupt now. */
+
+  stm32wb_tickless_disableint(g_tickless.channel);
+  stm32wb_tickless_ackint(g_tickless.channel);
+
+  g_tickless.pending = false;
+
+  nxsched_timer_expiration();
+}
+
+/****************************************************************************
+ * Name: stm32wb_timing_handler
+ *
+ * Description:
+ *   Timer interrupt callback.  When the freerun timer counter overflows,
+ *   this interrupt will occur.  We will just increment an overflow count.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void stm32wb_timing_handler(void)
+{
+  g_tickless.overflow++;
+
+  STM32WB_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_handler
+ *
+ * Description:
+ *   Generic interrupt handler for this timer.  It checks the source of the
+ *   interrupt and fires the appropriate handler.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int stm32wb_tickless_handler(int irq, void *context, void *arg)
+{
+  int interrupt_flags = stm32wb_tickless_getint();
+
+  if (interrupt_flags & GTIM_SR_UIF)
+    {
+      stm32wb_timing_handler();
+    }
+
+  if (interrupt_flags & (1 << g_tickless.channel))
+    {
+      stm32wb_interval_handler();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_timer_initialize
+ *
+ * Description:
+ *   Initializes all platform-specific timer facilities.  This function is
+ *   called early in the initialization sequence by up_initialize().
+ *   On return, the current up-time should be available from
+ *   up_timer_gettime() and the interval timer is ready for use (but not
+ *   actively timing.
+ *
+ *   Provided by platform-specific code and called from the architecture-
+ *   specific logic.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called early in the initialization sequence before any special
+ *   concurrency protections are required.
+ *
+ ****************************************************************************/
+
+void up_timer_initialize(void)
+{
+  switch (CONFIG_STM32WB_TICKLESS_TIMER)
+    {
+#ifdef CONFIG_STM32WB_TIM1
+      case 1:
+        g_tickless.base = STM32WB_TIM1_BASE;
+        break;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM2
+      case 2:
+        g_tickless.base = STM32WB_TIM2_BASE;
+        break;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM16
+      case 16:
+        g_tickless.base = STM32WB_TIM16_BASE;
+        break;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM17
+      case 17:
+        g_tickless.base = STM32WB_TIM17_BASE;
+        break;
+#endif
+
+      default:
+        DEBUGASSERT(0);
+    }
+
+  /* Get the TC frequency that corresponds to the requested resolution */
+
+  g_tickless.frequency = USEC_PER_SEC / (uint32_t)CONFIG_USEC_PER_TICK;
+  g_tickless.timer     = CONFIG_STM32WB_TICKLESS_TIMER;
+  g_tickless.channel   = CONFIG_STM32WB_TICKLESS_CHANNEL;
+  g_tickless.pending   = false;
+  g_tickless.period    = 0;
+  g_tickless.overflow  = 0;
+
+  tmrinfo("timer=%d channel=%d frequency=%lu Hz\n",
+           g_tickless.timer, g_tickless.channel, g_tickless.frequency);
+
+  g_tickless.tch = stm32wb_tim_init(g_tickless.timer);
+  if (!g_tickless.tch)
+    {
+      tmrerr("ERROR: Failed to allocate TIM%d\n", g_tickless.timer);
+      DEBUGASSERT(0);
+    }
+
+  STM32WB_TIM_SETCLOCK(g_tickless.tch, g_tickless.frequency);
+
+  /* Set up to receive the callback when the counter overflow occurs */
+
+  STM32WB_TIM_SETISR(g_tickless.tch, stm32wb_tickless_handler, NULL, 0);
+
+  /* Initialize interval to zero */
+
+  STM32WB_TIM_SETCOMPARE(g_tickless.tch, g_tickless.channel, 0);
+
+  /* Setup compare channel for the interval timing */
+
+  stm32wb_tickless_setchannel(g_tickless.channel);
+
+  /* Set timer period */
+
+#ifdef HAVE_32BIT_TICKLESS
+  STM32WB_TIM_SETPERIOD(g_tickless.tch, UINT32_MAX);
+#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP
+  g_oneshot_maxticks = UINT32_MAX;
+#endif
+#else
+  STM32WB_TIM_SETPERIOD(g_tickless.tch, UINT16_MAX);
+#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP
+  g_oneshot_maxticks = UINT16_MAX;
+#endif
+#endif
+
+  /* Initialize the counter */
+
+  STM32WB_TIM_SETMODE(g_tickless.tch, STM32WB_TIM_MODE_UP);
+
+  /* Start the timer */
+
+  STM32WB_TIM_ACKINT(g_tickless.tch, ~0);
+  STM32WB_TIM_ENABLEINT(g_tickless.tch, GTIM_DIER_UIE);
+}
+
+/****************************************************************************
+ * Name: up_timer_gettime
+ *
+ * Description:
+ *   Return the elapsed time since power-up (or, more correctly, since
+ *   up_timer_initialize() was called).  This function is functionally
+ *   equivalent to:
+ *
+ *      int clock_gettime(clockid_t clockid, struct timespec *ts);
+ *
+ *   when clockid is CLOCK_MONOTONIC.
+ *
+ *   This function provides the basis for reporting the current time and
+ *   also is used to eliminate error build-up from small errors in interval
+ *   time calculations.
+ *
+ *   Provided by platform-specific code and called from the RTOS base code.
+ *
+ * Input Parameters:
+ *   ts - Provides the location in which to return the up-time.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned on
+ *   any failure.
+ *
+ * Assumptions:
+ *   Called from the normal tasking context.  The implementation must
+ *   provide whatever mutual exclusion is necessary for correct operation.
+ *   This can include disabling interrupts in order to assure atomic register
+ *   operations.
+ *
+ ****************************************************************************/
+
+int up_timer_gettime(struct timespec *ts)
+{
+  uint64_t usec;
+  uint32_t counter;
+  uint32_t verify;
+  uint32_t overflow;
+  uint32_t sec;
+  int pending;
+  irqstate_t flags;
+
+  DEBUGASSERT(g_tickless.tch && ts);
+
+  /* Temporarily disable the overflow counter.  NOTE that we have to be
+   * careful here because  stm32wb_tc_getpending() will reset the pending
+   * interrupt status.  If we do not handle the overflow here then, it will
+   * be lost.
+   */
+
+  flags    = enter_critical_section();
+
+  overflow = g_tickless.overflow;
+  counter  = STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+  pending  = STM32WB_TIM_CHECKINT(g_tickless.tch, GTIM_SR_UIF);
+  verify   = STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+
+  /* If an interrupt was pending before we re-enabled interrupts,
+   * then the overflow needs to be incremented.
+   */
+
+  if (pending)
+    {
+      STM32WB_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF);
+
+      /* Increment the overflow count and use the value of the
+       * guaranteed to be AFTER the overflow occurred.
+       */
+
+      overflow++;
+      counter = verify;
+
+      /* Update tickless overflow counter. */
+
+      g_tickless.overflow = overflow;
+    }
+
+  leave_critical_section(flags);
+
+  tmrinfo("counter=%lu (%lu) overflow=%lu, pending=%i\n",
+         (unsigned long)counter,  (unsigned long)verify,
+         (unsigned long)overflow, pending);
+  tmrinfo("frequency=%lu\n", g_tickless.frequency);
+
+  /* Convert the whole thing to units of microseconds.
+   *
+   *   frequency = ticks / second
+   *   seconds   = ticks * frequency
+   *   usecs     = (ticks * USEC_PER_SEC) / frequency;
+   */
+#ifdef HAVE_32BIT_TICKLESS
+  usec = ((((uint64_t)overflow << 32) + (uint64_t)counter) * USEC_PER_SEC) /
+         g_tickless.frequency;
+#else
+  usec = ((((uint64_t)overflow << 16) + (uint64_t)counter) * USEC_PER_SEC) /
+         g_tickless.frequency;
+#endif
+
+  /* And return the value of the timer */
+
+  sec         = (uint32_t)(usec / USEC_PER_SEC);
+  ts->tv_sec  = sec;
+  ts->tv_nsec = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC;
+
+  tmrinfo("usec=%llu ts=(%lu, %lu)\n",
+          usec, (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec);
+
+  return OK;
+}
+
+#ifdef CONFIG_CLOCK_TIMEKEEPING
+
+/****************************************************************************
+ * Name: up_timer_getcounter
+ *
+ * Description:
+ *   To be provided
+ *
+ * Input Parameters:
+ *   cycles - 64-bit return value
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+int up_timer_getcounter(uint64_t *cycles)
+{
+  *cycles = (uint64_t)STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: up_timer_getmask
+ *
+ * Description:
+ *   To be provided
+ *
+ * Input Parameters:
+ *   mask - Location to return the 64-bit mask
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void up_timer_getmask(uint64_t *mask)
+{
+  DEBUGASSERT(mask != NULL);
+#ifdef HAVE_32BIT_TICKLESS
+  *mask = UINT32_MAX;
+#else
+  *mask = UINT16_MAX;
+#endif
+}
+
+#endif /* CONFIG_CLOCK_TIMEKEEPING */
+
+/****************************************************************************
+ * Name: up_timer_cancel
+ *
+ * Description:
+ *   Cancel the interval timer and return the time remaining on the timer.
+ *   These two steps need to be as nearly atomic as possible.
+ *   nxsched_timer_expiration() will not be called unless the timer is
+ *   restarted with up_timer_start().
+ *
+ *   If, as a race condition, the timer has already expired when this
+ *   function is called, then that pending interrupt must be cleared so
+ *   that up_timer_start() and the remaining time of zero should be
+ *   returned.
+ *
+ *   NOTE: This function may execute at a high rate with no timer running (as
+ *   when pre-emption is enabled and disabled).
+ *
+ *   Provided by platform-specific code and called from the RTOS base code.
+ *
+ * Input Parameters:
+ *   ts - Location to return the remaining time.  Zero should be returned
+ *        if the timer is not active.  ts may be zero in which case the
+ *        time remaining is not returned.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success.  A call to up_timer_cancel() when
+ *   the timer is not active should also return success; a negated errno
+ *   value is returned on any failure.
+ *
+ * Assumptions:
+ *   May be called from interrupt level handling or from the normal tasking
+ *   level.  Interrupts may need to be disabled internally to assure
+ *   non-reentrancy.
+ *
+ ****************************************************************************/
+
+int up_timer_cancel(struct timespec *ts)
+{
+  irqstate_t flags;
+  uint64_t usec;
+  uint64_t sec;
+  uint64_t nsec;
+  uint32_t count;
+  uint32_t period;
+
+  /* Was the timer running? */
+
+  flags = enter_critical_section();
+  if (!g_tickless.pending)
+    {
+      /* No.. Just return zero timer remaining and successful cancellation.
+       * This function may execute at a high rate with no timer running
+       * (as when pre-emption is enabled and disabled).
+       */
+
+      if (ts)
+        {
+          ts->tv_sec  = 0;
+          ts->tv_nsec = 0;
+        }
+
+      leave_critical_section(flags);
+      return OK;
+    }
+
+  /* Yes.. Get the timer counter and period registers and disable the compare
+   * interrupt.
+   */
+
+  tmrinfo("Cancelling...\n");
+
+  /* Disable the interrupt. */
+
+  stm32wb_tickless_disableint(g_tickless.channel);
+
+  count  = STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+  period = g_tickless.period;
+
+  g_tickless.pending = false;
+  leave_critical_section(flags);
+
+  /* Did the caller provide us with a location to return the time
+   * remaining?
+   */
+
+  if (ts != NULL)
+    {
+      /* Yes.. then calculate and return the time remaining on the
+       * oneshot timer.
+       */
+
+      tmrinfo("period=%lu count=%lu\n",
+             (unsigned long)period, (unsigned long)count);
+
+#ifndef HAVE_32BIT_TICKLESS
+      if (count > period)
+        {
+          /* Handle rollover */
+
+          period += UINT16_MAX;
+        }
+      else if (count == period)
+#else
+      if (count >= period)
+#endif
+        {
+          /* No time remaining */
+
+          ts->tv_sec  = 0;
+          ts->tv_nsec = 0;
+          return OK;
+        }
+
+      /* The total time remaining is the difference.  Convert that
+       * to units of microseconds.
+       *
+       *   frequency = ticks / second
+       *   seconds   = ticks * frequency
+       *   usecs     = (ticks * USEC_PER_SEC) / frequency;
+       */
+
+      usec        = (((uint64_t)(period - count)) * USEC_PER_SEC) /
+                    g_tickless.frequency;
+
+      /* Return the time remaining in the correct form */
+
+      sec         = usec / USEC_PER_SEC;
+      nsec        = ((usec) - (sec * USEC_PER_SEC)) * NSEC_PER_USEC;

Review Comment:
   ```suggestion
         nsec        = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC;
   ```



##########
arch/arm/src/stm32wb/stm32wb_tickless.c:
##########
@@ -0,0 +1,837 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_tickless.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Tickless OS Support.
+ *
+ * When CONFIG_SCHED_TICKLESS is enabled, all support for timer interrupts
+ * is suppressed and the platform specific code is expected to provide the
+ * following custom functions.
+ *
+ *   void up_timer_initialize(void): Initializes the timer facilities.
+ *     Called early in the initialization sequence (by up_initialize()).
+ *   int up_timer_gettime(struct timespec *ts):  Returns the current
+ *     time from the platform specific time source.
+ *   int up_timer_cancel(void):  Cancels the interval timer.
+ *   int up_timer_start(const struct timespec *ts): Start (or re-starts)
+ *     the interval timer.
+ *
+ * The RTOS will provide the following interfaces for use by the platform-
+ * specific interval timer implementation:
+ *
+ *   void nxsched_timer_expiration(void):  Called by the platform-specific
+ *     logic when the interval timer expires.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * STM32WB Timer Usage
+ *
+ * This implementation uses one timer:  A free running timer to provide
+ * the current time and a capture/compare channel for timed-events.
+ *
+ * There are two interrupts generated from our timer, the overflow interrupt
+ * which drives the timing handler and the capture/compare interrupt which
+ * drives the interval handler.  There are some low level timer control
+ * functions implemented here because the API of stm32wb_tim.c does not
+ * provide adequate control over capture/compare interrupts.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <nuttx/arch.h>
+#include <debug.h>
+
+#include "arm_internal.h"
+#include "stm32wb_tim.h"
+
+#ifdef CONFIG_SCHED_TICKLESS
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Only TIM2 is a 32-bit timer */
+
+#undef HAVE_32BIT_TICKLESS
+
+#ifdef CONFIG_STM32WB_TICKLESS_TIMER
+#  if CONFIG_STM32WB_TICKLESS_TIMER == 2
+#    define HAVE_32BIT_TICKLESS 1
+#  endif
+#else
+#  error "STM32WB_TICKLESS_TIMER must be defined for tickless configuration"
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_tickless_s
+{
+  uint8_t timer;                     /* The timer/counter in use */
+  uint8_t channel;                   /* The timer channel to use
+                                      * for intervals */
+  struct stm32wb_tim_dev_s *tch;     /* Pointer returned by
+                                      * stm32wb_tim_init() */
+  uint32_t frequency;
+  uint32_t overflow;                 /* Timer counter overflow */
+  volatile bool pending;             /* True: pending task */
+  uint32_t period;                   /* Interval period */
+  uint32_t base;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct stm32wb_tickless_s g_tickless;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_getreg16
+ *
+ * Description:
+ *   Get a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline uint16_t stm32wb_getreg16(uint8_t offset)
+{
+  return getreg16(g_tickless.base + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_putreg16
+ *
+ * Description:
+ *   Put a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_putreg16(uint8_t offset, uint16_t value)
+{
+  putreg16(value, g_tickless.base + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_modifyreg16
+ *
+ * Description:
+ *   Modify a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_modifyreg16(uint8_t offset, uint16_t clearbits,
+                                     uint16_t setbits)
+{
+  modifyreg16(g_tickless.base + offset, clearbits, setbits);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_enableint
+ ****************************************************************************/
+
+static inline void stm32wb_tickless_enableint(int channel)
+{
+  stm32wb_modifyreg16(STM32WB_TIM_DIER_OFFSET, 0, 1 << channel);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_disableint
+ ****************************************************************************/
+
+static inline void stm32wb_tickless_disableint(int channel)
+{
+  stm32wb_modifyreg16(STM32WB_TIM_DIER_OFFSET, 1 << channel, 0);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_ackint
+ ****************************************************************************/
+
+static inline void stm32wb_tickless_ackint(int channel)
+{
+  stm32wb_putreg16(STM32WB_TIM_SR_OFFSET, ~(1 << channel));
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_getint
+ ****************************************************************************/
+
+static inline uint16_t stm32wb_tickless_getint(void)
+{
+  return stm32wb_getreg16(STM32WB_TIM_SR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_setchannel
+ ****************************************************************************/
+
+static int stm32wb_tickless_setchannel(uint8_t channel)
+{
+  uint16_t ccmr_orig = 0;
+  uint16_t ccmr_val = 0;
+  uint16_t ccer_val;
+  uint8_t ccmr_offset = STM32WB_TIM_CCMR1_OFFSET;
+
+  /* Further we use range as 0..3; if channel=0 it will also overflow here */
+
+  if (--channel > 3)
+    {
+      return -EINVAL;
+    }
+
+  /* Assume that channel is disabled and polarity is active high */
+
+  ccer_val = stm32wb_getreg16(STM32WB_TIM_CCER_OFFSET);
+  ccer_val &= ~(GTIM_CCER_CCXE(channel) | GTIM_CCER_CCXP(channel));
+
+  /* Frozen mode because we don't want to change the GPIO, preload register
+   * disabled.
+   */
+
+  ccmr_val = GTIM_CCMR_OCXM_FRZN(channel);
+
+  /* Set polarity */
+
+  ccer_val |= GTIM_CCER_CCXP(channel);
+
+  if (channel > 1)
+    {
+      ccmr_offset = STM32WB_TIM_CCMR2_OFFSET;
+    }
+
+  ccmr_orig  = stm32wb_getreg16(ccmr_offset);
+  ccmr_orig &= ~(GTIM_CCMR_OCXM_MASK(channel) | GTIM_CCMR_OCXPE(channel));
+  ccmr_orig |= ccmr_val;
+  stm32wb_putreg16(ccmr_offset, ccmr_orig);
+  stm32wb_putreg16(STM32WB_TIM_CCER_OFFSET, ccer_val);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_interval_handler
+ *
+ * Description:
+ *   Called when the timer counter matches the compare register
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called early in the initialization sequence before any special
+ *   concurrency protections are required.
+ *
+ ****************************************************************************/
+
+static void stm32wb_interval_handler(void)
+{
+  tmrinfo("Expired...\n");
+
+  /* Disable the compare interrupt now. */
+
+  stm32wb_tickless_disableint(g_tickless.channel);
+  stm32wb_tickless_ackint(g_tickless.channel);
+
+  g_tickless.pending = false;
+
+  nxsched_timer_expiration();
+}
+
+/****************************************************************************
+ * Name: stm32wb_timing_handler
+ *
+ * Description:
+ *   Timer interrupt callback.  When the freerun timer counter overflows,
+ *   this interrupt will occur.  We will just increment an overflow count.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void stm32wb_timing_handler(void)
+{
+  g_tickless.overflow++;
+
+  STM32WB_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_handler
+ *
+ * Description:
+ *   Generic interrupt handler for this timer.  It checks the source of the
+ *   interrupt and fires the appropriate handler.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int stm32wb_tickless_handler(int irq, void *context, void *arg)
+{
+  int interrupt_flags = stm32wb_tickless_getint();
+
+  if (interrupt_flags & GTIM_SR_UIF)
+    {
+      stm32wb_timing_handler();
+    }
+
+  if (interrupt_flags & (1 << g_tickless.channel))
+    {
+      stm32wb_interval_handler();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_timer_initialize
+ *
+ * Description:
+ *   Initializes all platform-specific timer facilities.  This function is
+ *   called early in the initialization sequence by up_initialize().
+ *   On return, the current up-time should be available from
+ *   up_timer_gettime() and the interval timer is ready for use (but not
+ *   actively timing.
+ *
+ *   Provided by platform-specific code and called from the architecture-
+ *   specific logic.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called early in the initialization sequence before any special
+ *   concurrency protections are required.
+ *
+ ****************************************************************************/
+
+void up_timer_initialize(void)
+{
+  switch (CONFIG_STM32WB_TICKLESS_TIMER)
+    {
+#ifdef CONFIG_STM32WB_TIM1
+      case 1:
+        g_tickless.base = STM32WB_TIM1_BASE;
+        break;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM2
+      case 2:
+        g_tickless.base = STM32WB_TIM2_BASE;
+        break;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM16
+      case 16:
+        g_tickless.base = STM32WB_TIM16_BASE;
+        break;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM17
+      case 17:
+        g_tickless.base = STM32WB_TIM17_BASE;
+        break;
+#endif
+
+      default:
+        DEBUGASSERT(0);
+    }
+
+  /* Get the TC frequency that corresponds to the requested resolution */
+
+  g_tickless.frequency = USEC_PER_SEC / (uint32_t)CONFIG_USEC_PER_TICK;
+  g_tickless.timer     = CONFIG_STM32WB_TICKLESS_TIMER;
+  g_tickless.channel   = CONFIG_STM32WB_TICKLESS_CHANNEL;
+  g_tickless.pending   = false;
+  g_tickless.period    = 0;
+  g_tickless.overflow  = 0;
+
+  tmrinfo("timer=%d channel=%d frequency=%lu Hz\n",
+           g_tickless.timer, g_tickless.channel, g_tickless.frequency);
+
+  g_tickless.tch = stm32wb_tim_init(g_tickless.timer);
+  if (!g_tickless.tch)
+    {
+      tmrerr("ERROR: Failed to allocate TIM%d\n", g_tickless.timer);
+      DEBUGASSERT(0);
+    }
+
+  STM32WB_TIM_SETCLOCK(g_tickless.tch, g_tickless.frequency);
+
+  /* Set up to receive the callback when the counter overflow occurs */
+
+  STM32WB_TIM_SETISR(g_tickless.tch, stm32wb_tickless_handler, NULL, 0);
+
+  /* Initialize interval to zero */
+
+  STM32WB_TIM_SETCOMPARE(g_tickless.tch, g_tickless.channel, 0);
+
+  /* Setup compare channel for the interval timing */
+
+  stm32wb_tickless_setchannel(g_tickless.channel);
+
+  /* Set timer period */
+
+#ifdef HAVE_32BIT_TICKLESS
+  STM32WB_TIM_SETPERIOD(g_tickless.tch, UINT32_MAX);
+#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP
+  g_oneshot_maxticks = UINT32_MAX;
+#endif
+#else
+  STM32WB_TIM_SETPERIOD(g_tickless.tch, UINT16_MAX);
+#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP
+  g_oneshot_maxticks = UINT16_MAX;
+#endif
+#endif
+
+  /* Initialize the counter */
+
+  STM32WB_TIM_SETMODE(g_tickless.tch, STM32WB_TIM_MODE_UP);
+
+  /* Start the timer */
+
+  STM32WB_TIM_ACKINT(g_tickless.tch, ~0);
+  STM32WB_TIM_ENABLEINT(g_tickless.tch, GTIM_DIER_UIE);
+}
+
+/****************************************************************************
+ * Name: up_timer_gettime
+ *
+ * Description:
+ *   Return the elapsed time since power-up (or, more correctly, since
+ *   up_timer_initialize() was called).  This function is functionally
+ *   equivalent to:
+ *
+ *      int clock_gettime(clockid_t clockid, struct timespec *ts);
+ *
+ *   when clockid is CLOCK_MONOTONIC.
+ *
+ *   This function provides the basis for reporting the current time and
+ *   also is used to eliminate error build-up from small errors in interval
+ *   time calculations.
+ *
+ *   Provided by platform-specific code and called from the RTOS base code.
+ *
+ * Input Parameters:
+ *   ts - Provides the location in which to return the up-time.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned on
+ *   any failure.
+ *
+ * Assumptions:
+ *   Called from the normal tasking context.  The implementation must
+ *   provide whatever mutual exclusion is necessary for correct operation.
+ *   This can include disabling interrupts in order to assure atomic register
+ *   operations.
+ *
+ ****************************************************************************/
+
+int up_timer_gettime(struct timespec *ts)
+{
+  uint64_t usec;
+  uint32_t counter;
+  uint32_t verify;
+  uint32_t overflow;
+  uint32_t sec;
+  int pending;
+  irqstate_t flags;
+
+  DEBUGASSERT(g_tickless.tch && ts);
+
+  /* Temporarily disable the overflow counter.  NOTE that we have to be
+   * careful here because  stm32wb_tc_getpending() will reset the pending
+   * interrupt status.  If we do not handle the overflow here then, it will
+   * be lost.
+   */
+
+  flags    = enter_critical_section();
+
+  overflow = g_tickless.overflow;
+  counter  = STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+  pending  = STM32WB_TIM_CHECKINT(g_tickless.tch, GTIM_SR_UIF);
+  verify   = STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+
+  /* If an interrupt was pending before we re-enabled interrupts,
+   * then the overflow needs to be incremented.
+   */
+
+  if (pending)
+    {
+      STM32WB_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF);
+
+      /* Increment the overflow count and use the value of the
+       * guaranteed to be AFTER the overflow occurred.
+       */
+
+      overflow++;
+      counter = verify;
+
+      /* Update tickless overflow counter. */
+
+      g_tickless.overflow = overflow;
+    }
+
+  leave_critical_section(flags);
+
+  tmrinfo("counter=%lu (%lu) overflow=%lu, pending=%i\n",
+         (unsigned long)counter,  (unsigned long)verify,
+         (unsigned long)overflow, pending);
+  tmrinfo("frequency=%lu\n", g_tickless.frequency);
+
+  /* Convert the whole thing to units of microseconds.
+   *
+   *   frequency = ticks / second
+   *   seconds   = ticks * frequency
+   *   usecs     = (ticks * USEC_PER_SEC) / frequency;
+   */
+#ifdef HAVE_32BIT_TICKLESS
+  usec = ((((uint64_t)overflow << 32) + (uint64_t)counter) * USEC_PER_SEC) /
+         g_tickless.frequency;
+#else
+  usec = ((((uint64_t)overflow << 16) + (uint64_t)counter) * USEC_PER_SEC) /
+         g_tickless.frequency;
+#endif
+
+  /* And return the value of the timer */
+
+  sec         = (uint32_t)(usec / USEC_PER_SEC);
+  ts->tv_sec  = sec;
+  ts->tv_nsec = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC;
+
+  tmrinfo("usec=%llu ts=(%lu, %lu)\n",
+          usec, (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec);
+
+  return OK;
+}
+
+#ifdef CONFIG_CLOCK_TIMEKEEPING
+
+/****************************************************************************
+ * Name: up_timer_getcounter
+ *
+ * Description:
+ *   To be provided
+ *
+ * Input Parameters:
+ *   cycles - 64-bit return value
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+int up_timer_getcounter(uint64_t *cycles)
+{
+  *cycles = (uint64_t)STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: up_timer_getmask
+ *
+ * Description:
+ *   To be provided
+ *
+ * Input Parameters:
+ *   mask - Location to return the 64-bit mask
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void up_timer_getmask(uint64_t *mask)
+{
+  DEBUGASSERT(mask != NULL);
+#ifdef HAVE_32BIT_TICKLESS
+  *mask = UINT32_MAX;
+#else
+  *mask = UINT16_MAX;
+#endif
+}
+
+#endif /* CONFIG_CLOCK_TIMEKEEPING */
+
+/****************************************************************************
+ * Name: up_timer_cancel
+ *
+ * Description:
+ *   Cancel the interval timer and return the time remaining on the timer.
+ *   These two steps need to be as nearly atomic as possible.
+ *   nxsched_timer_expiration() will not be called unless the timer is
+ *   restarted with up_timer_start().
+ *
+ *   If, as a race condition, the timer has already expired when this
+ *   function is called, then that pending interrupt must be cleared so
+ *   that up_timer_start() and the remaining time of zero should be
+ *   returned.
+ *
+ *   NOTE: This function may execute at a high rate with no timer running (as
+ *   when pre-emption is enabled and disabled).
+ *
+ *   Provided by platform-specific code and called from the RTOS base code.
+ *
+ * Input Parameters:
+ *   ts - Location to return the remaining time.  Zero should be returned
+ *        if the timer is not active.  ts may be zero in which case the
+ *        time remaining is not returned.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success.  A call to up_timer_cancel() when
+ *   the timer is not active should also return success; a negated errno
+ *   value is returned on any failure.
+ *
+ * Assumptions:
+ *   May be called from interrupt level handling or from the normal tasking
+ *   level.  Interrupts may need to be disabled internally to assure
+ *   non-reentrancy.
+ *
+ ****************************************************************************/
+
+int up_timer_cancel(struct timespec *ts)
+{
+  irqstate_t flags;
+  uint64_t usec;
+  uint64_t sec;
+  uint64_t nsec;
+  uint32_t count;
+  uint32_t period;
+
+  /* Was the timer running? */
+
+  flags = enter_critical_section();
+  if (!g_tickless.pending)
+    {
+      /* No.. Just return zero timer remaining and successful cancellation.
+       * This function may execute at a high rate with no timer running
+       * (as when pre-emption is enabled and disabled).
+       */
+
+      if (ts)
+        {
+          ts->tv_sec  = 0;
+          ts->tv_nsec = 0;
+        }
+
+      leave_critical_section(flags);
+      return OK;
+    }
+
+  /* Yes.. Get the timer counter and period registers and disable the compare
+   * interrupt.
+   */
+
+  tmrinfo("Cancelling...\n");
+
+  /* Disable the interrupt. */
+
+  stm32wb_tickless_disableint(g_tickless.channel);
+
+  count  = STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+  period = g_tickless.period;
+
+  g_tickless.pending = false;
+  leave_critical_section(flags);
+
+  /* Did the caller provide us with a location to return the time
+   * remaining?
+   */
+
+  if (ts != NULL)
+    {
+      /* Yes.. then calculate and return the time remaining on the
+       * oneshot timer.
+       */
+
+      tmrinfo("period=%lu count=%lu\n",
+             (unsigned long)period, (unsigned long)count);
+
+#ifndef HAVE_32BIT_TICKLESS
+      if (count > period)
+        {
+          /* Handle rollover */
+
+          period += UINT16_MAX;
+        }
+      else if (count == period)
+#else
+      if (count >= period)
+#endif
+        {
+          /* No time remaining */
+
+          ts->tv_sec  = 0;
+          ts->tv_nsec = 0;
+          return OK;
+        }
+
+      /* The total time remaining is the difference.  Convert that
+       * to units of microseconds.
+       *
+       *   frequency = ticks / second
+       *   seconds   = ticks * frequency
+       *   usecs     = (ticks * USEC_PER_SEC) / frequency;
+       */
+
+      usec        = (((uint64_t)(period - count)) * USEC_PER_SEC) /
+                    g_tickless.frequency;
+
+      /* Return the time remaining in the correct form */
+
+      sec         = usec / USEC_PER_SEC;
+      nsec        = ((usec) - (sec * USEC_PER_SEC)) * NSEC_PER_USEC;
+
+      ts->tv_sec  = (time_t)sec;
+      ts->tv_nsec = (unsigned long)nsec;
+
+      tmrinfo("remaining (%lu, %lu)\n",
+             (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec);

Review Comment:
   ```suggestion
                 (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec);
   ```



##########
arch/arm/src/stm32wb/stm32wb_tickless.c:
##########
@@ -0,0 +1,837 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_tickless.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Tickless OS Support.
+ *
+ * When CONFIG_SCHED_TICKLESS is enabled, all support for timer interrupts
+ * is suppressed and the platform specific code is expected to provide the
+ * following custom functions.
+ *
+ *   void up_timer_initialize(void): Initializes the timer facilities.
+ *     Called early in the initialization sequence (by up_initialize()).
+ *   int up_timer_gettime(struct timespec *ts):  Returns the current
+ *     time from the platform specific time source.
+ *   int up_timer_cancel(void):  Cancels the interval timer.
+ *   int up_timer_start(const struct timespec *ts): Start (or re-starts)
+ *     the interval timer.
+ *
+ * The RTOS will provide the following interfaces for use by the platform-
+ * specific interval timer implementation:
+ *
+ *   void nxsched_timer_expiration(void):  Called by the platform-specific
+ *     logic when the interval timer expires.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * STM32WB Timer Usage
+ *
+ * This implementation uses one timer:  A free running timer to provide
+ * the current time and a capture/compare channel for timed-events.
+ *
+ * There are two interrupts generated from our timer, the overflow interrupt
+ * which drives the timing handler and the capture/compare interrupt which
+ * drives the interval handler.  There are some low level timer control
+ * functions implemented here because the API of stm32wb_tim.c does not
+ * provide adequate control over capture/compare interrupts.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <nuttx/arch.h>
+#include <debug.h>
+
+#include "arm_internal.h"
+#include "stm32wb_tim.h"
+
+#ifdef CONFIG_SCHED_TICKLESS
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Only TIM2 is a 32-bit timer */
+
+#undef HAVE_32BIT_TICKLESS
+
+#ifdef CONFIG_STM32WB_TICKLESS_TIMER
+#  if CONFIG_STM32WB_TICKLESS_TIMER == 2
+#    define HAVE_32BIT_TICKLESS 1
+#  endif
+#else
+#  error "STM32WB_TICKLESS_TIMER must be defined for tickless configuration"
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_tickless_s
+{
+  uint8_t timer;                     /* The timer/counter in use */
+  uint8_t channel;                   /* The timer channel to use
+                                      * for intervals */
+  struct stm32wb_tim_dev_s *tch;     /* Pointer returned by
+                                      * stm32wb_tim_init() */
+  uint32_t frequency;
+  uint32_t overflow;                 /* Timer counter overflow */
+  volatile bool pending;             /* True: pending task */
+  uint32_t period;                   /* Interval period */
+  uint32_t base;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct stm32wb_tickless_s g_tickless;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_getreg16
+ *
+ * Description:
+ *   Get a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline uint16_t stm32wb_getreg16(uint8_t offset)
+{
+  return getreg16(g_tickless.base + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_putreg16
+ *
+ * Description:
+ *   Put a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_putreg16(uint8_t offset, uint16_t value)
+{
+  putreg16(value, g_tickless.base + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_modifyreg16
+ *
+ * Description:
+ *   Modify a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_modifyreg16(uint8_t offset, uint16_t clearbits,
+                                     uint16_t setbits)
+{
+  modifyreg16(g_tickless.base + offset, clearbits, setbits);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_enableint
+ ****************************************************************************/
+
+static inline void stm32wb_tickless_enableint(int channel)
+{
+  stm32wb_modifyreg16(STM32WB_TIM_DIER_OFFSET, 0, 1 << channel);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_disableint
+ ****************************************************************************/
+
+static inline void stm32wb_tickless_disableint(int channel)
+{
+  stm32wb_modifyreg16(STM32WB_TIM_DIER_OFFSET, 1 << channel, 0);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_ackint
+ ****************************************************************************/
+
+static inline void stm32wb_tickless_ackint(int channel)
+{
+  stm32wb_putreg16(STM32WB_TIM_SR_OFFSET, ~(1 << channel));
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_getint
+ ****************************************************************************/
+
+static inline uint16_t stm32wb_tickless_getint(void)
+{
+  return stm32wb_getreg16(STM32WB_TIM_SR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_setchannel
+ ****************************************************************************/
+
+static int stm32wb_tickless_setchannel(uint8_t channel)
+{
+  uint16_t ccmr_orig = 0;
+  uint16_t ccmr_val = 0;
+  uint16_t ccer_val;
+  uint8_t ccmr_offset = STM32WB_TIM_CCMR1_OFFSET;
+
+  /* Further we use range as 0..3; if channel=0 it will also overflow here */
+
+  if (--channel > 3)
+    {
+      return -EINVAL;
+    }
+
+  /* Assume that channel is disabled and polarity is active high */
+
+  ccer_val = stm32wb_getreg16(STM32WB_TIM_CCER_OFFSET);
+  ccer_val &= ~(GTIM_CCER_CCXE(channel) | GTIM_CCER_CCXP(channel));
+
+  /* Frozen mode because we don't want to change the GPIO, preload register
+   * disabled.
+   */
+
+  ccmr_val = GTIM_CCMR_OCXM_FRZN(channel);
+
+  /* Set polarity */
+
+  ccer_val |= GTIM_CCER_CCXP(channel);
+
+  if (channel > 1)
+    {
+      ccmr_offset = STM32WB_TIM_CCMR2_OFFSET;
+    }
+
+  ccmr_orig  = stm32wb_getreg16(ccmr_offset);
+  ccmr_orig &= ~(GTIM_CCMR_OCXM_MASK(channel) | GTIM_CCMR_OCXPE(channel));
+  ccmr_orig |= ccmr_val;
+  stm32wb_putreg16(ccmr_offset, ccmr_orig);
+  stm32wb_putreg16(STM32WB_TIM_CCER_OFFSET, ccer_val);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_interval_handler
+ *
+ * Description:
+ *   Called when the timer counter matches the compare register
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called early in the initialization sequence before any special
+ *   concurrency protections are required.
+ *
+ ****************************************************************************/
+
+static void stm32wb_interval_handler(void)
+{
+  tmrinfo("Expired...\n");
+
+  /* Disable the compare interrupt now. */
+
+  stm32wb_tickless_disableint(g_tickless.channel);
+  stm32wb_tickless_ackint(g_tickless.channel);
+
+  g_tickless.pending = false;
+
+  nxsched_timer_expiration();
+}
+
+/****************************************************************************
+ * Name: stm32wb_timing_handler
+ *
+ * Description:
+ *   Timer interrupt callback.  When the freerun timer counter overflows,
+ *   this interrupt will occur.  We will just increment an overflow count.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void stm32wb_timing_handler(void)
+{
+  g_tickless.overflow++;
+
+  STM32WB_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_handler
+ *
+ * Description:
+ *   Generic interrupt handler for this timer.  It checks the source of the
+ *   interrupt and fires the appropriate handler.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int stm32wb_tickless_handler(int irq, void *context, void *arg)
+{
+  int interrupt_flags = stm32wb_tickless_getint();
+
+  if (interrupt_flags & GTIM_SR_UIF)
+    {
+      stm32wb_timing_handler();
+    }
+
+  if (interrupt_flags & (1 << g_tickless.channel))
+    {
+      stm32wb_interval_handler();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_timer_initialize
+ *
+ * Description:
+ *   Initializes all platform-specific timer facilities.  This function is
+ *   called early in the initialization sequence by up_initialize().
+ *   On return, the current up-time should be available from
+ *   up_timer_gettime() and the interval timer is ready for use (but not
+ *   actively timing.
+ *
+ *   Provided by platform-specific code and called from the architecture-
+ *   specific logic.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called early in the initialization sequence before any special
+ *   concurrency protections are required.
+ *
+ ****************************************************************************/
+
+void up_timer_initialize(void)
+{
+  switch (CONFIG_STM32WB_TICKLESS_TIMER)
+    {
+#ifdef CONFIG_STM32WB_TIM1
+      case 1:
+        g_tickless.base = STM32WB_TIM1_BASE;
+        break;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM2
+      case 2:
+        g_tickless.base = STM32WB_TIM2_BASE;
+        break;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM16
+      case 16:
+        g_tickless.base = STM32WB_TIM16_BASE;
+        break;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM17
+      case 17:
+        g_tickless.base = STM32WB_TIM17_BASE;
+        break;
+#endif
+
+      default:
+        DEBUGASSERT(0);
+    }
+
+  /* Get the TC frequency that corresponds to the requested resolution */
+
+  g_tickless.frequency = USEC_PER_SEC / (uint32_t)CONFIG_USEC_PER_TICK;
+  g_tickless.timer     = CONFIG_STM32WB_TICKLESS_TIMER;
+  g_tickless.channel   = CONFIG_STM32WB_TICKLESS_CHANNEL;
+  g_tickless.pending   = false;
+  g_tickless.period    = 0;
+  g_tickless.overflow  = 0;
+
+  tmrinfo("timer=%d channel=%d frequency=%lu Hz\n",
+           g_tickless.timer, g_tickless.channel, g_tickless.frequency);
+
+  g_tickless.tch = stm32wb_tim_init(g_tickless.timer);
+  if (!g_tickless.tch)
+    {
+      tmrerr("ERROR: Failed to allocate TIM%d\n", g_tickless.timer);
+      DEBUGASSERT(0);
+    }
+
+  STM32WB_TIM_SETCLOCK(g_tickless.tch, g_tickless.frequency);
+
+  /* Set up to receive the callback when the counter overflow occurs */
+
+  STM32WB_TIM_SETISR(g_tickless.tch, stm32wb_tickless_handler, NULL, 0);
+
+  /* Initialize interval to zero */
+
+  STM32WB_TIM_SETCOMPARE(g_tickless.tch, g_tickless.channel, 0);
+
+  /* Setup compare channel for the interval timing */
+
+  stm32wb_tickless_setchannel(g_tickless.channel);
+
+  /* Set timer period */
+
+#ifdef HAVE_32BIT_TICKLESS
+  STM32WB_TIM_SETPERIOD(g_tickless.tch, UINT32_MAX);
+#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP
+  g_oneshot_maxticks = UINT32_MAX;
+#endif
+#else
+  STM32WB_TIM_SETPERIOD(g_tickless.tch, UINT16_MAX);
+#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP
+  g_oneshot_maxticks = UINT16_MAX;
+#endif
+#endif
+
+  /* Initialize the counter */
+
+  STM32WB_TIM_SETMODE(g_tickless.tch, STM32WB_TIM_MODE_UP);
+
+  /* Start the timer */
+
+  STM32WB_TIM_ACKINT(g_tickless.tch, ~0);
+  STM32WB_TIM_ENABLEINT(g_tickless.tch, GTIM_DIER_UIE);
+}
+
+/****************************************************************************
+ * Name: up_timer_gettime
+ *
+ * Description:
+ *   Return the elapsed time since power-up (or, more correctly, since
+ *   up_timer_initialize() was called).  This function is functionally
+ *   equivalent to:
+ *
+ *      int clock_gettime(clockid_t clockid, struct timespec *ts);
+ *
+ *   when clockid is CLOCK_MONOTONIC.
+ *
+ *   This function provides the basis for reporting the current time and
+ *   also is used to eliminate error build-up from small errors in interval
+ *   time calculations.
+ *
+ *   Provided by platform-specific code and called from the RTOS base code.
+ *
+ * Input Parameters:
+ *   ts - Provides the location in which to return the up-time.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned on
+ *   any failure.
+ *
+ * Assumptions:
+ *   Called from the normal tasking context.  The implementation must
+ *   provide whatever mutual exclusion is necessary for correct operation.
+ *   This can include disabling interrupts in order to assure atomic register
+ *   operations.
+ *
+ ****************************************************************************/
+
+int up_timer_gettime(struct timespec *ts)
+{
+  uint64_t usec;
+  uint32_t counter;
+  uint32_t verify;
+  uint32_t overflow;
+  uint32_t sec;
+  int pending;
+  irqstate_t flags;
+
+  DEBUGASSERT(g_tickless.tch && ts);
+
+  /* Temporarily disable the overflow counter.  NOTE that we have to be
+   * careful here because  stm32wb_tc_getpending() will reset the pending
+   * interrupt status.  If we do not handle the overflow here then, it will
+   * be lost.
+   */
+
+  flags    = enter_critical_section();
+
+  overflow = g_tickless.overflow;
+  counter  = STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+  pending  = STM32WB_TIM_CHECKINT(g_tickless.tch, GTIM_SR_UIF);
+  verify   = STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+
+  /* If an interrupt was pending before we re-enabled interrupts,
+   * then the overflow needs to be incremented.
+   */
+
+  if (pending)
+    {
+      STM32WB_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF);
+
+      /* Increment the overflow count and use the value of the
+       * guaranteed to be AFTER the overflow occurred.
+       */
+
+      overflow++;
+      counter = verify;
+
+      /* Update tickless overflow counter. */
+
+      g_tickless.overflow = overflow;
+    }
+
+  leave_critical_section(flags);
+
+  tmrinfo("counter=%lu (%lu) overflow=%lu, pending=%i\n",
+         (unsigned long)counter,  (unsigned long)verify,
+         (unsigned long)overflow, pending);
+  tmrinfo("frequency=%lu\n", g_tickless.frequency);
+
+  /* Convert the whole thing to units of microseconds.
+   *
+   *   frequency = ticks / second
+   *   seconds   = ticks * frequency
+   *   usecs     = (ticks * USEC_PER_SEC) / frequency;
+   */
+#ifdef HAVE_32BIT_TICKLESS
+  usec = ((((uint64_t)overflow << 32) + (uint64_t)counter) * USEC_PER_SEC) /
+         g_tickless.frequency;
+#else
+  usec = ((((uint64_t)overflow << 16) + (uint64_t)counter) * USEC_PER_SEC) /
+         g_tickless.frequency;
+#endif
+
+  /* And return the value of the timer */
+
+  sec         = (uint32_t)(usec / USEC_PER_SEC);
+  ts->tv_sec  = sec;
+  ts->tv_nsec = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC;
+
+  tmrinfo("usec=%llu ts=(%lu, %lu)\n",
+          usec, (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec);
+
+  return OK;
+}
+
+#ifdef CONFIG_CLOCK_TIMEKEEPING
+
+/****************************************************************************
+ * Name: up_timer_getcounter
+ *
+ * Description:
+ *   To be provided
+ *
+ * Input Parameters:
+ *   cycles - 64-bit return value
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+int up_timer_getcounter(uint64_t *cycles)
+{
+  *cycles = (uint64_t)STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: up_timer_getmask
+ *
+ * Description:
+ *   To be provided
+ *
+ * Input Parameters:
+ *   mask - Location to return the 64-bit mask
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void up_timer_getmask(uint64_t *mask)
+{
+  DEBUGASSERT(mask != NULL);
+#ifdef HAVE_32BIT_TICKLESS
+  *mask = UINT32_MAX;
+#else
+  *mask = UINT16_MAX;
+#endif
+}
+
+#endif /* CONFIG_CLOCK_TIMEKEEPING */
+
+/****************************************************************************
+ * Name: up_timer_cancel
+ *
+ * Description:
+ *   Cancel the interval timer and return the time remaining on the timer.
+ *   These two steps need to be as nearly atomic as possible.
+ *   nxsched_timer_expiration() will not be called unless the timer is
+ *   restarted with up_timer_start().
+ *
+ *   If, as a race condition, the timer has already expired when this
+ *   function is called, then that pending interrupt must be cleared so
+ *   that up_timer_start() and the remaining time of zero should be
+ *   returned.
+ *
+ *   NOTE: This function may execute at a high rate with no timer running (as
+ *   when pre-emption is enabled and disabled).
+ *
+ *   Provided by platform-specific code and called from the RTOS base code.
+ *
+ * Input Parameters:
+ *   ts - Location to return the remaining time.  Zero should be returned
+ *        if the timer is not active.  ts may be zero in which case the
+ *        time remaining is not returned.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success.  A call to up_timer_cancel() when
+ *   the timer is not active should also return success; a negated errno
+ *   value is returned on any failure.
+ *
+ * Assumptions:
+ *   May be called from interrupt level handling or from the normal tasking
+ *   level.  Interrupts may need to be disabled internally to assure
+ *   non-reentrancy.
+ *
+ ****************************************************************************/
+
+int up_timer_cancel(struct timespec *ts)
+{
+  irqstate_t flags;
+  uint64_t usec;
+  uint64_t sec;
+  uint64_t nsec;
+  uint32_t count;
+  uint32_t period;
+
+  /* Was the timer running? */
+
+  flags = enter_critical_section();
+  if (!g_tickless.pending)
+    {
+      /* No.. Just return zero timer remaining and successful cancellation.
+       * This function may execute at a high rate with no timer running
+       * (as when pre-emption is enabled and disabled).
+       */
+
+      if (ts)
+        {
+          ts->tv_sec  = 0;
+          ts->tv_nsec = 0;
+        }
+
+      leave_critical_section(flags);
+      return OK;
+    }
+
+  /* Yes.. Get the timer counter and period registers and disable the compare
+   * interrupt.
+   */
+
+  tmrinfo("Cancelling...\n");
+
+  /* Disable the interrupt. */
+
+  stm32wb_tickless_disableint(g_tickless.channel);
+
+  count  = STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+  period = g_tickless.period;
+
+  g_tickless.pending = false;
+  leave_critical_section(flags);
+
+  /* Did the caller provide us with a location to return the time
+   * remaining?
+   */
+
+  if (ts != NULL)
+    {
+      /* Yes.. then calculate and return the time remaining on the
+       * oneshot timer.
+       */
+
+      tmrinfo("period=%lu count=%lu\n",
+             (unsigned long)period, (unsigned long)count);
+
+#ifndef HAVE_32BIT_TICKLESS
+      if (count > period)
+        {
+          /* Handle rollover */
+
+          period += UINT16_MAX;
+        }
+      else if (count == period)
+#else
+      if (count >= period)
+#endif
+        {
+          /* No time remaining */
+
+          ts->tv_sec  = 0;
+          ts->tv_nsec = 0;
+          return OK;
+        }
+
+      /* The total time remaining is the difference.  Convert that
+       * to units of microseconds.
+       *
+       *   frequency = ticks / second
+       *   seconds   = ticks * frequency
+       *   usecs     = (ticks * USEC_PER_SEC) / frequency;
+       */
+
+      usec        = (((uint64_t)(period - count)) * USEC_PER_SEC) /
+                    g_tickless.frequency;
+
+      /* Return the time remaining in the correct form */
+
+      sec         = usec / USEC_PER_SEC;
+      nsec        = ((usec) - (sec * USEC_PER_SEC)) * NSEC_PER_USEC;
+
+      ts->tv_sec  = (time_t)sec;
+      ts->tv_nsec = (unsigned long)nsec;
+
+      tmrinfo("remaining (%lu, %lu)\n",
+             (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: up_timer_start
+ *
+ * Description:
+ *   Start the interval timer.  nxsched_timer_expiration() will be
+ *   called at the completion of the timeout (unless up_timer_cancel
+ *   is called to stop the timing.
+ *
+ *   Provided by platform-specific code and called from the RTOS base code.
+ *
+ * Input Parameters:
+ *   ts - Provides the time interval until nxsched_timer_expiration() is
+ *        called.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned on
+ *   any failure.
+ *
+ * Assumptions:
+ *   May be called from interrupt level handling or from the normal tasking
+ *   level.  Interrupts may need to be disabled internally to assure
+ *   non-reentrancy.
+ *
+ ****************************************************************************/
+
+int up_timer_start(const struct timespec *ts)
+{
+  uint64_t usec;
+  uint64_t period;
+  uint32_t count;
+  irqstate_t flags;
+
+  tmrinfo("ts=(%lu, %lu)\n",
+          (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec);
+  DEBUGASSERT(ts);
+  DEBUGASSERT(g_tickless.tch);
+
+  /* Was an interval already running? */
+
+  flags = enter_critical_section();
+  if (g_tickless.pending)
+    {
+      /* Yes.. then cancel it */
+
+      tmrinfo("Already running... cancelling\n");
+      up_timer_cancel(NULL);
+    }
+
+  /* Express the delay in microseconds */
+
+  usec = (uint64_t)ts->tv_sec * USEC_PER_SEC +
+         (uint64_t)(ts->tv_nsec / NSEC_PER_USEC);
+
+  /* Get the timer counter frequency and determine the number of counts need
+   * to achieve the requested delay.
+   *
+   *   frequency = ticks / second
+   *   ticks     = seconds * frequency
+   *             = (usecs * frequency) / USEC_PER_SEC;
+   */
+
+  period = (usec * (uint64_t)g_tickless.frequency) / USEC_PER_SEC;
+  count  = STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+
+  tmrinfo("usec=%llu period=%08llx\n", usec, period);
+
+  /* Set interval compare value. Rollover is fine,
+   * channel will trigger on the next period.
+   */
+#ifdef HAVE_32BIT_TICKLESS
+  DEBUGASSERT(period <= UINT32_MAX);
+  g_tickless.period = (uint32_t)(period + count);
+#else
+  DEBUGASSERT(period <= UINT16_MAX);
+  g_tickless.period = (uint16_t)(period + count);
+#endif
+
+  STM32WB_TIM_SETCOMPARE(g_tickless.tch, g_tickless.channel,
+                       g_tickless.period);

Review Comment:
   ```suggestion
     STM32WB_TIM_SETCOMPARE(g_tickless.tch, g_tickless.channel,
                            g_tickless.period);
   ```



##########
arch/arm/src/stm32wb/stm32wb_spi.c:
##########
@@ -0,0 +1,1819 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * The external functions, stm32wb_spi1/2select and stm32wb_spi1/2status
+ * must be provided by board-specific logic.  They are implementations of the
+ * select and status methods of the SPI interface defined by struct spi_ops_s
+ * (see include/nuttx/spi/spi.h).  All other methods (including
+ * stm32wb_spibus_initialize()) are provided by common STM32 logic.  To use
+ * this common SPI logic on your board:
+ *
+ *   1. Provide logic in stm32wb_board_initialize() to configure SPI chip
+ *      select pins.
+ *   2. Provide stm32wb_spi1/2select() and stm32wb_spi1/2status()
+ *      functions in your board-specific logic.  These functions will perform
+ *      chip selection and status operations using GPIOs in the way your
+ *      board is configured.
+ *   3. Add a calls to stm32wb_spibus_initialize() in your low level
+ *      application initialization logic
+ *   4. The handle returned by stm32wb_spibus_initialize() may then be used
+ *      to bind the SPI driver to higher level logic (e.g., calling
+ *      mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ *      the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+/* This driver is ported from the stm32 one, which only supports 8 and 16
+ * bits transfers. The STM32WB family supports frame size from 4 to 16 bits,
+ * but we do not support that yet. For the moment, we replace uses of the
+ * CR1_DFF bit with a check of the CR2_DS[0..3] bits. If the value is
+ * SPI_CR2_DS_16BIT it means 16 bits, else 8 bits.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/power/pm.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_spi.h"
+
+#include <arch/board/board.h>
+
+#if defined(CONFIG_STM32WB_SPI1) || defined(CONFIG_STM32WB_SPI2)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* SPI interrupts */
+
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+#  error "Interrupt driven SPI not yet supported"
+#endif
+
+/* Can't have both interrupt driven SPI and SPI DMA */
+
+#if defined(CONFIG_STM32WB_SPI_INTERRUPTS) && defined(CONFIG_STM32WB_SPI_DMA)
+#  error "Cannot enable both interrupt mode and DMA mode for SPI"
+#endif
+
+/* SPI DMA priority */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+
+#  if defined(CONFIG_SPI_DMAPRIO)
+#    define SPI_DMA_PRIO  CONFIG_SPI_DMAPRIO
+#  else
+#    define SPI_DMA_PRIO  DMA_CCR_PRIMED
+#  endif
+
+#  if (SPI_DMA_PRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_SPI_DMAPRIO"
+#  endif
+
+#endif
+
+/* DMA channel configuration */
+
+#define SPI_RXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC            )
+#define SPI_RXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC            )
+#define SPI_RXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS                         )
+#define SPI_RXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS                          )
+#define SPI_TXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS             |DMA_CCR_DIR)
+#define SPI_TXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS              |DMA_CCR_DIR)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_spidev_s
+{
+  struct spi_dev_s spidev;       /* Externally visible part of the SPI interface */
+  uint32_t         spibase;      /* SPIn base address */
+  uint32_t         spiclock;     /* Clocking for the SPI module */
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  uint8_t          spiirq;       /* SPI IRQ number */
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  volatile uint8_t rxresult;     /* Result of the RX DMA */
+  volatile uint8_t txresult;     /* Result of the RX DMA */
+#ifdef CONFIG_SPI_TRIGGER
+  bool             defertrig;    /* Flag indicating that trigger should be deferred */
+  bool             trigarmed;    /* Flag indicating that the trigger is armed */
+#endif
+  uint16_t         rxch;         /* The RX DMA channel number */
+  uint16_t         txch;         /* The TX DMA channel number */
+  DMA_HANDLE       rxdma;        /* DMA channel handle for RX transfers */
+  DMA_HANDLE       txdma;        /* DMA channel handle for TX transfers */
+  sem_t            rxsem;        /* Wait for RX DMA to complete */
+  sem_t            txsem;        /* Wait for TX DMA to complete */
+  uint32_t         txccr;        /* DMA control register for TX transfers */
+  uint32_t         rxccr;        /* DMA control register for RX transfers */
+#endif
+  bool             initialized;  /* Has SPI interface been initialized */
+  sem_t            exclsem;      /* Held while chip is selected for mutual exclusion */
+  uint32_t         frequency;    /* Requested clock frequency */
+  uint32_t         actual;       /* Actual clock frequency */
+  uint8_t          nbits;        /* Width of word in bits (4 through 16) */
+  uint8_t          mode;         /* Mode 0,1,2,3 */
+#ifdef CONFIG_PM
+  struct pm_callback_s pm_cb;    /* PM callbacks */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset);
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value);
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv);
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t byte);
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv);
+
+/* DMA support */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int         spi_dmarxwait(struct stm32wb_spidev_s *priv);
+static int         spi_dmatxwait(struct stm32wb_spidev_s *priv);
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv);
+static void        spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                                  void *rxbuffer, void *rxdummy,
+                                  size_t nwords);
+static void        spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                                  const void *txbuffer,
+                                  const void *txdummy, size_t nwords);
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv);
+#endif
+
+/* SPI methods */
+
+static int         spi_lock(struct spi_dev_s *dev, bool lock);
+static uint32_t    spi_setfrequency(struct spi_dev_s *dev,
+                                    uint32_t frequency);
+static void        spi_setmode(struct spi_dev_s *dev,
+                               enum spi_mode_e mode);
+static void        spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int         spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t    spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void        spi_exchange(struct spi_dev_s *dev,
+                                const void *txbuffer,
+                                void *rxbuffer, size_t nwords);
+#ifdef CONFIG_SPI_TRIGGER
+static int         spi_trigger(struct spi_dev_s *dev);
+#endif
+#ifndef CONFIG_SPI_EXCHANGE
+static void        spi_sndblock(struct spi_dev_s *dev,
+                                const void *txbuffer, size_t nwords);
+static void        spi_recvblock(struct spi_dev_s *dev,
+                                 void *rxbuffer, size_t nwords);
+#endif
+
+/* Initialization */
+
+static void        spi_bus_initialize(struct stm32wb_spidev_s *priv);
+
+/* PM interface */
+
+#ifdef CONFIG_PM
+static int         spi_pm_prepare(struct pm_callback_s *cb, int domain,
+                                  enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI1
+static const struct spi_ops_s g_spi1ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi1select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi1status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi1cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi1register,  /* Provided externally */
+#else
+  .registercallback  = 0,                   /* Not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi1dev =
+{
+  .spidev   =
+    {
+      &g_spi1ops
+    },
+  .spibase  = STM32WB_SPI1_BASE,
+  .spiclock = STM32WB_PCLK2_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI1,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  /* lines must be configured in board.h */
+
+  .rxch     = DMAMAP_SPI1_RX,
+  .txch     = DMAMAP_SPI1_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+#ifdef CONFIG_STM32WB_SPI2
+static const struct spi_ops_s g_spi2ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi2select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi2status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi2cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi2register,  /* provided externally */
+#else
+  .registercallback  = 0,  /* not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi2dev =
+{
+  .spidev   =
+    {
+      &g_spi2ops
+    },
+  .spibase  = STM32WB_SPI2_BASE,
+  .spiclock = STM32WB_PCLK1_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI2,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  .rxch     = DMACHAN_SPI2_RX,
+  .txch     = DMACHAN_SPI2_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spi_getreg
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg16(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg
+ *
+ * Description:
+ *   Write a 16-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 16-bit value to be written
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value)
+{
+  putreg16(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_getreg8
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 8-bit register
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_getreg8(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg8(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg8
+ *
+ * Description:
+ *   Write a 8-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 8-bit value to be written
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg8(struct stm32wb_spidev_s *priv,
+                               uint8_t offset, uint8_t value)
+{
+  putreg8(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_readword
+ *
+ * Description:
+ *   Read one word (TWO bytes!) from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Word as read
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_readbyte
+ *
+ * Description:
+ *   Read one byte from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Byte as read
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_readbyte(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg8(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_writeword
+ *
+ * Description:
+ *   Write one 16-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Word to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t word)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg(priv, STM32WB_SPI_DR_OFFSET, word);
+}
+
+/****************************************************************************
+ * Name: spi_writebyte
+ *
+ * Description:
+ *   Write one 8-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Byte to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writebyte(struct stm32wb_spidev_s *priv,
+                                 uint8_t byte)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg8(priv, STM32WB_SPI_DR_OFFSET, byte);
+}
+
+/****************************************************************************
+ * Name: spi_16bitmode
+ *
+ * Description:
+ *   Check if the SPI is operating in 16-bit mode
+ *
+ * Input Parameters:
+ *   priv     - Device-specific state data
+ *
+ * Returned Value:
+ *   true: 16-bit mode, false: 8-bit mode
+ *
+ ****************************************************************************/
+
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv)
+{
+  return (priv->nbits > 8);
+}
+
+/****************************************************************************
+ * Name: spi_dmarxwaitw
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmarxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->rxsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->rxresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwait
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmatxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->txsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->txresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv)
+{
+  nxsem_post(&priv->rxsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv)
+{
+  nxsem_post(&priv->txsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->rxresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmarxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->txresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmatxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxsetup
+ *
+ * Description:
+ *   Setup to perform RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                           void *rxbuffer, void *rxdummy, size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA16_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA8_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Configure the RX DMA */
+
+  stm32wb_dmasetup(priv->rxdma, priv->spibase + STM32WB_SPI_DR_OFFSET,
+                 (uint32_t)rxbuffer, nwords, priv->rxccr);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxsetup
+ *
+ * Description:
+ *   Setup to perform TX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                           const void *txbuffer, const void *txdummy,
+                           size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA16_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA8_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Setup the TX DMA */
+
+  stm32wb_dmasetup(priv->txdma, priv->spibase + STM32WB_SPI_DR_OFFSET,
+                 (uint32_t)txbuffer, nwords, priv->txccr);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxstart
+ *
+ * Description:
+ *   Start RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv)
+{
+  priv->rxresult = 0;
+  stm32wb_dmastart(priv->rxdma, spi_dmarxcallback, priv, false);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxstart
+ *
+ * Description:
+ *   Start TX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv)
+{
+  priv->txresult = 0;
+  stm32wb_dmastart(priv->txdma, spi_dmatxcallback, priv, false);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_modifycr
+ *
+ * Description:
+ *   Clear and set bits in the CR1 or CR2 register
+ *
+ * Input Parameters:
+ *   priv    - Device-specific state data
+ *   clrbits - The bits to clear
+ *   setbits - The bits to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_modifycr(uint32_t addr, struct stm32wb_spidev_s *priv,
+                         uint16_t setbits, uint16_t clrbits)
+{
+  uint16_t cr;
+
+  cr  = spi_getreg(priv, addr);
+  cr &= ~clrbits;
+  cr |= setbits;
+  spi_putreg(priv, addr, cr);
+}
+
+/****************************************************************************
+ * Name: spi_lock
+ *
+ * Description:
+ *   On SPI buses where there are multiple devices, it will be necessary to
+ *   lock SPI to have exclusive access to the buses for a sequence of
+ *   transfers.  The bus should be locked before the chip is selected. After
+ *   locking the SPI bus, the caller should then also call the setfrequency,
+ *   setbits, and setmode methods to make sure that the SPI is properly
+ *   configured for the device.  If the SPI bus is being shared, then it
+ *   may have been left in an incompatible state.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   lock - true: Lock spi bus, false: unlock SPI bus
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int spi_lock(struct spi_dev_s *dev, bool lock)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  int ret;
+
+  if (lock)
+    {
+      ret = nxsem_wait_uninterruptible(&priv->exclsem);
+    }
+  else
+    {
+      ret = nxsem_post(&priv->exclsem);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: spi_setfrequency
+ *
+ * Description:
+ *   Set the SPI frequency.
+ *
+ * Input Parameters:
+ *   dev -       Device-specific state data
+ *   frequency - The SPI frequency requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint16_t setbits;
+  uint32_t actual;
+
+  /* Limit to max possible (if STM32WB_SPI_CLK_MAX is defined in board.h) */
+
+  if (frequency > STM32WB_SPI_CLK_MAX)
+    {
+      frequency = STM32WB_SPI_CLK_MAX;
+    }
+
+  /* Has the frequency changed? */
+
+  if (frequency != priv->frequency)
+    {
+      /* Choices are limited by PCLK frequency with a set of divisors */
+
+      if (frequency >= priv->spiclock >> 1)
+        {
+          /* More than fPCLK/2.  This is as fast as we can go */
+
+          setbits = SPI_CR1_FPCLCKd2; /* 000: fPCLK/2 */
+          actual = priv->spiclock >> 1;
+        }
+      else if (frequency >= priv->spiclock >> 2)
+        {
+          /* Between fPCLCK/2 and fPCLCK/4, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd4; /* 001: fPCLK/4 */
+          actual = priv->spiclock >> 2;
+        }
+      else if (frequency >= priv->spiclock >> 3)
+        {
+          /* Between fPCLCK/4 and fPCLCK/8, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd8; /* 010: fPCLK/8 */
+          actual = priv->spiclock >> 3;
+        }
+      else if (frequency >= priv->spiclock >> 4)
+        {
+          /* Between fPCLCK/8 and fPCLCK/16, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd16; /* 011: fPCLK/16 */
+          actual = priv->spiclock >> 4;
+        }
+      else if (frequency >= priv->spiclock >> 5)
+        {
+          /* Between fPCLCK/16 and fPCLCK/32, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd32; /* 100: fPCLK/32 */
+          actual = priv->spiclock >> 5;
+        }
+      else if (frequency >= priv->spiclock >> 6)
+        {
+          /* Between fPCLCK/32 and fPCLCK/64, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd64; /*  101: fPCLK/64 */
+          actual = priv->spiclock >> 6;
+        }
+      else if (frequency >= priv->spiclock >> 7)
+        {
+          /* Between fPCLCK/64 and fPCLCK/128, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd128; /* 110: fPCLK/128 */
+          actual = priv->spiclock >> 7;
+        }
+      else
+        {
+          /* Less than fPCLK/128.  This is as slow as we can go */
+
+          setbits = SPI_CR1_FPCLCKd256; /* 111: fPCLK/256 */
+          actual = priv->spiclock >> 8;
+        }
+
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, setbits, SPI_CR1_BR_MASK);
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+      /* Save the frequency selection so that subsequent reconfigurations
+       * will be faster.
+       */
+
+      spiinfo("Frequency %" PRId32 "->%" PRId32 "\n", frequency, actual);
+
+      priv->frequency = frequency;
+      priv->actual    = actual;
+    }
+
+  return priv->actual;
+}
+
+/****************************************************************************
+ * Name: spi_setmode
+ *
+ * Description:
+ *   Set the SPI mode.  see enum spi_mode_e for mode definitions
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   mode - The SPI mode requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("mode=%d\n", mode);
+
+  /* Has the mode changed? */
+
+  if (mode != priv->mode)
+    {
+      /* Yes... Set CR1 appropriately */
+
+      switch (mode)
+        {
+        case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
+          setbits = 0;
+          clrbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          break;
+
+        case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
+          setbits = SPI_CR1_CPHA;
+          clrbits = SPI_CR1_CPOL;
+          break;
+
+        case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
+          setbits = SPI_CR1_CPOL;
+          clrbits = SPI_CR1_CPHA;
+          break;
+
+        case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
+          setbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          clrbits = 0;
+          break;
+
+        default:
+          return;
+        }
+
+        spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+        spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, setbits, clrbits);
+        spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+        /* Save the mode so that subsequent re-configurations will be
+         * faster.
+         */
+
+        priv->mode = mode;
+    }
+}
+
+/****************************************************************************
+ * Name: spi_setbits
+ *
+ * Description:
+ *   Set the number of bits per word. With STM32WB, this is not restricted to
+ *   8 or 16, but can be any value between 4 and 16.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *   nbits - The number of bits requested, negative value means LSB first.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_setbits(struct spi_dev_s *dev, int nbits)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("nbits=%d\n", nbits);
+
+  /* Has the number of bits changed? */
+
+  if (nbits != priv->nbits)
+    {
+      /* Yes... Set CR2 appropriately */
+
+      /* Set the number of bits (valid range 4-16) */
+
+      if (nbits < 4 || nbits > 16)
+        {
+          spierr("ERROR: nbits out of range: %d\n", nbits);
+          return;
+        }
+
+      clrbits = SPI_CR2_DS_MASK;
+      setbits = SPI_CR2_DS_VAL(nbits);
+
+      /* If nbits is <=8, then we are in byte mode and FRXTH shall be set
+       * (else, transaction will not complete).
+       */
+
+      if (nbits < 9)
+        {
+          setbits |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 1 byte */
+        }
+      else
+        {
+          clrbits |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 2 bytes */
+        }
+
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+      spi_modifycr(STM32WB_SPI_CR2_OFFSET, priv, setbits, clrbits);
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+      /* Save the selection so that subsequent re-configurations will be
+       * faster.
+       */
+
+      priv->nbits = nbits;
+    }
+}
+
+/****************************************************************************
+ * Name: spi_hwfeatures
+ *
+ * Description:
+ *   Set hardware-specific feature flags.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   features - H/W feature flags
+ *
+ * Returned Value:
+ *   Zero (OK) if the selected H/W features are enabled; A negated errno
+ *   value if any H/W feature is not supportable.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_HWFEATURES
+static int spi_hwfeatures(struct spi_dev_s *dev, spi_hwfeatures_t features)
+{
+#if defined(CONFIG_SPI_BITORDER) || defined(CONFIG_SPI_TRIGGER)
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+#endif
+
+#ifdef CONFIG_SPI_BITORDER
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("features=%08x\n", features);
+
+  /* Transfer data LSB first? */
+
+  if ((features & HWFEAT_LSBFIRST) != 0)
+    {
+      setbits = SPI_CR1_LSBFIRST;
+      clrbits = 0;
+    }
+  else
+    {
+      setbits = 0;
+      clrbits = SPI_CR1_LSBFIRST;
+    }
+
+  spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+  spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, setbits, clrbits);
+  spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+  features &= ~HWFEAT_LSBFIRST;
+#endif
+
+#ifdef CONFIG_SPI_TRIGGER
+/* Turn deferred trigger mode on or off.  Only applicable for DMA mode. If a
+ * transfer is deferred then the DMA will not actually be triggered until a
+ * subsequent call to SPI_TRIGGER to set it off. The thread will be waiting
+ * on the transfer completing as normal.
+ */
+
+  priv->defertrig = ((features & HWFEAT_TRIGGER) != 0);
+  features &= ~HWFEAT_TRIGGER;
+#endif
+
+  /* Other H/W features are not supported */
+
+  return (features == 0) ? OK : -ENOSYS;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_send
+ *
+ * Description:
+ *   Exchange one word on SPI
+ *
+ * Input Parameters:
+ *   dev - Device-specific state data
+ *   wd  - The word to send.  the size of the data is determined by the
+ *         number of bits selected for the SPI interface.
+ *
+ * Returned Value:
+ *   response
+ *
+ ****************************************************************************/
+
+static uint32_t spi_send(struct spi_dev_s *dev, uint32_t wd)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint32_t regval;
+  uint32_t ret;
+
+  DEBUGASSERT(priv && priv->spibase);
+
+  /* According to the number of bits, access data register as word or byte
+   * This is absolutely required because of packing. With <=8 bit frames,
+   * two bytes are received by a 16-bit read of the data register!
+   */
+
+  if (spi_16bitmode(priv))
+    {
+      spi_writeword(priv, (uint16_t)(wd & 0xffff));
+      ret = (uint32_t)spi_readword(priv);
+    }
+  else
+    {
+      spi_writebyte(priv, (uint8_t)(wd & 0xff));
+      ret = (uint32_t)spi_readbyte(priv);
+    }
+
+  /* Check and clear any error flags (Reading from the SR clears the error
+   * flags).
+   */
+
+  regval = spi_getreg(priv, STM32WB_SPI_SR_OFFSET);
+
+  if (spi_16bitmode(priv))
+    {
+      spiinfo("Sent: %04" PRIx32 " Return: %04" PRIx32
+              " Status: %02" PRIx32 "\n", wd, ret, regval);
+    }
+  else
+    {
+      spiinfo("Sent: %02" PRIx32 " Return: %02" PRIx32
+              " Status: %02" PRIx32 "\n", wd, ret, regval);
+    }
+
+  UNUSED(regval);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: spi_exchange (no DMA).  aka spi_exchange_nodma
+ *
+ * Description:
+ *   Exchange a block of data on SPI without using DMA
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   txbuffer - A pointer to the buffer of data to be sent
+ *   rxbuffer - A pointer to a buffer in which to receive data
+ *   nwords   - the length of data to be exchanged in units of words.
+ *              The wordsize is determined by the number of bits-per-word
+ *              selected for the SPI interface.  If nbits <= 8, the data is
+ *              packed into uint8_t's; if nbits >8, the data is packed into
+ *              uint16_t's
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if !defined(CONFIG_STM32WB_SPI_DMA) || defined(CONFIG_STM32WB_DMACAPABLE)
+#if !defined(CONFIG_STM32WB_SPI_DMA)
+static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+                         void *rxbuffer, size_t nwords)
+#else
+static void spi_exchange_nodma(struct spi_dev_s *dev,
+                               const void *txbuffer, void *rxbuffer,
+                               size_t nwords)
+#endif
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  DEBUGASSERT(priv && priv->spibase);
+
+  spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);
+
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode */
+
+      const uint16_t *src  = (const uint16_t *)txbuffer;
+            uint16_t *dest = (uint16_t *)rxbuffer;
+            uint16_t  word;
+
+      while (nwords-- > 0)
+        {
+          /* Get the next word to write.  Is there a source buffer? */
+
+          if (src)
+            {
+              word = *src++;
+            }
+          else
+            {
+              word = 0xffff;
+            }
+
+          /* Exchange one word */
+
+          word = (uint16_t)spi_send(dev, (uint32_t)word);
+
+          /* Is there a buffer to receive the return value? */
+
+          if (dest)
+            {
+              *dest++ = word;
+            }
+        }
+    }
+  else
+    {
+      /* 8-bit mode */
+
+      const uint8_t *src  = (const uint8_t *)txbuffer;
+            uint8_t *dest = (uint8_t *)rxbuffer;
+            uint8_t  word;
+
+      while (nwords-- > 0)
+        {
+          /* Get the next word to write.  Is there a source buffer? */
+
+          if (src)
+            {
+              word = *src++;
+            }
+          else
+            {
+              word = 0xff;
+            }
+
+          /* Exchange one word */
+
+          word = (uint8_t)spi_send(dev, (uint32_t)word);
+
+          /* Is there a buffer to receive the return value? */
+
+          if (dest)
+            {
+              *dest++ = word;
+            }
+        }
+    }
+}
+#endif /* !CONFIG_STM32WB_SPI_DMA || CONFIG_STM32WB_DMACAPABLE */
+
+/****************************************************************************
+ * Name: spi_exchange (with DMA capability)
+ *
+ * Description:
+ *   Exchange a block of data on SPI using DMA
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   txbuffer - A pointer to the buffer of data to be sent
+ *   rxbuffer - A pointer to a buffer in which to receive data
+ *   nwords   - the length of data to be exchanged in units of words.
+ *              The wordsize is determined by the number of bits-per-word
+ *              selected for the SPI interface.  If nbits <= 8, the data is
+ *              packed into uint8_t's; if nbits >8, the data is packed into
+ *              uint16_t's
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+                         void *rxbuffer, size_t nwords)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  int ret;
+
+#ifdef CONFIG_STM32WB_DMACAPABLE
+  if ((txbuffer &&
+       !stm32wb_dmacapable((uint32_t)txbuffer, nwords, priv->txccr)) ||
+      (rxbuffer &&
+       !stm32wb_dmacapable((uint32_t)rxbuffer, nwords, priv->rxccr)))

Review Comment:
   ```suggestion
     if ((txbuffer != NULL &&
          !stm32wb_dmacapable((uint32_t)txbuffer, nwords, priv->txccr)) ||
         (rxbuffer != NULL &&
          !stm32wb_dmacapable((uint32_t)rxbuffer, nwords, priv->rxccr)))
   ```



##########
arch/arm/src/stm32wb/stm32wb_spi.c:
##########
@@ -0,0 +1,1819 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * The external functions, stm32wb_spi1/2select and stm32wb_spi1/2status
+ * must be provided by board-specific logic.  They are implementations of the
+ * select and status methods of the SPI interface defined by struct spi_ops_s
+ * (see include/nuttx/spi/spi.h).  All other methods (including
+ * stm32wb_spibus_initialize()) are provided by common STM32 logic.  To use
+ * this common SPI logic on your board:
+ *
+ *   1. Provide logic in stm32wb_board_initialize() to configure SPI chip
+ *      select pins.
+ *   2. Provide stm32wb_spi1/2select() and stm32wb_spi1/2status()
+ *      functions in your board-specific logic.  These functions will perform
+ *      chip selection and status operations using GPIOs in the way your
+ *      board is configured.
+ *   3. Add a calls to stm32wb_spibus_initialize() in your low level
+ *      application initialization logic
+ *   4. The handle returned by stm32wb_spibus_initialize() may then be used
+ *      to bind the SPI driver to higher level logic (e.g., calling
+ *      mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ *      the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+/* This driver is ported from the stm32 one, which only supports 8 and 16
+ * bits transfers. The STM32WB family supports frame size from 4 to 16 bits,
+ * but we do not support that yet. For the moment, we replace uses of the
+ * CR1_DFF bit with a check of the CR2_DS[0..3] bits. If the value is
+ * SPI_CR2_DS_16BIT it means 16 bits, else 8 bits.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/power/pm.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_spi.h"
+
+#include <arch/board/board.h>
+
+#if defined(CONFIG_STM32WB_SPI1) || defined(CONFIG_STM32WB_SPI2)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* SPI interrupts */
+
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+#  error "Interrupt driven SPI not yet supported"
+#endif
+
+/* Can't have both interrupt driven SPI and SPI DMA */
+
+#if defined(CONFIG_STM32WB_SPI_INTERRUPTS) && defined(CONFIG_STM32WB_SPI_DMA)
+#  error "Cannot enable both interrupt mode and DMA mode for SPI"
+#endif
+
+/* SPI DMA priority */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+
+#  if defined(CONFIG_SPI_DMAPRIO)
+#    define SPI_DMA_PRIO  CONFIG_SPI_DMAPRIO
+#  else
+#    define SPI_DMA_PRIO  DMA_CCR_PRIMED
+#  endif
+
+#  if (SPI_DMA_PRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_SPI_DMAPRIO"
+#  endif
+
+#endif
+
+/* DMA channel configuration */
+
+#define SPI_RXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC            )
+#define SPI_RXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC            )
+#define SPI_RXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS                         )
+#define SPI_RXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS                          )
+#define SPI_TXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS             |DMA_CCR_DIR)
+#define SPI_TXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS              |DMA_CCR_DIR)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_spidev_s
+{
+  struct spi_dev_s spidev;       /* Externally visible part of the SPI interface */
+  uint32_t         spibase;      /* SPIn base address */
+  uint32_t         spiclock;     /* Clocking for the SPI module */
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  uint8_t          spiirq;       /* SPI IRQ number */
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  volatile uint8_t rxresult;     /* Result of the RX DMA */
+  volatile uint8_t txresult;     /* Result of the RX DMA */
+#ifdef CONFIG_SPI_TRIGGER
+  bool             defertrig;    /* Flag indicating that trigger should be deferred */
+  bool             trigarmed;    /* Flag indicating that the trigger is armed */
+#endif
+  uint16_t         rxch;         /* The RX DMA channel number */
+  uint16_t         txch;         /* The TX DMA channel number */
+  DMA_HANDLE       rxdma;        /* DMA channel handle for RX transfers */
+  DMA_HANDLE       txdma;        /* DMA channel handle for TX transfers */
+  sem_t            rxsem;        /* Wait for RX DMA to complete */
+  sem_t            txsem;        /* Wait for TX DMA to complete */
+  uint32_t         txccr;        /* DMA control register for TX transfers */
+  uint32_t         rxccr;        /* DMA control register for RX transfers */
+#endif
+  bool             initialized;  /* Has SPI interface been initialized */
+  sem_t            exclsem;      /* Held while chip is selected for mutual exclusion */
+  uint32_t         frequency;    /* Requested clock frequency */
+  uint32_t         actual;       /* Actual clock frequency */
+  uint8_t          nbits;        /* Width of word in bits (4 through 16) */
+  uint8_t          mode;         /* Mode 0,1,2,3 */
+#ifdef CONFIG_PM
+  struct pm_callback_s pm_cb;    /* PM callbacks */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset);
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value);
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv);
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t byte);
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv);
+
+/* DMA support */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int         spi_dmarxwait(struct stm32wb_spidev_s *priv);
+static int         spi_dmatxwait(struct stm32wb_spidev_s *priv);
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv);
+static void        spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                                  void *rxbuffer, void *rxdummy,
+                                  size_t nwords);
+static void        spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                                  const void *txbuffer,
+                                  const void *txdummy, size_t nwords);
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv);
+#endif
+
+/* SPI methods */
+
+static int         spi_lock(struct spi_dev_s *dev, bool lock);
+static uint32_t    spi_setfrequency(struct spi_dev_s *dev,
+                                    uint32_t frequency);
+static void        spi_setmode(struct spi_dev_s *dev,
+                               enum spi_mode_e mode);
+static void        spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int         spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t    spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void        spi_exchange(struct spi_dev_s *dev,
+                                const void *txbuffer,
+                                void *rxbuffer, size_t nwords);
+#ifdef CONFIG_SPI_TRIGGER
+static int         spi_trigger(struct spi_dev_s *dev);
+#endif
+#ifndef CONFIG_SPI_EXCHANGE
+static void        spi_sndblock(struct spi_dev_s *dev,
+                                const void *txbuffer, size_t nwords);
+static void        spi_recvblock(struct spi_dev_s *dev,
+                                 void *rxbuffer, size_t nwords);
+#endif
+
+/* Initialization */
+
+static void        spi_bus_initialize(struct stm32wb_spidev_s *priv);
+
+/* PM interface */
+
+#ifdef CONFIG_PM
+static int         spi_pm_prepare(struct pm_callback_s *cb, int domain,
+                                  enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI1
+static const struct spi_ops_s g_spi1ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi1select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi1status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi1cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi1register,  /* Provided externally */
+#else
+  .registercallback  = 0,                   /* Not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi1dev =
+{
+  .spidev   =
+    {
+      &g_spi1ops
+    },
+  .spibase  = STM32WB_SPI1_BASE,
+  .spiclock = STM32WB_PCLK2_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI1,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  /* lines must be configured in board.h */
+
+  .rxch     = DMAMAP_SPI1_RX,
+  .txch     = DMAMAP_SPI1_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+#ifdef CONFIG_STM32WB_SPI2
+static const struct spi_ops_s g_spi2ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi2select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi2status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi2cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi2register,  /* provided externally */
+#else
+  .registercallback  = 0,  /* not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi2dev =
+{
+  .spidev   =
+    {
+      &g_spi2ops
+    },
+  .spibase  = STM32WB_SPI2_BASE,
+  .spiclock = STM32WB_PCLK1_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI2,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  .rxch     = DMACHAN_SPI2_RX,
+  .txch     = DMACHAN_SPI2_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spi_getreg
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg16(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg
+ *
+ * Description:
+ *   Write a 16-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 16-bit value to be written
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value)
+{
+  putreg16(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_getreg8
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 8-bit register
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_getreg8(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg8(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg8
+ *
+ * Description:
+ *   Write a 8-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 8-bit value to be written
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg8(struct stm32wb_spidev_s *priv,
+                               uint8_t offset, uint8_t value)
+{
+  putreg8(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_readword
+ *
+ * Description:
+ *   Read one word (TWO bytes!) from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Word as read
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_readbyte
+ *
+ * Description:
+ *   Read one byte from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Byte as read
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_readbyte(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg8(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_writeword
+ *
+ * Description:
+ *   Write one 16-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Word to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t word)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg(priv, STM32WB_SPI_DR_OFFSET, word);
+}
+
+/****************************************************************************
+ * Name: spi_writebyte
+ *
+ * Description:
+ *   Write one 8-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Byte to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writebyte(struct stm32wb_spidev_s *priv,
+                                 uint8_t byte)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg8(priv, STM32WB_SPI_DR_OFFSET, byte);
+}
+
+/****************************************************************************
+ * Name: spi_16bitmode
+ *
+ * Description:
+ *   Check if the SPI is operating in 16-bit mode
+ *
+ * Input Parameters:
+ *   priv     - Device-specific state data
+ *
+ * Returned Value:
+ *   true: 16-bit mode, false: 8-bit mode
+ *
+ ****************************************************************************/
+
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv)
+{
+  return (priv->nbits > 8);
+}
+
+/****************************************************************************
+ * Name: spi_dmarxwaitw
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmarxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->rxsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->rxresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwait
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmatxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->txsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->txresult == 0 && ret == OK);
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv)
+{
+  nxsem_post(&priv->rxsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxwakeup
+ *
+ * Description:
+ *   Signal that DMA is complete
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv)
+{
+  nxsem_post(&priv->txsem);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->rxresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmarxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxcallback
+ *
+ * Description:
+ *   Called when the RX DMA completes
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)arg;
+
+  /* Wake-up the SPI driver */
+
+  priv->txresult = isr | 0x80;  /* OR'ed with 0x80 to assure non-zero */
+  spi_dmatxwakeup(priv);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxsetup
+ *
+ * Description:
+ *   Setup to perform RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                           void *rxbuffer, void *rxdummy, size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA16_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to receive data in? */
+
+      if (rxbuffer)
+        {
+          priv->rxccr = SPI_RXDMA8_CONFIG;
+        }
+      else
+        {
+          rxbuffer    = rxdummy;
+          priv->rxccr = SPI_RXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Configure the RX DMA */
+
+  stm32wb_dmasetup(priv->rxdma, priv->spibase + STM32WB_SPI_DR_OFFSET,
+                 (uint32_t)rxbuffer, nwords, priv->rxccr);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxsetup
+ *
+ * Description:
+ *   Setup to perform TX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static void spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                           const void *txbuffer, const void *txdummy,
+                           size_t nwords)
+{
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA16_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA16NULL_CONFIG;
+        }
+    }
+  else
+    {
+      /* 8-bit mode -- is there a buffer to transfer data from? */
+
+      if (txbuffer)
+        {
+          priv->txccr = SPI_TXDMA8_CONFIG;
+        }
+      else
+        {
+          txbuffer    = txdummy;
+          priv->txccr = SPI_TXDMA8NULL_CONFIG;
+        }
+    }
+
+  /* Setup the TX DMA */
+
+  stm32wb_dmasetup(priv->txdma, priv->spibase + STM32WB_SPI_DR_OFFSET,
+                 (uint32_t)txbuffer, nwords, priv->txccr);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmarxstart
+ *
+ * Description:
+ *   Start RX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv)
+{
+  priv->rxresult = 0;
+  stm32wb_dmastart(priv->rxdma, spi_dmarxcallback, priv, false);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_dmatxstart
+ *
+ * Description:
+ *   Start TX DMA
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv)
+{
+  priv->txresult = 0;
+  stm32wb_dmastart(priv->txdma, spi_dmatxcallback, priv, false);
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_modifycr
+ *
+ * Description:
+ *   Clear and set bits in the CR1 or CR2 register
+ *
+ * Input Parameters:
+ *   priv    - Device-specific state data
+ *   clrbits - The bits to clear
+ *   setbits - The bits to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_modifycr(uint32_t addr, struct stm32wb_spidev_s *priv,
+                         uint16_t setbits, uint16_t clrbits)
+{
+  uint16_t cr;
+
+  cr  = spi_getreg(priv, addr);
+  cr &= ~clrbits;
+  cr |= setbits;
+  spi_putreg(priv, addr, cr);
+}
+
+/****************************************************************************
+ * Name: spi_lock
+ *
+ * Description:
+ *   On SPI buses where there are multiple devices, it will be necessary to
+ *   lock SPI to have exclusive access to the buses for a sequence of
+ *   transfers.  The bus should be locked before the chip is selected. After
+ *   locking the SPI bus, the caller should then also call the setfrequency,
+ *   setbits, and setmode methods to make sure that the SPI is properly
+ *   configured for the device.  If the SPI bus is being shared, then it
+ *   may have been left in an incompatible state.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   lock - true: Lock spi bus, false: unlock SPI bus
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int spi_lock(struct spi_dev_s *dev, bool lock)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  int ret;
+
+  if (lock)
+    {
+      ret = nxsem_wait_uninterruptible(&priv->exclsem);
+    }
+  else
+    {
+      ret = nxsem_post(&priv->exclsem);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: spi_setfrequency
+ *
+ * Description:
+ *   Set the SPI frequency.
+ *
+ * Input Parameters:
+ *   dev -       Device-specific state data
+ *   frequency - The SPI frequency requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint16_t setbits;
+  uint32_t actual;
+
+  /* Limit to max possible (if STM32WB_SPI_CLK_MAX is defined in board.h) */
+
+  if (frequency > STM32WB_SPI_CLK_MAX)
+    {
+      frequency = STM32WB_SPI_CLK_MAX;
+    }
+
+  /* Has the frequency changed? */
+
+  if (frequency != priv->frequency)
+    {
+      /* Choices are limited by PCLK frequency with a set of divisors */
+
+      if (frequency >= priv->spiclock >> 1)
+        {
+          /* More than fPCLK/2.  This is as fast as we can go */
+
+          setbits = SPI_CR1_FPCLCKd2; /* 000: fPCLK/2 */
+          actual = priv->spiclock >> 1;
+        }
+      else if (frequency >= priv->spiclock >> 2)
+        {
+          /* Between fPCLCK/2 and fPCLCK/4, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd4; /* 001: fPCLK/4 */
+          actual = priv->spiclock >> 2;
+        }
+      else if (frequency >= priv->spiclock >> 3)
+        {
+          /* Between fPCLCK/4 and fPCLCK/8, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd8; /* 010: fPCLK/8 */
+          actual = priv->spiclock >> 3;
+        }
+      else if (frequency >= priv->spiclock >> 4)
+        {
+          /* Between fPCLCK/8 and fPCLCK/16, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd16; /* 011: fPCLK/16 */
+          actual = priv->spiclock >> 4;
+        }
+      else if (frequency >= priv->spiclock >> 5)
+        {
+          /* Between fPCLCK/16 and fPCLCK/32, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd32; /* 100: fPCLK/32 */
+          actual = priv->spiclock >> 5;
+        }
+      else if (frequency >= priv->spiclock >> 6)
+        {
+          /* Between fPCLCK/32 and fPCLCK/64, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd64; /*  101: fPCLK/64 */
+          actual = priv->spiclock >> 6;
+        }
+      else if (frequency >= priv->spiclock >> 7)
+        {
+          /* Between fPCLCK/64 and fPCLCK/128, pick the slower */
+
+          setbits = SPI_CR1_FPCLCKd128; /* 110: fPCLK/128 */
+          actual = priv->spiclock >> 7;
+        }
+      else
+        {
+          /* Less than fPCLK/128.  This is as slow as we can go */
+
+          setbits = SPI_CR1_FPCLCKd256; /* 111: fPCLK/256 */
+          actual = priv->spiclock >> 8;
+        }
+
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, setbits, SPI_CR1_BR_MASK);
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+      /* Save the frequency selection so that subsequent reconfigurations
+       * will be faster.
+       */
+
+      spiinfo("Frequency %" PRId32 "->%" PRId32 "\n", frequency, actual);
+
+      priv->frequency = frequency;
+      priv->actual    = actual;
+    }
+
+  return priv->actual;
+}
+
+/****************************************************************************
+ * Name: spi_setmode
+ *
+ * Description:
+ *   Set the SPI mode.  see enum spi_mode_e for mode definitions
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   mode - The SPI mode requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("mode=%d\n", mode);
+
+  /* Has the mode changed? */
+
+  if (mode != priv->mode)
+    {
+      /* Yes... Set CR1 appropriately */
+
+      switch (mode)
+        {
+        case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
+          setbits = 0;
+          clrbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          break;
+
+        case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
+          setbits = SPI_CR1_CPHA;
+          clrbits = SPI_CR1_CPOL;
+          break;
+
+        case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
+          setbits = SPI_CR1_CPOL;
+          clrbits = SPI_CR1_CPHA;
+          break;
+
+        case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
+          setbits = SPI_CR1_CPOL | SPI_CR1_CPHA;
+          clrbits = 0;
+          break;
+
+        default:
+          return;
+        }
+
+        spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+        spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, setbits, clrbits);
+        spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+        /* Save the mode so that subsequent re-configurations will be
+         * faster.
+         */
+
+        priv->mode = mode;
+    }
+}
+
+/****************************************************************************
+ * Name: spi_setbits
+ *
+ * Description:
+ *   Set the number of bits per word. With STM32WB, this is not restricted to
+ *   8 or 16, but can be any value between 4 and 16.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *   nbits - The number of bits requested, negative value means LSB first.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void spi_setbits(struct spi_dev_s *dev, int nbits)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("nbits=%d\n", nbits);
+
+  /* Has the number of bits changed? */
+
+  if (nbits != priv->nbits)
+    {
+      /* Yes... Set CR2 appropriately */
+
+      /* Set the number of bits (valid range 4-16) */
+
+      if (nbits < 4 || nbits > 16)
+        {
+          spierr("ERROR: nbits out of range: %d\n", nbits);
+          return;
+        }
+
+      clrbits = SPI_CR2_DS_MASK;
+      setbits = SPI_CR2_DS_VAL(nbits);
+
+      /* If nbits is <=8, then we are in byte mode and FRXTH shall be set
+       * (else, transaction will not complete).
+       */
+
+      if (nbits < 9)
+        {
+          setbits |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 1 byte */
+        }
+      else
+        {
+          clrbits |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 2 bytes */
+        }
+
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+      spi_modifycr(STM32WB_SPI_CR2_OFFSET, priv, setbits, clrbits);
+      spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+      /* Save the selection so that subsequent re-configurations will be
+       * faster.
+       */
+
+      priv->nbits = nbits;
+    }
+}
+
+/****************************************************************************
+ * Name: spi_hwfeatures
+ *
+ * Description:
+ *   Set hardware-specific feature flags.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   features - H/W feature flags
+ *
+ * Returned Value:
+ *   Zero (OK) if the selected H/W features are enabled; A negated errno
+ *   value if any H/W feature is not supportable.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_HWFEATURES
+static int spi_hwfeatures(struct spi_dev_s *dev, spi_hwfeatures_t features)
+{
+#if defined(CONFIG_SPI_BITORDER) || defined(CONFIG_SPI_TRIGGER)
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+#endif
+
+#ifdef CONFIG_SPI_BITORDER
+  uint16_t setbits;
+  uint16_t clrbits;
+
+  spiinfo("features=%08x\n", features);
+
+  /* Transfer data LSB first? */
+
+  if ((features & HWFEAT_LSBFIRST) != 0)
+    {
+      setbits = SPI_CR1_LSBFIRST;
+      clrbits = 0;
+    }
+  else
+    {
+      setbits = 0;
+      clrbits = SPI_CR1_LSBFIRST;
+    }
+
+  spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, 0, SPI_CR1_SPE);
+  spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, setbits, clrbits);
+  spi_modifycr(STM32WB_SPI_CR1_OFFSET, priv, SPI_CR1_SPE, 0);
+
+  features &= ~HWFEAT_LSBFIRST;
+#endif
+
+#ifdef CONFIG_SPI_TRIGGER
+/* Turn deferred trigger mode on or off.  Only applicable for DMA mode. If a
+ * transfer is deferred then the DMA will not actually be triggered until a
+ * subsequent call to SPI_TRIGGER to set it off. The thread will be waiting
+ * on the transfer completing as normal.
+ */
+
+  priv->defertrig = ((features & HWFEAT_TRIGGER) != 0);
+  features &= ~HWFEAT_TRIGGER;
+#endif
+
+  /* Other H/W features are not supported */
+
+  return (features == 0) ? OK : -ENOSYS;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_send
+ *
+ * Description:
+ *   Exchange one word on SPI
+ *
+ * Input Parameters:
+ *   dev - Device-specific state data
+ *   wd  - The word to send.  the size of the data is determined by the
+ *         number of bits selected for the SPI interface.
+ *
+ * Returned Value:
+ *   response
+ *
+ ****************************************************************************/
+
+static uint32_t spi_send(struct spi_dev_s *dev, uint32_t wd)
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  uint32_t regval;
+  uint32_t ret;
+
+  DEBUGASSERT(priv && priv->spibase);
+
+  /* According to the number of bits, access data register as word or byte
+   * This is absolutely required because of packing. With <=8 bit frames,
+   * two bytes are received by a 16-bit read of the data register!
+   */
+
+  if (spi_16bitmode(priv))
+    {
+      spi_writeword(priv, (uint16_t)(wd & 0xffff));
+      ret = (uint32_t)spi_readword(priv);
+    }
+  else
+    {
+      spi_writebyte(priv, (uint8_t)(wd & 0xff));
+      ret = (uint32_t)spi_readbyte(priv);
+    }
+
+  /* Check and clear any error flags (Reading from the SR clears the error
+   * flags).
+   */
+
+  regval = spi_getreg(priv, STM32WB_SPI_SR_OFFSET);
+
+  if (spi_16bitmode(priv))
+    {
+      spiinfo("Sent: %04" PRIx32 " Return: %04" PRIx32
+              " Status: %02" PRIx32 "\n", wd, ret, regval);
+    }
+  else
+    {
+      spiinfo("Sent: %02" PRIx32 " Return: %02" PRIx32
+              " Status: %02" PRIx32 "\n", wd, ret, regval);
+    }
+
+  UNUSED(regval);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: spi_exchange (no DMA).  aka spi_exchange_nodma
+ *
+ * Description:
+ *   Exchange a block of data on SPI without using DMA
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *   txbuffer - A pointer to the buffer of data to be sent
+ *   rxbuffer - A pointer to a buffer in which to receive data
+ *   nwords   - the length of data to be exchanged in units of words.
+ *              The wordsize is determined by the number of bits-per-word
+ *              selected for the SPI interface.  If nbits <= 8, the data is
+ *              packed into uint8_t's; if nbits >8, the data is packed into
+ *              uint16_t's
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if !defined(CONFIG_STM32WB_SPI_DMA) || defined(CONFIG_STM32WB_DMACAPABLE)
+#if !defined(CONFIG_STM32WB_SPI_DMA)
+static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+                         void *rxbuffer, size_t nwords)
+#else
+static void spi_exchange_nodma(struct spi_dev_s *dev,
+                               const void *txbuffer, void *rxbuffer,
+                               size_t nwords)
+#endif
+{
+  struct stm32wb_spidev_s *priv = (struct stm32wb_spidev_s *)dev;
+  DEBUGASSERT(priv && priv->spibase);
+
+  spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);
+
+  /* 8- or 16-bit mode? */
+
+  if (spi_16bitmode(priv))
+    {
+      /* 16-bit mode */
+
+      const uint16_t *src  = (const uint16_t *)txbuffer;
+            uint16_t *dest = (uint16_t *)rxbuffer;
+            uint16_t  word;

Review Comment:
   ```suggestion
         const uint16_t *src  = (const uint16_t *)txbuffer;
         uint16_t *dest = (uint16_t *)rxbuffer;
         uint16_t  word;
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] onegray commented on a diff in pull request #6422: New stm32wb chip family

Posted by GitBox <gi...@apache.org>.
onegray commented on code in PR #6422:
URL: https://github.com/apache/incubator-nuttx/pull/6422#discussion_r908914777


##########
arch/arm/src/stm32wb/stm32wb_irq.c:
##########
@@ -0,0 +1,545 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_irq.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <debug.h>
+#include <assert.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include "arm_internal.h"
+#include "nvic.h"
+#ifdef CONFIG_ARCH_RAMVECTORS
+#  include "ram_vectors.h"
+#endif
+#include "stm32wb_rtc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Get a 32-bit version of the default priority */
+
+#define DEFPRIORITY32 \
+  (NVIC_SYSH_PRIORITY_DEFAULT << 24 | \
+   NVIC_SYSH_PRIORITY_DEFAULT << 16 | \
+   NVIC_SYSH_PRIORITY_DEFAULT << 8  | \
+   NVIC_SYSH_PRIORITY_DEFAULT)

Review Comment:
   Sorry, not clear why do you recommend this?  Precedence of `<<` is upper than `|` and redundant parenthesis are not recommended by code style.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] onegray commented on pull request #6422: New stm32wb chip family

Posted by GitBox <gi...@apache.org>.
onegray commented on PR #6422:
URL: https://github.com/apache/incubator-nuttx/pull/6422#issuecomment-1169227781

   @pkarashchenko  Thank you for the review. It is impressive how thoroughly you studied the code and noted so many issues.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] onegray commented on a diff in pull request #6422: New stm32wb chip family

Posted by GitBox <gi...@apache.org>.
onegray commented on code in PR #6422:
URL: https://github.com/apache/incubator-nuttx/pull/6422#discussion_r909503411


##########
arch/arm/src/stm32wb/stm32wb_spi.c:
##########
@@ -0,0 +1,1819 @@
+/****************************************************************************

Review Comment:
   The meaning differences is caused by the fact that this SPI implementation is derived from stm32l4, while the stm32wl5 is based on generic stm32. Mostly they are the same, but from the git log I see that the stm32l4 SPI implementation is ahead by this commit:
   [|arch/arm/src/stm32l4: Add PM hooks to I2C, SPI and 1-wire](https://github.com/apache/incubator-nuttx/commit/a36c7a779ad977fc58704af0b5165a18bd5d5982)
   
   And the generic stm32 is ahead by those three:
   [stm32: Use the consistent type for stm32_dmacapable](https://github.com/apache/incubator-nuttx/commit/01abc41caf98a384c969ca7a7af2e7c35213d7a1)
   [stm32:spi Add buffers for DMA](https://github.com/apache/incubator-nuttx/commit/6189b2c8bd36a6ee2a9b1e5fd58b994ecf585dc2)
   [arm/stm32 add STM32_SPI_DMATHRESHOLD](https://github.com/apache/incubator-nuttx/commit/afb2248b7a47ec4ab72f10fe21100d538af67934)
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] pkarashchenko commented on a diff in pull request #6422: New stm32wb chip family

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on code in PR #6422:
URL: https://github.com/apache/incubator-nuttx/pull/6422#discussion_r910399171


##########
boards/arm/stm32wb/nucleo-wb55rg/src/stm32_autoleds.c:
##########
@@ -0,0 +1,107 @@
+/****************************************************************************
+ * boards/arm/stm32wb/nucleo-wb55rg/src/stm32_autoleds.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <debug.h>
+
+#include <nuttx/board.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "arm_internal.h"
+#include "stm32wb.h"
+#include "nucleo-wb55rg.h"
+
+#ifdef CONFIG_ARCH_LEDS
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: board_autoled_initialize
+ ****************************************************************************/
+
+void board_autoled_initialize(void)
+{
+  /* Configure LEDs GPIO for output. Initial state is OFF */
+
+  stm32wb_configgpio(GPIO_LED1);
+  stm32wb_configgpio(GPIO_LED2);
+  stm32wb_configgpio(GPIO_LED3);
+}
+
+/****************************************************************************
+ * Name: board_autoled_on
+ ****************************************************************************/
+
+void board_autoled_on(int led)
+{
+  switch (led)
+    {
+    default:
+      break;
+
+    case BOARD_LED1:
+      stm32wb_gpiowrite(GPIO_LED1, true);
+      break;
+
+    case BOARD_LED2:
+      stm32wb_gpiowrite(GPIO_LED2, true);
+      break;
+
+    case BOARD_LED3:
+      stm32wb_gpiowrite(GPIO_LED3, true);
+      break;
+    }
+}
+
+/****************************************************************************
+ * Name: board_autoled_off
+ ****************************************************************************/
+
+void board_autoled_off(int led)
+{
+  switch (led)
+    {
+    default:
+      break;
+
+    case BOARD_LED1:
+      stm32wb_gpiowrite(GPIO_LED1, false);
+      break;
+
+    case BOARD_LED2:
+      stm32wb_gpiowrite(GPIO_LED2, false);
+      break;
+
+    case BOARD_LED3:
+      stm32wb_gpiowrite(GPIO_LED3, false);
+      break;

Review Comment:
   please add 2 spaces for case block



##########
arch/arm/src/stm32wb/stm32wb_irq.c:
##########
@@ -0,0 +1,545 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_irq.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <debug.h>
+#include <assert.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include "arm_internal.h"
+#include "nvic.h"
+#ifdef CONFIG_ARCH_RAMVECTORS
+#  include "ram_vectors.h"
+#endif
+#include "stm32wb_rtc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Get a 32-bit version of the default priority */
+
+#define DEFPRIORITY32 \
+  (NVIC_SYSH_PRIORITY_DEFAULT << 24 | \
+   NVIC_SYSH_PRIORITY_DEFAULT << 16 | \
+   NVIC_SYSH_PRIORITY_DEFAULT << 8  | \
+   NVIC_SYSH_PRIORITY_DEFAULT)
+
+/* Given the address of a NVIC ENABLE register, this is the offset to
+ * the corresponding CLEAR ENABLE register.
+ */
+
+#define NVIC_ENA_OFFSET    (0)
+#define NVIC_CLRENA_OFFSET (NVIC_IRQ0_31_CLEAR - NVIC_IRQ0_31_ENABLE)
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/* g_current_regs[] holds a references to the current interrupt level
+ * register storage structure.  If is non-NULL only during interrupt
+ * processing.  Access to g_current_regs[] must be through the macro
+ * CURRENT_REGS for portability.
+ */
+
+volatile uint32_t *g_current_regs[1];
+
+/* This is the address of the  exception vector table (determined by the
+ * linker script).
+ */
+
+#if defined(__ICCARM__)
+/* _vectors replaced on __vector_table for IAR C-SPY Simulator */
+
+extern uint32_t __vector_table[];
+#else
+extern uint32_t _vectors[];
+#endif

Review Comment:
   Optional: Maybe include `arch/arm/src/stm32wb/stm32wb_rcc.h` instead (as it has the same block)?



##########
arch/arm/src/stm32wb/stm32wb_tickless.c:
##########
@@ -0,0 +1,837 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_tickless.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Tickless OS Support.
+ *
+ * When CONFIG_SCHED_TICKLESS is enabled, all support for timer interrupts
+ * is suppressed and the platform specific code is expected to provide the
+ * following custom functions.
+ *
+ *   void up_timer_initialize(void): Initializes the timer facilities.
+ *     Called early in the initialization sequence (by up_initialize()).
+ *   int up_timer_gettime(struct timespec *ts):  Returns the current
+ *     time from the platform specific time source.
+ *   int up_timer_cancel(void):  Cancels the interval timer.
+ *   int up_timer_start(const struct timespec *ts): Start (or re-starts)
+ *     the interval timer.
+ *
+ * The RTOS will provide the following interfaces for use by the platform-
+ * specific interval timer implementation:
+ *
+ *   void nxsched_timer_expiration(void):  Called by the platform-specific
+ *     logic when the interval timer expires.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * STM32WB Timer Usage
+ *
+ * This implementation uses one timer:  A free running timer to provide
+ * the current time and a capture/compare channel for timed-events.
+ *
+ * There are two interrupts generated from our timer, the overflow interrupt
+ * which drives the timing handler and the capture/compare interrupt which
+ * drives the interval handler.  There are some low level timer control
+ * functions implemented here because the API of stm32wb_tim.c does not
+ * provide adequate control over capture/compare interrupts.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <nuttx/arch.h>
+#include <debug.h>
+
+#include "arm_internal.h"
+#include "stm32wb_tim.h"
+
+#ifdef CONFIG_SCHED_TICKLESS
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Only TIM2 is a 32-bit timer */
+
+#undef HAVE_32BIT_TICKLESS
+
+#ifdef CONFIG_STM32WB_TICKLESS_TIMER
+#  if CONFIG_STM32WB_TICKLESS_TIMER == 2
+#    define HAVE_32BIT_TICKLESS 1
+#  endif
+#else
+#  error "STM32WB_TICKLESS_TIMER must be defined for tickless configuration"
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_tickless_s
+{
+  uint8_t timer;                     /* The timer/counter in use */
+  uint8_t channel;                   /* The timer channel to use
+                                      * for intervals */
+  struct stm32wb_tim_dev_s *tch;     /* Pointer returned by
+                                      * stm32wb_tim_init() */
+  uint32_t frequency;
+  uint32_t overflow;                 /* Timer counter overflow */
+  volatile bool pending;             /* True: pending task */
+  uint32_t period;                   /* Interval period */
+  uint32_t base;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct stm32wb_tickless_s g_tickless;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_getreg16
+ *
+ * Description:
+ *   Get a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline uint16_t stm32wb_getreg16(uint8_t offset)
+{
+  return getreg16(g_tickless.base + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_putreg16
+ *
+ * Description:
+ *   Put a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_putreg16(uint8_t offset, uint16_t value)
+{
+  putreg16(value, g_tickless.base + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_modifyreg16
+ *
+ * Description:
+ *   Modify a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_modifyreg16(uint8_t offset, uint16_t clearbits,
+                                     uint16_t setbits)

Review Comment:
   ```suggestion
   static inline void stm32wb_modifyreg16(uint8_t offset, uint16_t clearbits,
                                          uint16_t setbits)
   ```



##########
arch/arm/src/stm32wb/stm32wb_idle.c:
##########
@@ -0,0 +1,192 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_idle.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/board.h>
+#include <nuttx/power/pm.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb_pm.h"
+#include "stm32wb_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Does the board support an IDLE LED to indicate that the board is in the
+ * IDLE state?
+ */
+
+#if defined(CONFIG_ARCH_LEDS) && defined(LED_IDLE)
+#  define BEGIN_IDLE() board_autoled_on(LED_IDLE)
+#  define END_IDLE()   board_autoled_off(LED_IDLE)
+#else
+#  define BEGIN_IDLE()
+#  define END_IDLE()
+#endif
+
+#define PM_IDLE_DOMAIN 0 /* Revisit */
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_idlepm
+ *
+ * Description:
+ *   Perform IDLE state power management.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void up_idlepm(void)
+{
+  static enum pm_state_e oldstate = PM_NORMAL;
+  enum pm_state_e newstate;
+  irqstate_t flags;
+  int ret;
+
+  /* Decide, which power saving level can be obtained */
+
+  newstate = pm_checkstate(PM_IDLE_DOMAIN);
+
+  /* Check for state changes */
+
+  if (newstate != oldstate)
+    {
+      flags = enter_critical_section();
+
+      /* Perform board-specific, state-dependent logic here */
+
+      _info("newstate= %d oldstate=%d\n", newstate, oldstate);
+
+      /* Then force the global state change */
+
+      ret = pm_changestate(PM_IDLE_DOMAIN, newstate);
+      if (ret < 0)
+        {
+          /* The new state change failed, revert to the preceding state */
+
+          pm_changestate(PM_IDLE_DOMAIN, oldstate);
+        }
+      else
+        {
+          /* Save the new state */
+
+          oldstate = newstate;
+        }
+
+      /* MCU-specific power management logic */
+
+      switch (newstate)
+        {
+        case PM_NORMAL:
+          break;
+
+        case PM_IDLE:
+          break;
+
+        case PM_STANDBY:
+
+          /* Enter STOP mode */
+
+          BEGIN_IDLE();
+          stm32wb_pmstop(true);
+          END_IDLE();
+
+          /* Set correct clock again after returning from STOP */
+
+          stm32wb_clockenable();
+
+          /* Inform of all drivers of the new state */
+
+          ret = pm_changestate(PM_IDLE_DOMAIN, PM_NORMAL);
+          if (ret >= 0)
+            {
+              oldstate = PM_NORMAL;

Review Comment:
   Please add 2 spaces for case block



##########
arch/arm/src/stm32wb/stm32wb_waste.c:
##########
@@ -0,0 +1,44 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_waste.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+
+#include "stm32wb_waste.h"
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+uint32_t g_waste_counter = 0;

Review Comment:
   Minor
   ```suggestion
   uint32_t g_waste_counter;
   ```
   zero inited by C standard



##########
boards/arm/stm32wb/nucleo-wb55rg/src/stm32_userleds.c:
##########
@@ -0,0 +1,215 @@
+/****************************************************************************
+ * boards/arm/stm32wb/nucleo-wb55rg/src/stm32_userleds.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <debug.h>
+
+#include <arch/board/board.h>
+#include <nuttx/power/pm.h>
+
+#include "chip.h"
+#include "arm_internal.h"
+#include "stm32wb.h"
+#include "nucleo-wb55rg.h"
+
+#ifndef CONFIG_ARCH_LEDS
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* LED Power Management */
+
+#ifdef CONFIG_PM
+static void led_pm_notify(struct pm_callback_s *cb, int domain,
+                          enum pm_state_e pmstate);
+static int led_pm_prepare(struct pm_callback_s *cb, int domain,
+                          enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static struct pm_callback_s g_ledscb =
+{
+  .notify  = led_pm_notify,
+  .prepare = led_pm_prepare,
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: led_pm_notify
+ *
+ * Description:
+ *   Notify the driver of new power state. This callback is called after
+ *   all drivers have had the opportunity to prepare for the new power state.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void led_pm_notify(struct pm_callback_s *cb, int domain,
+                          enum pm_state_e pmstate)
+{
+  switch (pmstate)
+    {
+      case(PM_NORMAL):
+        {
+          /* Restore normal LEDs operation */
+        }
+        break;
+
+      case(PM_IDLE):
+        {
+          /* Entering IDLE mode - Turn leds off */
+
+          stm32wb_gpiowrite(GPIO_LED_RED, 0);
+          stm32wb_gpiowrite(GPIO_LED_GREEN, 0);
+        }
+        break;
+
+      case(PM_STANDBY):
+        {
+          /* Entering STANDBY mode - Logic for PM_STANDBY goes here */
+
+          stm32wb_gpiowrite(GPIO_LED_RED, 0);
+          stm32wb_gpiowrite(GPIO_LED_GREEN, 0);
+        }
+        break;
+
+      case(PM_SLEEP):
+        {
+          /* Entering SLEEP mode - Logic for PM_SLEEP goes here */
+
+          stm32wb_gpiowrite(GPIO_LED_RED, 0);
+          stm32wb_gpiowrite(GPIO_LED_GREEN, 0);
+        }
+        break;
+
+      default:
+        {
+          /* Should not get here */
+        }
+        break;
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: led_pm_prepare
+ *
+ * Description:
+ *   Request the driver to prepare for a new power state. This is a warning
+ *   that the system is about to enter into a new power state. The driver
+ *   should begin whatever operations that may be required to enter power
+ *   state. The driver may abort the state change mode by returning a
+ *   non-zero value from the callback function.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static int led_pm_prepare(struct pm_callback_s *cb, int domain,
+                          enum pm_state_e pmstate)
+{
+  /* No preparation to change power modes is required by the LEDs driver.
+   * We always accept the state change by returning OK.
+   */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: board_userled_initialize
+ ****************************************************************************/
+
+uint32_t board_userled_initialize(void)
+{
+  stm32wb_configgpio(GPIO_LED_RED);
+  stm32wb_configgpio(GPIO_LED_GREEN);
+  stm32wb_configgpio(GPIO_LED_BLUE);
+  return BOARD_NLEDS;
+}
+
+/****************************************************************************
+ * Name: board_userled
+ ****************************************************************************/
+
+void board_userled(int led, bool ledon)
+{
+  switch (led)
+  {
+  case BOARD_LED_RED:
+    stm32wb_gpiowrite(GPIO_LED_RED, ledon);
+    break;
+
+  case GPIO_LED_GREEN:
+    stm32wb_gpiowrite(GPIO_LED_GREEN, ledon);
+    break;
+
+  case GPIO_LED_BLUE:
+    stm32wb_gpiowrite(GPIO_LED_BLUE, ledon);
+    break;

Review Comment:
   please add 2 spaces for case block. Also `default` case is welcomed



##########
arch/arm/src/stm32wb/stm32wb_tickless.c:
##########
@@ -0,0 +1,837 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_tickless.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Tickless OS Support.
+ *
+ * When CONFIG_SCHED_TICKLESS is enabled, all support for timer interrupts
+ * is suppressed and the platform specific code is expected to provide the
+ * following custom functions.
+ *
+ *   void up_timer_initialize(void): Initializes the timer facilities.
+ *     Called early in the initialization sequence (by up_initialize()).
+ *   int up_timer_gettime(struct timespec *ts):  Returns the current
+ *     time from the platform specific time source.
+ *   int up_timer_cancel(void):  Cancels the interval timer.
+ *   int up_timer_start(const struct timespec *ts): Start (or re-starts)
+ *     the interval timer.
+ *
+ * The RTOS will provide the following interfaces for use by the platform-
+ * specific interval timer implementation:
+ *
+ *   void nxsched_timer_expiration(void):  Called by the platform-specific
+ *     logic when the interval timer expires.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * STM32WB Timer Usage
+ *
+ * This implementation uses one timer:  A free running timer to provide
+ * the current time and a capture/compare channel for timed-events.
+ *
+ * There are two interrupts generated from our timer, the overflow interrupt
+ * which drives the timing handler and the capture/compare interrupt which
+ * drives the interval handler.  There are some low level timer control
+ * functions implemented here because the API of stm32wb_tim.c does not
+ * provide adequate control over capture/compare interrupts.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <nuttx/arch.h>
+#include <debug.h>
+
+#include "arm_internal.h"
+#include "stm32wb_tim.h"
+
+#ifdef CONFIG_SCHED_TICKLESS
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Only TIM2 is a 32-bit timer */
+
+#undef HAVE_32BIT_TICKLESS
+
+#ifdef CONFIG_STM32WB_TICKLESS_TIMER
+#  if CONFIG_STM32WB_TICKLESS_TIMER == 2
+#    define HAVE_32BIT_TICKLESS 1
+#  endif
+#else
+#  error "STM32WB_TICKLESS_TIMER must be defined for tickless configuration"
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_tickless_s
+{
+  uint8_t timer;                     /* The timer/counter in use */
+  uint8_t channel;                   /* The timer channel to use
+                                      * for intervals */
+  struct stm32wb_tim_dev_s *tch;     /* Pointer returned by
+                                      * stm32wb_tim_init() */
+  uint32_t frequency;
+  uint32_t overflow;                 /* Timer counter overflow */
+  volatile bool pending;             /* True: pending task */
+  uint32_t period;                   /* Interval period */
+  uint32_t base;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct stm32wb_tickless_s g_tickless;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_getreg16
+ *
+ * Description:
+ *   Get a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline uint16_t stm32wb_getreg16(uint8_t offset)
+{
+  return getreg16(g_tickless.base + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_putreg16
+ *
+ * Description:
+ *   Put a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_putreg16(uint8_t offset, uint16_t value)
+{
+  putreg16(value, g_tickless.base + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_modifyreg16
+ *
+ * Description:
+ *   Modify a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_modifyreg16(uint8_t offset, uint16_t clearbits,
+                                     uint16_t setbits)
+{
+  modifyreg16(g_tickless.base + offset, clearbits, setbits);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_enableint
+ ****************************************************************************/
+
+static inline void stm32wb_tickless_enableint(int channel)
+{
+  stm32wb_modifyreg16(STM32WB_TIM_DIER_OFFSET, 0, 1 << channel);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_disableint
+ ****************************************************************************/
+
+static inline void stm32wb_tickless_disableint(int channel)
+{
+  stm32wb_modifyreg16(STM32WB_TIM_DIER_OFFSET, 1 << channel, 0);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_ackint
+ ****************************************************************************/
+
+static inline void stm32wb_tickless_ackint(int channel)
+{
+  stm32wb_putreg16(STM32WB_TIM_SR_OFFSET, ~(1 << channel));
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_getint
+ ****************************************************************************/
+
+static inline uint16_t stm32wb_tickless_getint(void)
+{
+  return stm32wb_getreg16(STM32WB_TIM_SR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_setchannel
+ ****************************************************************************/
+
+static int stm32wb_tickless_setchannel(uint8_t channel)
+{
+  uint16_t ccmr_orig = 0;
+  uint16_t ccmr_val = 0;
+  uint16_t ccer_val;
+  uint8_t ccmr_offset = STM32WB_TIM_CCMR1_OFFSET;
+
+  /* Further we use range as 0..3; if channel=0 it will also overflow here */
+
+  if (--channel > 3)
+    {
+      return -EINVAL;
+    }
+
+  /* Assume that channel is disabled and polarity is active high */
+
+  ccer_val = stm32wb_getreg16(STM32WB_TIM_CCER_OFFSET);
+  ccer_val &= ~(GTIM_CCER_CCXE(channel) | GTIM_CCER_CCXP(channel));
+
+  /* Frozen mode because we don't want to change the GPIO, preload register
+   * disabled.
+   */
+
+  ccmr_val = GTIM_CCMR_OCXM_FRZN(channel);
+
+  /* Set polarity */
+
+  ccer_val |= GTIM_CCER_CCXP(channel);
+
+  if (channel > 1)
+    {
+      ccmr_offset = STM32WB_TIM_CCMR2_OFFSET;
+    }
+
+  ccmr_orig  = stm32wb_getreg16(ccmr_offset);
+  ccmr_orig &= ~(GTIM_CCMR_OCXM_MASK(channel) | GTIM_CCMR_OCXPE(channel));
+  ccmr_orig |= ccmr_val;
+  stm32wb_putreg16(ccmr_offset, ccmr_orig);
+  stm32wb_putreg16(STM32WB_TIM_CCER_OFFSET, ccer_val);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_interval_handler
+ *
+ * Description:
+ *   Called when the timer counter matches the compare register
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called early in the initialization sequence before any special
+ *   concurrency protections are required.
+ *
+ ****************************************************************************/
+
+static void stm32wb_interval_handler(void)
+{
+  tmrinfo("Expired...\n");
+
+  /* Disable the compare interrupt now. */
+
+  stm32wb_tickless_disableint(g_tickless.channel);
+  stm32wb_tickless_ackint(g_tickless.channel);
+
+  g_tickless.pending = false;
+
+  nxsched_timer_expiration();
+}
+
+/****************************************************************************
+ * Name: stm32wb_timing_handler
+ *
+ * Description:
+ *   Timer interrupt callback.  When the freerun timer counter overflows,
+ *   this interrupt will occur.  We will just increment an overflow count.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void stm32wb_timing_handler(void)
+{
+  g_tickless.overflow++;
+
+  STM32WB_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_handler
+ *
+ * Description:
+ *   Generic interrupt handler for this timer.  It checks the source of the
+ *   interrupt and fires the appropriate handler.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int stm32wb_tickless_handler(int irq, void *context, void *arg)
+{
+  int interrupt_flags = stm32wb_tickless_getint();
+
+  if (interrupt_flags & GTIM_SR_UIF)
+    {
+      stm32wb_timing_handler();
+    }
+
+  if (interrupt_flags & (1 << g_tickless.channel))
+    {
+      stm32wb_interval_handler();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_timer_initialize
+ *
+ * Description:
+ *   Initializes all platform-specific timer facilities.  This function is
+ *   called early in the initialization sequence by up_initialize().
+ *   On return, the current up-time should be available from
+ *   up_timer_gettime() and the interval timer is ready for use (but not
+ *   actively timing.
+ *
+ *   Provided by platform-specific code and called from the architecture-
+ *   specific logic.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called early in the initialization sequence before any special
+ *   concurrency protections are required.
+ *
+ ****************************************************************************/
+
+void up_timer_initialize(void)
+{
+  switch (CONFIG_STM32WB_TICKLESS_TIMER)
+    {
+#ifdef CONFIG_STM32WB_TIM1
+      case 1:
+        g_tickless.base = STM32WB_TIM1_BASE;
+        break;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM2
+      case 2:
+        g_tickless.base = STM32WB_TIM2_BASE;
+        break;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM16
+      case 16:
+        g_tickless.base = STM32WB_TIM16_BASE;
+        break;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM17
+      case 17:
+        g_tickless.base = STM32WB_TIM17_BASE;
+        break;
+#endif
+
+      default:
+        DEBUGASSERT(0);
+    }
+
+  /* Get the TC frequency that corresponds to the requested resolution */
+
+  g_tickless.frequency = USEC_PER_SEC / (uint32_t)CONFIG_USEC_PER_TICK;
+  g_tickless.timer     = CONFIG_STM32WB_TICKLESS_TIMER;
+  g_tickless.channel   = CONFIG_STM32WB_TICKLESS_CHANNEL;
+  g_tickless.pending   = false;
+  g_tickless.period    = 0;
+  g_tickless.overflow  = 0;
+
+  tmrinfo("timer=%d channel=%d frequency=%lu Hz\n",
+           g_tickless.timer, g_tickless.channel, g_tickless.frequency);
+
+  g_tickless.tch = stm32wb_tim_init(g_tickless.timer);
+  if (!g_tickless.tch)
+    {
+      tmrerr("ERROR: Failed to allocate TIM%d\n", g_tickless.timer);
+      DEBUGASSERT(0);
+    }
+
+  STM32WB_TIM_SETCLOCK(g_tickless.tch, g_tickless.frequency);
+
+  /* Set up to receive the callback when the counter overflow occurs */
+
+  STM32WB_TIM_SETISR(g_tickless.tch, stm32wb_tickless_handler, NULL, 0);
+
+  /* Initialize interval to zero */
+
+  STM32WB_TIM_SETCOMPARE(g_tickless.tch, g_tickless.channel, 0);
+
+  /* Setup compare channel for the interval timing */
+
+  stm32wb_tickless_setchannel(g_tickless.channel);
+
+  /* Set timer period */
+
+#ifdef HAVE_32BIT_TICKLESS
+  STM32WB_TIM_SETPERIOD(g_tickless.tch, UINT32_MAX);
+#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP
+  g_oneshot_maxticks = UINT32_MAX;
+#endif
+#else
+  STM32WB_TIM_SETPERIOD(g_tickless.tch, UINT16_MAX);
+#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP
+  g_oneshot_maxticks = UINT16_MAX;
+#endif
+#endif
+
+  /* Initialize the counter */
+
+  STM32WB_TIM_SETMODE(g_tickless.tch, STM32WB_TIM_MODE_UP);
+
+  /* Start the timer */
+
+  STM32WB_TIM_ACKINT(g_tickless.tch, ~0);
+  STM32WB_TIM_ENABLEINT(g_tickless.tch, GTIM_DIER_UIE);
+}
+
+/****************************************************************************
+ * Name: up_timer_gettime
+ *
+ * Description:
+ *   Return the elapsed time since power-up (or, more correctly, since
+ *   up_timer_initialize() was called).  This function is functionally
+ *   equivalent to:
+ *
+ *      int clock_gettime(clockid_t clockid, struct timespec *ts);
+ *
+ *   when clockid is CLOCK_MONOTONIC.
+ *
+ *   This function provides the basis for reporting the current time and
+ *   also is used to eliminate error build-up from small errors in interval
+ *   time calculations.
+ *
+ *   Provided by platform-specific code and called from the RTOS base code.
+ *
+ * Input Parameters:
+ *   ts - Provides the location in which to return the up-time.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned on
+ *   any failure.
+ *
+ * Assumptions:
+ *   Called from the normal tasking context.  The implementation must
+ *   provide whatever mutual exclusion is necessary for correct operation.
+ *   This can include disabling interrupts in order to assure atomic register
+ *   operations.
+ *
+ ****************************************************************************/
+
+int up_timer_gettime(struct timespec *ts)
+{
+  uint64_t usec;
+  uint32_t counter;
+  uint32_t verify;
+  uint32_t overflow;
+  uint32_t sec;
+  int pending;
+  irqstate_t flags;
+
+  DEBUGASSERT(g_tickless.tch && ts);
+
+  /* Temporarily disable the overflow counter.  NOTE that we have to be
+   * careful here because  stm32wb_tc_getpending() will reset the pending
+   * interrupt status.  If we do not handle the overflow here then, it will
+   * be lost.
+   */
+
+  flags    = enter_critical_section();
+
+  overflow = g_tickless.overflow;
+  counter  = STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+  pending  = STM32WB_TIM_CHECKINT(g_tickless.tch, GTIM_SR_UIF);
+  verify   = STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+
+  /* If an interrupt was pending before we re-enabled interrupts,
+   * then the overflow needs to be incremented.
+   */
+
+  if (pending)
+    {
+      STM32WB_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF);
+
+      /* Increment the overflow count and use the value of the
+       * guaranteed to be AFTER the overflow occurred.
+       */
+
+      overflow++;
+      counter = verify;
+
+      /* Update tickless overflow counter. */
+
+      g_tickless.overflow = overflow;
+    }
+
+  leave_critical_section(flags);
+
+  tmrinfo("counter=%lu (%lu) overflow=%lu, pending=%i\n",
+         (unsigned long)counter,  (unsigned long)verify,
+         (unsigned long)overflow, pending);

Review Comment:
   ```suggestion
     tmrinfo("counter=%lu (%lu) overflow=%lu, pending=%i\n",
             (unsigned long)counter, (unsigned long)verify,
             (unsigned long)overflow, pending);
   ```



##########
arch/arm/src/stm32wb/stm32wb_tim.h:
##########
@@ -0,0 +1,252 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_tim.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_STM32WB_TIM_H
+#define __ARCH_ARM_SRC_STM32WB_STM32WB_TIM_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "chip.h"
+#include "hardware/stm32wb_tim.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Generalized register definitions */
+
+#define GTIM_CR1_CEN            TIM1_CR1_CEN
+#define GTIM_CR1_OPM            TIM1_CR1_OPM
+#define GTIM_CR1_ARPE           TIM1_CR1_ARPE
+#define GTIM_DIER_UIE           TIM1_DIER_UIE
+#define GTIM_SR_UIF             TIM1_SR_UIF
+#define GTIM_EGR_UG             TIM1_EGR_UG
+#define TIM_1_2_CR1_DIR         TIM1_CR1_DIR
+#define TIM_1_2_CR1_CMS_CNTR1   TIM1_CR1_CMS_CNTR1
+
+#define GTIM_CCER_CCX_SHIFT(ch) (ch << 2)         /* 4-bits shift
+                                                   * per channel */
+#define GTIM_CCMR_OCX_SHIFT(ch) ((ch & 0x1) << 3) /* 8-bits shift

Review Comment:
   ```suggestion
   #define GTIM_CCER_CCX_SHIFT(ch) ((ch) << 2)         /* 4-bits shift
                                                      * per channel */
   #define GTIM_CCMR_OCX_SHIFT(ch) (((ch) & 0x1) << 3) /* 8-bits shift
   ```



##########
arch/arm/src/stm32wb/stm32wb_rtc.c:
##########
@@ -0,0 +1,1831 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_rtc.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <errno.h>
+#include <debug.h>
+#include <assert.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/time.h>
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb_rcc.h"
+#include "stm32wb_pwr.h"
+#include "stm32wb_rtc.h"
+#include "stm32wb_exti.h"
+
+#ifdef CONFIG_STM32WB_RTC
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* This RTC implementation supports
+ *  - date/time RTC hardware
+ *  - extended functions Alarm A and B
+ * */
+
+#ifndef CONFIG_RTC_DATETIME
+#  error "CONFIG_RTC_DATETIME must be set to use this driver"
+#endif
+
+#ifdef CONFIG_RTC_HIRES
+#  error "CONFIG_RTC_HIRES must NOT be set with this driver"
+#endif
+
+#ifndef CONFIG_STM32WB_PWR
+#  error "CONFIG_STM32WB_PWR must selected to use this driver"
+#endif
+
+/* Constants ****************************************************************/
+
+#define SYNCHRO_TIMEOUT               (0x00020000)
+#define INITMODE_TIMEOUT              (0x00010000)
+
+#define RTC_ALRMR_ENABLE              (0x00000000)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+typedef unsigned int rtc_alarmreg_t;
+
+struct alm_cbinfo_s
+{
+  volatile alm_callback_t ac_cb; /* Client callback function */
+  volatile void *ac_arg;         /* Argument to pass with the callback function */
+};
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_RTC_ALARM
+/* Callback to use when an EXTI is activated  */
+
+static struct alm_cbinfo_s g_alarmcb[RTC_ALARM_LAST];
+static bool g_alarm_enabled;  /* True: Alarm interrupts are enabled */
+#endif
+
+#ifdef CONFIG_RTC_PERIODIC
+static wakeupcb_t g_wakeupcb;
+static bool g_wakeup_enabled;  /* True: Wakeup interrupts are enabled */
+#endif
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/* g_rtc_enabled is set true after the RTC has successfully initialized */
+
+volatile bool g_rtc_enabled = false;

Review Comment:
   Optional:
   ```suggestion
   volatile bool g_rtc_enabled;
   ```
   zero inited according to C standard



##########
boards/arm/stm32wb/nucleo-wb55rg/src/stm32_userleds.c:
##########
@@ -0,0 +1,215 @@
+/****************************************************************************
+ * boards/arm/stm32wb/nucleo-wb55rg/src/stm32_userleds.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <debug.h>
+
+#include <arch/board/board.h>
+#include <nuttx/power/pm.h>
+
+#include "chip.h"
+#include "arm_internal.h"
+#include "stm32wb.h"
+#include "nucleo-wb55rg.h"
+
+#ifndef CONFIG_ARCH_LEDS
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* LED Power Management */
+
+#ifdef CONFIG_PM
+static void led_pm_notify(struct pm_callback_s *cb, int domain,
+                          enum pm_state_e pmstate);
+static int led_pm_prepare(struct pm_callback_s *cb, int domain,
+                          enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static struct pm_callback_s g_ledscb =
+{
+  .notify  = led_pm_notify,
+  .prepare = led_pm_prepare,
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: led_pm_notify
+ *
+ * Description:
+ *   Notify the driver of new power state. This callback is called after
+ *   all drivers have had the opportunity to prepare for the new power state.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void led_pm_notify(struct pm_callback_s *cb, int domain,
+                          enum pm_state_e pmstate)
+{
+  switch (pmstate)
+    {
+      case(PM_NORMAL):
+        {
+          /* Restore normal LEDs operation */
+        }
+        break;
+
+      case(PM_IDLE):
+        {
+          /* Entering IDLE mode - Turn leds off */
+
+          stm32wb_gpiowrite(GPIO_LED_RED, 0);
+          stm32wb_gpiowrite(GPIO_LED_GREEN, 0);
+        }
+        break;
+
+      case(PM_STANDBY):
+        {
+          /* Entering STANDBY mode - Logic for PM_STANDBY goes here */
+
+          stm32wb_gpiowrite(GPIO_LED_RED, 0);
+          stm32wb_gpiowrite(GPIO_LED_GREEN, 0);
+        }
+        break;
+
+      case(PM_SLEEP):
+        {
+          /* Entering SLEEP mode - Logic for PM_SLEEP goes here */
+
+          stm32wb_gpiowrite(GPIO_LED_RED, 0);
+          stm32wb_gpiowrite(GPIO_LED_GREEN, 0);
+        }
+        break;
+
+      default:
+        {
+          /* Should not get here */
+        }
+        break;
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: led_pm_prepare
+ *
+ * Description:
+ *   Request the driver to prepare for a new power state. This is a warning
+ *   that the system is about to enter into a new power state. The driver
+ *   should begin whatever operations that may be required to enter power
+ *   state. The driver may abort the state change mode by returning a
+ *   non-zero value from the callback function.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static int led_pm_prepare(struct pm_callback_s *cb, int domain,
+                          enum pm_state_e pmstate)
+{
+  /* No preparation to change power modes is required by the LEDs driver.
+   * We always accept the state change by returning OK.
+   */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: board_userled_initialize
+ ****************************************************************************/
+
+uint32_t board_userled_initialize(void)
+{
+  stm32wb_configgpio(GPIO_LED_RED);
+  stm32wb_configgpio(GPIO_LED_GREEN);
+  stm32wb_configgpio(GPIO_LED_BLUE);
+  return BOARD_NLEDS;
+}
+
+/****************************************************************************
+ * Name: board_userled
+ ****************************************************************************/
+
+void board_userled(int led, bool ledon)
+{
+  switch (led)
+  {
+  case BOARD_LED_RED:
+    stm32wb_gpiowrite(GPIO_LED_RED, ledon);
+    break;
+
+  case GPIO_LED_GREEN:
+    stm32wb_gpiowrite(GPIO_LED_GREEN, ledon);
+    break;
+
+  case GPIO_LED_BLUE:
+    stm32wb_gpiowrite(GPIO_LED_BLUE, ledon);
+    break;
+  }
+}
+
+/****************************************************************************
+ * Name: board_userled_all
+ ****************************************************************************/
+
+void board_userled_all(uint32_t ledset)
+{
+  stm32wb_gpiowrite(GPIO_LED1, (ledset & BOARD_LED1_BIT) != 0);
+  stm32wb_gpiowrite(GPIO_LED2, (ledset & BOARD_LED2_BIT) != 0);
+  stm32wb_gpiowrite(GPIO_LED3, (ledset & BOARD_LED3_BIT) != 0);
+}
+
+/****************************************************************************
+ * Name: stm32_led_pminitialize
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+void stm32_led_pminitialize(void)
+{
+  /* Register to receive power management callbacks */
+
+  int ret = pm_register(&g_ledscb);
+  DEBUGASSERT(ret == OK);
+  UNUSED(ret);

Review Comment:
   Minor: maybe `DEBUGVERIFY` could be used here instead of 3 lines



##########
arch/arm/src/stm32wb/stm32wb_serial.c:
##########
@@ -0,0 +1,2884 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_serial.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/serial/serial.h>
+#include <nuttx/power/pm.h>
+#include <arch/board/board.h>
+
+#ifdef CONFIG_SERIAL_TERMIOS
+#  include <termios.h>
+#endif
+
+#include "arm_internal.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_uart.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Some sanity checks *******************************************************/
+
+/* DMA configuration */
+
+#ifdef SERIAL_HAVE_RXDMA
+
+/* Currently RS-485 support cannot be enabled when RXDMA is in use due to
+ * lack of testing - RS-485 support was developed on STM32F1x
+ */
+
+#  if (defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_RS485))
+#    error "RXDMA and RS-485 cannot be enabled at the same time for the same U[S]ART"
+#  endif
+
+/* For the stm32wb, there are alternate DMA channels for USART1.
+ * Logic in the board.h file make the DMA channel selection by defining
+ * the following in the board.h file.
+ */
+
+#  if defined(CONFIG_USART1_RXDMA) && !defined(DMAMAP_USART1_RX)
+#    error "USART1 DMA channel not defined (DMAMAP_USART1_RX)"
+#  endif
+
+/* The DMA buffer size when using RX DMA to emulate a FIFO.
+ *
+ * When streaming data, the generic serial layer will be called
+ * every time the FIFO receives half this number of bytes.
+ *
+ * The buffer size should be an even multiple of ARMV7M_DCACHE_LINESIZE.
+ */
+
+#  if !defined(CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE) || \
+      CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE == 0
+#    define RXDMA_BUFFER_SIZE 32
+#  else
+#    define RXDMA_BUFFER_SIZE ((CONFIG_STM32WB_SERIAL_RXDMA_BUFFER_SIZE + 31) & ~31)
+#  endif
+
+/* DMA priority */
+
+#  ifndef CONFIG_USART_RXDMAPRIO
+#    define CONFIG_USART_RXDMAPRIO  DMA_CCR_PRIMED
+#  endif
+#  if (CONFIG_USART_RXDMAPRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_USART_RXDMAPRIO"
+#  endif
+
+/* DMA control words */
+
+#  define SERIAL_DMA_CONTROL_WORD      \
+              (DMA_CCR_CIRC          | \
+               DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  ifdef CONFIG_SERIAL_IFLOWCONTROL
+#    define SERIAL_DMA_IFLOW_CONTROL_WORD \
+              (DMA_CCR_MINC          | \
+               DMA_CCR_PSIZE_8BITS   | \
+               DMA_CCR_MSIZE_8BITS   | \
+               CONFIG_USART_RXDMAPRIO)
+#  endif
+
+#endif
+
+/* Power management definitions */
+
+#if defined(CONFIG_PM) && !defined(CONFIG_STM32WB_PM_SERIAL_ACTIVITY)
+#  define CONFIG_STM32WB_PM_SERIAL_ACTIVITY  10
+#endif
+#if defined(CONFIG_PM)
+#  define PM_IDLE_DOMAIN             0 /* Revisit */
+#endif
+
+/* Keep track if a Break was set
+ *
+ * Note:
+ *
+ * 1) This value is set in the priv->ie but never written to the control
+ *    register. It must not collide with USART_CR1_USED_INTS or USART_CR3_EIE
+ * 2) USART_CR3_EIE is also carried in the up_dev_s ie member.
+ *
+ * See stm32wb_serial_restoreusartint where the masking is done.
+ */
+
+#ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+#  define USART_CR1_IE_BREAK_INPROGRESS_SHFTS 15
+#  define USART_CR1_IE_BREAK_INPROGRESS (1 << USART_CR1_IE_BREAK_INPROGRESS_SHFTS)
+#endif
+
+#ifdef USE_SERIALDRIVER
+#ifdef HAVE_UART
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_serial_s
+{
+  struct uart_dev_s dev;       /* Generic UART device */
+  uint16_t          ie;        /* Saved interrupt mask bits value */
+  uint16_t          sr;        /* Saved status bits */
+
+  /* Has been initialized and HW is setup. */
+
+  bool              initialized;
+
+#ifdef CONFIG_PM
+  bool              suspended; /* UART device has been suspended. */
+
+  /* Interrupt mask value stored before suspending for stop mode. */
+
+  uint16_t          suspended_ie;
+#endif
+
+  /* If termios are supported, then the following fields may vary at
+   * runtime.
+   */
+
+#ifdef CONFIG_SERIAL_TERMIOS
+  uint8_t           parity;    /* 0=none, 1=odd, 2=even */
+  uint8_t           bits;      /* Number of bits (7 or 8) */
+  bool              stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  bool              iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  bool              oflow;     /* output flow control (CTS) enabled */
+#endif
+  uint32_t          baud;      /* Configured baud */
+#else
+  const uint8_t     parity;    /* 0=none, 1=odd, 2=even */
+  const uint8_t     bits;      /* Number of bits (7 or 8) */
+  const bool        stopbits2; /* True: Configure with 2 stop bits instead of 1 */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const bool        iflow;     /* input flow control (RTS) enabled */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const bool        oflow;     /* output flow control (CTS) enabled */
+#endif
+  const uint32_t    baud;      /* Configured baud */
+#endif
+
+  const uint8_t     irq;       /* IRQ associated with this USART */
+  const uint32_t    apbclock;  /* PCLK 1 or 2 frequency */
+  const uint32_t    usartbase; /* Base address of USART registers */
+  const uint32_t    tx_gpio;   /* U[S]ART TX GPIO pin configuration */
+  const uint32_t    rx_gpio;   /* U[S]ART RX GPIO pin configuration */
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  const uint32_t    rts_gpio;  /* U[S]ART RTS GPIO pin configuration */
+#endif
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  const uint32_t    cts_gpio;  /* U[S]ART CTS GPIO pin configuration */
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+  const unsigned int rxdma_channel; /* DMA channel assigned */
+#endif
+
+  /* RX DMA state */
+
+#ifdef SERIAL_HAVE_RXDMA
+  DMA_HANDLE        rxdma;     /* currently-open receive DMA stream */
+  bool              rxenable;  /* DMA-based reception en/disable */
+#ifdef CONFIG_PM
+  bool              rxdmasusp; /* Rx DMA suspended */
+#endif
+  uint32_t          rxdmanext; /* Next byte in the DMA buffer to be read */
+  char       *const rxfifo;    /* Receive DMA buffer */
+#endif
+
+#ifdef HAVE_RS485
+  const uint32_t    rs485_dir_gpio;     /* U[S]ART RS-485 DIR GPIO pin configuration */
+  const bool        rs485_dir_polarity; /* U[S]ART RS-485 DIR pin state for TX enabled */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev);
+#endif
+static int  stm32wb_serial_setup(struct uart_dev_s *dev);
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_attach(struct uart_dev_s *dev);
+static void stm32wb_serial_detach(struct uart_dev_s *dev);
+static int  up_interrupt(int irq, void *context, void *arg);
+static int  stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                 unsigned long arg);
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int  stm32wb_serial_receive(struct uart_dev_s *dev,
+                                   unsigned int *status);
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev);
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper);
+#endif
+static void stm32wb_serial_send(struct uart_dev_s *dev, int ch);
+static void stm32wb_serial_txint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_txready(struct uart_dev_s *dev);
+
+#ifdef SERIAL_HAVE_RXDMA
+static int  stm32wb_serial_dmasetup(struct uart_dev_s *dev);
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev);
+static int  stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                      unsigned int *status);
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv);
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_dmaiflowrestart(struct stm32wb_serial_s *priv);
+#endif
+static void stm32wb_serial_dmarxint(struct uart_dev_s *dev, bool enable);
+static bool stm32wb_serial_dmarxavailable(struct uart_dev_s *dev);
+
+static void stm32wb_serial_dmarxcallback(DMA_HANDLE handle, uint8_t status,
+                                         void *arg);
+#endif
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend);
+static void stm32wb_serial_pm_setsuspend(bool suspend);
+static void stm32wb_serial_pmnotify(struct pm_callback_s *cb,
+                                    int domain, enum pm_state_e pmstate);
+static int  stm32wb_serial_pmprepare(struct pm_callback_s *cb,
+                                     int domain, enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static const struct uart_ops_s g_uart_ops =
+{
+  .setup          = stm32wb_serial_setup,
+  .shutdown       = stm32wb_serial_shutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_receive,
+  .rxint          = stm32wb_serial_rxint,
+  .rxavailable    = stm32wb_serial_rxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+static const struct uart_ops_s g_uart_dma_ops =
+{
+  .setup          = stm32wb_serial_dmasetup,
+  .shutdown       = stm32wb_serial_dmashutdown,
+  .attach         = stm32wb_serial_attach,
+  .detach         = stm32wb_serial_detach,
+  .ioctl          = stm32wb_serial_ioctl,
+  .receive        = stm32wb_serial_dmareceive,
+  .rxint          = stm32wb_serial_dmarxint,
+  .rxavailable    = stm32wb_serial_dmarxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = stm32wb_serial_rxflowcontrol,
+#endif
+  .send           = stm32wb_serial_send,
+  .txint          = stm32wb_serial_txint,
+  .txready        = stm32wb_serial_txready,
+  .txempty        = stm32wb_serial_txready,
+};
+#endif
+
+/* I/O buffers */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static char g_lpuart1rxbuffer[CONFIG_LPUART1_RXBUFSIZE];
+static char g_lpuart1txbuffer[CONFIG_LPUART1_TXBUFSIZE];
+# ifdef CONFIG_LPUART1_RXDMA
+static char g_lpuart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static char g_usart1rxbuffer[CONFIG_USART1_RXBUFSIZE];
+static char g_usart1txbuffer[CONFIG_USART1_TXBUFSIZE];
+# ifdef CONFIG_USART1_RXDMA
+static char g_usart1rxfifo[RXDMA_BUFFER_SIZE];
+# endif
+#endif
+
+/* This describes the state of the STM32WB LPUART1 port. */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static struct stm32wb_serial_s g_lpuart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 1
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_LPUART1_RXBUFSIZE,
+        .buffer  = g_lpuart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_LPUART1_TXBUFSIZE,
+        .buffer  = g_lpuart1txbuffer,
+      },
+#ifdef CONFIG_LPUART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_lpuart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_LPUART1,
+  .parity        = CONFIG_LPUART1_PARITY,
+  .bits          = CONFIG_LPUART1_BITS,
+  .stopbits2     = CONFIG_LPUART1_2STOP,
+  .baud          = CONFIG_LPUART1_BAUD,
+  .apbclock      = STM32WB_PCLK1_FREQUENCY,
+  .usartbase     = STM32WB_LPUART1_BASE,
+  .tx_gpio       = GPIO_LPUART1_TX,
+  .rx_gpio       = GPIO_LPUART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_LPUART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_LPUART1_RTS,
+#endif
+#ifdef CONFIG_LPUART1_RXDMA
+  .rxdma_channel = DMAMAP_LPUART1_RX,
+  .rxfifo        = g_lpuart1rxfifo,
+#endif
+
+#ifdef CONFIG_LPUART1_RS485
+  .rs485_dir_gpio = GPIO_LPUART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This describes the state of the STM32WB USART1 port. */
+
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+static struct stm32wb_serial_s g_usart1priv =
+{
+  .dev =
+    {
+#if CONSOLE_UART == 2
+      .isconsole = true,
+#endif
+      .recv      =
+      {
+        .size    = CONFIG_USART1_RXBUFSIZE,
+        .buffer  = g_usart1rxbuffer,
+      },
+      .xmit      =
+      {
+        .size    = CONFIG_USART1_TXBUFSIZE,
+        .buffer  = g_usart1txbuffer,
+      },
+#ifdef CONFIG_USART1_RXDMA
+      .ops       = &g_uart_dma_ops,
+#else
+      .ops       = &g_uart_ops,
+#endif
+      .priv      = &g_usart1priv,
+    },
+
+  .irq           = STM32WB_IRQ_USART1,
+  .parity        = CONFIG_USART1_PARITY,
+  .bits          = CONFIG_USART1_BITS,
+  .stopbits2     = CONFIG_USART1_2STOP,
+  .baud          = CONFIG_USART1_BAUD,
+  .apbclock      = STM32WB_PCLK2_FREQUENCY,
+  .usartbase     = STM32WB_USART1_BASE,
+  .tx_gpio       = GPIO_USART1_TX,
+  .rx_gpio       = GPIO_USART1_RX,
+#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_USART1_OFLOWCONTROL)
+  .oflow         = true,
+  .cts_gpio      = GPIO_USART1_CTS,
+#endif
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_USART1_IFLOWCONTROL)
+  .iflow         = true,
+  .rts_gpio      = GPIO_USART1_RTS,
+#endif
+#ifdef CONFIG_USART1_RXDMA
+  .rxdma_channel = DMAMAP_USART1_RX,
+  .rxfifo        = g_usart1rxfifo,
+#endif
+
+#ifdef CONFIG_USART1_RS485
+  .rs485_dir_gpio = GPIO_USART1_RS485_DIR,
+#  if (CONFIG_USART1_RS485_DIR_POLARITY == 0)
+  .rs485_dir_polarity = false,
+#  else
+  .rs485_dir_polarity = true,
+#  endif
+#endif
+};
+#endif
+
+/* This table lets us iterate over the configured USARTs */
+
+static struct stm32wb_serial_s *
+const g_uart_devs[STM32WB_NLPUART + STM32WB_NUSART] =
+{
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  [0] = &g_lpuart1priv,
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+  [1] = &g_usart1priv,
+#endif
+};
+
+#ifdef CONFIG_PM
+static struct
+{
+  struct pm_callback_s pm_cb;
+  bool serial_suspended;
+} g_serialpm =
+  {
+    .pm_cb.notify  = stm32wb_serial_pmnotify,
+    .pm_cb.prepare = stm32wb_serial_pmprepare,
+    .serial_suspended = false
+  };
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_serial_getreg
+ ****************************************************************************/
+
+static inline
+uint32_t stm32wb_serial_getreg(struct stm32wb_serial_s *priv, int offset)
+{
+  return getreg32(priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_putreg
+ ****************************************************************************/
+
+static inline void stm32wb_serial_putreg(struct stm32wb_serial_s *priv,
+                                         int offset, uint32_t value)
+{
+  putreg32(value, priv->usartbase + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setusartint
+ ****************************************************************************/
+
+static inline
+void stm32wb_serial_setusartint(struct stm32wb_serial_s *priv, uint16_t ie)
+{
+  uint32_t cr;
+
+  /* Save the interrupt mask */
+
+  priv->ie = ie;
+
+  /* And restore the interrupt state (see the interrupt
+   * enable/usage table above)
+   */
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  cr &= ~(USART_CR1_USED_INTS);
+  cr |= (ie & (USART_CR1_USED_INTS));
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr);
+
+  cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  cr &= ~USART_CR3_EIE;
+  cr |= (ie & USART_CR3_EIE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+}
+
+/****************************************************************************
+ * Name: up_restoreusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_restoreusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  stm32wb_serial_setusartint(priv, ie);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_disableusartint
+ ****************************************************************************/
+
+static void stm32wb_serial_disableusartint(struct stm32wb_serial_s *priv,
+                                           uint16_t *ie)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  if (ie)
+    {
+      uint32_t cr1;
+      uint32_t cr3;
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning            Usage
+       * ------------------ --------------- ------------------ ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected          (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete          (used only
+       *                                                        for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag        (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag          (not used)
+       */
+
+      cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+      cr3 = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+      /* Return the current interrupt mask value for the used interrupts.
+       * Notice that this depends on the fact that none of the used interrupt
+       * enable bits overlap. This logic would fail if we needed the break
+       * interrupt!
+       */
+
+      *ie = (cr1 & (USART_CR1_USED_INTS)) | (cr3 & USART_CR3_EIE);
+    }
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_setusartint(priv, 0);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmanextrx
+ *
+ * Description:
+ *   Returns the index into the RX FIFO where the DMA will place the next
+ *   byte that it receives.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmanextrx(struct stm32wb_serial_s *priv)
+{
+  size_t dmaresidual;
+
+  dmaresidual = stm32wb_dmaresidual(priv->rxdma);
+
+  return (RXDMA_BUFFER_SIZE - (int)dmaresidual);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_usart
+ *
+ * Description:
+ *   Set the serial line baud rate (USART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setbaud_usart(struct stm32wb_serial_s *priv)
+{
+  /* This first implementation is for U[S]ARTs that support oversampling
+   * by 8 in additional to the standard oversampling by 16.
+   */
+
+  uint32_t usartdiv8;
+  uint32_t cr1;
+  uint32_t brr;
+
+  /* In case of oversampling by 8, the equation is:
+   *
+   *   baud      = 2 * fCK / usartdiv8
+   *   usartdiv8 = 2 * fCK / baud
+   */
+
+  usartdiv8 = ((priv->apbclock << 1) + (priv->baud >> 1)) / priv->baud;
+
+  /* Baud rate for standard USART (SPI mode included):
+   *
+   * In case of oversampling by 16, the equation is:
+   *   baud       = fCK / usartdiv16
+   *   usartdiv16 = fCK / baud
+   *              = 2 * usartdiv8
+   */
+
+  /* Use oversamply by 8 only if the divisor is small.  But what is small? */
+
+  cr1 = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  brr = stm32wb_serial_getreg(priv, STM32WB_USART_BRR_OFFSET);
+  brr &= ~(USART_BRR_MANT_MASK | USART_BRR_FRAC_MASK);
+
+  if (usartdiv8 > 100)
+    {
+      /* Use usartdiv16 */
+
+      brr  |= (usartdiv8 + 1) >> 1;
+
+      /* Clear oversampling by 8 to enable oversampling by 16 */
+
+      cr1 &= ~USART_CR1_OVER8;
+    }
+  else
+    {
+      DEBUGASSERT(usartdiv8 >= 8);
+
+      /* Perform mysterious operations on bits 0-3 */
+
+      brr  |= ((usartdiv8 & 0xfff0) | ((usartdiv8 & 0x000f) >> 1));
+
+      /* Set oversampling by 8 */
+
+      cr1 |= USART_CR1_OVER8;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setbaud_lpuart
+ *
+ * Description:
+ *   Set the serial line baud rate (LPUART only).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+static void stm32wb_serial_setbaud_lpuart(struct stm32wb_serial_s *priv)
+{
+  uint32_t brr;
+
+  /* The equation is:
+   *
+   *   baud = 256 * fCK / brr
+   *   brr  = 256 * fCK / baud
+   *
+   * It is forbidden to write values lower than LPUART_BRR_MIN in
+   * the LPUART_BRR register. fCK must range from 3 x baud rate to
+   * 4096 x baud rate.
+   */
+
+  brr = (((uint64_t)priv->apbclock << 8) + (priv->baud >> 1)) / priv->baud;
+  brr &= LPUART_BRR_MASK;
+
+  if (brr < LPUART_BRR_MIN)
+    {
+      brr = LPUART_BRR_MIN;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_BRR_OFFSET, brr);
+}
+#endif
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setformat
+ *
+ * Description:
+ *   Set the serial line format and speed.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+static void stm32wb_serial_setformat(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Set baud rate */
+
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+  if (priv->usartbase == STM32WB_LPUART1_BASE)
+    {
+      stm32wb_serial_setbaud_lpuart(priv);
+    }
+  else
+#endif
+    {
+      stm32wb_serial_setbaud_usart(priv);
+    }
+
+  /* Configure parity mode */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0 | USART_CR1_M1);
+
+  if (priv->parity == 1)       /* Odd parity */
+    {
+      regval |= (USART_CR1_PCE | USART_CR1_PS);
+    }
+  else if (priv->parity == 2)  /* Even parity */
+    {
+      regval |= USART_CR1_PCE;
+    }
+
+  /* Configure word length (parity uses one of configured bits)
+   *
+   * Default: 1 start, 8 data (no parity), n stop, OR
+   *          1 start, 7 data + parity, n stop
+   */
+
+  if (priv->bits == 9 || (priv->bits == 8 && priv->parity != 0))
+    {
+      /* Select: 1 start, 8 data + parity, n stop, OR
+       *         1 start, 9 data (no parity), n stop.
+       */
+
+      regval |= USART_CR1_M0;
+    }
+  else if (priv->bits == 7 && priv->parity == 0)
+    {
+      /* Select: 1 start, 7 data (no parity), n stop, OR
+       */
+
+      regval |= USART_CR1_M1;
+    }
+
+  /* Else Select: 1 start, 7 data + parity, n stop, OR
+   *              1 start, 8 data (no parity), n stop.
+   */
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure STOP bits */
+
+  regval = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK);
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure hardware flow control */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSE | USART_CR3_RTSE);
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && !defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      regval |= USART_CR3_RTSE;
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->oflow && (priv->cts_gpio != 0))
+    {
+      regval |= USART_CR3_CTSE;
+    }
+#endif
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+}
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+/****************************************************************************
+ * Name: stm32wb_serial_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripheral.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_setsuspend(struct uart_dev_s *dev, bool suspend)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#ifdef SERIAL_HAVE_RXDMA
+  bool dmarestored = false;
+#endif
+
+  if (priv->suspended == suspend)
+    {
+      return;
+    }
+
+  priv->suspended = suspend;
+
+  if (suspend)
+    {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Force RTS high to prevent further Rx. */
+
+          stm32wb_configgpio((priv->rts_gpio & ~GPIO_MODE_MASK)
+                             | (GPIO_OUTPUT | GPIO_OUTPUT_SET));
+        }
+#endif
+
+      /* Disable interrupts to prevent Tx. */
+
+      stm32wb_serial_disableusartint(priv, &priv->suspended_ie);
+
+      /* Wait last Tx to complete. */
+
+      while ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+              USART_ISR_TC) == 0);
+
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && !priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow && priv->rxdmanext == RXDMA_BUFFER_SIZE)
+            {
+              /* Rx DMA in non-circular iflow mode and already stopped
+               * at end of DMA buffer. No need to suspend.
+               */
+            }
+          else
+#endif
+            {
+              /* Suspend Rx DMA. */
+
+              stm32wb_dmastop(priv->rxdma);
+              priv->rxdmasusp = true;
+            }
+        }
+#endif
+    }
+  else
+    {
+#ifdef SERIAL_HAVE_RXDMA
+      if (priv->dev.ops == &g_uart_dma_ops && priv->rxdmasusp)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              stm32wb_serial_dmaiflowrestart(priv);
+            }
+          else
+#endif
+            {
+              /* This USART does not have HW flow-control. Unconditionally
+               * re-enable DMA (might loss unprocessed bytes received
+               * to DMA buffer before suspending).
+               */
+
+              stm32wb_serial_dmareenable(priv);
+              priv->rxdmasusp = false;
+            }
+
+          dmarestored = true;
+        }
+#endif
+
+      /* Re-enable interrupts to resume Tx. */
+
+      stm32wb_serial_restoreusartint(priv, priv->suspended_ie);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Restore peripheral RTS control. */
+
+          stm32wb_configgpio(priv->rts_gpio);
+        }
+#endif
+    }
+
+#ifdef SERIAL_HAVE_RXDMA
+  if (dmarestored)
+    {
+      irqstate_t flags;
+
+      flags = enter_critical_section();
+
+      /* Perform initial Rx DMA buffer fetch to wake-up serial device
+       * activity.
+       */
+
+      if (priv->rxdma != NULL)
+        {
+          stm32wb_serial_dmarxcallback(priv->rxdma, 0, priv);
+        }
+
+      leave_critical_section(flags);
+    }
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_pm_setsuspend
+ *
+ * Description:
+ *   Suspend or resume serial peripherals for/from deep-sleep/stop modes.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_pm_setsuspend(bool suspend)
+{
+  int n;
+
+  /* Already in desired state? */
+
+  if (suspend == g_serialpm.serial_suspended)
+    {
+      return;
+    }
+
+  g_serialpm.serial_suspended = suspend;
+
+  for (n = 0; n < STM32WB_NLPUART + STM32WB_NUSART; n++)
+    {
+      struct stm32wb_serial_s *priv = g_uart_devs[n];
+
+      if (priv != NULL && priv->initialized != NULL)
+        {
+          stm32wb_serial_setsuspend(&priv->dev, suspend);
+        }
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_setapbclock
+ *
+ * Description:
+ *   Enable or disable APB clock for the USART peripheral
+ *
+ * Input Parameters:
+ *   dev - A reference to the UART driver state structure
+ *   on  - Enable clock if 'on' is 'true' and disable if 'false'
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_setapbclock(struct uart_dev_s *dev, bool on)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t rcc_en;
+  uint32_t regaddr;
+
+  /* Determine which USART to configure */
+
+  switch (priv->usartbase)
+    {
+      default:
+        return;
+#ifdef CONFIG_STM32WB_LPUART1_SERIALDRIVER
+      case STM32WB_LPUART1_BASE:
+        rcc_en = RCC_APB1ENR2_LPUART1EN;
+        regaddr = STM32WB_RCC_APB1ENR2;
+        break;
+#endif
+#ifdef CONFIG_STM32WB_USART1_SERIALDRIVER
+      case STM32WB_USART1_BASE:
+        rcc_en = RCC_APB2ENR_USART1EN;
+        regaddr = STM32WB_RCC_APB2ENR;
+        break;
+#endif
+    }
+
+  /* Enable/disable APB 1/2 clock for USART */
+
+  if (on)
+    {
+      modifyreg32(regaddr, 0, rcc_en);
+    }
+  else
+    {
+      modifyreg32(regaddr, rcc_en, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_setup
+ *
+ * Description:
+ *   Configure the USART baud, bits, parity, etc. This method is called the
+ *   first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_setup(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+  uint32_t regval;
+
+  /* Note: The logic here depends on the fact that that the USART module
+   * was enabled in stm32wb_lowsetup().
+   */
+
+  /* Enable USART APB1/2 clock */
+
+  stm32wb_serial_setapbclock(dev, true);
+
+  /* Configure pins for USART use */
+
+  stm32wb_configgpio(priv->tx_gpio);
+  stm32wb_configgpio(priv->rx_gpio);
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->cts_gpio != 0)
+    {
+      stm32wb_configgpio(priv->cts_gpio);
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->rts_gpio != 0)
+    {
+      uint32_t config = priv->rts_gpio;
+
+#ifdef CONFIG_STM32WB_FLOWCONTROL_BROKEN
+      /* Instead of letting hw manage this pin, we will bitbang */
+
+      config = (config & ~GPIO_MODE_MASK) | GPIO_OUTPUT;
+#endif
+      stm32wb_configgpio(config);
+    }
+#endif
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_configgpio(priv->rs485_dir_gpio);
+      stm32wb_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity);
+    }
+#endif
+
+  /* Configure CR2 */
+
+  /* Clear STOP, CLKEN, CPOL, CPHA, LBCL, and interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+  regval &= ~(USART_CR2_STOP_MASK | USART_CR2_CLKEN | USART_CR2_CPOL |
+              USART_CR2_CPHA | USART_CR2_LBCL | USART_CR2_LBDIE);
+
+  /* Configure STOP bits */
+
+  if (priv->stopbits2)
+    {
+      regval |= USART_CR2_STOP2;
+    }
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, regval);
+
+  /* Configure CR1 */
+
+  /* Clear TE, REm and all interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_TE | USART_CR1_RE | USART_CR1_ALLINTS);
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Configure CR3 */
+
+  /* Clear CTSE, RTSE, and all interrupt enable bits */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval &= ~(USART_CR3_CTSIE | USART_CR3_CTSE | USART_CR3_RTSE |
+              USART_CR3_EIE);
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+
+  /* Configure the USART line format and speed. */
+
+  stm32wb_serial_setformat(dev);
+
+  /* Enable Rx, Tx, and the USART */
+
+  regval      = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval     |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+#endif /* CONFIG_SUPPRESS_UART_CONFIG */
+
+  /* Set up the cached interrupt enables value */
+
+  priv->ie    = 0;
+
+  /* Mark device as initialized. */
+
+  priv->initialized = true;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmasetup
+ *
+ * Description:
+ *   Configure the USART baud, bits, parity, etc. This method is called the
+ *   first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmasetup(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int result;
+  uint32_t regval;
+
+  /* Do the basic UART setup first, unless we are the console */
+
+  if (!dev->isconsole)
+    {
+      result = stm32wb_serial_setup(dev);
+      if (result != OK)
+        {
+          return result;
+        }
+    }
+
+  /* Acquire the DMA channel.  This should always succeed. */
+
+  priv->rxdma = stm32wb_dmachannel(priv->rxdma_channel);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Configure for non-circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                     priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                     (uint32_t)priv->rxfifo,
+                     RXDMA_BUFFER_SIZE,
+                     SERIAL_DMA_IFLOW_CONTROL_WORD);
+    }
+  else
+#endif
+    {
+      /* Configure for circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                     priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                     (uint32_t)priv->rxfifo,
+                     RXDMA_BUFFER_SIZE,
+                     SERIAL_DMA_CONTROL_WORD);
+    }
+
+  /* Reset our DMA shadow pointer to match the address just
+   * programmed above.
+   */
+
+  priv->rxdmanext = 0;
+
+  /* Enable receive DMA for the UART */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+  regval |= USART_CR3_DMAR;
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, regval);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Start the DMA channel, and arrange for callbacks at the full point
+       * in the FIFO. After buffer gets full, hardware flow-control kicks
+       * in and DMA transfer is stopped.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback, priv,
+                       false);
+    }
+  else
+#endif
+    {
+      /* Start the DMA channel, and arrange for callbacks at the half and
+       * full points in the FIFO.  This ensures that we have half a FIFO
+       * worth of time to claim bytes before they are overwritten.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback, priv,
+                       true);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_shutdown
+ *
+ * Description:
+ *   Disable the USART.  This method is called when the serial
+ *   port is closed
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_shutdown(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t regval;
+
+  /* Mark device as uninitialized. */
+
+  priv->initialized = false;
+
+  /* Disable all interrupts */
+
+  stm32wb_serial_disableusartint(priv, NULL);
+
+  /* Disable USART APB1/2 clock */
+
+  stm32wb_serial_setapbclock(dev, false);
+
+  /* Disable Rx, Tx, and the UART */
+
+  regval  = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+  regval &= ~(USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
+  stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, regval);
+
+  /* Release pins. "If the serial-attached device is powered down, the TX
+   * pin causes back-powering, potentially confusing the device to the point
+   * of complete lock-up."
+   *
+   * REVISIT:  Is unconfiguring the pins appropriate for all device?  If not,
+   * then this may need to be a configuration option.
+   */
+
+  stm32wb_unconfiggpio(priv->tx_gpio);
+  stm32wb_unconfiggpio(priv->rx_gpio);
+
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+  if (priv->cts_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->cts_gpio);
+    }
+#endif
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->rts_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->rts_gpio);
+    }
+#endif
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_unconfiggpio(priv->rs485_dir_gpio);
+    }
+#endif
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmashutdown
+ *
+ * Description:
+ *   Disable the USART.  This method is called when the serial
+ *   port is closed
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void stm32wb_serial_dmashutdown(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  /* Perform the normal UART shutdown */
+
+  stm32wb_serial_shutdown(dev);
+
+  /* Stop the DMA channel */
+
+  stm32wb_dmastop(priv->rxdma);
+
+  /* Release the DMA channel */
+
+  stm32wb_dmafree(priv->rxdma);
+  priv->rxdma = NULL;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_attach
+ *
+ * Description:
+ *   Configure the USART to operation in interrupt driven mode.  This method
+ *   is called when the serial port is opened.  Normally, this is just after
+ *   the the setup() method is called, however, the serial console may
+ *   operate in a non-interrupt driven mode during the boot phase.
+ *
+ *   RX and TX interrupts are not enabled when by the attach method (unless
+ *   the hardware supports multiple levels of interrupt enabling). The RX
+ *   and TX interrupts are not enabled until the txint() and rxint()
+ *   methods are called.
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_attach(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int ret;
+
+  /* Attach and enable the IRQ */
+
+  ret = irq_attach(priv->irq, up_interrupt, priv);
+  if (ret == OK)
+    {
+      /* Enable the interrupt (RX and TX interrupts are still disabled
+       * in the USART
+       */
+
+      up_enable_irq(priv->irq);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_detach
+ *
+ * Description:
+ *   Detach USART interrupts.  This method is called when the serial port is
+ *   closed normally just before the shutdown method is called.
+ *   The exception is the serial console which is never shutdown.
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_detach(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  up_disable_irq(priv->irq);
+  irq_detach(priv->irq);
+}
+
+/****************************************************************************
+ * Name: up_interrupt
+ *
+ * Description:
+ *   This is the USART interrupt handler.  It will be invoked when an
+ *   interrupt received on the 'irq'  It should call uart_transmitchars or
+ *   uart_receivechar to perform the appropriate data transfers.  The
+ *   interrupt handling logic must be able to map the 'irq' number into the
+ *   appropriate uart_dev_s structure in order to call these functions.
+ *
+ ****************************************************************************/
+
+static int up_interrupt(int irq, void *context, void *arg)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)arg;
+  int  passes;
+  bool handled;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Report serial activity to the power management logic */
+
+#if defined(CONFIG_PM) && CONFIG_STM32WB_PM_SERIAL_ACTIVITY > 0
+  pm_activity(PM_IDLE_DOMAIN, CONFIG_STM32WB_PM_SERIAL_ACTIVITY);
+#endif
+
+  /* Loop until there are no characters to be transferred or,
+   * until we have been looping for a long time.
+   */
+
+  handled = true;
+  for (passes = 0; passes < 256 && handled; passes++)
+    {
+      handled = false;
+
+      /* Get the masked USART status word. */
+
+      priv->sr = stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET);
+
+      /* USART interrupts:
+       *
+       * Enable             Status          Meaning             Usage
+       * ------------------ --------------- ------------------- ----------
+       * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+       *                                     Detected           (not used)
+       * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+       *                                     Ready to be Read
+       * "              "   USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR1_TCIE     USART_ISR_TC     Transmission
+       *                                     Complete           (used only
+       *                                                         for RS-485)
+       * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+       *                                     Register Empty
+       * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+       *
+       * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag         (not used)
+       * USART_CR3_EIE      USART_ISR_FE     Framing Error
+       * "           "      USART_ISR_NE     Noise Flag
+       * "           "      USART_ISR_ORE    Overrun Error
+       *                                     Detected
+       * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag           (not used)
+       *
+       * NOTE: Some of these status bits must be cleared by explicitly
+       * writing one to the ICR register: USART_ICR_CTSCF, USART_ICR_LBDCF.
+       * Note of those are currently being used.
+       */
+
+#ifdef HAVE_RS485
+      /* Transmission of whole buffer is over - TC is set, TXEIE is cleared.
+       * Note - this should be first, to have the most recent TC bit value
+       * from SR register - sending data affects TC, but without refresh we
+       * will not know that...
+       */
+
+      if ((priv->sr & USART_ISR_TC) != 0 &&
+          (priv->ie & USART_CR1_TCIE) != 0 &&
+          (priv->ie & USART_CR1_TXEIE) == 0)
+        {
+          stm32wb_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity);
+          stm32wb_serial_restoreusartint(priv, priv->ie & ~USART_CR1_TCIE);
+        }
+#endif
+
+      /* Handle incoming, receive bytes. */
+
+      if ((priv->sr & USART_ISR_RXNE) != 0 &&
+          (priv->ie & USART_CR1_RXNEIE) != 0)
+        {
+          /* Received data ready... process incoming bytes.  NOTE the check
+           * for RXNEIE:  We cannot call uart_recvchards of RX interrupts
+           * are disabled.
+           */
+
+          uart_recvchars(&priv->dev);
+          handled = true;
+        }
+
+      /* We may still have to read from the DR register to clear any pending
+       * error conditions.
+       */
+
+      else if ((priv->sr &
+                (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE)) != 0)
+        {
+          /* These errors are cleared by writing the corresponding bit to the
+           * interrupt clear register (ICR).
+           */
+
+          stm32wb_serial_putreg(priv, STM32WB_USART_ICR_OFFSET,
+                                (USART_ICR_NCF | USART_ICR_ORECF |
+                                 USART_ICR_FECF));
+        }
+
+      /* Handle outgoing, transmit bytes */
+
+      if ((priv->sr & USART_ISR_TXE) != 0 &&
+          (priv->ie & USART_CR1_TXEIE) != 0)
+        {
+          /* Transmit data register empty ... process outgoing bytes */
+
+          uart_xmitchars(&priv->dev);
+          handled = true;
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_ioctl
+ *
+ * Description:
+ *   All ioctl calls will be routed through this method
+ *
+ ****************************************************************************/
+
+static int stm32wb_serial_ioctl(struct file *filep, int cmd,
+                                unsigned long arg)
+{
+#if defined(CONFIG_SERIAL_TERMIOS) || defined(CONFIG_SERIAL_TIOCSERGSTRUCT)
+  struct inode      *inode = filep->f_inode;
+  struct uart_dev_s *dev   = inode->i_private;
+#endif
+#if defined(CONFIG_SERIAL_TERMIOS)
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+#endif
+  int                ret    = OK;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT
+    case TIOCSERGSTRUCT:
+      {
+        struct stm32wb_serial_s *user = (struct stm32wb_serial_s *)arg;
+        if (!user)
+          {
+            ret = -EINVAL;
+          }
+        else
+          {
+            memcpy(user, dev, sizeof(struct stm32wb_serial_s));
+          }
+      }
+      break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_SINGLEWIRE
+    case TIOCSSINGLEWIRE:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, HDSEL can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Change the TX port to be open-drain/push-pull and enable/disable
+         * half-duplex mode.
+         */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR3_OFFSET);
+
+        if ((arg & SER_SINGLEWIRE_ENABLED) != 0)
+          {
+            uint32_t gpio_val = (arg & SER_SINGLEWIRE_PUSHPULL) ==
+                                 SER_SINGLEWIRE_PUSHPULL ?
+                                 GPIO_PUSHPULL : GPIO_OPENDRAIN;
+            gpio_val |=
+              (arg & SER_SINGLEWIRE_PULL_MASK) == SER_SINGLEWIRE_PULLUP ?
+                                                  GPIO_PULLUP : GPIO_FLOAT;
+
+            gpio_val |=
+              (arg & SER_SINGLEWIRE_PULL_MASK) == SER_SINGLEWIRE_PULLDOWN ?
+                                                  GPIO_PULLDOWN : GPIO_FLOAT;
+
+            stm32wb_configgpio((priv->tx_gpio & ~(GPIO_PUPD_MASK |
+                                                  GPIO_OPENDRAIN)) |
+                                                  gpio_val);
+
+            cr |= USART_CR3_HDSEL;
+          }
+        else
+          {
+            stm32wb_configgpio((priv->tx_gpio & ~(GPIO_PUPD_MASK |
+                                                  GPIO_OPENDRAIN)) |
+                                                  GPIO_PUSHPULL);
+
+            cr &= ~USART_CR3_HDSEL;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR3_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_INVERT
+    case TIOCSINVERT:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, {R,T}XINV can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Enable/disable signal inversion. */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+
+        if (arg & SER_INVERT_ENABLED_RX)
+          {
+            cr |= USART_CR2_RXINV;
+          }
+        else
+          {
+            cr &= ~USART_CR2_RXINV;
+          }
+
+        if (arg & SER_INVERT_ENABLED_TX)
+          {
+            cr |= USART_CR2_TXINV;
+          }
+        else
+          {
+            cr &= ~USART_CR2_TXINV;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_STM32WB_USART_SWAP
+    case TIOCSSWAP:
+      {
+        uint32_t cr1;
+        uint32_t cr1_ue;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Get the original state of UE */
+
+        cr1    = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        cr1_ue = cr1 & USART_CR1_UE;
+        cr1   &= ~USART_CR1_UE;
+
+        /* Disable UE, SWAP can only be written when UE=0 */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1);
+
+        /* Enable/disable Swap mode. */
+
+        uint32_t cr = stm32wb_serial_getreg(priv, STM32WB_USART_CR2_OFFSET);
+
+        if (arg == SER_SWAP_ENABLED)
+          {
+            cr |= USART_CR2_SWAP;
+          }
+        else
+          {
+            cr &= ~USART_CR2_SWAP;
+          }
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR2_OFFSET, cr);
+
+        /* Re-enable UE if appropriate */
+
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET, cr1 | cr1_ue);
+        leave_critical_section(flags);
+      }
+     break;
+#endif
+
+#ifdef CONFIG_SERIAL_TERMIOS
+    case TCGETS:
+      {
+        struct termios *termiosp = (struct termios *)arg;
+
+        if (!termiosp)
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        /* Note that since we only support 8/9 bit modes and
+         * there is no way to report 9-bit mode, we always claim 8.
+         */
+
+        termiosp->c_cflag =
+          ((priv->parity != 0) ? PARENB : 0) |
+          ((priv->parity == 1) ? PARODD : 0) |
+          ((priv->stopbits2) ? CSTOPB : 0) |
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+          ((priv->oflow) ? CCTS_OFLOW : 0) |
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          ((priv->iflow) ? CRTS_IFLOW : 0) |
+#endif
+          CS8;
+
+        cfsetispeed(termiosp, priv->baud);
+
+        /* TODO: CCTS_IFLOW, CCTS_OFLOW */
+      }
+      break;
+
+    case TCSETS:
+      {
+        struct termios *termiosp = (struct termios *)arg;
+
+        if (!termiosp)
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        /* Perform some sanity checks before accepting any changes */
+
+        if (((termiosp->c_cflag & CSIZE) != CS8)
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+            || ((termiosp->c_cflag & CCTS_OFLOW) && (priv->cts_gpio == 0))
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+            || ((termiosp->c_cflag & CRTS_IFLOW) && (priv->rts_gpio == 0))
+#endif
+           )
+          {
+            ret = -EINVAL;
+            break;
+          }
+
+        if (termiosp->c_cflag & PARENB)
+          {
+            priv->parity = (termiosp->c_cflag & PARODD) ? 1 : 2;
+          }
+        else
+          {
+            priv->parity = 0;
+          }
+
+        priv->stopbits2 = (termiosp->c_cflag & CSTOPB) != 0;
+#ifdef CONFIG_SERIAL_OFLOWCONTROL
+        priv->oflow = (termiosp->c_cflag & CCTS_OFLOW) != 0;
+#endif
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+        priv->iflow = (termiosp->c_cflag & CRTS_IFLOW) != 0;
+#endif
+
+        /* Note that since there is no way to request 9-bit mode
+         * and no way to support 5/6/7-bit modes, we ignore them
+         * all here.
+         */
+
+        /* Note that only cfgetispeed is used because we have knowledge
+         * that only one speed is supported.
+         */
+
+        priv->baud = cfgetispeed(termiosp);
+
+        /* Effect the changes immediately - note that we do not implement
+         * TCSADRAIN / TCSAFLUSH
+         */
+
+        stm32wb_serial_setformat(dev);
+      }
+      break;
+#endif /* CONFIG_SERIAL_TERMIOS */
+
+#ifdef CONFIG_STM32WB_USART_BREAKS
+#  ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+    case TIOCSBRK:  /* BSD compatibility: Turn break on, unconditionally */
+      {
+        irqstate_t flags;
+        uint32_t tx_break;
+
+        flags = enter_critical_section();
+
+        /* Disable any further tx activity */
+
+        priv->ie |= USART_CR1_IE_BREAK_INPROGRESS;
+
+        stm32wb_serial_txint(dev, false);
+
+        /* Configure TX as a GPIO output pin and Send a break signal */
+
+        tx_break = GPIO_OUTPUT |
+                   (~(GPIO_MODE_MASK | GPIO_OUTPUT_SET) & priv->tx_gpio);
+        stm32wb_configgpio(tx_break);
+
+        leave_critical_section(flags);
+      }
+      break;
+
+    case TIOCCBRK:  /* BSD compatibility: Turn break off, unconditionally */
+      {
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+
+        /* Configure TX back to U(S)ART */
+
+        stm32wb_configgpio(priv->tx_gpio);
+
+        priv->ie &= ~USART_CR1_IE_BREAK_INPROGRESS;
+
+        /* Enable further tx activity */
+
+        stm32wb_serial_txint(dev, true);
+
+        leave_critical_section(flags);
+      }
+      break;
+#  else
+    case TIOCSBRK:  /* No BSD compatibility: Turn break on for M bit times */
+      {
+        uint32_t cr1;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+        cr1   = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET,
+                              cr1 | USART_CR1_SBK);
+        leave_critical_section(flags);
+      }
+      break;
+
+    case TIOCCBRK:  /* No BSD compatibility: May turn off break too soon */
+      {
+        uint32_t cr1;
+        irqstate_t flags;
+
+        flags = enter_critical_section();
+        cr1   = stm32wb_serial_getreg(priv, STM32WB_USART_CR1_OFFSET);
+        stm32wb_serial_putreg(priv, STM32WB_USART_CR1_OFFSET,
+                              cr1 & ~USART_CR1_SBK);
+        leave_critical_section(flags);
+      }
+      break;
+#  endif
+#endif
+
+    default:
+      ret = -ENOTTY;
+      break;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_receive
+ *
+ * Description:
+ *   Called (usually) from the interrupt level to receive one
+ *   character from the USART.  Error bits associated with the
+ *   receipt are provided in the return 'status'.
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static int stm32wb_serial_receive(struct uart_dev_s *dev,
+                                  unsigned int *status)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  uint32_t rdr;
+
+  /* Get the Rx byte */
+
+  rdr      = stm32wb_serial_getreg(priv, STM32WB_USART_RDR_OFFSET);
+
+  /* Get the Rx byte plux error information.  Return those in status */
+
+  *status  = priv->sr << 16 | rdr;
+  priv->sr = 0;
+
+  /* Then return the actual received byte */
+
+  return rdr & 0xff;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxint
+ *
+ * Description:
+ *   Call to enable or disable RX interrupts
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static void stm32wb_serial_rxint(struct uart_dev_s *dev, bool enable)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  irqstate_t flags;
+  uint16_t ie;
+
+  /* USART receive interrupts:
+   *
+   * Enable             Status          Meaning                Usage
+   * ------------------ --------------- ------------------     ----------
+   * USART_CR1_IDLEIE   USART_ISR_IDLE   Idle Line
+   *                                     Detected              (not used)
+   * USART_CR1_RXNEIE   USART_ISR_RXNE   Received Data
+   *                                     Ready to be Read
+   * "              "   USART_ISR_ORE    Overrun Error
+   *                                     Detected
+   * USART_CR1_PEIE     USART_ISR_PE     Parity Error
+   *
+   * USART_CR2_LBDIE    USART_ISR_LBD    Break Flag            (not used)
+   * USART_CR3_EIE      USART_ISR_FE     Framing Error
+   * "           "      USART_ISR_NE     Noise Flag
+   * "           "      USART_ISR_ORE    Overrun Error
+   *                                     Detected
+   */
+
+  flags = enter_critical_section();
+  ie = priv->ie;
+  if (enable)
+    {
+      /* Receive an interrupt when their is anything in the Rx data register
+       * (or an Rx timeout occurs).
+       */
+
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+#ifdef CONFIG_USART_ERRINTS
+      ie |= (USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR3_EIE);
+#else
+      ie |= USART_CR1_RXNEIE;
+#endif
+#endif
+    }
+  else
+    {
+      ie &= ~(USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR3_EIE);
+    }
+
+  /* Then set the new interrupt state */
+
+  stm32wb_serial_restoreusartint(priv, ie);
+  leave_critical_section(flags);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxavailable
+ *
+ * Description:
+ *   Return true if the receive register is not empty
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_HAVE_ONLY_DMA
+static bool stm32wb_serial_rxavailable(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  return ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+           USART_ISR_RXNE) != 0);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_rxflowcontrol
+ *
+ * Description:
+ *   Called when Rx buffer is full (or exceeds configured watermark levels
+ *   if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is defined).
+ *   Return true if UART activated RX flow control to block more incoming
+ *   data
+ *
+ * Input Parameters:
+ *   dev       - UART device instance
+ *   nbuffered - the number of characters currently buffered
+ *               (if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is
+ *               not defined the value will be 0 for an empty buffer or the
+ *               defined buffer size for a full buffer)
+ *   upper     - true indicates the upper watermark was crossed where
+ *               false indicates the lower watermark has been crossed
+ *
+ * Returned Value:
+ *   true if RX flow control activated.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+static bool stm32wb_serial_rxflowcontrol(struct uart_dev_s *dev,
+                                         unsigned int nbuffered, bool upper)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS) && \
+    defined(CONFIG_STM32WB_FLOWCONTROL_BROKEN)
+  if (priv->iflow && (priv->rts_gpio != 0))
+    {
+      /* Assert/de-assert nRTS set it high resume/stop sending */
+
+      stm32wb_gpiowrite(priv->rts_gpio, upper);
+
+      if (upper)
+        {
+          /* With heavy Rx traffic, RXNE might be set and data pending.
+           * Returning 'true' in such case would cause RXNE left unhandled
+           * and causing interrupt storm. Sending end might be also be slow
+           * to react on nRTS, and returning 'true' here would prevent
+           * processing that data.
+           *
+           * Therefore, return 'false' so input data is still being processed
+           * until sending end reacts on nRTS signal and stops sending more.
+           */
+
+          return false;
+        }
+
+      return upper;
+    }
+
+#else
+  if (priv->iflow)
+    {
+      /* Is the RX buffer full? */
+
+      if (upper)
+        {
+          /* Disable Rx interrupt to prevent more data being from
+           * peripheral.  When hardware RTS is enabled, this will
+           * prevent more data from coming in.
+           *
+           * This function is only called when UART recv buffer is full,
+           * that is: "dev->recv.head + 1 == dev->recv.tail".
+           *
+           * Logic in "uart_read" will automatically toggle Rx interrupts
+           * when buffer is read empty and thus we do not have to re-
+           * enable Rx interrupts.
+           */
+
+          uart_disablerxint(dev);
+          return true;
+        }
+
+      /* No.. The RX buffer is empty */
+
+      else
+        {
+          /* We might leave Rx interrupt disabled if full recv buffer was
+           * read empty.  Enable Rx interrupt to make sure that more input is
+           * received.
+           */
+
+          uart_enablerxint(dev);
+        }
+    }
+#endif
+
+  return false;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmareceive
+ *
+ * Description:
+ *   Called (usually) from the interrupt level to receive one
+ *   character from the USART.  Error bits associated with the
+ *   receipt are provided in the return 'status'.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int stm32wb_serial_dmareceive(struct uart_dev_s *dev,
+                                     unsigned int *status)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  int c = 0;
+
+  if (stm32wb_serial_dmanextrx(priv) != priv->rxdmanext)
+    {
+      c = priv->rxfifo[priv->rxdmanext];
+
+      priv->rxdmanext++;
+      if (priv->rxdmanext == RXDMA_BUFFER_SIZE)
+        {
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+          if (priv->iflow)
+            {
+              /* RX DMA buffer full. RX paused, RTS line pulled up to prevent
+               * more input data from other end.
+               */
+            }
+          else
+#endif
+            {
+              priv->rxdmanext = 0;
+            }
+        }
+    }
+
+  return c;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmareenable
+ *
+ * Description:
+ *   Call to re-enable RX DMA.
+ *
+ ****************************************************************************/
+
+#if defined(SERIAL_HAVE_RXDMA)
+static void stm32wb_serial_dmareenable(struct stm32wb_serial_s *priv)
+{
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Configure for non-circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                       priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                       (uint32_t)priv->rxfifo,
+                       RXDMA_BUFFER_SIZE,
+                       SERIAL_DMA_IFLOW_CONTROL_WORD);
+    }
+  else
+#endif
+    {
+      /* Configure for circular DMA reception into the RX FIFO */
+
+      stm32wb_dmasetup(priv->rxdma,
+                       priv->usartbase + STM32WB_USART_RDR_OFFSET,
+                       (uint32_t)priv->rxfifo,
+                       RXDMA_BUFFER_SIZE,
+                       SERIAL_DMA_CONTROL_WORD);
+    }
+
+  /* Reset our DMA shadow pointer to match the address just
+   * programmed above.
+   */
+
+  priv->rxdmanext = 0;
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Start the DMA channel, and arrange for callbacks at the full point
+       * in the FIFO. After buffer gets full, hardware flow-control kicks
+       * in and DMA transfer is stopped.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback, priv,
+                       false);
+    }
+  else
+#endif
+    {
+      /* Start the DMA channel, and arrange for callbacks at the half and
+       * full points in the FIFO.  This ensures that we have half a FIFO
+       * worth of time to claim bytes before they are overwritten.
+       */
+
+      stm32wb_dmastart(priv->rxdma, stm32wb_serial_dmarxcallback, priv,
+                       true);
+    }
+
+#ifdef CONFIG_PM
+  /* Clear DMA suspended flag. */
+
+  priv->rxdmasusp = false;
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmaiflowrestart
+ *
+ * Description:
+ *   Call to restart RX DMA for input flow-controlled USART
+ *
+ ****************************************************************************/
+
+#if defined(SERIAL_HAVE_RXDMA) && defined(CONFIG_SERIAL_IFLOWCONTROL)
+static bool stm32wb_serial_dmaiflowrestart(struct stm32wb_serial_s *priv)
+{
+  if (!priv->rxenable)
+    {
+      /* Rx not enabled by upper layer. */
+
+      return false;
+    }
+
+  if (priv->rxdmanext != RXDMA_BUFFER_SIZE)
+    {
+#ifdef CONFIG_PM
+      if (priv->rxdmasusp)
+        {
+          /* Rx DMA in suspended state. */
+
+          if (stm32wb_serial_dmarxavailable(&priv->dev))
+            {
+              /* DMA buffer has unprocessed data, do not re-enable yet. */
+
+              return false;
+            }
+        }
+      else
+#endif
+        {
+          return false;
+        }
+    }
+
+  /* DMA is stopped or suspended and DMA buffer does not have pending data,
+   * re-enabling without data loss is now safe.
+   */
+
+  stm32wb_serial_dmareenable(priv);
+
+  return true;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmarxint
+ *
+ * Description:
+ *   Call to enable or disable RX interrupts
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void stm32wb_serial_dmarxint(struct uart_dev_s *dev, bool enable)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  /* En/disable DMA reception.
+   *
+   * Note that it is not safe to check for available bytes and immediately
+   * pass them to uart_recvchars as that could potentially recurse back
+   * to us again.  Instead, bytes must wait until the next up_dma_poll or
+   * DMA event.
+   */
+
+  priv->rxenable = enable;
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  if (priv->iflow)
+    {
+      /* Re-enable RX DMA. */
+
+      stm32wb_serial_dmaiflowrestart(priv);
+    }
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmarxavailable
+ *
+ * Description:
+ *   Return true if the receive register is not empty
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static bool stm32wb_serial_dmarxavailable(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  /* Compare our receive pointer to the current DMA pointer, if they
+   * do not match, then there are bytes to be received.
+   */
+
+  return (stm32wb_serial_dmanextrx(priv) != priv->rxdmanext);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_send
+ *
+ * Description:
+ *   This method will send one byte on the USART
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_send(struct uart_dev_s *dev, int ch)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+#ifdef HAVE_RS485
+  if (priv->rs485_dir_gpio != 0)
+    {
+      stm32wb_gpiowrite(priv->rs485_dir_gpio, priv->rs485_dir_polarity);
+    }
+#endif
+
+  stm32wb_serial_putreg(priv, STM32WB_USART_TDR_OFFSET, (uint32_t)ch);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_txint
+ *
+ * Description:
+ *   Call to enable or disable TX interrupts
+ *
+ ****************************************************************************/
+
+static void stm32wb_serial_txint(struct uart_dev_s *dev, bool enable)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+
+  irqstate_t flags;
+
+  /* USART transmit interrupts:
+   *
+   * Enable             Status          Meaning            Usage
+   * ------------------ --------------- ----------------   ----------
+   * USART_CR1_TCIE     USART_ISR_TC     Transmission
+   *                                     Complete          (used only
+   *                                                       for RS-485)
+   * USART_CR1_TXEIE    USART_ISR_TXE    Transmit Data
+   *                                     Register Empty
+   * USART_CR3_CTSIE    USART_ISR_CTS    CTS flag          (not used)
+   */
+
+  flags = enter_critical_section();
+  if (enable)
+    {
+      /* Set to receive an interrupt when the TX data register is empty */
+
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+      uint16_t ie = priv->ie | USART_CR1_TXEIE;
+
+      /* If RS-485 is supported on this U[S]ART, then also enable the
+       * transmission complete interrupt.
+       */
+
+#  ifdef HAVE_RS485
+      if (priv->rs485_dir_gpio != 0)
+        {
+          ie |= USART_CR1_TCIE;
+        }
+#  endif
+
+#  ifdef CONFIG_STM32WB_SERIALBRK_BSDCOMPAT
+      if (priv->ie & USART_CR1_IE_BREAK_INPROGRESS)
+        {
+          leave_critical_section(flags);
+          return;
+        }
+#  endif
+
+      stm32wb_serial_restoreusartint(priv, ie);
+
+      /* Fake a TX interrupt here by just calling uart_xmitchars() with
+       * interrupts disabled (note this may recurse).
+       */
+
+      uart_xmitchars(dev);
+#endif
+    }
+  else
+    {
+      /* Disable the TX interrupt */
+
+      stm32wb_serial_restoreusartint(priv, priv->ie & ~USART_CR1_TXEIE);
+    }
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_txready
+ *
+ * Description:
+ *   Return true if the transmit data register is empty
+ *
+ ****************************************************************************/
+
+static bool stm32wb_serial_txready(struct uart_dev_s *dev)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)dev->priv;
+  return ((stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET) &
+           USART_ISR_TXE) != 0);
+}
+
+/****************************************************************************
+ * Name: stm32wb_serial_dmarxcallback
+ *
+ * Description:
+ *   This function checks the current DMA state and calls the generic
+ *   serial stack when bytes appear to be available.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void stm32wb_serial_dmarxcallback(DMA_HANDLE handle, uint8_t status,
+                                         void *arg)
+{
+  struct stm32wb_serial_s *priv = (struct stm32wb_serial_s *)arg;
+
+  if (priv->rxenable && stm32wb_serial_dmarxavailable(&priv->dev))
+    {
+      uart_recvchars(&priv->dev);
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+      if (priv->iflow)
+        {
+          /* Re-enable RX DMA. */
+
+          stm32wb_serial_dmaiflowrestart(priv);
+        }
+#endif
+    }
+
+  /* Get the masked USART status word to check and clear error flags.
+   *
+   * When wake-up from low power mode was not fast enough, UART is resumed
+   * too late and sometimes exactly when character was coming over UART,
+   * resulting to frame error.
+
+   * If error flag is not cleared, Rx DMA will be stuck. Clearing errors
+   * will release Rx DMA.
+   */
+
+  priv->sr = stm32wb_serial_getreg(priv, STM32WB_USART_ISR_OFFSET);
+
+  if ((priv->sr & (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE)) != 0)
+    {
+      stm32wb_serial_putreg(priv, STM32WB_USART_ICR_OFFSET,
+                            (USART_ICR_NCF | USART_ICR_ORECF |
+                             USART_ICR_FECF));
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_pmnotify
+ *
+ * Description:
+ *   Notify the driver of new power state. This callback is  called after
+ *   all drivers have had the opportunity to prepare for the new power state.
+ *
+ * Input Parameters:
+ *
+ *    cb - Returned to the driver. The driver version of the callback
+ *         structure may include additional, driver-specific state data at
+ *         the end of the structure.
+ *
+ *    pmstate - Identifies the new PM state
+ *
+ * Returned Value:
+ *   None - The driver already agreed to transition to the low power
+ *   consumption state when when it returned OK to the prepare() call.
+ *
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static void stm32wb_serial_pmnotify(struct pm_callback_s *cb, int domain,
+                                    enum pm_state_e pmstate)
+{
+  switch (pmstate)
+    {
+      case PM_NORMAL:
+        stm32wb_serial_pm_setsuspend(false);
+        break;
+
+      case PM_IDLE:
+        stm32wb_serial_pm_setsuspend(false);
+        break;
+
+      case PM_STANDBY:
+        /* TODO: Alternative configuration and logic for enabling serial in
+         *       Stop 1 mode with HSI16 missing. Current logic allows
+         *       suspending serial peripherals for Stop 0/1/2 when serial
+         *       Rx/Tx buffers are empty (checked in pmprepare).
+         */
+
+        stm32wb_serial_pm_setsuspend(true);
+        break;
+
+      case PM_SLEEP:
+        stm32wb_serial_pm_setsuspend(true);
+        break;
+
+      default:
+        DEBUGASSERT(0);
+        break;
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32wb_serial_pmprepare
+ *
+ * Description:
+ *   Request the driver to prepare for a new power state. This is a warning
+ *   that the system is about to enter into a new power state. The driver
+ *   should begin whatever operations that may be required to enter power
+ *   state. The driver may abort the state change mode by returning a
+ *   non-zero value from the callback function.
+ *
+ * Input Parameters:
+ *
+ *    cb - Returned to the driver. The driver version of the callback
+ *         structure may include additional, driver-specific state data at
+ *         the end of the structure.
+ *
+ *    pmstate - Identifies the new PM state
+ *
+ * Returned Value:
+ *   Zero - (OK) means the event was successfully processed and that the
+ *          driver is prepared for the PM state change.
+ *
+ *   Non-zero - means that the driver is not prepared to perform the tasks
+ *              needed achieve this power setting and will cause the state
+ *              change to be aborted. NOTE: The prepare() method will also
+ *              be called when reverting from lower back to higher power
+ *              consumption modes (say because another driver refused a
+ *              lower power state change). Drivers are not permitted to
+ *              return non-zero values when reverting back to higher power
+ *              consumption modes!
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PM
+static int stm32wb_serial_pmprepare(struct pm_callback_s *cb, int domain,
+                                    enum pm_state_e pmstate)
+{
+  int n;
+
+  /* Logic to prepare for a reduced power state goes here. */
+
+  switch (pmstate)
+    {
+      case PM_NORMAL:
+      case PM_IDLE:
+        break;
+
+      case PM_STANDBY:
+      case PM_SLEEP:
+
+#ifdef SERIAL_HAVE_RXDMA
+        /* Flush Rx DMA buffers before checking state of serial device
+         * buffers.
+         */
+
+        stm32wb_serial_dma_poll();
+#endif
+
+        /* Check if any of the active ports have data pending on Tx/Rx
+         * buffers.
+         */
+
+        for (n = 0; n < STM32WB_NLPUART + STM32WB_NUSART; n++)
+          {
+            struct stm32wb_serial_s *priv = g_uart_devs[n];
+
+            if (!priv || !priv->initialized)
+              {
+                /* Not active, skip. */
+
+                continue;
+              }
+
+            if (priv->suspended)
+              {
+                /* Port already suspended, skip. */
+
+                continue;
+              }
+
+            /* Check if port has data pending (Rx & Tx). */
+
+            if (priv->dev.xmit.head != priv->dev.xmit.tail)
+              {
+                return ERROR;
+              }
+
+            if (priv->dev.recv.head != priv->dev.recv.tail)
+              {
+                return ERROR;
+              }
+          }
+        break;
+
+      default:
+        DEBUGASSERT(0);
+        break;
+    }
+
+  return OK;
+}
+#endif
+
+#endif /* HAVE_UART */
+#endif /* USE_SERIALDRIVER */
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+#ifdef USE_SERIALDRIVER
+
+/****************************************************************************
+ * Name: arm_earlyserialinit
+ *
+ * Description:
+ *   Performs the low level USART initialization early in debug so that the
+ *   serial console will be available during bootup.  This must be called
+ *   before arm_serialinit.
+ *
+ ****************************************************************************/
+
+#ifdef USE_EARLYSERIALINIT
+void arm_earlyserialinit(void)
+{
+#ifdef HAVE_UART
+  unsigned i;
+
+  /* Disable all USART interrupts */
+
+  for (i = 0; i < STM32WB_NLPUART + STM32WB_NUSART; i++)
+    {
+      if (g_uart_devs[i])
+        {
+          stm32wb_serial_disableusartint(g_uart_devs[i], NULL);
+        }
+    }
+
+  /* Configure whichever one is the console */
+
+#if CONSOLE_UART > 0
+  stm32wb_serial_setup(&g_uart_devs[CONSOLE_UART - 1]->dev);
+#endif
+#endif /* HAVE UART */
+}
+#endif /* USE_EARLYSERIALINIT */
+
+/****************************************************************************
+ * Name: arm_serialinit
+ *
+ * Description:
+ *   Register serial console and serial ports.  This assumes
+ *   that arm_earlyserialinit was called previously.
+ *
+ ****************************************************************************/
+
+void arm_serialinit(void)
+{
+#ifdef HAVE_UART
+  char devname[16];
+  unsigned i;
+  unsigned minor = 0;
+#ifdef CONFIG_PM
+  int ret;
+#endif
+
+  /* Register to receive power management callbacks */
+
+#ifdef CONFIG_PM
+  ret = pm_register(&g_serialpm.pm_cb);
+  DEBUGASSERT(ret == OK);
+  UNUSED(ret);

Review Comment:
   Minor: maybe `DEBUGVERIFY` can be used



##########
boards/arm/stm32wb/nucleo-wb55rg/src/stm32_autoleds.c:
##########
@@ -0,0 +1,107 @@
+/****************************************************************************
+ * boards/arm/stm32wb/nucleo-wb55rg/src/stm32_autoleds.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <debug.h>
+
+#include <nuttx/board.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "arm_internal.h"
+#include "stm32wb.h"
+#include "nucleo-wb55rg.h"
+
+#ifdef CONFIG_ARCH_LEDS
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: board_autoled_initialize
+ ****************************************************************************/
+
+void board_autoled_initialize(void)
+{
+  /* Configure LEDs GPIO for output. Initial state is OFF */
+
+  stm32wb_configgpio(GPIO_LED1);
+  stm32wb_configgpio(GPIO_LED2);
+  stm32wb_configgpio(GPIO_LED3);
+}
+
+/****************************************************************************
+ * Name: board_autoled_on
+ ****************************************************************************/
+
+void board_autoled_on(int led)
+{
+  switch (led)
+    {
+    default:
+      break;
+
+    case BOARD_LED1:
+      stm32wb_gpiowrite(GPIO_LED1, true);
+      break;
+
+    case BOARD_LED2:
+      stm32wb_gpiowrite(GPIO_LED2, true);
+      break;
+
+    case BOARD_LED3:
+      stm32wb_gpiowrite(GPIO_LED3, true);
+      break;

Review Comment:
   Please add 2 spaces for case block



##########
arch/arm/src/stm32wb/stm32wb_tim_lowerhalf.c:
##########
@@ -0,0 +1,486 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_tim_lowerhalf.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/timers/timer.h>
+#include <arch/board/board.h>
+
+#include "stm32wb_tim.h"
+
+#if defined(CONFIG_TIMER) && \
+    (defined(CONFIG_STM32WB_TIM1) || defined(CONFIG_STM32WB_TIM2) || \
+     defined(CONFIG_STM32WB_TIM16) || defined(CONFIG_STM32WB_TIM17))
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define STM32WB_TIM1_RES   16
+#define STM32WB_TIM2_RES   32
+#define STM32WB_TIM16_RES  16
+#define STM32WB_TIM17_RES  16
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure provides the private representation of the "lower-half"
+ * driver state structure.  This structure must be cast-compatible with the
+ * timer_lowerhalf_s structure.
+ */
+
+struct stm32wb_lowerhalf_s
+{
+  const struct timer_ops_s *ops;        /* Lower half operations */
+  struct stm32wb_tim_dev_s *tim;        /* stm32 timer driver */
+  tccb_t                    callback;   /* Current upper half interrupt callback */
+  void                     *arg;        /* Argument passed to upper half callback */
+  bool                      started;    /* True: Timer has been started */
+  const uint8_t             resolution; /* Number of bits in the timer (16 or 32 bits) */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Interrupt handling *******************************************************/
+
+static int stm32wb_timer_handler(int irq, void *context, void *arg);
+
+/* "Lower half" driver methods **********************************************/
+
+static int stm32wb_start(struct timer_lowerhalf_s *lower);
+static int stm32wb_stop(struct timer_lowerhalf_s *lower);
+static int stm32wb_getstatus(struct timer_lowerhalf_s *lower,
+                             struct timer_status_s *status);
+static int stm32wb_settimeout(struct timer_lowerhalf_s *lower,
+                              uint32_t timeout);
+static void stm32wb_setcallback(struct timer_lowerhalf_s *lower,
+                                tccb_t callback, void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* "Lower half" driver methods */
+
+static const struct timer_ops_s g_timer_ops =
+{
+  .start       = stm32wb_start,
+  .stop        = stm32wb_stop,
+  .getstatus   = stm32wb_getstatus,
+  .settimeout  = stm32wb_settimeout,
+  .setcallback = stm32wb_setcallback,
+  .ioctl       = NULL,
+};
+
+#ifdef CONFIG_STM32WB_TIM1
+static struct stm32wb_lowerhalf_s g_tim1_lowerhalf =
+{
+  .ops         = &g_timer_ops,
+  .resolution  = STM32WB_TIM1_RES,
+};
+#endif
+
+#ifdef CONFIG_STM32WB_TIM2
+static struct stm32wb_lowerhalf_s g_tim2_lowerhalf =
+{
+  .ops         = &g_timer_ops,
+  .resolution  = STM32WB_TIM2_RES,
+};
+#endif
+
+#ifdef CONFIG_STM32WB_TIM16
+static struct stm32wb_lowerhalf_s g_tim16_lowerhalf =
+{
+  .ops         = &g_timer_ops,
+  .resolution  = STM32WB_TIM16_RES,
+};
+#endif
+
+#ifdef CONFIG_STM32WB_TIM17
+static struct stm32wb_lowerhalf_s g_tim17_lowerhalf =
+{
+  .ops         = &g_timer_ops,
+  .resolution  = STM32WB_TIM17_RES,
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_timer_handler
+ *
+ * Description:
+ *   timer interrupt handler
+ *
+ * Input Parameters:
+ *
+ * Returned Value:
+ *
+ ****************************************************************************/
+
+static int stm32wb_timer_handler(int irq, void *context, void *arg)
+{
+  struct stm32wb_lowerhalf_s *lower = (struct stm32wb_lowerhalf_s *)arg;
+  uint32_t next_interval_us = 0;
+
+  STM32WB_TIM_ACKINT(lower->tim, GTIM_DIER_UIE);
+
+  if (lower->callback(&next_interval_us, lower->arg))
+    {
+      if (next_interval_us > 0)
+        {
+          STM32WB_TIM_SETPERIOD(lower->tim, next_interval_us);
+        }
+    }
+  else
+    {
+      stm32wb_stop((struct timer_lowerhalf_s *)lower);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_start
+ *
+ * Description:
+ *   Start the timer, resetting the time to the current timeout,
+ *
+ * Input Parameters:
+ *   lower - A pointer the publicly visible representation of the
+ *           "lower-half" driver state structure.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int stm32wb_start(struct timer_lowerhalf_s *lower)
+{
+  struct stm32wb_lowerhalf_s *priv = (struct stm32wb_lowerhalf_s *)lower;
+
+  if (!priv->started)
+    {
+      STM32WB_TIM_SETMODE(priv->tim, STM32WB_TIM_MODE_UP);
+
+      if (priv->callback != NULL)
+        {
+          STM32WB_TIM_SETISR(priv->tim, stm32wb_timer_handler, priv, 0);
+          STM32WB_TIM_ENABLEINT(priv->tim, GTIM_DIER_UIE);
+        }
+
+      priv->started = true;
+      return OK;
+    }
+
+  /* Return EBUSY to indicate that the timer was already running */
+
+  return -EBUSY;
+}
+
+/****************************************************************************
+ * Name: stm32wb_stop
+ *
+ * Description:
+ *   Stop the timer
+ *
+ * Input Parameters:
+ *   lower - A pointer the publicly visible representation of the
+ *           "lower-half" driver state structure.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int stm32wb_stop(struct timer_lowerhalf_s *lower)
+{
+  struct stm32wb_lowerhalf_s *priv = (struct stm32wb_lowerhalf_s *)lower;
+
+  if (priv->started)
+    {
+      STM32WB_TIM_SETMODE(priv->tim, STM32WB_TIM_MODE_DISABLED);
+      STM32WB_TIM_DISABLEINT(priv->tim, GTIM_DIER_UIE);
+      STM32WB_TIM_SETISR(priv->tim, NULL, NULL, 0);
+      priv->started = false;
+      return OK;
+    }
+
+  /* Return ENODEV to indicate that the timer was not running */
+
+  return -ENODEV;
+}
+
+/****************************************************************************
+ * Name: stm32wb_getstatus
+ *
+ * Description:
+ *   get timer status
+ *
+ * Input Parameters:
+ *   lower  - A pointer the publicly visible representation of the "lower-
+ *            half" driver state structure.
+ *   status - The location to return the status information.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int stm32wb_getstatus(struct timer_lowerhalf_s *lower,
+                             struct timer_status_s *status)
+{
+  struct stm32wb_lowerhalf_s *priv = (struct stm32wb_lowerhalf_s *)lower;
+  uint64_t maxtimeout;
+  uint32_t timeout;
+  uint32_t clock;
+  uint32_t period;
+  uint32_t clock_factor;
+
+  DEBUGASSERT(priv);
+
+  /* Return the status bit */
+
+  status->flags = 0;
+  if (priv->started)
+    {
+      status->flags |= TCFLAGS_ACTIVE;
+    }
+
+  if (priv->callback)
+    {
+      status->flags |= TCFLAGS_HANDLER;
+    }
+
+  /* Get timeout */
+
+  maxtimeout = (1 << priv->resolution) - 1;
+  clock      = STM32WB_TIM_GETCLOCK(priv->tim);
+  period     = STM32WB_TIM_GETPERIOD(priv->tim);
+
+  if (clock == 1000000)
+    {
+      timeout = period;
+    }
+  else
+    {
+      timeout = (maxtimeout * 1000000) / clock;
+    }
+
+  status->timeout = timeout;
+
+  /* Get the time remaining until the timer expires (in microseconds) */
+
+  clock_factor     = (clock == 1000000) ? 1 : (clock / 1000000);

Review Comment:
   just a note:
   seems like `clock_factor = clock / 1000000;` would give same effect



##########
arch/arm/src/stm32wb/stm32wb_tickless.c:
##########
@@ -0,0 +1,837 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_tickless.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Tickless OS Support.
+ *
+ * When CONFIG_SCHED_TICKLESS is enabled, all support for timer interrupts
+ * is suppressed and the platform specific code is expected to provide the
+ * following custom functions.
+ *
+ *   void up_timer_initialize(void): Initializes the timer facilities.
+ *     Called early in the initialization sequence (by up_initialize()).
+ *   int up_timer_gettime(struct timespec *ts):  Returns the current
+ *     time from the platform specific time source.
+ *   int up_timer_cancel(void):  Cancels the interval timer.
+ *   int up_timer_start(const struct timespec *ts): Start (or re-starts)
+ *     the interval timer.
+ *
+ * The RTOS will provide the following interfaces for use by the platform-
+ * specific interval timer implementation:
+ *
+ *   void nxsched_timer_expiration(void):  Called by the platform-specific
+ *     logic when the interval timer expires.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * STM32WB Timer Usage
+ *
+ * This implementation uses one timer:  A free running timer to provide
+ * the current time and a capture/compare channel for timed-events.
+ *
+ * There are two interrupts generated from our timer, the overflow interrupt
+ * which drives the timing handler and the capture/compare interrupt which
+ * drives the interval handler.  There are some low level timer control
+ * functions implemented here because the API of stm32wb_tim.c does not
+ * provide adequate control over capture/compare interrupts.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <nuttx/arch.h>
+#include <debug.h>
+
+#include "arm_internal.h"
+#include "stm32wb_tim.h"
+
+#ifdef CONFIG_SCHED_TICKLESS
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Only TIM2 is a 32-bit timer */
+
+#undef HAVE_32BIT_TICKLESS
+
+#ifdef CONFIG_STM32WB_TICKLESS_TIMER
+#  if CONFIG_STM32WB_TICKLESS_TIMER == 2
+#    define HAVE_32BIT_TICKLESS 1
+#  endif
+#else
+#  error "STM32WB_TICKLESS_TIMER must be defined for tickless configuration"
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_tickless_s
+{
+  uint8_t timer;                     /* The timer/counter in use */
+  uint8_t channel;                   /* The timer channel to use
+                                      * for intervals */
+  struct stm32wb_tim_dev_s *tch;     /* Pointer returned by
+                                      * stm32wb_tim_init() */
+  uint32_t frequency;
+  uint32_t overflow;                 /* Timer counter overflow */
+  volatile bool pending;             /* True: pending task */
+  uint32_t period;                   /* Interval period */
+  uint32_t base;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct stm32wb_tickless_s g_tickless;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_getreg16
+ *
+ * Description:
+ *   Get a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline uint16_t stm32wb_getreg16(uint8_t offset)
+{
+  return getreg16(g_tickless.base + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_putreg16
+ *
+ * Description:
+ *   Put a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_putreg16(uint8_t offset, uint16_t value)
+{
+  putreg16(value, g_tickless.base + offset);
+}
+
+/****************************************************************************
+ * Name: stm32wb_modifyreg16
+ *
+ * Description:
+ *   Modify a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_modifyreg16(uint8_t offset, uint16_t clearbits,
+                                     uint16_t setbits)
+{
+  modifyreg16(g_tickless.base + offset, clearbits, setbits);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_enableint
+ ****************************************************************************/
+
+static inline void stm32wb_tickless_enableint(int channel)
+{
+  stm32wb_modifyreg16(STM32WB_TIM_DIER_OFFSET, 0, 1 << channel);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_disableint
+ ****************************************************************************/
+
+static inline void stm32wb_tickless_disableint(int channel)
+{
+  stm32wb_modifyreg16(STM32WB_TIM_DIER_OFFSET, 1 << channel, 0);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_ackint
+ ****************************************************************************/
+
+static inline void stm32wb_tickless_ackint(int channel)
+{
+  stm32wb_putreg16(STM32WB_TIM_SR_OFFSET, ~(1 << channel));
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_getint
+ ****************************************************************************/
+
+static inline uint16_t stm32wb_tickless_getint(void)
+{
+  return stm32wb_getreg16(STM32WB_TIM_SR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_setchannel
+ ****************************************************************************/
+
+static int stm32wb_tickless_setchannel(uint8_t channel)
+{
+  uint16_t ccmr_orig = 0;
+  uint16_t ccmr_val = 0;
+  uint16_t ccer_val;
+  uint8_t ccmr_offset = STM32WB_TIM_CCMR1_OFFSET;
+
+  /* Further we use range as 0..3; if channel=0 it will also overflow here */
+
+  if (--channel > 3)
+    {
+      return -EINVAL;
+    }
+
+  /* Assume that channel is disabled and polarity is active high */
+
+  ccer_val = stm32wb_getreg16(STM32WB_TIM_CCER_OFFSET);
+  ccer_val &= ~(GTIM_CCER_CCXE(channel) | GTIM_CCER_CCXP(channel));
+
+  /* Frozen mode because we don't want to change the GPIO, preload register
+   * disabled.
+   */
+
+  ccmr_val = GTIM_CCMR_OCXM_FRZN(channel);
+
+  /* Set polarity */
+
+  ccer_val |= GTIM_CCER_CCXP(channel);
+
+  if (channel > 1)
+    {
+      ccmr_offset = STM32WB_TIM_CCMR2_OFFSET;
+    }
+
+  ccmr_orig  = stm32wb_getreg16(ccmr_offset);
+  ccmr_orig &= ~(GTIM_CCMR_OCXM_MASK(channel) | GTIM_CCMR_OCXPE(channel));
+  ccmr_orig |= ccmr_val;
+  stm32wb_putreg16(ccmr_offset, ccmr_orig);
+  stm32wb_putreg16(STM32WB_TIM_CCER_OFFSET, ccer_val);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_interval_handler
+ *
+ * Description:
+ *   Called when the timer counter matches the compare register
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called early in the initialization sequence before any special
+ *   concurrency protections are required.
+ *
+ ****************************************************************************/
+
+static void stm32wb_interval_handler(void)
+{
+  tmrinfo("Expired...\n");
+
+  /* Disable the compare interrupt now. */
+
+  stm32wb_tickless_disableint(g_tickless.channel);
+  stm32wb_tickless_ackint(g_tickless.channel);
+
+  g_tickless.pending = false;
+
+  nxsched_timer_expiration();
+}
+
+/****************************************************************************
+ * Name: stm32wb_timing_handler
+ *
+ * Description:
+ *   Timer interrupt callback.  When the freerun timer counter overflows,
+ *   this interrupt will occur.  We will just increment an overflow count.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void stm32wb_timing_handler(void)
+{
+  g_tickless.overflow++;
+
+  STM32WB_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF);
+}
+
+/****************************************************************************
+ * Name: stm32wb_tickless_handler
+ *
+ * Description:
+ *   Generic interrupt handler for this timer.  It checks the source of the
+ *   interrupt and fires the appropriate handler.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int stm32wb_tickless_handler(int irq, void *context, void *arg)
+{
+  int interrupt_flags = stm32wb_tickless_getint();
+
+  if (interrupt_flags & GTIM_SR_UIF)
+    {
+      stm32wb_timing_handler();
+    }
+
+  if (interrupt_flags & (1 << g_tickless.channel))
+    {
+      stm32wb_interval_handler();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_timer_initialize
+ *
+ * Description:
+ *   Initializes all platform-specific timer facilities.  This function is
+ *   called early in the initialization sequence by up_initialize().
+ *   On return, the current up-time should be available from
+ *   up_timer_gettime() and the interval timer is ready for use (but not
+ *   actively timing.
+ *
+ *   Provided by platform-specific code and called from the architecture-
+ *   specific logic.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called early in the initialization sequence before any special
+ *   concurrency protections are required.
+ *
+ ****************************************************************************/
+
+void up_timer_initialize(void)
+{
+  switch (CONFIG_STM32WB_TICKLESS_TIMER)
+    {
+#ifdef CONFIG_STM32WB_TIM1
+      case 1:
+        g_tickless.base = STM32WB_TIM1_BASE;
+        break;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM2
+      case 2:
+        g_tickless.base = STM32WB_TIM2_BASE;
+        break;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM16
+      case 16:
+        g_tickless.base = STM32WB_TIM16_BASE;
+        break;
+#endif
+
+#ifdef CONFIG_STM32WB_TIM17
+      case 17:
+        g_tickless.base = STM32WB_TIM17_BASE;
+        break;
+#endif
+
+      default:
+        DEBUGASSERT(0);
+    }
+
+  /* Get the TC frequency that corresponds to the requested resolution */
+
+  g_tickless.frequency = USEC_PER_SEC / (uint32_t)CONFIG_USEC_PER_TICK;
+  g_tickless.timer     = CONFIG_STM32WB_TICKLESS_TIMER;
+  g_tickless.channel   = CONFIG_STM32WB_TICKLESS_CHANNEL;
+  g_tickless.pending   = false;
+  g_tickless.period    = 0;
+  g_tickless.overflow  = 0;
+
+  tmrinfo("timer=%d channel=%d frequency=%lu Hz\n",
+           g_tickless.timer, g_tickless.channel, g_tickless.frequency);
+
+  g_tickless.tch = stm32wb_tim_init(g_tickless.timer);
+  if (!g_tickless.tch)
+    {
+      tmrerr("ERROR: Failed to allocate TIM%d\n", g_tickless.timer);
+      DEBUGASSERT(0);
+    }
+
+  STM32WB_TIM_SETCLOCK(g_tickless.tch, g_tickless.frequency);
+
+  /* Set up to receive the callback when the counter overflow occurs */
+
+  STM32WB_TIM_SETISR(g_tickless.tch, stm32wb_tickless_handler, NULL, 0);
+
+  /* Initialize interval to zero */
+
+  STM32WB_TIM_SETCOMPARE(g_tickless.tch, g_tickless.channel, 0);
+
+  /* Setup compare channel for the interval timing */
+
+  stm32wb_tickless_setchannel(g_tickless.channel);
+
+  /* Set timer period */
+
+#ifdef HAVE_32BIT_TICKLESS
+  STM32WB_TIM_SETPERIOD(g_tickless.tch, UINT32_MAX);
+#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP
+  g_oneshot_maxticks = UINT32_MAX;
+#endif
+#else
+  STM32WB_TIM_SETPERIOD(g_tickless.tch, UINT16_MAX);
+#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP
+  g_oneshot_maxticks = UINT16_MAX;
+#endif
+#endif
+
+  /* Initialize the counter */
+
+  STM32WB_TIM_SETMODE(g_tickless.tch, STM32WB_TIM_MODE_UP);
+
+  /* Start the timer */
+
+  STM32WB_TIM_ACKINT(g_tickless.tch, ~0);
+  STM32WB_TIM_ENABLEINT(g_tickless.tch, GTIM_DIER_UIE);
+}
+
+/****************************************************************************
+ * Name: up_timer_gettime
+ *
+ * Description:
+ *   Return the elapsed time since power-up (or, more correctly, since
+ *   up_timer_initialize() was called).  This function is functionally
+ *   equivalent to:
+ *
+ *      int clock_gettime(clockid_t clockid, struct timespec *ts);
+ *
+ *   when clockid is CLOCK_MONOTONIC.
+ *
+ *   This function provides the basis for reporting the current time and
+ *   also is used to eliminate error build-up from small errors in interval
+ *   time calculations.
+ *
+ *   Provided by platform-specific code and called from the RTOS base code.
+ *
+ * Input Parameters:
+ *   ts - Provides the location in which to return the up-time.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; a negated errno value is returned on
+ *   any failure.
+ *
+ * Assumptions:
+ *   Called from the normal tasking context.  The implementation must
+ *   provide whatever mutual exclusion is necessary for correct operation.
+ *   This can include disabling interrupts in order to assure atomic register
+ *   operations.
+ *
+ ****************************************************************************/
+
+int up_timer_gettime(struct timespec *ts)
+{
+  uint64_t usec;
+  uint32_t counter;
+  uint32_t verify;
+  uint32_t overflow;
+  uint32_t sec;
+  int pending;
+  irqstate_t flags;
+
+  DEBUGASSERT(g_tickless.tch && ts);
+
+  /* Temporarily disable the overflow counter.  NOTE that we have to be
+   * careful here because  stm32wb_tc_getpending() will reset the pending
+   * interrupt status.  If we do not handle the overflow here then, it will
+   * be lost.
+   */
+
+  flags    = enter_critical_section();
+
+  overflow = g_tickless.overflow;
+  counter  = STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+  pending  = STM32WB_TIM_CHECKINT(g_tickless.tch, GTIM_SR_UIF);
+  verify   = STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+
+  /* If an interrupt was pending before we re-enabled interrupts,
+   * then the overflow needs to be incremented.
+   */
+
+  if (pending)
+    {
+      STM32WB_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF);
+
+      /* Increment the overflow count and use the value of the
+       * guaranteed to be AFTER the overflow occurred.
+       */
+
+      overflow++;
+      counter = verify;
+
+      /* Update tickless overflow counter. */
+
+      g_tickless.overflow = overflow;
+    }
+
+  leave_critical_section(flags);
+
+  tmrinfo("counter=%lu (%lu) overflow=%lu, pending=%i\n",
+         (unsigned long)counter,  (unsigned long)verify,
+         (unsigned long)overflow, pending);
+  tmrinfo("frequency=%lu\n", g_tickless.frequency);
+
+  /* Convert the whole thing to units of microseconds.
+   *
+   *   frequency = ticks / second
+   *   seconds   = ticks * frequency
+   *   usecs     = (ticks * USEC_PER_SEC) / frequency;
+   */
+#ifdef HAVE_32BIT_TICKLESS
+  usec = ((((uint64_t)overflow << 32) + (uint64_t)counter) * USEC_PER_SEC) /
+         g_tickless.frequency;
+#else
+  usec = ((((uint64_t)overflow << 16) + (uint64_t)counter) * USEC_PER_SEC) /
+         g_tickless.frequency;
+#endif
+
+  /* And return the value of the timer */
+
+  sec         = (uint32_t)(usec / USEC_PER_SEC);
+  ts->tv_sec  = sec;
+  ts->tv_nsec = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC;
+
+  tmrinfo("usec=%llu ts=(%lu, %lu)\n",
+          usec, (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec);
+
+  return OK;
+}
+
+#ifdef CONFIG_CLOCK_TIMEKEEPING
+
+/****************************************************************************
+ * Name: up_timer_getcounter
+ *
+ * Description:
+ *   To be provided
+ *
+ * Input Parameters:
+ *   cycles - 64-bit return value
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+int up_timer_getcounter(uint64_t *cycles)
+{
+  *cycles = (uint64_t)STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: up_timer_getmask
+ *
+ * Description:
+ *   To be provided
+ *
+ * Input Parameters:
+ *   mask - Location to return the 64-bit mask
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void up_timer_getmask(uint64_t *mask)
+{
+  DEBUGASSERT(mask != NULL);
+#ifdef HAVE_32BIT_TICKLESS
+  *mask = UINT32_MAX;
+#else
+  *mask = UINT16_MAX;
+#endif
+}
+
+#endif /* CONFIG_CLOCK_TIMEKEEPING */
+
+/****************************************************************************
+ * Name: up_timer_cancel
+ *
+ * Description:
+ *   Cancel the interval timer and return the time remaining on the timer.
+ *   These two steps need to be as nearly atomic as possible.
+ *   nxsched_timer_expiration() will not be called unless the timer is
+ *   restarted with up_timer_start().
+ *
+ *   If, as a race condition, the timer has already expired when this
+ *   function is called, then that pending interrupt must be cleared so
+ *   that up_timer_start() and the remaining time of zero should be
+ *   returned.
+ *
+ *   NOTE: This function may execute at a high rate with no timer running (as
+ *   when pre-emption is enabled and disabled).
+ *
+ *   Provided by platform-specific code and called from the RTOS base code.
+ *
+ * Input Parameters:
+ *   ts - Location to return the remaining time.  Zero should be returned
+ *        if the timer is not active.  ts may be zero in which case the
+ *        time remaining is not returned.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success.  A call to up_timer_cancel() when
+ *   the timer is not active should also return success; a negated errno
+ *   value is returned on any failure.
+ *
+ * Assumptions:
+ *   May be called from interrupt level handling or from the normal tasking
+ *   level.  Interrupts may need to be disabled internally to assure
+ *   non-reentrancy.
+ *
+ ****************************************************************************/
+
+int up_timer_cancel(struct timespec *ts)
+{
+  irqstate_t flags;
+  uint64_t usec;
+  uint64_t sec;
+  uint64_t nsec;
+  uint32_t count;
+  uint32_t period;
+
+  /* Was the timer running? */
+
+  flags = enter_critical_section();
+  if (!g_tickless.pending)
+    {
+      /* No.. Just return zero timer remaining and successful cancellation.
+       * This function may execute at a high rate with no timer running
+       * (as when pre-emption is enabled and disabled).
+       */
+
+      if (ts)
+        {
+          ts->tv_sec  = 0;
+          ts->tv_nsec = 0;
+        }
+
+      leave_critical_section(flags);
+      return OK;
+    }
+
+  /* Yes.. Get the timer counter and period registers and disable the compare
+   * interrupt.
+   */
+
+  tmrinfo("Cancelling...\n");
+
+  /* Disable the interrupt. */
+
+  stm32wb_tickless_disableint(g_tickless.channel);
+
+  count  = STM32WB_TIM_GETCOUNTER(g_tickless.tch);
+  period = g_tickless.period;
+
+  g_tickless.pending = false;
+  leave_critical_section(flags);
+
+  /* Did the caller provide us with a location to return the time
+   * remaining?
+   */
+
+  if (ts != NULL)
+    {
+      /* Yes.. then calculate and return the time remaining on the
+       * oneshot timer.
+       */
+
+      tmrinfo("period=%lu count=%lu\n",
+             (unsigned long)period, (unsigned long)count);

Review Comment:
   ```suggestion
         tmrinfo("period=%lu count=%lu\n",
                 (unsigned long)period, (unsigned long)count);
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] pkarashchenko commented on a diff in pull request #6422: New stm32wb chip family

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on code in PR #6422:
URL: https://github.com/apache/incubator-nuttx/pull/6422#discussion_r909297778


##########
arch/arm/src/stm32wb/hardware/stm32wb_dmamux.h:
##########
@@ -0,0 +1,284 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_dmamux.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_DMAMUX_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_DMAMUX_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "chip.h"
+#include "stm32wb_dma.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DMAMUX1 0
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_DMAMUX_CXCR_OFFSET(x)  (0x0000 + 0x0004*(x)) /* DMAMUX1 request line multiplexer channel x configuration register */
+#define STM32WB_DMAMUX_C0CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(0)
+#define STM32WB_DMAMUX_C1CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(1)
+#define STM32WB_DMAMUX_C2CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(2)
+#define STM32WB_DMAMUX_C3CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(3)
+#define STM32WB_DMAMUX_C4CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(4)
+#define STM32WB_DMAMUX_C5CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(5)
+#define STM32WB_DMAMUX_C6CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(6)
+#define STM32WB_DMAMUX_C7CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(7)
+#define STM32WB_DMAMUX_C8CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(8)
+#define STM32WB_DMAMUX_C9CR_OFFSET     STM32WB_DMAMUX_CXCR_OFFSET(9)
+#define STM32WB_DMAMUX_C10CR_OFFSET    STM32WB_DMAMUX_CXCR_OFFSET(10)
+#define STM32WB_DMAMUX_C11CR_OFFSET    STM32WB_DMAMUX_CXCR_OFFSET(11)
+#define STM32WB_DMAMUX_C12CR_OFFSET    STM32WB_DMAMUX_CXCR_OFFSET(12)
+#define STM32WB_DMAMUX_C13CR_OFFSET    STM32WB_DMAMUX_CXCR_OFFSET(13)
+                                              /* 0x034-0x07C: Reserved */
+#define STM32WB_DMAMUX_CSR_OFFSET      0x0080 /* DMAMUX1 request line multiplexer interrupt channel status register */
+#define STM32WB_DMAMUX_CFR_OFFSET      0x0084 /* DMAMUX1 request line multiplexer interrupt clear flag register */
+                                              /* 0x088-0x0FC: Reserved */
+
+#define STM32WB_DMAMUX_RGXCR_OFFSET(x) (0x0100+0x004*(x)) /* DMAMUX1 request generator channel x configuration register */

Review Comment:
   ```suggestion
   #define STM32WB_DMAMUX_RGXCR_OFFSET(x) (0x0100 + 0x004 * (x)) /* DMAMUX1 request generator channel x configuration register */
   ```



##########
arch/arm/src/stm32wb/stm32wb_dma.h:
##########
@@ -0,0 +1,281 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_dma.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_STM32WB_DMA_H
+#define __ARCH_ARM_SRC_STM32WB_STM32WB_DMA_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+
+#include "chip.h"
+
+#include "hardware/stm32wb_dma.h"
+#include "hardware/stm32wb_dmamux.h"
+
+/* These definitions provide the bit encoding of the 'status' parameter
+ * passed to the DMA callback function (see dma_callback_t).
+ */
+
+#  define DMA_STATUS_TEIF         DMA_CHAN_TEIF_BIT     /* Channel Transfer Error */
+#  define DMA_STATUS_HTIF         DMA_CHAN_HTIF_BIT     /* Channel Half Transfer */
+#  define DMA_STATUS_TCIF         DMA_CHAN_TCIF_BIT     /* Channel Transfer Complete */
+
+#define DMA_STATUS_ERROR          (DMA_STATUS_TEIF)
+#define DMA_STATUS_SUCCESS        (DMA_STATUS_TCIF|DMA_STATUS_HTIF)

Review Comment:
   ```suggestion
   #define DMA_STATUS_SUCCESS        (DMA_STATUS_TCIF | DMA_STATUS_HTIF)
   ```



##########
arch/arm/src/stm32wb/stm32wb_spi.c:
##########
@@ -0,0 +1,1819 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * The external functions, stm32wb_spi1/2select and stm32wb_spi1/2status
+ * must be provided by board-specific logic.  They are implementations of the
+ * select and status methods of the SPI interface defined by struct spi_ops_s
+ * (see include/nuttx/spi/spi.h).  All other methods (including
+ * stm32wb_spibus_initialize()) are provided by common STM32 logic.  To use
+ * this common SPI logic on your board:
+ *
+ *   1. Provide logic in stm32wb_board_initialize() to configure SPI chip
+ *      select pins.
+ *   2. Provide stm32wb_spi1/2select() and stm32wb_spi1/2status()
+ *      functions in your board-specific logic.  These functions will perform
+ *      chip selection and status operations using GPIOs in the way your
+ *      board is configured.
+ *   3. Add a calls to stm32wb_spibus_initialize() in your low level
+ *      application initialization logic
+ *   4. The handle returned by stm32wb_spibus_initialize() may then be used
+ *      to bind the SPI driver to higher level logic (e.g., calling
+ *      mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ *      the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+/* This driver is ported from the stm32 one, which only supports 8 and 16
+ * bits transfers. The STM32WB family supports frame size from 4 to 16 bits,
+ * but we do not support that yet. For the moment, we replace uses of the
+ * CR1_DFF bit with a check of the CR2_DS[0..3] bits. If the value is
+ * SPI_CR2_DS_16BIT it means 16 bits, else 8 bits.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/power/pm.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_spi.h"
+
+#include <arch/board/board.h>
+
+#if defined(CONFIG_STM32WB_SPI1) || defined(CONFIG_STM32WB_SPI2)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* SPI interrupts */
+
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+#  error "Interrupt driven SPI not yet supported"
+#endif
+
+/* Can't have both interrupt driven SPI and SPI DMA */
+
+#if defined(CONFIG_STM32WB_SPI_INTERRUPTS) && defined(CONFIG_STM32WB_SPI_DMA)
+#  error "Cannot enable both interrupt mode and DMA mode for SPI"
+#endif
+
+/* SPI DMA priority */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+
+#  if defined(CONFIG_SPI_DMAPRIO)
+#    define SPI_DMA_PRIO  CONFIG_SPI_DMAPRIO
+#  else
+#    define SPI_DMA_PRIO  DMA_CCR_PRIMED
+#  endif
+
+#  if (SPI_DMA_PRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_SPI_DMAPRIO"
+#  endif
+
+#endif
+
+/* DMA channel configuration */
+
+#define SPI_RXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC            )
+#define SPI_RXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC            )
+#define SPI_RXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS                         )
+#define SPI_RXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS                          )
+#define SPI_TXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS             |DMA_CCR_DIR)
+#define SPI_TXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS              |DMA_CCR_DIR)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_spidev_s
+{
+  struct spi_dev_s spidev;       /* Externally visible part of the SPI interface */
+  uint32_t         spibase;      /* SPIn base address */
+  uint32_t         spiclock;     /* Clocking for the SPI module */
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  uint8_t          spiirq;       /* SPI IRQ number */
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  volatile uint8_t rxresult;     /* Result of the RX DMA */
+  volatile uint8_t txresult;     /* Result of the RX DMA */
+#ifdef CONFIG_SPI_TRIGGER
+  bool             defertrig;    /* Flag indicating that trigger should be deferred */
+  bool             trigarmed;    /* Flag indicating that the trigger is armed */
+#endif
+  uint16_t         rxch;         /* The RX DMA channel number */
+  uint16_t         txch;         /* The TX DMA channel number */
+  DMA_HANDLE       rxdma;        /* DMA channel handle for RX transfers */
+  DMA_HANDLE       txdma;        /* DMA channel handle for TX transfers */
+  sem_t            rxsem;        /* Wait for RX DMA to complete */
+  sem_t            txsem;        /* Wait for TX DMA to complete */
+  uint32_t         txccr;        /* DMA control register for TX transfers */
+  uint32_t         rxccr;        /* DMA control register for RX transfers */
+#endif
+  bool             initialized;  /* Has SPI interface been initialized */
+  sem_t            exclsem;      /* Held while chip is selected for mutual exclusion */
+  uint32_t         frequency;    /* Requested clock frequency */
+  uint32_t         actual;       /* Actual clock frequency */
+  uint8_t          nbits;        /* Width of word in bits (4 through 16) */
+  uint8_t          mode;         /* Mode 0,1,2,3 */
+#ifdef CONFIG_PM
+  struct pm_callback_s pm_cb;    /* PM callbacks */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset);
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value);
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv);
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t byte);
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv);
+
+/* DMA support */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int         spi_dmarxwait(struct stm32wb_spidev_s *priv);
+static int         spi_dmatxwait(struct stm32wb_spidev_s *priv);
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv);
+static void        spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                                  void *rxbuffer, void *rxdummy,
+                                  size_t nwords);
+static void        spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                                  const void *txbuffer,
+                                  const void *txdummy, size_t nwords);
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv);
+#endif
+
+/* SPI methods */
+
+static int         spi_lock(struct spi_dev_s *dev, bool lock);
+static uint32_t    spi_setfrequency(struct spi_dev_s *dev,
+                                    uint32_t frequency);
+static void        spi_setmode(struct spi_dev_s *dev,
+                               enum spi_mode_e mode);
+static void        spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int         spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t    spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void        spi_exchange(struct spi_dev_s *dev,
+                                const void *txbuffer,
+                                void *rxbuffer, size_t nwords);
+#ifdef CONFIG_SPI_TRIGGER
+static int         spi_trigger(struct spi_dev_s *dev);
+#endif
+#ifndef CONFIG_SPI_EXCHANGE
+static void        spi_sndblock(struct spi_dev_s *dev,
+                                const void *txbuffer, size_t nwords);
+static void        spi_recvblock(struct spi_dev_s *dev,
+                                 void *rxbuffer, size_t nwords);
+#endif
+
+/* Initialization */
+
+static void        spi_bus_initialize(struct stm32wb_spidev_s *priv);
+
+/* PM interface */
+
+#ifdef CONFIG_PM
+static int         spi_pm_prepare(struct pm_callback_s *cb, int domain,
+                                  enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI1
+static const struct spi_ops_s g_spi1ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi1select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi1status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi1cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi1register,  /* Provided externally */
+#else
+  .registercallback  = 0,                   /* Not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi1dev =
+{
+  .spidev   =
+    {
+      &g_spi1ops
+    },
+  .spibase  = STM32WB_SPI1_BASE,
+  .spiclock = STM32WB_PCLK2_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI1,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  /* lines must be configured in board.h */
+
+  .rxch     = DMAMAP_SPI1_RX,
+  .txch     = DMAMAP_SPI1_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+#ifdef CONFIG_STM32WB_SPI2
+static const struct spi_ops_s g_spi2ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi2select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi2status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi2cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi2register,  /* provided externally */
+#else
+  .registercallback  = 0,  /* not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi2dev =
+{
+  .spidev   =
+    {
+      &g_spi2ops
+    },
+  .spibase  = STM32WB_SPI2_BASE,
+  .spiclock = STM32WB_PCLK1_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI2,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  .rxch     = DMACHAN_SPI2_RX,
+  .txch     = DMACHAN_SPI2_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spi_getreg
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg16(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg
+ *
+ * Description:
+ *   Write a 16-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 16-bit value to be written
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value)
+{
+  putreg16(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_getreg8
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 8-bit register
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_getreg8(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg8(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg8
+ *
+ * Description:
+ *   Write a 8-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 8-bit value to be written
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg8(struct stm32wb_spidev_s *priv,
+                               uint8_t offset, uint8_t value)
+{
+  putreg8(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_readword
+ *
+ * Description:
+ *   Read one word (TWO bytes!) from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Word as read
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_readbyte
+ *
+ * Description:
+ *   Read one byte from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Byte as read
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_readbyte(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg8(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_writeword
+ *
+ * Description:
+ *   Write one 16-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Word to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t word)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg(priv, STM32WB_SPI_DR_OFFSET, word);
+}
+
+/****************************************************************************
+ * Name: spi_writebyte
+ *
+ * Description:
+ *   Write one 8-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Byte to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writebyte(struct stm32wb_spidev_s *priv,
+                                 uint8_t byte)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg8(priv, STM32WB_SPI_DR_OFFSET, byte);
+}
+
+/****************************************************************************
+ * Name: spi_16bitmode
+ *
+ * Description:
+ *   Check if the SPI is operating in 16-bit mode
+ *
+ * Input Parameters:
+ *   priv     - Device-specific state data
+ *
+ * Returned Value:
+ *   true: 16-bit mode, false: 8-bit mode
+ *
+ ****************************************************************************/
+
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv)
+{
+  return (priv->nbits > 8);
+}
+
+/****************************************************************************
+ * Name: spi_dmarxwaitw
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmarxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->rxsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->rxresult == 0 && ret == OK);

Review Comment:
   I reviewed again this part and I think you are right. My main concern was that semaphore could be signalled after waiter exists with `ECANCELED` and the next call will take semaphore with no wait because of the previous call as there is not reset of a semaphore before DMA transaction starts. But logic is a bit more tricky here. The "previous" calls are consumed by `priv->rxresult == 0` condition, so even semaphore wait returns OK we will try to take semaphore again due to other condition, so the only exit criteria from the wait loop is either `ECANCELED` or `priv->rxresult != 0` + `ret == OK`. So the checking of return value makes sense and I we will need to fix part from https://github.com/apache/incubator-nuttx/pull/6492 as well. probably I will do it in the new PR.



##########
arch/arm/src/stm32wb/hardware/stm32wb_dmamux.h:
##########
@@ -0,0 +1,284 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/hardware/stm32wb_dmamux.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_DMAMUX_H
+#define __ARCH_ARM_SRC_STM32WB_HARDWARE_STM32WB_DMAMUX_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "chip.h"
+#include "stm32wb_dma.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DMAMUX1 0
+
+/* Register Offsets *********************************************************/
+
+#define STM32WB_DMAMUX_CXCR_OFFSET(x)  (0x0000 + 0x0004*(x)) /* DMAMUX1 request line multiplexer channel x configuration register */

Review Comment:
   ```suggestion
   #define STM32WB_DMAMUX_CXCR_OFFSET(x)  (0x0000 + 0x0004 * (x)) /* DMAMUX1 request line multiplexer channel x configuration register */
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] onegray commented on pull request #6422: New chip family stm32wb (WIP)

Posted by GitBox <gi...@apache.org>.
onegray commented on PR #6422:
URL: https://github.com/apache/incubator-nuttx/pull/6422#issuecomment-1162356527

   > I think it is always a good idea to make incremental PR, a big PR like this one makes things hard to other people to review. But all contributions are welcome, if you prefer to do this way we need to adapt to your way! :-)
   
   I completely agree, just want to finish some testing.  


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] acassis commented on a diff in pull request #6422: New chip family stm32wb (WIP)

Posted by GitBox <gi...@apache.org>.
acassis commented on code in PR #6422:
URL: https://github.com/apache/incubator-nuttx/pull/6422#discussion_r899028473


##########
arch/arm/include/stm32wb/chip.h:
##########
@@ -0,0 +1,80 @@
+/************************************************************************************
+ * arch/arm/include/stm32wb/chip.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ************************************************************************************/
+
+#ifndef __ARCH_ARM_INCLUDE_STM32WB_CHIP_H
+#define __ARCH_ARM_INCLUDE_STM32WB_CHIP_H
+
+/************************************************************************************
+ * Included Files
+ ************************************************************************************/
+
+#include <nuttx/config.h>
+
+/************************************************************************************
+ * Pre-processor Definitions
+ ************************************************************************************/
+
+#if defined (CONFIG_ARCH_CHIP_STM32WB55R)  /* VFQFPN68 package, 1024Kb FLASH, 256KiB SRAM */
+#  define STM32WB_NFSMC                    0   /* No FSMC */
+#  define STM32WB_NATIM                    1   /* One advanced timers TIM1 */
+#  define STM32WB_NGTIM32                  1   /* 32-bit general timers TIM2 with DMA */
+#  define STM32WB_NGTIM16                  2   /* 16-bit general timers TIM16-17 with DMA */
+#  define STM32WB_NBTIM                    0   /* No basic timers */
+#  define STM32WB_NLPTIM                   2   /* Two low-power timers, LPTIM1-2 */
+#  define STM32WB_NGTIMNDMA                0   /* No general timers without DMA */
+#  define STM32WB_NDMA                     2   /* DMA1-2 with 7 channels each */
+#  define STM32WB_NSPI                     3   /* SPI1-2, QSPI */
+#  define STM32WB_NI2S                     1   /* SAI1 (dual channel high quality audio) */
+#  define STM32WB_NUSART                   2   /* USART1, LPUART1 */
+#  define STM32WB_NI2C                     2   /* I2C1, I2C3 */
+#  define STM32WB_NCAN                     0   /* No CAN */
+#  define STM32WB_NSDIO                    0   /* One SDIO interface */

Review Comment:
   ```suggestion
   #  define STM32WB_NSDIO                    0   /* No SDIO interface */
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] acassis commented on pull request #6422: New chip family stm32wb (WIP)

Posted by GitBox <gi...@apache.org>.
acassis commented on PR #6422:
URL: https://github.com/apache/incubator-nuttx/pull/6422#issuecomment-1161797385

   I think it is all a good idea to make incremental PR, a big PR like this one makes things hard to other people to review. But all contributions are welcome, if you prefer to do this way we need to adapt to your way! :-)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] xiaoxiang781216 merged pull request #6422: New stm32wb chip family

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 merged PR #6422:
URL: https://github.com/apache/incubator-nuttx/pull/6422


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] onegray commented on a diff in pull request #6422: New stm32wb chip family

Posted by GitBox <gi...@apache.org>.
onegray commented on code in PR #6422:
URL: https://github.com/apache/incubator-nuttx/pull/6422#discussion_r908924546


##########
arch/arm/src/stm32wb/stm32wb_spi.c:
##########
@@ -0,0 +1,1819 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_spi.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * The external functions, stm32wb_spi1/2select and stm32wb_spi1/2status
+ * must be provided by board-specific logic.  They are implementations of the
+ * select and status methods of the SPI interface defined by struct spi_ops_s
+ * (see include/nuttx/spi/spi.h).  All other methods (including
+ * stm32wb_spibus_initialize()) are provided by common STM32 logic.  To use
+ * this common SPI logic on your board:
+ *
+ *   1. Provide logic in stm32wb_board_initialize() to configure SPI chip
+ *      select pins.
+ *   2. Provide stm32wb_spi1/2select() and stm32wb_spi1/2status()
+ *      functions in your board-specific logic.  These functions will perform
+ *      chip selection and status operations using GPIOs in the way your
+ *      board is configured.
+ *   3. Add a calls to stm32wb_spibus_initialize() in your low level
+ *      application initialization logic
+ *   4. The handle returned by stm32wb_spibus_initialize() may then be used
+ *      to bind the SPI driver to higher level logic (e.g., calling
+ *      mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ *      the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+/* This driver is ported from the stm32 one, which only supports 8 and 16
+ * bits transfers. The STM32WB family supports frame size from 4 to 16 bits,
+ * but we do not support that yet. For the moment, we replace uses of the
+ * CR1_DFF bit with a check of the CR2_DS[0..3] bits. If the value is
+ * SPI_CR2_DS_16BIT it means 16 bits, else 8 bits.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/power/pm.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32wb.h"
+#include "stm32wb_gpio.h"
+#include "stm32wb_dma.h"
+#include "stm32wb_spi.h"
+
+#include <arch/board/board.h>
+
+#if defined(CONFIG_STM32WB_SPI1) || defined(CONFIG_STM32WB_SPI2)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* SPI interrupts */
+
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+#  error "Interrupt driven SPI not yet supported"
+#endif
+
+/* Can't have both interrupt driven SPI and SPI DMA */
+
+#if defined(CONFIG_STM32WB_SPI_INTERRUPTS) && defined(CONFIG_STM32WB_SPI_DMA)
+#  error "Cannot enable both interrupt mode and DMA mode for SPI"
+#endif
+
+/* SPI DMA priority */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+
+#  if defined(CONFIG_SPI_DMAPRIO)
+#    define SPI_DMA_PRIO  CONFIG_SPI_DMAPRIO
+#  else
+#    define SPI_DMA_PRIO  DMA_CCR_PRIMED
+#  endif
+
+#  if (SPI_DMA_PRIO & ~DMA_CCR_PL_MASK) != 0
+#    error "Illegal value for CONFIG_SPI_DMAPRIO"
+#  endif
+
+#endif
+
+/* DMA channel configuration */
+
+#define SPI_RXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC            )
+#define SPI_RXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC            )
+#define SPI_RXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS                         )
+#define SPI_RXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS                          )
+#define SPI_TXDMA16_CONFIG        (SPI_DMA_PRIO|DMA_CCR_MSIZE_16BITS|DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA8_CONFIG         (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS |DMA_CCR_MINC|DMA_CCR_DIR)
+#define SPI_TXDMA16NULL_CONFIG    (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_16BITS             |DMA_CCR_DIR)
+#define SPI_TXDMA8NULL_CONFIG     (SPI_DMA_PRIO|DMA_CCR_MSIZE_8BITS |DMA_CCR_PSIZE_8BITS              |DMA_CCR_DIR)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32wb_spidev_s
+{
+  struct spi_dev_s spidev;       /* Externally visible part of the SPI interface */
+  uint32_t         spibase;      /* SPIn base address */
+  uint32_t         spiclock;     /* Clocking for the SPI module */
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  uint8_t          spiirq;       /* SPI IRQ number */
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  volatile uint8_t rxresult;     /* Result of the RX DMA */
+  volatile uint8_t txresult;     /* Result of the RX DMA */
+#ifdef CONFIG_SPI_TRIGGER
+  bool             defertrig;    /* Flag indicating that trigger should be deferred */
+  bool             trigarmed;    /* Flag indicating that the trigger is armed */
+#endif
+  uint16_t         rxch;         /* The RX DMA channel number */
+  uint16_t         txch;         /* The TX DMA channel number */
+  DMA_HANDLE       rxdma;        /* DMA channel handle for RX transfers */
+  DMA_HANDLE       txdma;        /* DMA channel handle for TX transfers */
+  sem_t            rxsem;        /* Wait for RX DMA to complete */
+  sem_t            txsem;        /* Wait for TX DMA to complete */
+  uint32_t         txccr;        /* DMA control register for TX transfers */
+  uint32_t         rxccr;        /* DMA control register for RX transfers */
+#endif
+  bool             initialized;  /* Has SPI interface been initialized */
+  sem_t            exclsem;      /* Held while chip is selected for mutual exclusion */
+  uint32_t         frequency;    /* Requested clock frequency */
+  uint32_t         actual;       /* Actual clock frequency */
+  uint8_t          nbits;        /* Width of word in bits (4 through 16) */
+  uint8_t          mode;         /* Mode 0,1,2,3 */
+#ifdef CONFIG_PM
+  struct pm_callback_s pm_cb;    /* PM callbacks */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset);
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value);
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv);
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t byte);
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv);
+
+/* DMA support */
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int         spi_dmarxwait(struct stm32wb_spidev_s *priv);
+static int         spi_dmatxwait(struct stm32wb_spidev_s *priv);
+static inline void spi_dmarxwakeup(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxwakeup(struct stm32wb_spidev_s *priv);
+static void        spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr,
+                                     void *arg);
+static void        spi_dmarxsetup(struct stm32wb_spidev_s *priv,
+                                  void *rxbuffer, void *rxdummy,
+                                  size_t nwords);
+static void        spi_dmatxsetup(struct stm32wb_spidev_s *priv,
+                                  const void *txbuffer,
+                                  const void *txdummy, size_t nwords);
+static inline void spi_dmarxstart(struct stm32wb_spidev_s *priv);
+static inline void spi_dmatxstart(struct stm32wb_spidev_s *priv);
+#endif
+
+/* SPI methods */
+
+static int         spi_lock(struct spi_dev_s *dev, bool lock);
+static uint32_t    spi_setfrequency(struct spi_dev_s *dev,
+                                    uint32_t frequency);
+static void        spi_setmode(struct spi_dev_s *dev,
+                               enum spi_mode_e mode);
+static void        spi_setbits(struct spi_dev_s *dev, int nbits);
+#ifdef CONFIG_SPI_HWFEATURES
+static int         spi_hwfeatures(struct spi_dev_s *dev,
+                                  spi_hwfeatures_t features);
+#endif
+static uint32_t    spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void        spi_exchange(struct spi_dev_s *dev,
+                                const void *txbuffer,
+                                void *rxbuffer, size_t nwords);
+#ifdef CONFIG_SPI_TRIGGER
+static int         spi_trigger(struct spi_dev_s *dev);
+#endif
+#ifndef CONFIG_SPI_EXCHANGE
+static void        spi_sndblock(struct spi_dev_s *dev,
+                                const void *txbuffer, size_t nwords);
+static void        spi_recvblock(struct spi_dev_s *dev,
+                                 void *rxbuffer, size_t nwords);
+#endif
+
+/* Initialization */
+
+static void        spi_bus_initialize(struct stm32wb_spidev_s *priv);
+
+/* PM interface */
+
+#ifdef CONFIG_PM
+static int         spi_pm_prepare(struct pm_callback_s *cb, int domain,
+                                  enum pm_state_e pmstate);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI1
+static const struct spi_ops_s g_spi1ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi1select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi1status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi1cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi1register,  /* Provided externally */
+#else
+  .registercallback  = 0,                   /* Not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi1dev =
+{
+  .spidev   =
+    {
+      &g_spi1ops
+    },
+  .spibase  = STM32WB_SPI1_BASE,
+  .spiclock = STM32WB_PCLK2_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI1,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  /* lines must be configured in board.h */
+
+  .rxch     = DMAMAP_SPI1_RX,
+  .txch     = DMAMAP_SPI1_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+#ifdef CONFIG_STM32WB_SPI2
+static const struct spi_ops_s g_spi2ops =
+{
+  .lock              = spi_lock,
+  .select            = stm32wb_spi2select,
+  .setfrequency      = spi_setfrequency,
+  .setmode           = spi_setmode,
+  .setbits           = spi_setbits,
+#ifdef CONFIG_SPI_HWFEATURES
+  .hwfeatures        = spi_hwfeatures,
+#endif
+  .status            = stm32wb_spi2status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = stm32wb_spi2cmddata,
+#endif
+  .send              = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = spi_exchange,
+#else
+  .sndblock          = spi_sndblock,
+  .recvblock         = spi_recvblock,
+#endif
+#ifdef CONFIG_SPI_TRIGGER
+  .trigger           = spi_trigger,
+#endif
+#ifdef CONFIG_SPI_CALLBACK
+  .registercallback  = stm32wb_spi2register,  /* provided externally */
+#else
+  .registercallback  = 0,  /* not implemented */
+#endif
+};
+
+static struct stm32wb_spidev_s g_spi2dev =
+{
+  .spidev   =
+    {
+      &g_spi2ops
+    },
+  .spibase  = STM32WB_SPI2_BASE,
+  .spiclock = STM32WB_PCLK1_FREQUENCY,
+#ifdef CONFIG_STM32WB_SPI_INTERRUPTS
+  .spiirq   = STM32WB_IRQ_SPI2,
+#endif
+#ifdef CONFIG_STM32WB_SPI_DMA
+  .rxch     = DMACHAN_SPI2_RX,
+  .txch     = DMACHAN_SPI2_TX,
+#endif
+#ifdef CONFIG_PM
+  .pm_cb.prepare = spi_pm_prepare,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spi_getreg
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_getreg(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg16(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg
+ *
+ * Description:
+ *   Write a 16-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 16-bit value to be written
+ *
+ * Returned Value:
+ *   The contents of the 16-bit register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg(struct stm32wb_spidev_s *priv,
+                              uint8_t offset, uint16_t value)
+{
+  putreg16(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_getreg8
+ *
+ * Description:
+ *   Get the contents of the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *
+ * Returned Value:
+ *   The contents of the 8-bit register
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_getreg8(struct stm32wb_spidev_s *priv,
+                                  uint8_t offset)
+{
+  return getreg8(priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_putreg8
+ *
+ * Description:
+ *   Write a 8-bit value to the SPI register at offset
+ *
+ * Input Parameters:
+ *   priv   - private SPI device structure
+ *   offset - offset to the register of interest
+ *   value  - the 8-bit value to be written
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg8(struct stm32wb_spidev_s *priv,
+                               uint8_t offset, uint8_t value)
+{
+  putreg8(value, priv->spibase + offset);
+}
+
+/****************************************************************************
+ * Name: spi_readword
+ *
+ * Description:
+ *   Read one word (TWO bytes!) from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Word as read
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_readword(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_readbyte
+ *
+ * Description:
+ *   Read one byte from SPI
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *
+ * Returned Value:
+ *   Byte as read
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_readbyte(struct stm32wb_spidev_s *priv)
+{
+  /* Wait until the receive buffer is not empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0);
+
+  /* Then return the received byte */
+
+  return spi_getreg8(priv, STM32WB_SPI_DR_OFFSET);
+}
+
+/****************************************************************************
+ * Name: spi_writeword
+ *
+ * Description:
+ *   Write one 16-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Word to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writeword(struct stm32wb_spidev_s *priv,
+                                 uint16_t word)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg(priv, STM32WB_SPI_DR_OFFSET, word);
+}
+
+/****************************************************************************
+ * Name: spi_writebyte
+ *
+ * Description:
+ *   Write one 8-bit frame to the SPI FIFO
+ *
+ * Input Parameters:
+ *   priv - Device-specific state data
+ *   byte - Byte to send
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void spi_writebyte(struct stm32wb_spidev_s *priv,
+                                 uint8_t byte)
+{
+  /* Wait until the transmit buffer is empty */
+
+  while ((spi_getreg(priv, STM32WB_SPI_SR_OFFSET) & SPI_SR_TXE) == 0);
+
+  /* Then send the byte */
+
+  spi_putreg8(priv, STM32WB_SPI_DR_OFFSET, byte);
+}
+
+/****************************************************************************
+ * Name: spi_16bitmode
+ *
+ * Description:
+ *   Check if the SPI is operating in 16-bit mode
+ *
+ * Input Parameters:
+ *   priv     - Device-specific state data
+ *
+ * Returned Value:
+ *   true: 16-bit mode, false: 8-bit mode
+ *
+ ****************************************************************************/
+
+static inline bool spi_16bitmode(struct stm32wb_spidev_s *priv)
+{
+  return (priv->nbits > 8);
+}
+
+/****************************************************************************
+ * Name: spi_dmarxwaitw
+ *
+ * Description:
+ *   Wait for DMA to complete.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32WB_SPI_DMA
+static int spi_dmarxwait(struct stm32wb_spidev_s *priv)
+{
+  int ret;
+
+  /* Take the semaphore (perhaps waiting).  If the result is zero, then the
+   * DMA must not really have completed???
+   */
+
+  do
+    {
+      ret = nxsem_wait_uninterruptible(&priv->rxsem);
+
+      /* The only expected error is ECANCELED which would occur if the
+       * calling thread were canceled.
+       */
+
+      DEBUGASSERT(ret == OK || ret == -ECANCELED);
+    }
+  while (priv->rxresult == 0 && ret == OK);

Review Comment:
   As I understand the meaning of this `-ECANCELED` return code, the correct behavior would be to break the waiting loop because the task needs to be cancelled.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org