1 /*
2 * Copyright (c) 2024 STMicroelectronics
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9 #include <zephyr/init.h>
10 #include <zephyr/types.h>
11 #include <soc.h>
12 #include <zephyr/arch/cpu.h>
13 #include <zephyr/drivers/gpio.h>
14 #include <zephyr/devicetree.h>
15
16 #include <stm32_ll_system.h>
17 #include <stm32_ll_pwr.h>
18
19 #include <zephyr/dt-bindings/power/stm32_pwr.h>
20
21 #include "stm32_wkup_pins.h"
22
23 #include <zephyr/logging/log.h>
24
25 LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL);
26
27 #define DT_DRV_COMPAT st_stm32_pwr
28
29 #define STM32_PWR_NODE DT_NODELABEL(pwr)
30
31 #define PWR_STM32_MAX_NB_WKUP_PINS DT_INST_PROP(0, wkup_pins_nb)
32
33 #define PWR_STM32_WKUP_PIN_SRCS_NB DT_INST_PROP_OR(0, wkup_pin_srcs, 1)
34
35 #define PWR_STM32_WKUP_PINS_POLARITY DT_INST_PROP_OR(0, wkup_pins_pol, 0)
36
37 #define PWR_STM32_WKUP_PINS_PUPD_CFG DT_INST_PROP_OR(0, wkup_pins_pupd, 0)
38
39 /** @cond INTERNAL_HIDDEN */
40
41 /**
42 * @brief flags for wake-up pin polarity configuration
43 * @{
44 */
45
46 /* detection of wake-up event on the high level : rising edge */
47 #define STM32_PWR_WKUP_PIN_P_RISING 0
48 /* detection of wake-up event on the low level : falling edge */
49 #define STM32_PWR_WKUP_PIN_P_FALLING 1
50
51 /** @} */
52
53 /**
54 * @brief flags for configuration of pull-ups & pull-downs of GPIO ports
55 * that are associated with wake-up pins
56 * @{
57 */
58
59 #define STM32_PWR_WKUP_PIN_NOPULL 0
60 #define STM32_PWR_WKUP_PIN_PULLUP 1
61 #define STM32_PWR_WKUP_PIN_PULLDOWN (1 << 2)
62
63 /** @} */
64
65 /**
66 * @brief Structure for storing the devicetree configuration of a wake-up pin.
67 */
68 struct wkup_pin_dt_cfg_t {
69 /* starts from 1 */
70 uint32_t wkup_pin_id;
71 /* GPIO pin(s) associated with wake-up pin */
72 struct gpio_dt_spec gpios_cfgs[PWR_STM32_WKUP_PIN_SRCS_NB];
73 };
74
75 #define WKUP_PIN_NODE_LABEL(i) wkup_pin_##i
76
77 #define WKUP_PIN_NODE_ID_BY_IDX(idx) DT_CHILD(STM32_PWR_NODE, WKUP_PIN_NODE_LABEL(idx))
78
79 static const struct gpio_dt_spec empty_gpio = {.port = NULL, .pin = 0, .dt_flags = 0};
80
81 /* cell_idx starts from 0 */
82 #define WKUP_PIN_GPIOS_CFG_DT_BY_IDX(cell_idx, node_id) \
83 GPIO_DT_SPEC_GET_BY_IDX_OR(node_id, wkup_gpios, cell_idx, empty_gpio)
84
85 #define NB_DEF(i, _) i
86
87 /**
88 * @brief Get wake-up pin configuration from a given devicetree node.
89 *
90 * This returns a static initializer for a <tt>struct wkup_pin_dt_cfg_t</tt>
91 * filled with data from a given devicetree node.
92 *
93 * @param node_id Devicetree node identifier.
94 *
95 * @return Static initializer for a wkup_pin_dt_cfg_t structure.
96 */
97 #define WKUP_PIN_CFG_DT(node_id) \
98 { \
99 .wkup_pin_id = DT_REG_ADDR(node_id), \
100 .gpios_cfgs = \
101 { \
102 FOR_EACH_FIXED_ARG(WKUP_PIN_GPIOS_CFG_DT_BY_IDX, (,), node_id, \
103 LISTIFY(PWR_STM32_WKUP_PIN_SRCS_NB, NB_DEF, (,))) \
104 } \
105 }
106
107 /* wkup_pin idx starts from 1 */
108 #define WKUP_PIN_CFG_DT_BY_IDX(idx) WKUP_PIN_CFG_DT(WKUP_PIN_NODE_ID_BY_IDX(idx))
109
110 #define PWR_STM32_WKUP_PIN_LOOKUP_MEMBER(i, _) CONCAT(LL_PWR_WAKEUP_PIN, UTIL_INC(i))
111
112 #define WKUP_PIN_CFG_DT_COMMA(wkup_pin_id) WKUP_PIN_CFG_DT(wkup_pin_id),
113
114 /** @endcond */
115
116 /**
117 * @brief Structure for passing the runtime configuration of a given wake-up pin.
118 */
119 struct wkup_pin_cfg_t {
120 uint32_t wkup_pin_id;
121 #if PWR_STM32_WKUP_PINS_POLARITY
122 bool polarity; /* True if detection on the low level : falling edge */
123 #endif /* PWR_STM32_WKUP_PINS_POLARITY */
124 #if PWR_STM32_WKUP_PINS_PUPD_CFG
125 uint32_t pupd_cfg; /* pull-up/down config of GPIO port associated w/ this wkup pin */
126 uint32_t ll_gpio_port; /* GPIO port associated with this wake-up pin */
127 uint32_t ll_gpio_pin; /* GPIO pin associated with this wake-up pin */
128 #endif /* PWR_STM32_WKUP_PINS_PUPD_CFG */
129 uint32_t src_selection; /* The source signal to use with this wake-up pin */
130 };
131
132 /**
133 * @brief LookUp Table to store LL_PWR_WAKEUP_PINx for each wake-up pin.
134 */
135 static const uint32_t table_wakeup_pins[PWR_STM32_MAX_NB_WKUP_PINS + 1] = {
136 0,
137 LISTIFY(PWR_STM32_MAX_NB_WKUP_PINS, PWR_STM32_WKUP_PIN_LOOKUP_MEMBER, (,)),
138 };
139
140 static struct wkup_pin_dt_cfg_t wkup_pins_cfgs[] = {
141 DT_FOREACH_CHILD(STM32_PWR_NODE, WKUP_PIN_CFG_DT_COMMA)};
142
143 #if PWR_STM32_WKUP_PINS_PUPD_CFG
144
145 /**
146 * @brief Array containing pointers to each GPIO port.
147 *
148 * Entry will be NULL if the GPIO port is not enabled.
149 * Also used to get GPIO port index from device ptr, so having
150 * NULL entries for unused GPIO ports is desired.
151 */
152 static const struct device *const gpio_ports[] = {
153 DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpioa)),
154 DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpiob)),
155 DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpioc)),
156 DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpiod)),
157 DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpioe)),
158 DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpiof)),
159 DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpiog)),
160 DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpioh)),
161 DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpioi)),
162 DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpioj)),
163 DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpiok)),
164 };
165
166 /* Number of GPIO ports. */
167 static const size_t gpio_ports_cnt = ARRAY_SIZE(gpio_ports);
168
169 /**
170 * @brief LookUp Table to store LL_PWR_GPIO_x of each GPIO port.
171 */
172 static const uint32_t table_ll_pwr_gpio_ports[] = {
173 #ifdef CONFIG_SOC_SERIES_STM32U5X
174 (uint32_t)LL_PWR_GPIO_PORTA, (uint32_t)LL_PWR_GPIO_PORTB, (uint32_t)LL_PWR_GPIO_PORTC,
175 (uint32_t)LL_PWR_GPIO_PORTD, (uint32_t)LL_PWR_GPIO_PORTE, (uint32_t)LL_PWR_GPIO_PORTF,
176 (uint32_t)LL_PWR_GPIO_PORTG, (uint32_t)LL_PWR_GPIO_PORTH, (uint32_t)LL_PWR_GPIO_PORTI,
177 (uint32_t)LL_PWR_GPIO_PORTJ
178 #else
179 LL_PWR_GPIO_A, LL_PWR_GPIO_B, LL_PWR_GPIO_C, LL_PWR_GPIO_D, LL_PWR_GPIO_E,
180 LL_PWR_GPIO_F, LL_PWR_GPIO_G, LL_PWR_GPIO_H, LL_PWR_GPIO_I, LL_PWR_GPIO_J
181 #endif /* CONFIG_SOC_SERIES_STM32U5X */
182 };
183
184 static const size_t pwr_ll_gpio_ports_cnt = ARRAY_SIZE(table_ll_pwr_gpio_ports);
185
186 #endif /* PWR_STM32_WKUP_PINS_PUPD_CFG */
187
188 /**
189 * @brief Configure & enable a wake-up pin.
190 *
191 * @param wakeup_pin_cfg wake-up pin runtime configuration.
192 *
193 */
wkup_pin_setup(const struct wkup_pin_cfg_t * wakeup_pin_cfg)194 static void wkup_pin_setup(const struct wkup_pin_cfg_t *wakeup_pin_cfg)
195 {
196 uint32_t wkup_pin_index = wakeup_pin_cfg->wkup_pin_id;
197
198 #if PWR_STM32_WKUP_PINS_POLARITY
199 /* Set wake-up pin polarity */
200 if (wakeup_pin_cfg->polarity == STM32_PWR_WKUP_PIN_P_FALLING) {
201 LL_PWR_SetWakeUpPinPolarityLow(table_wakeup_pins[wkup_pin_index]);
202 } else {
203 LL_PWR_SetWakeUpPinPolarityHigh(table_wakeup_pins[wkup_pin_index]);
204 }
205 #endif /* PWR_STM32_WKUP_PINS_POLARITY */
206
207 #if PWR_STM32_WKUP_PINS_PUPD_CFG
208 if (wakeup_pin_cfg->pupd_cfg == STM32_PWR_WKUP_PIN_NOPULL) {
209 LL_PWR_DisableGPIOPullUp(wakeup_pin_cfg->ll_gpio_port,
210 (1u << wakeup_pin_cfg->ll_gpio_pin));
211 LL_PWR_DisableGPIOPullDown(wakeup_pin_cfg->ll_gpio_port,
212 (1u << wakeup_pin_cfg->ll_gpio_pin));
213
214 } else if ((wakeup_pin_cfg->pupd_cfg == STM32_PWR_WKUP_PIN_PULLUP) &&
215 wakeup_pin_cfg->ll_gpio_port) {
216 LL_PWR_EnableGPIOPullUp(wakeup_pin_cfg->ll_gpio_port,
217 (1u << wakeup_pin_cfg->ll_gpio_pin));
218
219 } else if ((wakeup_pin_cfg->pupd_cfg == STM32_PWR_WKUP_PIN_PULLDOWN) &&
220 wakeup_pin_cfg->ll_gpio_port) {
221 LL_PWR_EnableGPIOPullDown(wakeup_pin_cfg->ll_gpio_port,
222 (1u << wakeup_pin_cfg->ll_gpio_pin));
223 }
224 #endif /* PWR_STM32_WKUP_PINS_PUPD_CFG */
225
226 #if defined(CONFIG_SOC_SERIES_STM32U5X) || defined(CONFIG_SOC_SERIES_STM32WBAX)
227 /* Select the proper wake-up signal source */
228 if (wakeup_pin_cfg->src_selection & STM32_PWR_WKUP_PIN_SRC_0) {
229 LL_PWR_SetWakeUpPinSignal0Selection(table_wakeup_pins[wkup_pin_index]);
230 } else if (wakeup_pin_cfg->src_selection & STM32_PWR_WKUP_PIN_SRC_1) {
231 LL_PWR_SetWakeUpPinSignal1Selection(table_wakeup_pins[wkup_pin_index]);
232 } else if (wakeup_pin_cfg->src_selection & STM32_PWR_WKUP_PIN_SRC_2) {
233 LL_PWR_SetWakeUpPinSignal2Selection(table_wakeup_pins[wkup_pin_index]);
234 } else {
235 LL_PWR_SetWakeUpPinSignal3Selection(table_wakeup_pins[wkup_pin_index]);
236 }
237 #endif /* CONFIG_SOC_SERIES_STM32U5X or CONFIG_SOC_SERIES_STM32WBAX */
238
239 LL_PWR_EnableWakeUpPin(table_wakeup_pins[wkup_pin_index]);
240 }
241
242 /**
243 * @brief Exported function to configure a given GPIO pin as a source for wake-up pins
244 *
245 * @param gpio Container for GPIO pin information specified in devicetree
246 *
247 * @return 0 on success, -EINVAL if said GPIO pin is not associated with any wake-up pin.
248 */
stm32_pwr_wkup_pin_cfg_gpio(const struct gpio_dt_spec * gpio)249 int stm32_pwr_wkup_pin_cfg_gpio(const struct gpio_dt_spec *gpio)
250 {
251 struct wkup_pin_cfg_t wakeup_pin_cfg;
252 struct wkup_pin_dt_cfg_t *wkup_pin_dt_cfg;
253 struct gpio_dt_spec *wkup_pin_gpio_cfg;
254 bool found_gpio = false;
255 int i;
256 int j;
257
258 UNUSED(empty_gpio);
259
260 /* Look for a wake-up pin that has this GPIO pin as a source, if any */
261 for (i = 0; i < PWR_STM32_MAX_NB_WKUP_PINS; i++) {
262 wkup_pin_dt_cfg = &(wkup_pins_cfgs[i]);
263 for (j = 0; j < PWR_STM32_WKUP_PIN_SRCS_NB; j++) {
264 wkup_pin_gpio_cfg = &(wkup_pin_dt_cfg->gpios_cfgs[j]);
265 if (wkup_pin_gpio_cfg->port == gpio->port) {
266 if (wkup_pin_gpio_cfg->pin == gpio->pin) {
267 found_gpio = true;
268 break;
269 }
270 }
271 }
272 if (found_gpio) {
273 break;
274 };
275 }
276
277 if (!found_gpio) {
278 LOG_DBG("Couldn't find a wake-up event correspending to GPIO %s pin %d\n",
279 gpio->port->name, gpio->pin);
280 LOG_DBG("=> It cannot be used as a wake-up source\n");
281 return -EINVAL;
282 }
283
284 wakeup_pin_cfg.wkup_pin_id = wkup_pin_dt_cfg->wkup_pin_id;
285
286 /* Each wake-up pin on STM32U5 is associated with 4 wkup srcs, 3 of them correspond to GPIOs. */
287 #if defined(CONFIG_SOC_SERIES_STM32U5X) || defined(CONFIG_SOC_SERIES_STM32WBAX)
288 wakeup_pin_cfg.src_selection = wkup_pin_gpio_cfg->dt_flags &
289 (STM32_PWR_WKUP_PIN_SRC_0 |
290 STM32_PWR_WKUP_PIN_SRC_1 |
291 STM32_PWR_WKUP_PIN_SRC_2);
292 #else
293 wakeup_pin_cfg.src_selection = 0;
294 #endif /* CONFIG_SOC_SERIES_STM32U5X or CONFIG_SOC_SERIES_STM32WBAX */
295
296 #if PWR_STM32_WKUP_PINS_POLARITY
297 /*
298 * get polarity config from GPIO flags specified in device "gpios" property
299 */
300 if (gpio->dt_flags & GPIO_ACTIVE_LOW) {
301 wakeup_pin_cfg.polarity = STM32_PWR_WKUP_PIN_P_FALLING;
302 } else {
303 wakeup_pin_cfg.polarity = STM32_PWR_WKUP_PIN_P_RISING;
304 }
305 #endif /* PWR_STM32_WKUP_PINS_POLARITY */
306
307 #if PWR_STM32_WKUP_PINS_PUPD_CFG
308 wakeup_pin_cfg.ll_gpio_port = 0;
309 for (i = 0; i < gpio_ports_cnt; i++) {
310 if ((gpio_ports[i] == gpio->port) && (i < pwr_ll_gpio_ports_cnt)) {
311 wakeup_pin_cfg.ll_gpio_port = table_ll_pwr_gpio_ports[i];
312 break;
313 }
314 }
315
316 wakeup_pin_cfg.ll_gpio_pin = gpio->pin;
317
318 /*
319 * Get pull-up/down config, if any, from GPIO flags specified in device "gpios" property
320 */
321 if (gpio->dt_flags & GPIO_PULL_UP) {
322 wakeup_pin_cfg.pupd_cfg = STM32_PWR_WKUP_PIN_PULLUP;
323 } else if (gpio->dt_flags & GPIO_PULL_DOWN) {
324 wakeup_pin_cfg.pupd_cfg = STM32_PWR_WKUP_PIN_PULLDOWN;
325 } else {
326 wakeup_pin_cfg.pupd_cfg = STM32_PWR_WKUP_PIN_NOPULL;
327 }
328 #endif /* PWR_STM32_WKUP_PINS_PUPD_CFG */
329
330 wkup_pin_setup(&wakeup_pin_cfg);
331
332 return 0;
333 }
334
335 /**
336 * @brief Exported function to activate pull-ups/pull-downs configuration of GPIO ports
337 * associated with wake-up pins.
338 */
stm32_pwr_wkup_pin_cfg_pupd(void)339 void stm32_pwr_wkup_pin_cfg_pupd(void)
340 {
341 #if PWR_STM32_WKUP_PINS_PUPD_CFG
342 #ifdef CONFIG_SOC_SERIES_STM32U5X
343 LL_PWR_EnablePUPDConfig();
344 #else
345 LL_PWR_EnablePUPDCfg();
346 #endif /* CONFIG_SOC_SERIES_STM32U5X */
347 #else
348 return;
349 #endif /* PWR_STM32_WKUP_PINS_PUPD_CFG */
350 }
351