From 33910f38767318e96f49f8a53aa1ce18fd6483eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 20:45:55 +0000 Subject: [PATCH] [documentation][device_driver_model] Add pinctrl framework documentation (EN) - module 4/26 Co-authored-by: BernardXiong <1241087+BernardXiong@users.noreply.github.com> --- .../device_driver_model/pinctrl/README.md | 560 ++++++++++++++++++ 1 file changed, 560 insertions(+) create mode 100644 documentation/6.components/device-driver/device_driver_model/pinctrl/README.md diff --git a/documentation/6.components/device-driver/device_driver_model/pinctrl/README.md b/documentation/6.components/device-driver/device_driver_model/pinctrl/README.md new file mode 100644 index 0000000000..22677199df --- /dev/null +++ b/documentation/6.components/device-driver/device_driver_model/pinctrl/README.md @@ -0,0 +1,560 @@ +# Pin Control Framework + +## Introduction + +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. + +### General Overview + +Pin control is essential for: + +- **Pin Multiplexing**: Selecting between different functions for the same physical pin (UART, SPI, I2C, GPIO, etc.) +- **Electrical Configuration**: Setting pull-up/pull-down resistors, drive strength, and other electrical properties +- **Dynamic Reconfiguration**: Changing pin configurations at runtime based on system state +- **Power Management**: Configuring pins for low-power modes +- **GPIO Management**: Coordinating GPIO and peripheral pin usage + +### RT-Thread Implementation + +The RT-Thread pinctrl framework, located in `components/drivers/pinctrl/`, integrates with the pin device driver framework and provides: + +1. **Consumer API**: Functions for device drivers to apply pin configurations +2. **Provider API**: Framework for implementing pinctrl drivers +3. **Device Tree Integration**: Automatic pin configuration from DT +4. **Pin States**: Multiple named configurations per device (default, sleep, etc.) +5. **GPIO Request**: Coordination between GPIO and peripheral usage + +## Kconfig Configuration + +### Main Configuration + +```kconfig +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 required +- `RT_USING_PIN`: Pin device driver framework required + +**Default**: Disabled (opt-in feature) + +### Pinctrl Driver Options + +#### SCMI Pinctrl Driver +```kconfig +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. + +#### Single-Register Pinctrl Driver +```kconfig +config RT_PINCTRL_SINGLE + bool "Single Pinctrl driver" + depends on RT_USING_PINCTRL + default n +``` + +Supports simple register-based pin controllers. + +## Device Tree Bindings + +### Pinctrl Provider Properties + +Pin controllers export their configuration capability using: + +```dts +#pinctrl-cells = ; /* Number of cells in pinctrl specifier */ +``` + +### Pinctrl Consumer Properties + +Devices reference pin configurations using: + +```dts +pinctrl-names = "default", "sleep"; /* State names */ +pinctrl-0 = <&state0_pins>; /* Pins for state 0 (default) */ +pinctrl-1 = <&state1_pins>; /* Pins for state 1 (sleep) */ +``` + +### Example: Pin Controller + +```dts +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; + }; +}; +``` + +### Consumer Usage Examples + +```dts +/* 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"; +}; +``` + +## Application Layer API + +### Overview + +The pinctrl API allows device drivers to apply pin configurations automatically from device tree or manually by name/index. + +### Applying Pin Configurations + +#### rt_pin_ctrl_confs_apply + +```c +rt_err_t rt_pin_ctrl_confs_apply(struct rt_device *device, int index); +``` + +Apply pin configuration by index. + +**Parameters**: +- `device`: Device structure pointer +- `index`: Configuration index (0 for pinctrl-0, 1 for pinctrl-1, etc.) + +**Returns**: +- `RT_EOK` on success +- Error code on failure + +**Example**: +```c +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_pin_ctrl_confs_apply_by_name + +```c +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 pointer +- `name`: State name (matches entry in `pinctrl-names`) + +**Returns**: +- `RT_EOK` on success +- Error code on failure + +**Example**: +```c +/* 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_pin_ctrl_confs_lookup + +```c +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 pointer +- `name`: State name to look up + +**Returns**: +- Configuration index on success +- Negative error code on failure + +**Example**: +```c +/* 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); +} +``` + +### GPIO-Related Functions + +#### rt_pin_get_named_pin + +```c +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 structure +- `propname`: Property name (e.g., "reset-gpios", "enable-gpios") +- `index`: Pin index in the property +- `out_mode`: Optional output for pin mode +- `out_value`: Optional output for pin value + +**Returns**: +- Pin number on success +- Negative error code on failure + +**Example**: +```c +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_pin_get_named_pin_count + +```c +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 structure +- `propname`: Property name + +**Returns**: +- Number of pins +- Negative error code on failure + +## Complete Application Example + +### Example: SPI Driver with Pinctrl + +```c +#include +#include +#include +#include + +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); +``` + +## Driver Implementation Guide + +### Key Structures + +#### rt_pin_ops (with pinctrl) + +```c +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); +}; +``` + +### Pin Configuration Parameters + +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 resistor +- `PIN_CONFIG_BIAS_PULL_DOWN`: Enable pull-down resistor +- `PIN_CONFIG_DRIVE_STRENGTH`: Set output drive strength +- `PIN_CONFIG_INPUT_ENABLE`: Enable input buffer +- `PIN_CONFIG_OUTPUT_ENABLE`: Enable output buffer +- `PIN_CONFIG_INPUT_DEBOUNCE`: Set input debounce time +- `PIN_CONFIG_SLEW_RATE`: Control signal slew rate + +## Best Practices + +### For Consumer Drivers + +1. **Apply pin configuration early**: During probe, before accessing hardware +2. **Use named states**: More maintainable than numeric indices +3. **Handle missing pinctrl gracefully**: Not all platforms require it +4. **Support power management**: Switch pin states for suspend/resume +5. **Check GPIO availability**: Verify GPIO pins exist before using them + +### Common Patterns + +#### Basic Pin Configuration + +```c +/* 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; +} +``` + +#### Power Management with Pinctrl + +```c +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; +} +``` + +#### GPIO with Pinctrl + +```c +/* 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); + } +} +``` + +## Troubleshooting + +### Common Issues + +1. **Pin configuration not applied** + - Check device tree: Ensure `pinctrl-*` properties exist + - Check compatible: Verify pinctrl driver is loaded + - Check Kconfig: Enable RT_USING_PINCTRL + +2. **GPIO conflicts** + - Check pin multiplexing: Pin may be claimed by peripheral + - Check pinctrl configuration: May conflict with GPIO usage + - Use `pin_ctrl_gpio_request` for coordination + +3. **Pin not working after configuration** + - Check electrical properties: Drive strength, pull resistors + - Check pin function: Verify correct function is selected + - Check hardware: Verify pin is connected correctly + +## Performance Considerations + +### Memory Usage + +- Pin configuration cached in device tree nodes +- Minimal runtime overhead +- Pin states stored per device + +### Timing + +- Configuration application: Usually fast (register writes) +- No complex calculations required +- One-time setup during probe + +## Related Modules + +- **pin**: Core pin device driver framework +- **gpio**: GPIO functionality +- **clk**: May need clocks enabled for pin configuration +- **pmdomain**: Power domain coordination + +## References + +- RT-Thread Source: `components/drivers/pinctrl/` +- Header File: `components/drivers/include/drivers/dev_pin.h` +- Pin Device Driver: `components/drivers/pin/` +- Device Tree Bindings: [Linux Pinctrl Bindings](https://www.kernel.org/doc/Documentation/devicetree/bindings/pinctrl/) +- [RT-Thread DM Documentation](../README.md)