From 9c10e1e9881cec46ef5332da94040a79f684ad3c Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Mon, 6 Sep 2021 14:33:46 +0200 Subject: [PATCH] drivers: pinctrl: stm32: initial version Add initial version for STM32 pinctrl driver. Driver has been written re-using many of the already existing parts in drivers/pinmux/pinmux_stm32.c. Signed-off-by: Gerard Marull-Paretas --- drivers/pinctrl/CMakeLists.txt | 1 + drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Kconfig.stm32 | 16 ++ drivers/pinctrl/pinctrl_stm32.c | 236 ++++++++++++++++++++++++++ soc/arm/st_stm32/common/pinctrl_soc.h | 102 +++++++++++ 5 files changed, 356 insertions(+) create mode 100644 drivers/pinctrl/Kconfig.stm32 create mode 100644 drivers/pinctrl/pinctrl_stm32.c create mode 100644 soc/arm/st_stm32/common/pinctrl_soc.h diff --git a/drivers/pinctrl/CMakeLists.txt b/drivers/pinctrl/CMakeLists.txt index 4bee9b55c8a..45fc7c2fa36 100644 --- a/drivers/pinctrl/CMakeLists.txt +++ b/drivers/pinctrl/CMakeLists.txt @@ -5,3 +5,4 @@ zephyr_library() zephyr_library_sources(common.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_GD32_AF pinctrl_gd32_af.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_GD32_AFIO pinctrl_gd32_afio.c) +zephyr_library_sources_ifdef(CONFIG_PINCTRL_STM32 pinctrl_stm32.c) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 7744ea475b0..fbdc1f13495 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -30,5 +30,6 @@ config PINCTRL_DYNAMIC peripheral at early boot stages depending on a certain input. source "drivers/pinctrl/Kconfig.gd32" +source "drivers/pinctrl/Kconfig.stm32" endif # PINCTRL diff --git a/drivers/pinctrl/Kconfig.stm32 b/drivers/pinctrl/Kconfig.stm32 new file mode 100644 index 00000000000..c423d1bcedd --- /dev/null +++ b/drivers/pinctrl/Kconfig.stm32 @@ -0,0 +1,16 @@ +# Copyright (c) 2021 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config PINCTRL_STM32 + bool "Pin controller driver for STM32 MCUs" + depends on SOC_FAMILY_STM32 + default y + help + Enable pin controller driver for STM32 MCUs + +config PINCTRL_STM32_REMAP_INIT_PRIORITY + int "Remap initialization priority" + default 2 + help + Initialization priority for the routine in charge of configuring the + remap for pins PA11/12. diff --git a/drivers/pinctrl/pinctrl_stm32.c b/drivers/pinctrl/pinctrl_stm32.c new file mode 100644 index 00000000000..1334b47aefe --- /dev/null +++ b/drivers/pinctrl/pinctrl_stm32.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2016 Open-RnD Sp. z o.o. + * Copyright (c) 2021 Linaro Limited + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include +#include + +/** + * @brief Array containing pointers to each GPIO port. + * + * Entries will be NULL if the GPIO port is not enabled. + */ +static const struct device * const gpio_ports[] = { + DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpioa)), + DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpiob)), + DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpioc)), + DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpiod)), + DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpioe)), + DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpiof)), + DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpiog)), + DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpioh)), + DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpioi)), + DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpioj)), + DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpiok)), +}; + +/** Number of GPIO ports. */ +static const size_t gpio_ports_cnt = ARRAY_SIZE(gpio_ports); + +#if DT_NODE_HAS_PROP(DT_NODELABEL(pinctrl), remap_pa11) +#define REMAP_PA11 DT_PROP(DT_NODELABEL(pinctrl), remap_pa11) +#endif +#if DT_NODE_HAS_PROP(DT_NODELABEL(pinctrl), remap_pa12) +#define REMAP_PA12 DT_PROP(DT_NODELABEL(pinctrl), remap_pa12) +#endif +#if DT_NODE_HAS_PROP(DT_NODELABEL(pinctrl), remap_pa11_pa12) +#define REMAP_PA11_PA12 DT_PROP(DT_NODELABEL(pinctrl), remap_pa11_pa12) +#endif + +#if REMAP_PA11 || REMAP_PA12 || REMAP_PA11_PA12 + +int stm32_pinmux_init_remap(const struct device *dev) +{ + ARG_UNUSED(dev); + +#if REMAP_PA11 || REMAP_PA12 + +#if !defined(CONFIG_SOC_SERIES_STM32G0X) +#error "Pin remap property available only on STM32G0 SoC series" +#endif + + LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG); +#if REMAP_PA11 + LL_SYSCFG_EnablePinRemap(LL_SYSCFG_PIN_RMP_PA11); +#endif +#if REMAP_PA12 + LL_SYSCFG_EnablePinRemap(LL_SYSCFG_PIN_RMP_PA12); +#endif + +#elif REMAP_PA11_PA12 + +#if !defined(SYSCFG_CFGR1_PA11_PA12_RMP) +#error "Pin remap property available only on STM32F070x SoC series" +#endif + + LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_SYSCFG); + LL_SYSCFG_EnablePinRemap(); + +#endif /* (REMAP_PA11 || REMAP_PA12) || REMAP_PA11_PA12 */ + + return 0; +} + +SYS_INIT(stm32_pinmux_init_remap, PRE_KERNEL_1, + CONFIG_PINCTRL_STM32_REMAP_INIT_PRIORITY); + +#endif /* REMAP_PA11 || REMAP_PA12 || REMAP_PA11_PA12 */ + +#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_pinctrl) + +/** + * @brief Helper function to check and apply provided pinctrl remap + * configuration. + * + * Check operation verifies that pin remapping configuration is the same on all + * pins. If configuration is valid AFIO clock is enabled and remap is applied + * + * @param pins List of pins to be configured. + * @param pin_cnt Number of pins. + * + * @retval 0 If successful + * @retval -EINVAL If pins have an incompatible set of remaps. + */ +static int stm32_pins_remap(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt) +{ + uint8_t pos; + uint32_t reg_val; + volatile uint32_t *reg; + uint16_t remap; + + remap = (uint8_t)STM32_DT_PINMUX_REMAP(pins[0].pinmux); + + /* not remappable */ + if (remap == NO_REMAP) { + return 0; + } + + for (size_t i = 1U; i < pin_cnt; i++) { + if (STM32_DT_PINMUX_REMAP(pins[i].pinmux) != remap) { + return -EINVAL; + } + } + + /* A valid remapping configuration is available */ + /* Apply remapping before proceeding with pin configuration */ + LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_AFIO); + + if (STM32_REMAP_REG_GET(remap) == 0U) { + reg = &AFIO->MAPR; + } else { + reg = &AFIO->MAPR2; + } + + pos = STM32_REMAP_SHIFT_GET(remap); + + reg_val = *reg; + reg_val &= ~(STM32_REMAP_MASK_GET(remap) << pos); + reg_val |= STM32_REMAP_VAL_GET(remap) << pos; + *reg = reg_val; + + return 0; +} + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_pinctrl) */ + +static int stm32_pin_configure(uint32_t pin, uint32_t func, uint32_t altf) +{ + const struct device *port_device; + int ret = 0; + + if (STM32_PORT(pin) >= gpio_ports_cnt) { + return -EINVAL; + } + + port_device = gpio_ports[STM32_PORT(pin)]; + + if ((port_device == NULL) || (!device_is_ready(port_device))) { + return -ENODEV; + } + +#ifdef CONFIG_PM_DEVICE_RUNTIME + ret = pm_device_runtime_get(port_device); + if (ret < 0) { + return ret; + } +#endif + + gpio_stm32_configure(port_device, STM32_PIN(pin), func, altf); + +#ifdef CONFIG_PM_DEVICE_RUNTIME + ret = pm_device_runtime_put(port_device); +#endif + + return ret; +} + +int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, + uintptr_t reg) +{ + uint32_t pin, mux; + uint32_t func = 0; + int ret = 0; + + ARG_UNUSED(reg); + +#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_pinctrl) + ret = stm32_pins_remap(pins, pin_cnt); + if (ret < 0) { + return ret; + } +#endif /* DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_pinctrl) */ + + for (uint8_t i = 0U; i < pin_cnt; i++) { + mux = pins[i].pinmux; + +#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_pinctrl) + uint32_t pupd; + + if (STM32_DT_PINMUX_FUNC(mux) == ALTERNATE) { + func = pins[i].pincfg | STM32_MODE_OUTPUT | STM32_CNF_ALT_FUNC; + } else if (STM32_DT_PINMUX_FUNC(mux) == ANALOG) { + func = pins[i].pincfg | STM32_MODE_INPUT | STM32_CNF_IN_ANALOG; + } else if (STM32_DT_PINMUX_FUNC(mux) == GPIO_IN) { + func = pins[i].pincfg | STM32_MODE_INPUT; + pupd = func & (STM32_PUPD_MASK << STM32_PUPD_SHIFT); + if (pupd == STM32_PUPD_NO_PULL) { + func = func | STM32_CNF_IN_FLOAT; + } else { + func = func | STM32_CNF_IN_PUPD; + } + } else { + /* Not supported */ + __ASSERT_NO_MSG(STM32_DT_PINMUX_FUNC(mux)); + } +#else + if (STM32_DT_PINMUX_FUNC(mux) < STM32_ANALOG) { + func = pins[i].pincfg | STM32_MODER_ALT_MODE; + } else if (STM32_DT_PINMUX_FUNC(mux) == STM32_ANALOG) { + func = STM32_MODER_ANALOG_MODE; + } else { + /* Not supported */ + __ASSERT_NO_MSG(STM32_DT_PINMUX_FUNC(mux)); + } +#endif /* DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_pinctrl) */ + + pin = STM32PIN(STM32_DT_PINMUX_PORT(mux), + STM32_DT_PINMUX_LINE(mux)); + + ret = stm32_pin_configure(pin, func, STM32_DT_PINMUX_FUNC(mux)); + if (ret < 0) { + return ret; + } + } + + return 0; +} diff --git a/soc/arm/st_stm32/common/pinctrl_soc.h b/soc/arm/st_stm32/common/pinctrl_soc.h new file mode 100644 index 00000000000..57e4cf0e5d3 --- /dev/null +++ b/soc/arm/st_stm32/common/pinctrl_soc.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2020 Linaro Ltd. + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * STM32 SoC specific helpers for pinctrl driver + */ + +#ifndef ZEPHYR_SOC_ARM_ST_STM32_COMMON_PINCTRL_SOC_H_ +#define ZEPHYR_SOC_ARM_ST_STM32_COMMON_PINCTRL_SOC_H_ + +#include +#include + +#ifdef CONFIG_SOC_SERIES_STM32F1X +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** @cond INTERNAL_HIDDEN */ + +/** Type for STM32 pin. */ +typedef struct pinctrl_soc_pin { + /** Pinmux settings (port, pin and function). */ + uint32_t pinmux; + /** Pin configuration (bias, drive and slew rate). */ + uint32_t pincfg; +} pinctrl_soc_pin_t; + +/** + * @brief Utility macro to initialize pinmux field in #pinctrl_pin_t. + * + * @param node_id Node identifier. + */ +#define Z_PINCTRL_STM32_PINMUX_INIT(node_id) DT_PROP(node_id, pinmux) + +#ifdef CONFIG_SOC_SERIES_STM32F1X +/** + * @brief Utility macro to initialize pincfg field in #pinctrl_pin_t (F1). + * + * @param node_id Node identifier. + */ +#define Z_PINCTRL_STM32_PINCFG_INIT(node_id) \ + (((STM32_NO_PULL * DT_PROP(node_id, bias_disable)) << STM32_PUPD_SHIFT) | \ + ((STM32_PULL_UP * DT_PROP(node_id, bias_pull_up)) << STM32_PUPD_SHIFT) | \ + ((STM32_PULL_DOWN * DT_PROP(node_id, bias_pull_down)) << STM32_PUPD_SHIFT) | \ + ((STM32_PUSH_PULL * DT_PROP(node_id, drive_push_pull)) << STM32_CNF_OUT_0_SHIFT) | \ + ((STM32_OPEN_DRAIN * DT_PROP(node_id, drive_open_drain)) << STM32_CNF_OUT_0_SHIFT) | \ + (DT_ENUM_IDX(node_id, slew_rate) << STM32_MODE_OSPEED_SHIFT)) +#else +/** + * @brief Utility macro to initialize pincfg field in #pinctrl_pin_t (non-F1). + * + * @param node_id Node identifier. + */ +#define Z_PINCTRL_STM32_PINCFG_INIT(node_id) \ + (((STM32_NO_PULL * DT_PROP(node_id, bias_disable)) << STM32_PUPDR_SHIFT) | \ + ((STM32_PULL_UP * DT_PROP(node_id, bias_pull_up)) << STM32_PUPDR_SHIFT) | \ + ((STM32_PULL_DOWN * DT_PROP(node_id, bias_pull_down)) << STM32_PUPDR_SHIFT) | \ + ((STM32_PUSH_PULL * DT_PROP(node_id, drive_push_pull)) << STM32_OTYPER_SHIFT) | \ + ((STM32_OPEN_DRAIN * DT_PROP(node_id, drive_open_drain)) << STM32_OTYPER_SHIFT) | \ + (DT_ENUM_IDX(node_id, slew_rate) << STM32_OSPEEDR_SHIFT)) +#endif /* CONFIG_SOC_SERIES_STM32F1X */ + +/** + * @brief Utility macro to initialize each pin. + * + * @param node_id Node identifier. + * @param state_prop State property name. + * @param idx State property entry index. + */ +#define Z_PINCTRL_STATE_PIN_INIT(node_id, state_prop, idx) \ + { .pinmux = Z_PINCTRL_STM32_PINMUX_INIT( \ + DT_PROP_BY_IDX(node_id, state_prop, idx)), \ + .pincfg = Z_PINCTRL_STM32_PINCFG_INIT( \ + DT_PROP_BY_IDX(node_id, state_prop, idx)) }, + +/** + * @brief Utility macro to initialize state pins contained in a given property. + * + * @param node_id Node identifier. + * @param prop Property name describing state pins. + */ +#define Z_PINCTRL_STATE_PINS_INIT(node_id, prop) \ + {DT_FOREACH_PROP_ELEM(node_id, prop, Z_PINCTRL_STATE_PIN_INIT)} + +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_SOC_ARM_ST_STM32_COMMON_PINCTRL_SOC_H_ */