diff --git a/drivers/pinctrl/CMakeLists.txt b/drivers/pinctrl/CMakeLists.txt index ec6cb34b0d2..4bee9b55c8a 100644 --- a/drivers/pinctrl/CMakeLists.txt +++ b/drivers/pinctrl/CMakeLists.txt @@ -4,3 +4,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) diff --git a/drivers/pinctrl/Kconfig.gd32 b/drivers/pinctrl/Kconfig.gd32 index f7ff8c3567e..7822033f016 100644 --- a/drivers/pinctrl/Kconfig.gd32 +++ b/drivers/pinctrl/Kconfig.gd32 @@ -2,6 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 DT_COMPAT_GIGADEVICE_GD32_PINCTRL_AF := gd,gd32-pinctrl-af +DT_COMPAT_GIGADEVICE_GD32_PINCTRL_AFIO := gd,gd32-pinctrl-afio config PINCTRL_GD32_AF bool "GD32 AF pin controller driver" @@ -10,3 +11,11 @@ config PINCTRL_GD32_AF help GD32 AF pin controller driver. This driver is used by series using the AF pin multiplexing model. + +config PINCTRL_GD32_AFIO + bool "GD32 AFIO pin controller driver" + depends on SOC_FAMILY_GD32 && GD32_HAS_AFIO_PINMUX + default $(dt_compat_enabled,$(DT_COMPAT_GIGADEVICE_GD32_PINCTRL_AFIO)) + help + GD32 AFIO pin controller driver. This driver is used by series using the + AFIO pin multiplexing model. diff --git a/drivers/pinctrl/pinctrl_gd32_afio.c b/drivers/pinctrl/pinctrl_gd32_afio.c new file mode 100644 index 00000000000..409d0c4f012 --- /dev/null +++ b/drivers/pinctrl/pinctrl_gd32_afio.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2021 Teslabs Engineering S.L. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/** AFIO DT node */ +#define AFIO_NODE DT_NODELABEL(afio) + +/** GPIO mode: input floating (CTL bits) */ +#define GPIO_MODE_INP_FLOAT 0x4U +/** GPIO mode: input with pull-up/down (CTL bits) */ +#define GPIO_MODE_INP_PUPD 0x8U +/** GPIO mode: output push-pull (CTL bits) */ +#define GPIO_MODE_ALT_PP 0x8U +/** GPIO mode: output open-drain (CTL bits) */ +#define GPIO_MODE_ALT_OD 0xCU + +/** Utility macro that expands to the GPIO port address if it exists */ +#define GD32_PORT_ADDR_OR_NONE(nodelabel) \ + COND_CODE_1(DT_NODE_EXISTS(DT_NODELABEL(nodelabel)), \ + (DT_REG_ADDR(DT_NODELABEL(nodelabel)),), ()) + +/** Utility macro that expands to the GPIO RCU if it exists */ +#define GD32_PORT_RCU_OR_NONE(nodelabel) \ + COND_CODE_1(DT_NODE_EXISTS(DT_NODELABEL(nodelabel)), \ + (DT_PROP(DT_NODELABEL(nodelabel), rcu_periph_clock),), ()) + +/** GD32 port addresses */ +static const uint32_t gd32_port_addrs[] = { + GD32_PORT_ADDR_OR_NONE(gpioa) + GD32_PORT_ADDR_OR_NONE(gpiob) + GD32_PORT_ADDR_OR_NONE(gpioc) + GD32_PORT_ADDR_OR_NONE(gpiod) + GD32_PORT_ADDR_OR_NONE(gpioe) + GD32_PORT_ADDR_OR_NONE(gpiof) + GD32_PORT_ADDR_OR_NONE(gpiog) +}; + +/** GD32 port RCUs */ +static const uint32_t gd32_port_rcus[] = { + GD32_PORT_RCU_OR_NONE(gpioa) + GD32_PORT_RCU_OR_NONE(gpiob) + GD32_PORT_RCU_OR_NONE(gpioc) + GD32_PORT_RCU_OR_NONE(gpiod) + GD32_PORT_RCU_OR_NONE(gpioe) + GD32_PORT_RCU_OR_NONE(gpiof) + GD32_PORT_RCU_OR_NONE(gpiog) +}; + +/** + * @brief Initialize AFIO + * + * This function enables AFIO clock and configures the I/O compensation if + * available and enabled in Devicetree. + * + * @retval 0 Always + */ +static int afio_init(const struct device *dev) +{ + ARG_UNUSED(dev); + + rcu_periph_clock_enable(DT_PROP(AFIO_NODE, rcu_periph_clock)); + +#ifdef AFIO_CPSCTL + if (DT_PROP(AFIO_NODE, enable_cps)) { + gpio_compensation_config(GPIO_COMPENSATION_ENABLE); + while (gpio_compensation_flag_get() == RESET) + ; + } +#endif /* AFIO_CPSCTL */ + + return 0; +} + +SYS_INIT(afio_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); + +/** + * @brief Helper function to configure the SPD register if available. + * + * @param port GPIO port address. + * @param pin_bit GPIO pin, set as bit position. + * @param speed GPIO speed. + * + * @return Value of the mode register (speed) that should be set later. + */ +static inline uint8_t configure_spd(uint32_t port, uint32_t pin_bit, + uint8_t speed) +{ + switch (speed) { + case GD32_OSPEED_MAX: +#ifdef GPIOx_SPD + GPIOx_SPD(port) |= pin_bit; +#endif /* GPIOx_SPD */ + return speed; + default: +#ifdef GPIOx_SPD + GPIOx_SPD(port) &= ~pin_bit; +#endif /* GPIOx_SPD */ + return speed + 1U; + } +} + +/** + * @brief Configure a pin. + * + * @param pin The pin to configure. + */ +static void configure_pin(pinctrl_soc_pin_t pin) +{ + uint8_t port_idx, mode, pin_num; + uint32_t rcu, port, pin_bit, reg_val; + volatile uint32_t *reg; + + port_idx = GD32_PORT_GET(pin); + __ASSERT_NO_MSG(port_idx < ARRAY_SIZE(gd32_port_addrs)); + + rcu = gd32_port_rcus[port_idx]; + port = gd32_port_addrs[port_idx]; + pin_num = GD32_PIN_GET(pin); + pin_bit = BIT(pin_num); + mode = GD32_MODE_GET(pin); + + if (pin_num < 8U) { + reg = &GPIO_CTL0(port); + } else { + reg = &GPIO_CTL1(port); + pin_num -= 8U; + } + + rcu_periph_clock_enable(rcu); + + reg_val = *reg; + reg_val &= ~GPIO_MODE_MASK(pin_num); + + if (mode == GD32_MODE_ALTERNATE) { + uint8_t mode; + + mode = configure_spd(port, pin_bit, GD32_OSPEED_GET(pin)); + + if (GD32_OTYPE_GET(pin) == GD32_OTYPE_PP) { + mode |= GPIO_MODE_ALT_PP; + } else { + mode |= GPIO_MODE_ALT_OD; + } + + reg_val |= GPIO_MODE_SET(pin_num, mode); + } else if (mode == GD32_MODE_GPIO_IN) { + uint8_t pupd = GD32_PUPD_GET(pin); + + if (pupd == GD32_PUPD_NONE) { + reg_val |= GPIO_MODE_SET(pin_num, GPIO_MODE_INP_FLOAT); + } else { + reg_val |= GPIO_MODE_SET(pin_num, GPIO_MODE_INP_PUPD); + + if (pupd == GD32_PUPD_PULLDOWN) { + GPIO_BC(port) = pin_bit; + } else if (pupd == GD32_PUPD_PULLUP) { + GPIO_BOP(port) = pin_bit; + } + } + } + + *reg = reg_val; + + rcu_periph_clock_disable(rcu); +} + +/** + * @brief Configure remap. + * + * @param remap Remap bit field as encoded by #GD32_REMAP. + */ +static void configure_remap(uint16_t remap) +{ + uint8_t pos; + uint32_t reg_val; + volatile uint32_t *reg; + + /* not remappable */ + if (remap == GD32_NORMP) { + return; + } + + if (GD32_REMAP_REG_GET(remap) == 0U) { + reg = &AFIO_PCF0; + } else { + reg = &AFIO_PCF1; + } + + pos = GD32_REMAP_POS_GET(remap); + + reg_val = *reg; + reg_val &= ~(GD32_REMAP_MSK_GET(remap) << pos); + reg_val |= GD32_REMAP_VAL_GET(remap) << pos; + *reg = reg_val; +} + +int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, + uintptr_t reg) +{ + ARG_UNUSED(reg); + + if (pin_cnt == 0U) { + return -EINVAL; + } + + /* same remap is encoded in all pins, so just pick the first */ + configure_remap(GD32_REMAP_GET(pins[0])); + + /* configure all pins */ + for (uint8_t i = 0U; i < pin_cnt; i++) { + configure_pin(pins[i]); + } + + return 0; +} diff --git a/dts/bindings/pinctrl/gd,gd32-afio.yaml b/dts/bindings/pinctrl/gd,gd32-afio.yaml new file mode 100644 index 00000000000..f47b541c89f --- /dev/null +++ b/dts/bindings/pinctrl/gd,gd32-afio.yaml @@ -0,0 +1,30 @@ +# Copyright (c) 2021 Teslabs Engineering S.L. +# SPDX-License-Identifier: Apache-2.0 + +description: | + The AFIO peripheral is used to configure pin remapping, EXTI sources and, + when available, enable the I/O compensation cell. + +compatible: "gd,gd32-afio" + +include: base.yaml + +properties: + reg: + required: true + + label: + required: true + + rcu-periph-clock: + type: int + description: Reset Control Unit Peripheral Clock ID + required: true + + enable-cps: + required: false + type: boolean + description: | + Enable the I/O compensation cell. This option should be enabled when the + output speed is greater than 50MHz to reduce the I/O noise effects on + the power supply. This option is only available on certain GD32 series. diff --git a/dts/bindings/pinctrl/gd,gd32-pinctrl-afio.yaml b/dts/bindings/pinctrl/gd,gd32-pinctrl-afio.yaml new file mode 100644 index 00000000000..265db611275 --- /dev/null +++ b/dts/bindings/pinctrl/gd,gd32-pinctrl-afio.yaml @@ -0,0 +1,137 @@ +# Copyright (c) 2021 Teslabs Engineering S.L. +# SPDX-License-Identifier: Apache-2.0 + +description: | + The GD32 pin controller (AFIO model) is a singleton node responsible for + controlling pin function selection and pin properties. For example, you can + use this node to route USART0 RX to pin PA10 and enable the pull-up resistor + on the pin. Remapping is also supported. + + The node has the 'pinctrl' node label set in your SoC's devicetree, + so you can modify it like this: + + &pinctrl { + /* your modifications go here */ + }; + + All device pin configurations should be placed in child nodes of the + 'pinctrl' node, as shown in this example: + + /* You can put this in places like a board-pinctrl.dtsi file in + * your board directory, or a devicetree overlay in your application. + */ + + /* include pre-defined combinations for the SoC variant used by the board */ + #include + + &pinctrl { + /* configuration for the usart0 "default" state */ + usart0_default: usart0_default { + /* group 1 */ + group1 { + /* configure PA9 as USART0 TX and PA11 as USART0 CTS (no remap) */ + pinmux = , ; + }; + /* group 2 */ + group2 { + /* configure PA10 as USART0 RX and PA12 as USART0 RTS (no remap) */ + pinmux = , ; + /* both PA10 and PA12 have pull-up enabled */ + bias-pull-up; + }; + + /* configuration for the usart0 "sleep" state */ + usart0_sleep: usart0_sleep { + /* group 1 */ + group1 { + /* configure PA9, PA10, PA11 and PA12 in analog mode */ + pinmux = , , , ; + }; + }; + + The 'usart0_default' child node encodes the pin configurations for a + particular state of a device; in this case, the default (that is, active) + state. Similarly, 'usart0_sleep' child node encodes the pin configurations + for the sleep state (used in device low power mode). Note that analog mode + is used for low power states because it disconnects the pin pull-up/down + resistor, schmitt trigger, and output buffer. + + As shown, pin configurations are organized in groups within each child node. + Each group can specify a list of pin function selections in the 'pinmux' + property. + + A group can also specify shared pin properties common to all the specified + pins, such as the 'bias-pull-up' property in group 2. Here is a list of + supported standard pin properties: + + - drive-push-pull: Push-pull drive mode (default, not required). Only + applies for GPIO_IN mode. + - drive-open-drain: Open-drain drive mode. Only applies for GPIO_IN mode. + - bias-disable: Disable pull-up/down (default, not required). Only applies + for GPIO_IN mode. + - bias-pull-up: Enable pull-up resistor. Only applies for GPIO_IN mode. + - bias-pull-down: Enable pull-down resistor. Only applies for GPIO_IN mode. + - slew-rate: Set the maximum speed (and so the slew-rate) of the output + signal (default: 2MHz). Only applies for ALTERNATE mode. + + Note that drive and bias options are mutually exclusive. + + Peripherals that are remappable will have their pre-defined macros suffixed + with the remap option being selected, for example: + + - CAN0_RX_PA11_NORMP: No remap + - CAN0_RX_PB8_PRMP: Partial remap + - CAN0_RX_PD0_FRMP: Full remap + + It is important that **ALL** pinmux entries share the same remap. For + example: + + &pinctrl { + can0_default: can0_default { + group1 { + pinmux = , ; + /* ^^^^ ^^^^ */ + /* CAN0 pins are remapped choosing the full remap option */ + }; + }; + }; + + To link pin configurations with a device, use a pinctrl-N property for some + number N, like this example you could place in your board's DTS file: + + #include "board-pinctrl.dtsi" + + &usart0 { + pinctrl-0 = <&usart0_default>; + pinctrl-1 = <&usart0_sleep>; + pinctrl-names = "default", "sleep"; + }; + +compatible: "gd,gd32-pinctrl-afio" + +include: gd,gd32-pinctrl-common.yaml + +child-binding: + description: | + Each child node defines the configuration for a particular state. + child-binding: + description: | + The grandchild nodes group pins that share the same pin configuration. + properties: + slew-rate: + required: false + type: string + default: "max-speed-2mhz" + enum: + - "max-speed-10mhz" + - "max-speed-2mhz" + - "max-speed-50mhz" + - "max-speed-highest" + description: | + Set the maximum speed of a pin. This setting effectively limits the + slew rate of the output signal. Defaults to "max-speed-2mhz", the SoC + default. The max-speed-highest option may not be available on all SoC + variants. If selected and not available the 50 MHz maximum speed will + be used instead. Note that usage of max-speed-highest may require + enabling the I/O compensation cell (refer to the gd,gd32-afio binding + for more details). diff --git a/modules/hal_gigadevice/Kconfig b/modules/hal_gigadevice/Kconfig index b327b9a5c71..21c60b0ec60 100644 --- a/modules/hal_gigadevice/Kconfig +++ b/modules/hal_gigadevice/Kconfig @@ -9,6 +9,11 @@ config GD32_HAS_AF_PINMUX help This option should be selected if the series use an AF pinmux model. +config GD32_HAS_AFIO_PINMUX + bool + help + This option should be selected if the series use an AFIO pinmux model. + config HAS_GD32_HAL bool select HAS_CMSIS_CORE if SOC_FAMILY_GD32_ARM diff --git a/soc/arm/gigadevice/common/pinctrl_soc.h b/soc/arm/gigadevice/common/pinctrl_soc.h index 062deb5b703..979a9eb465a 100644 --- a/soc/arm/gigadevice/common/pinctrl_soc.h +++ b/soc/arm/gigadevice/common/pinctrl_soc.h @@ -13,9 +13,14 @@ #define ZEPHYR_SOC_ARM_GIGADEVICE_COMMON_PINCTRL_SOC_H_ #include -#include #include +#ifdef CONFIG_PINCTRL_GD32_AF +#include +#else +#include +#endif /* CONFIG_PINCTRL_GD32_AF */ + #ifdef __cplusplus extern "C" { #endif @@ -24,10 +29,15 @@ extern "C" { /** @brief Type for GD32 pin. * - * Bits: + * Bits (AF model): * - 0-12: GD32_PINMUX_AF bit field. * - 13-25: Reserved. * - 26-31: Pin configuration bit field (@ref GD32_PINCFG). + * + * Bits (AFIO model): + * - 0-19: GD32_PINMUX_AFIO bit field. + * - 20-25: Reserved. + * - 26-31: Pin configuration bit field (@ref GD32_PINCFG). */ typedef uint32_t pinctrl_soc_pin_t; @@ -62,7 +72,7 @@ typedef uint32_t pinctrl_soc_pin_t; /** @endcond */ /** - * @name GD32 PUPD (values match the ones in the HAL). + * @name GD32 PUPD (values match the ones in the HAL for AF model). * @{ */ @@ -76,7 +86,7 @@ typedef uint32_t pinctrl_soc_pin_t; /** @} */ /** - * @name GD32 OTYPE (values match the ones in the HAL). + * @name GD32 OTYPE (values match the ones in the HAL for AF model). * @{ */ @@ -88,10 +98,12 @@ typedef uint32_t pinctrl_soc_pin_t; /** @} */ /** - * @name GD32 OSPEED (values match the ones in the HAL). + * @name GD32 OSPEED (values match the ones in the HAL for AF model, mode minus + * one for AFIO model). * @{ */ +#ifdef CONFIG_PINCTRL_GD32_AF /** Maximum 2MHz */ #define GD32_OSPEED_2MHZ 0U /** Maximum 25MHz */ @@ -100,6 +112,16 @@ typedef uint32_t pinctrl_soc_pin_t; #define GD32_OSPEED_50MHZ 2U /** Maximum 200MHz */ #define GD32_OSPEED_200MHZ 3U +#else +/** Maximum 10MHz */ +#define GD32_OSPEED_10MHZ 0U +/** Maximum 2MHz */ +#define GD32_OSPEED_2MHZ 1U +/** Maximum 50MHz */ +#define GD32_OSPEED_50MHZ 2U +/** Maximum speed */ +#define GD32_OSPEED_MAX 3U +#endif /* CONFIG_PINCTRL_GD32_AF */ /** @} */