drivers: pinctrl: gd32: initial support for AFIO based SoCs

Add a pin control driver for GD32 SoCs using the AFIO model.

Thanks to Gerson Fernando Budke for testing and implementation
suggestions.

Co-authored-by: Gerson Fernando Budke <gerson.budke@atl-electronics.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
This commit is contained in:
Gerard Marull-Paretas 2021-11-04 23:18:09 +01:00 committed by Anas Nashif
commit f8017dc5ad
7 changed files with 428 additions and 5 deletions

View file

@ -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)

View file

@ -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.

View file

@ -0,0 +1,219 @@
/*
* Copyright (c) 2021 Teslabs Engineering S.L.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <drivers/pinctrl.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 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;
}

View file

@ -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.

View file

@ -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 <dt-bindings/pinctrl/gd32f403z(k-i-g-e-c-b)xx-pinctrl.h>
&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 = <USART0_TX_PA9_NORMP>, <USART0_CTS_PA11_NORMP>;
};
/* group 2 */
group2 {
/* configure PA10 as USART0 RX and PA12 as USART0 RTS (no remap) */
pinmux = <USART0_RX_PA10_NORMP>, <USART0_RTS_PA12_NORMP>;
/* 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 = <ANALOG_PA9>, <ANALOG_PA10>, <ANALOG_PA12>, <ANALOG_PA11>;
};
};
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_RX_PD0_FRMP>, <CAN0_TX_PD1_FRMP>;
/* ^^^^ ^^^^ */
/* 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).

View file

@ -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

View file

@ -13,9 +13,14 @@
#define ZEPHYR_SOC_ARM_GIGADEVICE_COMMON_PINCTRL_SOC_H_
#include <devicetree.h>
#include <dt-bindings/pinctrl/gd32-af.h>
#include <zephyr/types.h>
#ifdef CONFIG_PINCTRL_GD32_AF
#include <dt-bindings/pinctrl/gd32-af.h>
#else
#include <dt-bindings/pinctrl/gd32-afio.h>
#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 */
/** @} */