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:
parent
5c44620753
commit
f8017dc5ad
7 changed files with 428 additions and 5 deletions
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
219
drivers/pinctrl/pinctrl_gd32_afio.c
Normal file
219
drivers/pinctrl/pinctrl_gd32_afio.c
Normal 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;
|
||||
}
|
30
dts/bindings/pinctrl/gd,gd32-afio.yaml
Normal file
30
dts/bindings/pinctrl/gd,gd32-afio.yaml
Normal 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.
|
137
dts/bindings/pinctrl/gd,gd32-pinctrl-afio.yaml
Normal file
137
dts/bindings/pinctrl/gd,gd32-pinctrl-afio.yaml
Normal 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).
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
||||
/** @} */
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue