227 lines
5.2 KiB
C
227 lines
5.2 KiB
C
/*
|
|
* 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;
|
|
}
|