|  | /* | 
|  | * Copyright (c) 2021 Teslabs Engineering S.L. | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include <zephyr/init.h> | 
|  | #include <zephyr/drivers/clock_control.h> | 
|  | #include <zephyr/drivers/clock_control/gd32.h> | 
|  | #include <zephyr/drivers/pinctrl.h> | 
|  |  | 
|  | #include <gd32_gpio.h> | 
|  |  | 
|  | /** 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 clock id if it exists */ | 
|  | #define GD32_PORT_CLOCK_ID_OR_NONE(nodelabel)				       \ | 
|  | COND_CODE_1(DT_NODE_EXISTS(DT_NODELABEL(nodelabel)),		       \ | 
|  | (DT_CLOCKS_CELL(DT_NODELABEL(nodelabel), id),), ()) | 
|  |  | 
|  | /** 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 clock identifiers */ | 
|  | static const uint16_t gd32_port_clkids[] = { | 
|  | GD32_PORT_CLOCK_ID_OR_NONE(gpioa) | 
|  | GD32_PORT_CLOCK_ID_OR_NONE(gpiob) | 
|  | GD32_PORT_CLOCK_ID_OR_NONE(gpioc) | 
|  | GD32_PORT_CLOCK_ID_OR_NONE(gpiod) | 
|  | GD32_PORT_CLOCK_ID_OR_NONE(gpioe) | 
|  | GD32_PORT_CLOCK_ID_OR_NONE(gpiof) | 
|  | GD32_PORT_CLOCK_ID_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(void) | 
|  | { | 
|  | uint16_t clkid = DT_CLOCKS_CELL(AFIO_NODE, id); | 
|  |  | 
|  |  | 
|  | (void)clock_control_on(GD32_CLOCK_CONTROLLER, | 
|  | (clock_control_subsys_t)&clkid); | 
|  |  | 
|  | #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 port, pin_bit, reg_val; | 
|  | volatile uint32_t *reg; | 
|  | uint16_t clkid; | 
|  |  | 
|  | port_idx = GD32_PORT_GET(pin); | 
|  | __ASSERT_NO_MSG(port_idx < ARRAY_SIZE(gd32_port_addrs)); | 
|  |  | 
|  | clkid = gd32_port_clkids[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; | 
|  | } | 
|  |  | 
|  | (void)clock_control_on(GD32_CLOCK_CONTROLLER, | 
|  | (clock_control_subsys_t)&clkid); | 
|  |  | 
|  | reg_val = *reg; | 
|  | reg_val &= ~GPIO_MODE_MASK(pin_num); | 
|  |  | 
|  | if (mode == GD32_MODE_ALTERNATE) { | 
|  | uint8_t new_mode; | 
|  |  | 
|  | new_mode = configure_spd(port, pin_bit, GD32_OSPEED_GET(pin)); | 
|  |  | 
|  | if (GD32_OTYPE_GET(pin) == GD32_OTYPE_PP) { | 
|  | new_mode |= GPIO_MODE_ALT_PP; | 
|  | } else { | 
|  | new_mode |= GPIO_MODE_ALT_OD; | 
|  | } | 
|  |  | 
|  | reg_val |= GPIO_MODE_SET(pin_num, new_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; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @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; | 
|  | } |