You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
226 lines
5.2 KiB
226 lines
5.2 KiB
/* |
|
* 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; |
|
}
|
|
|