The Pin Control (pinctrl) framework in RT-Thread provides a standardized way to configure pin multiplexing, electrical properties, and GPIO functionality. It allows device drivers to dynamically configure pins without hard-coding hardware-specific details.
Pin control is essential for:
The RT-Thread pinctrl framework, located in components/drivers/pinctrl/, integrates with the pin device driver framework and provides:
menuconfig RT_USING_PINCTRL
bool "Using Pin controllers device drivers"
depends on RT_USING_DM
depends on RT_USING_PIN
default n
Location in menuconfig:
RT-Thread Components → Device Drivers → Using Pin controllers device drivers
Dependencies:
RT_USING_DM: Device driver model requiredRT_USING_PIN: Pin device driver framework requiredDefault: Disabled (opt-in feature)
config RT_PINCTRL_SCMI
bool "Pinctrl driver via ARM SCMI interface"
depends on RT_USING_PINCTRL
depends on RT_FIRMWARE_ARM_SCMI
default n
Supports pin control through ARM SCMI interface.
config RT_PINCTRL_SINGLE
bool "Single Pinctrl driver"
depends on RT_USING_PINCTRL
default n
Supports simple register-based pin controllers.
Pin controllers export their configuration capability using:
#pinctrl-cells = <n>; /* Number of cells in pinctrl specifier */
Devices reference pin configurations using:
pinctrl-names = "default", "sleep"; /* State names */
pinctrl-0 = <&state0_pins>; /* Pins for state 0 (default) */
pinctrl-1 = <&state1_pins>; /* Pins for state 1 (sleep) */
pio: pinctrl@1c20800 {
compatible = "vendor,pinctrl";
reg = <0x1c20800 0x400>;
#pinctrl-cells = <1>;
/* Pin group definitions */
uart0_pins: uart0-pins {
pins = "PB8", "PB9";
function = "uart0";
drive-strength = <40>;
bias-pull-up;
};
uart0_sleep_pins: uart0-sleep-pins {
pins = "PB8", "PB9";
function = "gpio";
bias-disable;
};
spi0_pins: spi0-pins {
pins = "PC0", "PC1", "PC2", "PC3";
function = "spi0";
drive-strength = <40>;
};
i2c0_pins: i2c0-pins {
pins = "PA11", "PA12";
function = "i2c0";
bias-pull-up;
};
};
/* UART with pin configuration */
uart0: serial@1c28000 {
compatible = "vendor,uart";
reg = <0x1c28000 0x400>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&uart0_pins>;
pinctrl-1 = <&uart0_sleep_pins>;
status = "okay";
};
/* SPI with single pin state */
spi0: spi@1c68000 {
compatible = "vendor,spi";
reg = <0x1c68000 0x1000>;
pinctrl-names = "default";
pinctrl-0 = <&spi0_pins>;
status = "okay";
};
/* I2C with pin configuration */
i2c0: i2c@1c2ac00 {
compatible = "vendor,i2c";
reg = <0x1c2ac00 0x400>;
pinctrl-names = "default";
pinctrl-0 = <&i2c0_pins>;
status = "okay";
};
The pinctrl API allows device drivers to apply pin configurations automatically from device tree or manually by name/index.
rt_err_t rt_pin_ctrl_confs_apply(struct rt_device *device, int index);
Apply pin configuration by index.
Parameters:
device: Device structure pointerindex: Configuration index (0 for pinctrl-0, 1 for pinctrl-1, etc.)Returns:
RT_EOK on successExample:
struct rt_device *dev = &pdev->parent;
/* Apply default pin configuration (pinctrl-0) */
ret = rt_pin_ctrl_confs_apply(dev, 0);
if (ret != RT_EOK) {
LOG_E("Failed to apply pin configuration: %d", ret);
return ret;
}
rt_err_t rt_pin_ctrl_confs_apply_by_name(struct rt_device *device, const char *name);
Apply pin configuration by state name.
Parameters:
device: Device structure pointername: State name (matches entry in pinctrl-names)Returns:
RT_EOK on successExample:
/* Apply default pin configuration */
rt_pin_ctrl_confs_apply_by_name(dev, "default");
/* Later, switch to sleep configuration */
rt_pin_ctrl_confs_apply_by_name(dev, "sleep");
rt_ssize_t rt_pin_ctrl_confs_lookup(struct rt_device *device, const char *name);
Look up the index of a named pin configuration.
Parameters:
device: Device structure pointername: State name to look upReturns:
Example:
/* Find index of "sleep" configuration */
rt_ssize_t idx = rt_pin_ctrl_confs_lookup(dev, "sleep");
if (idx >= 0) {
rt_pin_ctrl_confs_apply(dev, idx);
}
rt_ssize_t rt_pin_get_named_pin(struct rt_device *dev, const char *propname,
int index, rt_uint8_t *out_mode, rt_uint8_t *out_value);
Get a GPIO pin from device tree property.
Parameters:
dev: Device structurepropname: Property name (e.g., "reset-gpios", "enable-gpios")index: Pin index in the propertyout_mode: Optional output for pin modeout_value: Optional output for pin valueReturns:
Example:
rt_uint8_t mode, value;
rt_ssize_t reset_pin;
/* Get reset GPIO from device tree */
reset_pin = rt_pin_get_named_pin(dev, "reset-gpios", 0, &mode, &value);
if (reset_pin >= 0) {
rt_pin_mode(reset_pin, PIN_MODE_OUTPUT);
rt_pin_write(reset_pin, PIN_LOW); /* Assert reset */
}
rt_ssize_t rt_pin_get_named_pin_count(struct rt_device *dev, const char *propname);
Get the number of GPIOs in a device tree property.
Parameters:
dev: Device structurepropname: Property nameReturns:
#include <rtthread.h>
#include <drivers/platform.h>
#include <drivers/spi.h>
#include <drivers/pin.h>
struct spi_device {
void *base;
int irq;
struct rt_clk *clk;
rt_ssize_t cs_pin;
struct rt_spi_bus spi_bus;
};
static rt_err_t spi_probe(struct rt_platform_device *pdev)
{
rt_err_t ret;
struct rt_device *dev = &pdev->parent;
struct spi_device *spi;
/* Allocate device structure */
spi = rt_calloc(1, sizeof(*spi));
if (!spi)
return -RT_ENOMEM;
/* Map MMIO region */
spi->base = rt_dm_dev_iomap(dev, 0);
if (!spi->base) {
ret = -RT_ERROR;
goto err_free;
}
/* Apply pin configuration from device tree */
ret = rt_pin_ctrl_confs_apply_by_name(dev, "default");
if (ret != RT_EOK) {
LOG_E("Failed to apply pin configuration: %d", ret);
goto err_unmap;
}
/* Get optional chip select GPIO */
spi->cs_pin = rt_pin_get_named_pin(dev, "cs-gpios", 0, RT_NULL, RT_NULL);
if (spi->cs_pin >= 0) {
rt_pin_mode(spi->cs_pin, PIN_MODE_OUTPUT);
rt_pin_write(spi->cs_pin, PIN_HIGH); /* Deassert CS */
LOG_I("Using GPIO %d for CS", spi->cs_pin);
}
/* Get clock */
spi->clk = rt_clk_get_by_name(dev, "spi");
if (!spi->clk) {
LOG_E("Failed to get SPI clock");
ret = -RT_ERROR;
goto err_unmap;
}
ret = rt_clk_prepare_enable(spi->clk);
if (ret != RT_EOK) {
goto err_put_clk;
}
/* Initialize SPI bus */
spi->spi_bus.parent.user_data = spi;
ret = rt_spi_bus_register(&spi->spi_bus, rt_dm_dev_get_name(dev), &spi_ops);
if (ret != RT_EOK) {
goto err_disable_clk;
}
pdev->priv = spi;
LOG_I("SPI device registered");
return RT_EOK;
err_disable_clk:
rt_clk_disable_unprepare(spi->clk);
err_put_clk:
rt_clk_put(spi->clk);
err_unmap:
rt_iounmap(spi->base);
err_free:
rt_free(spi);
return ret;
}
static rt_err_t spi_suspend(struct rt_device *dev)
{
/* Switch to sleep pin configuration */
return rt_pin_ctrl_confs_apply_by_name(dev, "sleep");
}
static rt_err_t spi_resume(struct rt_device *dev)
{
/* Restore default pin configuration */
return rt_pin_ctrl_confs_apply_by_name(dev, "default");
}
static const struct rt_ofw_node_id spi_ofw_ids[] = {
{ .compatible = "vendor,spi" },
{ /* sentinel */ }
};
static struct rt_platform_driver spi_driver = {
.name = "spi",
.ids = spi_ofw_ids,
.probe = spi_probe,
};
RT_PLATFORM_DRIVER_EXPORT(spi_driver);
struct rt_pin_ops {
/* Standard pin operations */
void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_uint8_t mode);
void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_uint8_t value);
rt_ssize_t (*pin_read)(struct rt_device *device, rt_base_t pin);
/* Pinctrl-specific operations */
rt_err_t (*pin_ctrl_confs_apply)(struct rt_device *device, void *fw_conf_np);
rt_err_t (*pin_ctrl_gpio_request)(struct rt_device *device, rt_base_t gpio, rt_uint32_t flags);
};
Common pin configuration parameters (from PIN_CONFIG_* enum):
PIN_CONFIG_BIAS_DISABLE: Disable bias (no pull-up/down)PIN_CONFIG_BIAS_PULL_UP: Enable pull-up resistorPIN_CONFIG_BIAS_PULL_DOWN: Enable pull-down resistorPIN_CONFIG_DRIVE_STRENGTH: Set output drive strengthPIN_CONFIG_INPUT_ENABLE: Enable input bufferPIN_CONFIG_OUTPUT_ENABLE: Enable output bufferPIN_CONFIG_INPUT_DEBOUNCE: Set input debounce timePIN_CONFIG_SLEW_RATE: Control signal slew rate/* Apply default pin configuration */
ret = rt_pin_ctrl_confs_apply_by_name(dev, "default");
if (ret != RT_EOK && ret != -RT_ENOSYS) {
/* Real error, not just missing pinctrl */
LOG_E("Pin configuration failed: %d", ret);
return ret;
}
static rt_err_t device_suspend(struct rt_device *dev)
{
/* Save device state */
/* Apply sleep pin configuration */
rt_pin_ctrl_confs_apply_by_name(dev, "sleep");
/* Disable clocks, etc. */
return RT_EOK;
}
static rt_err_t device_resume(struct rt_device *dev)
{
/* Enable clocks, etc. */
/* Restore default pin configuration */
rt_pin_ctrl_confs_apply_by_name(dev, "default");
/* Restore device state */
return RT_EOK;
}
/* Get multiple GPIOs */
rt_ssize_t num_gpios = rt_pin_get_named_pin_count(dev, "reset-gpios");
for (int i = 0; i < num_gpios; i++) {
rt_ssize_t pin = rt_pin_get_named_pin(dev, "reset-gpios", i,
RT_NULL, RT_NULL);
if (pin >= 0) {
rt_pin_mode(pin, PIN_MODE_OUTPUT);
rt_pin_write(pin, PIN_LOW);
}
}
Pin configuration not applied
pinctrl-* properties existGPIO conflicts
pin_ctrl_gpio_request for coordinationPin not working after configuration
components/drivers/pinctrl/components/drivers/include/drivers/dev_pin.hcomponents/drivers/pin/