The pinctrl subsystem is the part of the Linux kernel that decides what each physical pin on a system-on-chip does and how it behaves electrically. It has two halves: pin multiplexing (pinmux), which selects whether a pin acts as, say, I2C or a GPIO, and pin configuration (pinconf), which sets properties like pull-up resistors and drive strength. Board bring-up describes these choices in device tree, the driver core applies the default set automatically at probe, and a device driver rarely writes register code of its own.
On a modern SoC there are far more internal functions than there are physical pins, so most pins are shared. One ball on the package might be a UART line, an I2C signal, a PWM output, or a plain GPIO, depending on how an internal multiplexer is programmed. Getting that selection wrong is a classic bring-up failure: the bus never responds, or two peripherals fight over the same pin. The pinctrl subsystem is the kernel framework that manages this. It lives under drivers/pinctrl/ and is exposed to device drivers through include/linux/pinctrl/consumer.h. This deep dive explains the two jobs it does, how a driver asks for its pins, what the core does automatically, and how to debug the result.
Why an SoC needs the pinctrl subsystem
Before this framework existed, each board file programmed the pin multiplexer with SoC-specific register writes scattered across the kernel. Moving a driver to a new board meant editing that board code, and the same magic register values were copied from datasheet to source by hand. The pinctrl subsystem replaced that with a single model: a pin controller driver describes the SoC’s pins, groups, and functions once, and every consumer selects what it needs by name. The board-specific detail moves out of C code and into device tree.
The two halves: pinmux and pinconf
It helps to keep the two responsibilities separate in your head.
- Pin multiplexing (pinmux) answers “which function is routed to this pin?” A function such as
i2c0is associated with a group of pins, and selecting it connects those pins to the I2C controller inside the SoC. - Pin configuration (pinconf) answers “how does this pin behave electrically?” This covers bias (pull-up, pull-down, or none), drive strength, open-drain versus push-pull, and slew rate.
The kernel builds these as two cooperating parts, CONFIG_PINMUX and CONFIG_PINCONF, on top of the core in drivers/pinctrl/core.c. A given pin controller may implement one or both.
How a board describes pins in device tree
A peripheral node references the pin state it needs through two standard properties: pinctrl-names lists the state names, and pinctrl-0, pinctrl-1, and so on point to the pin settings for each named state. The settings themselves live under the pin controller node.
&pinctrl {
i2c0_pins: i2c0-pins {
pins = "GPIO_12", "GPIO_13";
function = "i2c0";
bias-pull-up;
drive-strength = <8>;
};
};
&i2c0 {
pinctrl-names = "default";
pinctrl-0 = <&i2c0_pins>;
status = "okay";
};Here function = "i2c0" is the pinmux choice, and bias-pull-up and drive-strength are generic pinconf properties. The exact property names a controller accepts depend on whether it uses the generic pinconf bindings (CONFIG_GENERIC_PINCONF) or its own, but the common ones such as bias-pull-up, bias-disable, and drive-strength are shared across many drivers.
What the driver core does at probe
The important convenience is that most drivers do nothing at all. When the driver core binds a device, it looks for a pin state named default and applies it before the driver’s own probe() runs. This happens in the device-core pinctrl glue, so by the time an I2C or SPI driver starts, its pins are already muxed and configured. A driver that only ever needs the default state can stay completely unaware of pinctrl.
A driver has to get involved only when it needs to switch states at runtime, most often to save power. The standard state names are default, init, sleep, and idle. A driver that wants to reconfigure pins for low power at suspend does this:
struct pinctrl *p;
struct pinctrl_state *def, *sleep;
p = devm_pinctrl_get(dev);
def = pinctrl_lookup_state(p, PINCTRL_STATE_DEFAULT);
sleep = pinctrl_lookup_state(p, PINCTRL_STATE_SLEEP);
/* at suspend */
pinctrl_select_state(p, sleep);
/* at resume */
pinctrl_select_state(p, def);devm_pinctrl_get() grabs the device’s pinctrl handle and ties it to the device lifetime; pinctrl_lookup_state() resolves a state name to a handle; and pinctrl_select_state() is what actually programs the pin controller hardware. If a driver needs only the default state applied explicitly, devm_pinctrl_get_select_default() does the get and select in one call.
Inside a pin controller driver
On the provider side, an SoC vendor writes a pin controller driver that fills in a struct pinctrl_desc and registers it, usually with devm_pinctrl_register(). The descriptor names every pin and supplies up to three sets of operations:
struct pinctrl_ops— enumerates pin groups and parses the device-tree nodes into pin settings.struct pinmux_ops— lists functions and implementsset_mux(), which routes a function onto a group.struct pinconf_ops— reads and writes electrical parameters for a pin.
When a consumer selects a state, the core walks the settings and calls into these operations, which perform the actual register writes. This is the only place the SoC-specific magic values live, so a bug in pin routing is almost always traceable to one driver rather than scattered board code.
Pinctrl and GPIO
Because a pin is often either a dedicated function or a GPIO, the two subsystems are linked. A pin controller registers a GPIO range that maps GPIO numbers to its pins, and when the GPIO subsystem requests a line, the core calls the pin controller’s gpio_request_enable() to mux that pin to GPIO. A strict flag in struct pinmux_ops tells the core to refuse a GPIO request on a pin already claimed by another function, which catches conflicts early instead of letting two users drive the same pin.
Debugging through debugfs
When CONFIG_DEBUG_FS is enabled, the pinctrl subsystem exposes its state under /sys/kernel/debug/pinctrl/. Each pin controller gets a directory whose files answer the two questions above: pinmux-pins shows which function currently owns each pin, and pinconf-pins shows the electrical configuration. pins, pingroups, and gpio-ranges describe what the controller offers.
raghu@techveda.org:~$ sudo ls /sys/kernel/debug/pinctrl/
44e10800.pinmux summary
raghu@techveda.org:~$ sudo cat /sys/kernel/debug/pinctrl/44e10800.pinmux/pinmux-pins
pin 42 (PIN42): device 44e0b000.i2c function i2c0 group i2c0_pins
pin 43 (PIN43): device 44e0b000.i2c function i2c0 group i2c0_pinsWhen a bus does not come up, this file is the fastest confirmation of whether the pins are muxed to the peripheral you expect or are still sitting on their reset function. The pinconf-pins file then tells you whether the bias and drive strength match what the board needs.
If you are working through board bring-up and want to build this knowledge in order, our Linux device drivers training covers pinctrl alongside GPIO, the device tree, and the driver model.
Key takeaways
- The pinctrl subsystem does two jobs: pinmux selects a pin’s function, and pinconf sets its electrical behaviour.
- Boards describe pin states in device tree with
pinctrl-namesandpinctrl-0; the register-level detail stays in one pin controller driver. - The driver core applies the
defaultstate beforeprobe(), so most drivers need no pinctrl code at all. - Runtime state switching uses
devm_pinctrl_get(),pinctrl_lookup_state(), andpinctrl_select_state(), with standard state namesdefault,init,sleep, andidle. /sys/kernel/debug/pinctrl/<controller>/pinmux-pinsandpinconf-pinsare the quickest way to verify muxing and configuration during bring-up.
Frequently asked questions
Does my device driver need to call the pinctrl API?
Usually no. The driver core applies the pin state named default before your probe() runs, so a driver that only needs default pins can ignore pinctrl entirely. You call the API only to switch states at runtime, for example to a low-power sleep state.
What is the difference between pinmux and pinconf?
Pinmux selects which internal function is routed to a pin, such as I2C versus GPIO. Pinconf sets the pin’s electrical properties, such as pull-up or pull-down bias and drive strength. A pin controller can implement one or both.
How do I check whether a pin is muxed correctly?
Read /sys/kernel/debug/pinctrl/<controller>/pinmux-pins. It lists each pin with the device, function, and group that currently owns it, which shows whether your peripheral actually got its pins.
Where do the pin settings come from?
From device tree. A peripheral node points to a pin configuration node through pinctrl-0, and that node names the function and lists generic properties such as bias-pull-up and drive-strength.




