/* * Copyright (c) 2021 Teslabs Engineering S.L. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #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 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; }