1 /*
2  * Copyright (c) 2024 STMicroelectronics
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief System/hardware module for STM32WB0 processor
10  */
11 
12 #include <zephyr/device.h>
13 #include <zephyr/init.h>
14 #include <stm32_ll_bus.h>
15 #include <stm32_ll_pwr.h>
16 #include <stm32_ll_system.h>
17 #include <stm32_ll_radio.h>
18 #include <zephyr/logging/log.h>
19 #include <zephyr/toolchain.h>
20 #include <cmsis_core.h>
21 #include <stdint.h>
22 
23 #include <system_stm32wb0x.h>
24 
25 #define LOG_LEVEL CONFIG_SOC_LOG_LEVEL
26 LOG_MODULE_REGISTER(soc);
27 
28 /**
29  * CMSIS System Core Clock: global variable holding the system core clock,
30  * which is the frequency supplied to the SysTick timer and processor core.
31  *
32  * On STM32WB0 series, after RESET, the system clock frequency is 16MHz.
33  */
34 uint32_t SystemCoreClock = 16000000U;
35 
36 /**
37  * RAM Virtual Register: special structure located at the start
38  * of SRAM0; used by the UART bootloader and the Low Power Manager.
39  * Data type definition comes from @ref system_stm32wb0xx.h
40  */
41 Z_GENERIC_SECTION("stm32wb0_RAM_VR")
42 __used RAM_VR_TypeDef RAM_VR;
43 
44 #if defined(CONFIG_BT)
45 /**
46  * SRAM0 memory reserved for usage by the MR_BLE Radio hardware.
47  *
48  * N.B.: radio driver defines CFG_BLE_NUM_RADIO_TASKS.
49  */
50 Z_GENERIC_SECTION("stm32wb0_BLUE_RAM")
51 static uint8_t __used __blue_RAM[sizeof(GLOBALSTATMACH_TypeDef) +
52 				 CFG_BLE_NUM_RADIO_TASKS * sizeof(STATMACH_TypeDef)];
53 #endif /* CONFIG_BT */
54 
55 /** Power Controller node (shorthand for upcoming macros) */
56 #define PWRC DT_INST(0, st_stm32wb0_pwr)
57 
58 /* Convert DTS properties to LL macros */
59 #define SMPS_PRESCALER	_CONCAT(LL_RCC_SMPS_DIV_, DT_PROP(PWRC, smps_clock_prescaler))
60 
61 #if SMPS_MODE != STM32WB0_SMPS_MODE_OFF
62 	BUILD_ASSERT(DT_NODE_HAS_PROP(PWRC, smps_bom),
63 		"smps-bom must be specified");
64 
65 	#define SMPS_BOM						\
66 		_CONCAT(LL_PWR_SMPS_BOM, DT_PROP(PWRC, smps_bom))
67 
68 	#define SMPS_LP_MODE						\
69 			COND_CODE_1(					\
70 				DT_PROP(PWRC, smps_lp_floating),	\
71 					(LL_PWR_SMPS_LPOPEN),		\
72 					(LL_PWR_NO_SMPS_LPOPEN))
73 
74 #if defined(PWR_CR5_SMPS_PRECH_CUR_SEL)
75 	#define SMPS_CURRENT_LIMIT					\
76 		_CONCAT(LL_PWR_SMPS_PRECH_LIMIT_CUR_,			\
77 			DT_STRING_UNQUOTED(PWRC, smps_current_limit))
78 #endif /* PWR_CR5_SMPS_PRECH_CUR_SEL */
79 
80 	#define SMPS_OUTPUT_VOLTAGE					\
81 			_CONCAT(LL_PWR_SMPS_OUTPUT_VOLTAGE_,		\
82 			DT_STRING_UNQUOTED(PWRC, smps_output_voltage))
83 #endif /* SMPS_MODE != STM32WB0_SMPS_MODE_OFF */
84 
configure_smps(void)85 static void configure_smps(void)
86 {
87 	/* Configure SMPS clock prescaler */
88 	LL_RCC_SetSMPSPrescaler(SMPS_PRESCALER);
89 
90 #if SMPS_MODE == STM32WB0_SMPS_MODE_OFF
91 	/* Disable SMPS */
92 	LL_PWR_SetSMPSMode(LL_PWR_NO_SMPS);
93 
94 	while (LL_PWR_IsSMPSReady()) {
95 		/* Wait for SMPS to turn off */
96 	}
97 #else
98 	/* Select correct BOM */
99 	LL_PWR_SetSMPSBOM(SMPS_BOM);
100 
101 	/* Configure low-power mode */
102 	LL_PWR_SetSMPSOpenMode(SMPS_LP_MODE);
103 
104 	/* Enable SMPS */
105 	LL_PWR_SetSMPSMode(LL_PWR_SMPS);
106 
107 	while (!LL_PWR_IsSMPSReady()) {
108 		/* Wait for SMPS to turn on */
109 	}
110 
111 	/* Place SMPS in PRECHARGE (BYPASS) mode.
112 	 * This is required to change SMPS output voltage,
113 	 * so we can do it unconditionally.
114 	 */
115 	LL_PWR_SetSMPSPrechargeMode(LL_PWR_SMPS_PRECHARGE);
116 	while (LL_PWR_IsSMPSinRUNMode()) {
117 		/* Wait for SMPS to enter PRECHARGE mode */
118 	}
119 
120 	if (SMPS_MODE == STM32WB0_SMPS_MODE_PRECHARGE) {
121 #if defined(PWR_CR5_SMPS_PRECH_CUR_SEL)
122 		/**
123 		 * SMPS should remain in PRECHARGE mode.
124 		 * We still have to configure the output current
125 		 * limit specified in Device Tree, though this
126 		 * can only be done if this SoC supports it.
127 		 */
128 		LL_PWR_SetSMPSPrechargeLimitCurrent(SMPS_CURRENT_LIMIT);
129 #endif /* PWR_CR5_SMPS_PRECH_CUR_SEL */
130 	} else {
131 		/**
132 		 * SMPS mode requested is RUN mode. Configure the output
133 		 * voltage to the desired value then exit PRECHARGE mode.
134 		 */
135 		LL_PWR_SMPS_SetOutputVoltageLevel(SMPS_OUTPUT_VOLTAGE);
136 
137 		/* Exit PRECHARGE mode (returns in RUN mode) */
138 		LL_PWR_SetSMPSPrechargeMode(LL_PWR_NO_SMPS_PRECHARGE);
139 		while (!LL_PWR_IsSMPSinRUNMode()) {
140 			/* Wait for SMPS to enter RUN mode */
141 		}
142 	}
143 #endif /* SMPS_MODE == STM32WB0_SMPS_MODE_OFF */
144 }
145 
146 /**
147  * @brief Perform basic hardware initialization at boot.
148  *
149  * This needs to be run from the very beginning,
150  * so the init priority has to be 0 (zero).
151  *
152  * @return 0
153  */
stm32wb0_init(void)154 static int stm32wb0_init(void)
155 {
156 	/* Update CMSIS SystemCoreClock variable (CLK_SYS) */
157 	/* On reset, the 64MHz HSI is selected as input to
158 	 * the SYSCLKPRE prescaler, set to 4, resulting in
159 	 * CLK_SYS being equal to 16MHz.
160 	 */
161 	SystemCoreClock = 16000000U;
162 
163 	/* Remap address 0 to user flash memory */
164 	LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_FLASH);
165 
166 	/**
167 	 * Save application exception vector address in RAM_VR.
168 	 * By now, SCB->VTOR should point to _vector_table,
169 	 * so use that value instead of _vector_table directly.
170 	 */
171 	RAM_VR.AppBase = SCB->VTOR;
172 
173 	/* Enable retention of all RAM banks in Deepstop */
174 	LL_PWR_EnableRAMBankRet(LL_PWR_RAMRET_1);
175 #if defined(LL_PWR_RAMRET_2)
176 	LL_PWR_EnableRAMBankRet(LL_PWR_RAMRET_2);
177 #endif
178 #if defined(LL_PWR_RAMRET_3)
179 	LL_PWR_EnableRAMBankRet(LL_PWR_RAMRET_3);
180 #endif
181 
182 	/* Configure SMPS step-down converter */
183 	configure_smps();
184 
185 	return 0;
186 }
187 
188 SYS_INIT(stm32wb0_init, PRE_KERNEL_1, 0);
189