This article introduces the relevant knowledge of the Linux pinctrl subsystem. Taking RK3568 as an example, it discusses the configuration rules of the pinctrl device tree and the driver matching mechanism based on the platform bus, and elaborates on core concepts such as pin groups and functions, as well as the key data structures of pinctrl under the Rockchip platform.
GPIO pins ⊂ SoC pins, and SoC pins are uniformly managed by pinctrl. The official Linux pinctrl binding recommends:GPIO controller as a child node of the pin controller
GPIO bank
Name
Pin Count
Base Address
GPIO0
gpio0
32
0xfdd60000
GPIO1
gpio1
32
0xfe740000
GPIO2
gpio2
32
0xfe750000
GPIO3
gpio3
32
0xfe760000
GPIO4
gpio4
32
0xfe770000
Each bank:
a set of registers
an interrupt number
two clocks (PCLK / DBCLK)
controls a set of physical pins: direction, level, GPIO interrupt
whether it isrk3568.dtsithe pinctrl node in the device tree, or the aboverk3568-pinctrl.dtsia series of multiplexing relationships in the device tree are written by Rockchip’s original BSP engineers; we only need to know how to use them, while the pinctrl client device tree is written by ourselves based on specific requirements
pinctrl driver
the device tree only stores device description information, while the specific functional implementation depends on the corresponding pinctrl driver.
according tork3568.dtsithe compatible property of the pinctrl node in the device treeto search for the driver, it can be found that the pinctrl driver file is in the kernel sourcedriver/pinctrl/pinctrl-rockchip.c
it can be seen that the pinctrl driver uses the platform bus, usingpostcore_initcallrather thanmodule_init
groups and function
in the pinctrl subsystem, there are two key concepts:Pin groupsandfunction
**Pin groups:**A pin group is a collection of pins with similar functions, constraints, or that work together. Each pin group is typically associated with a specific hardware function or peripheral. For example, one pin group can be used to control a serial communication interface (such as UART or SPI), while another pin group can be used to drive GPIO.
**Function:**Defines the functions on the chip that have peripheral capabilities. Each function node corresponds to configuration information for one or more IO groups. These functions can be peripheral functions such as UART, SPI, I2C, etc.
Next, usingrk3568-pinctrl.dtsiin the device tree filecan0andcan1as examples to illustrate the above content, the details are as follows:
In the device tree above,can0 and can1 correspond to two different functions,which are the CAN0 controller and CAN1 controller respectively。and each controller has two different groups of pin groups。
CAN0 Controller:
Pin Groupcan0m0-pins: This is the first pin group of the CAN0 controller, used to configure the pins of CAN0. It defines two pins:RK_PB4andRK_PB3
RK_PB4is used for the receive pin of CAN0 (can0_rxm0)
RK_PB3is used for the transmit pin of CAN0 (can0_txm0)。
Pin Groupcan0m1-pins: This is the second pin group of the CAN0 controller, also used to configure the pins of CAN0. It defines two pins:RK_PA2andRK_PA1。
RK_PA2is used for the receive pin of CAN0 (can0_rxm1)
RK_PA1is used for the transmit pin of CAN0 (can0_txm1)。
CAN1 Controller:
Pin Groupcan1m0-pins: This is the first pin group of the CAN1 controller, used to configure the pins of CAN1. It defines two pins:RK_PA0andRK_PA1。
RK_PA0Receive pin for CAN1 (can1_rxm0)
RK_PA1Transmit pin for CAN1 (can1_txm0)。
Pin groupcan1m1-pins: This is the second pin group of the CAN1 controller, also used to configure CAN1 pins. It defines two pins:RK_PC2andRK_PC3。
/** * struct pinctrl_desc - pin controller descriptor, register this to pin * control subsystem * @name: name for the pin controller * @pins: an array of pin descriptors describing all the pins handled by * this pin controller * @npins: number of descriptors in the array, usually just ARRAY_SIZE() * of the pins field above * @pctlops: pin control operation vtable, to support global concepts like * grouping of pins, this is optional. * @pmxops: pinmux operations vtable, if you support pinmuxing in your driver * @confops: pin config operations vtable, if you support pin configuration in * your driver * @owner: module providing the pin controller, used for refcounting * @num_custom_params: Number of driver-specific custom parameters to be parsed * from the hardware description * @custom_params: List of driver_specific custom parameters to be parsed from * the hardware description * @custom_conf_items: Information how to print @params in debugfs, must be * the same size as the @custom_params, i.e. @num_custom_params * @link_consumers: If true create a device link between pinctrl and its * consumers (i.e. the devices requesting pin control states). This is * sometimes necessary to ascertain the right suspend/resume order for * example. */ structpinctrl_desc { constchar *name;// Name of the pin controller conststructpinctrl_pin_desc *pins;// Pin descriptor array unsignedint npins;// Size of the pin descriptor array conststructpinctrl_ops *pctlops;// Pin control operation function pointer conststructpinmux_ops *pmxops;// Pin mux operation function pointer conststructpinconf_ops *confops;// Pin configuration operation function pointer structmodule *owner;// Module that owns this structure #ifdef CONFIG_GENERIC_PINCONF unsignedint num_custom_params;// Custom parameter count conststructpinconf_generic_params *custom_params;// Custom parameter array conststructpin_config_item *custom_conf_items;// Custom configuration item array #endif bool link_consumers; };
const char *name: Name of the pin controller, used to identify the uniqueness of the pin controller.
const struct pinctrl_pin_desc *pins: Pin descriptor array, is a pointer to pin descriptors, used to describe the properties and configuration of pins. Each pin descriptor contains information such as the pin’s name, number, and mode.
unsigned int npins: indicatesthe number of elements in the pin descriptor array, used to determine the length of the pin descriptor array.
const struct pinctrl_ops *pctlops: Pointer to pin control operation functions, used to define the operation interface of the pin controller. Through these operation functions, you canconfigure, enable, and disable pinsand other operations.
const struct pinmux_ops *pmxops: Pointer to pin multiplexing operation functions, used to define the multiplexing functionality of pins. The multiplexing function allowsswitching the pin’s function to different modesto meet different device requirements
const struct pinconf_ops *confops: Pointer to the pin configuration operation function, used to define additional configuration options for the pin. These configuration options can includethe pin’s pull-up, pull-down configuration, electrical characteristics, etc.。
struct module *owner: Pointer to the module that owns the pin controller structure. This field is used to track the owner of the pin controller structure.
unsigned int num_custom_params: Indicates the number of custom configuration parameters, used to describe the custom configuration parameters of the pin controller.
const struct pinconf_generic_params *custom_params: Pointer to custom configuration parameters, used to describe the attributes of the pin controller’s custom configuration parameters. Custom configuration parameters can be defined based on specific requirements to extend the configuration options of the pin controller.
const struct pin_config_item *custom_conf_items: Pointer to custom configuration items, used to describe the attributes of the pin controller’s custom configuration items. Custom configuration items can be defined based on specific requirements to extend the configuration options of the pin controller.
struct pinctrl_pin_desc
1 2 3 4 5 6 7 8 9 10 11 12
/** * struct pinctrl_pin_desc - boards/machines provide information on their * pins, pads or other muxable units in this struct * @number: unique pin number from the global pin number space * @name: a name for this pin * @drv_data: driver-defined per-pin data. pinctrl core does not touch this */ structpinctrl_pin_desc { unsigned number; constchar *name; void *drv_data; };
struct pinctrl_dev
In therockchip_pinctrl_probefunction, throughinfo->pctl_dev = devm_pinctrl_register(dev, ctrldesc, info)setting
/** * struct pinctrl_dev - pin control class device * @node: node to include this pin controller in the global pin controller list * @desc: the pin controller descriptor supplied when initializing this pin * controller * @pin_desc_tree: each pin descriptor for this pin controller is stored in * this radix tree * @pin_group_tree: optionally each pin group can be stored in this radix tree * @num_groups: optionally number of groups can be kept here * @pin_function_tree: optionally each function can be stored in this radix tree * @num_functions: optionally number of functions can be kept here * @gpio_ranges: a list of GPIO ranges that is handled by this pin controller, * ranges are added to this list at runtime * @dev: the device entry for this pin controller * @owner: module providing the pin controller, used for refcounting * @driver_data: driver data for drivers registering to the pin controller * subsystem * @p: result of pinctrl_get() for this device * @hog_default: default state for pins hogged by this device * @hog_sleep: sleep state for pins hogged by this device * @mutex: mutex taken on each pin controller specific action * @device_root: debugfs root for this device */ structpinctrl_dev { structlist_headnode;// the node in the pinctrl_dev linked list structpinctrl_desc *desc;// the pin controller description pointer structradix_tree_rootpin_desc_tree;// Radix tree of pin descriptor structures #ifdef CONFIG_GENERIC_PINCTRL_GROUPS structradix_tree_rootpin_group_tree;// Root of radix tree for pin group structures unsignedint num_groups;// Number of pin groups #endif #ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS structradix_tree_rootpin_function_tree;// Root of radix tree for pin function structures unsignedint num_functions;// Number of pin functions #endif structlist_headgpio_ranges;// GPIO range linked list structdevice *dev;// Device structure pointer structmodule *owner;// Pointer to the module owning the pinctrl_dev structure void *driver_data;// Driver's private data pointer structpinctrl *p;// Pointer to pinctrl structure structpinctrl_state *hog_default;// Hog default state structpinctrl_state *hog_sleep;// Hog sleep state structmutexmutex;// Mutex lock #ifdef CONFIG_DEBUG_FS structdentry *device_root;// Root node of the debug filesystem #endif };
struct rockchip_pinctrl
Rockchip, to adapt to the specific requirements and functions of Rockchip chips, hasstruct pinctrl_descwas encapsulated again. The encapsulatedstruct rockchip_pinctrlstructure is based onstruct pinctrl_descand adds fields and pointers related to Rockchip chips. This encapsulation provides better integration, ease of use, and extensibility while maintaining compatibility with the generic pin controller framework.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
structrockchip_pinctrl { structregmap *regmap_base;// Basic register mapping pointer int reg_size; // Register size structregmap *regmap_pull;// Pull register mapping pointer structregmap *regmap_pmu;// Power management unit register mapping pointer structdevice *dev;// Device pointer structrockchip_pin_ctrl *ctrl;// Rockchip chip pin controller pointer structpinctrl_descpctl;// Pin controller descriptor structpinctrl_dev *pctl_dev;// Pin controller device pointer structrockchip_pin_group *groups;// Rockchip chip pin group pointer unsignedint ngroups; // Number of pin groups structrockchip_pmx_func *functions;// Rockchip chip pin function pointer unsignedint nfunctions;// Number of pin functions };
struct regmap *regmap_base: Pointer to the basic register map (regmap). The basic register map is an interface for accessing chip registers, providing read and write operations on chip registers.
int reg_size: Indicates the byte size of the register, used to determine the address range of the register.
struct regmap *regmap_pull: Pointer to the pull register map. The pull register map is used to control the pull-up and pull-down functions on pins.
struct regmap *regmap_pmu: Pointer to the Power Management Unit (PMU) register map. The PMU register map is used to control the power management functions of pins.
struct device *dev: Pointer to the device structure. The device structure is used to represent hardware-related devices, including the device’s physical address, interrupts, and other information.
struct rockchip_pin_ctrl *ctrl: Pointer to the Rockchip chip pin controller. This structure stores information and operations specific to the Rockchip chip’s pin controller.
struct pinctrl_desc pctl: Contains an instance of the struct pinctrl_desc structure. Used to describe the properties and operations of the pin controller, including the pin controller’s name, pin descriptor array, function pointers, etc.
struct pinctrl_dev *pctl_dev: Pointer to the pin controller device structure. The pin controller device structure represents the device instance of the pin controller in the system, containing device information and operation interfaces related to the pin controller.
struct rockchip_pin_group *groups: Pointer to the Rockchip chip pin group. A pin group is a set of related pins that can be configured and managed together.
unsigned int ngroups: Indicates the size of the pin group array, used to determine the length of the pin group array.
struct rockchip_pmx_func *functions: Pointer to the Rockchip chip pin function. The pin function defines the different roles a pin can assume, such as UART, SPI, I2C, etc.
unsigned int nfunctions: The number of pin functions. It indicates the size of the pin function array, used to determine the length of the pin function array.
structrockchip_pin_ctrl { structrockchip_pin_bank *pin_banks;// Array pointer of pin banks u32 nr_banks;// Number of pin banks u32 nr_pins;// Number of pins char *label;// Pin controller label enumrockchip_pinctrl_typetype;// Pin controller type int grf_mux_offset;// Offset of GRF (Global Register File) mux register int pmu_mux_offset;// Offset of PMU (Power Management Unit) mux register int grf_drv_offset;// Offset of GRF drive register int pmu_drv_offset; structrockchip_mux_recalced_data *iomux_recalced; u32 niomux_recalced; structrockchip_mux_route_data *iomux_routes; u32 niomux_routes;
int (*pull_calc_reg)(struct rockchip_pin_bank *bank, int pin_num, struct regmap **regmap, int *reg, u8 *bit); int (*drv_calc_reg)(struct rockchip_pin_bank *bank, int pin_num, struct regmap **regmap, int *reg, u8 *bit); int (*schmitt_calc_reg)(struct rockchip_pin_bank *bank, int pin_num, struct regmap **regmap, int *reg, u8 *bit); int (*slew_rate_calc_reg)(struct rockchip_pin_bank *bank, int pin_num, struct regmap **regmap, int *reg, u8 *bit); };
/** * struct rockchip_pin_bank * @dev: the pinctrl device bind to the bank * @reg_base: register base of the gpio bank * @regmap_pull: optional separate register for additional pull settings * @clk: clock of the gpio bank * @db_clk: clock of the gpio debounce * @irq: interrupt of the gpio bank * @saved_masks: Saved content of GPIO_INTEN at suspend time. * @pin_base: first pin number * @nr_pins: number of pins in this bank * @name: name of the bank * @bank_num: number of the bank, to account for holes * @iomux: array describing the 4 iomux sources of the bank * @drv: array describing the 4 drive strength sources of the bank * @pull_type: array describing the 4 pull type sources of the bank * @valid: is all necessary information present * @of_node: dt node of this bank * @drvdata: common pinctrl basedata * @domain: irqdomain of the gpio bank * @gpio_chip: gpiolib chip * @grange: gpio range * @slock: spinlock for the gpio bank * @toggle_edge_mode: bit mask to toggle (falling/rising) edge mode * @recalced_mask: bit mask to indicate a need to recalulate the mask * @route_mask: bits describing the routing pins of per bank * @deferred_output: gpio output settings to be done after gpio bank probed * @deferred_lock: mutex for the deferred_output shared btw gpio and pinctrl */ structrockchip_pin_bank { structdevice *dev; void __iomem *reg_base; structregmap *regmap_pull; structclk *clk; structclk *db_clk; int irq; u32 saved_masks; u32 pin_base; u8 nr_pins; char *name; u8 bank_num; structrockchip_iomuxiomux[4]; structrockchip_drvdrv[4]; enumrockchip_pin_pull_typepull_type[4]; bool valid; structdevice_node *of_node; structrockchip_pinctrl *drvdata; structirq_domain *domain; structgpio_chipgpio_chip; structpinctrl_gpio_rangegrange; raw_spinlock_t slock; conststructrockchip_gpio_regs *gpio_regs; u32 gpio_type; u32 toggle_edge_mode; u32 recalced_mask; u32 route_mask; structlist_headdeferred_pins; structmutexdeferred_lock; };
rockchip_pinctrl_probe()
The probe function in Rockchip’s pinctrl driver is as follows:
staticintrockchip_pinctrl_probe(struct platform_device *pdev) { structrockchip_pinctrl *info;// Pointer to Rockchip GPIO controller info structure structdevice *dev = &pdev->dev; // Pointer to device structure structdevice_node *np = dev->of_node, *node; // Pointer to device node structrockchip_pin_ctrl *ctrl;// Pointer to Rockchip GPIO controller config structure structresource *res;// Pointer to device resource void __iomem *base;// Register base address pointer int ret;
// Check if the device tree node in the device structure exists; if not, report an error and return an error code if (!dev->of_node) return dev_err_probe(dev, -ENODEV, "device tree node not found\n");
// Use devm_kzalloc function to allocate a rockchip_pinctrl structure memory and initialize it to 0 // Allocate and initialize a rockchip_pinctrl structure info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM;
// Assign the device structure pointer to info->dev so that the device structure information can be used in subsequent code. info->dev = dev;
// Call rockchip_pinctrl_get_soc_data function to obtain the rockchip related to the device based on device information_pin_ctrl structure. // If acquisition fails, report an error and return an error code. ctrl = rockchip_pinctrl_get_soc_data(info, pdev);
if (!ctrl) return dev_err_probe(dev, -EINVAL, "driver data not available\n"); // Assign the obtained structure pointer to info->ctrl info->ctrl = ctrl;
// Use of_parse_phandle function to parse the node named "rockchip,grf" in the device tree and obtain the register mapping base address node = of_parse_phandle(np, "rockchip,grf", 0); if (node) { // If parsing is successful, call syscon_node_to_The regmap function converts the node to the base address of the register map and stores the result in info->regmap_in base info->regmap_base = syscon_node_to_regmap(node); of_node_put(node); if (IS_ERR(info->regmap_base)) return PTR_ERR(info->regmap_base); } else {// If the "rockchip,grf" node is not found, obtain a resource of type IORESOURCE_MEM to get the register base address
// Through platform_get_The resource function obtains a resource of type IORESOURCE_MEM to get the base address of the registers. // Then use devm_ioremap_The resource function maps the resource into memory and stores the result in base. base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(base)) return PTR_ERR(base); // Configure the maximum register address and name of the register map rockchip_regmap_config.max_register = resource_size(res) - 4; rockchip_regmap_config.name = "rockchip,pinctrl"; // Use devm_regmap_init_The mmio function initializes the register map and stores the result in info->regmap_in base info->regmap_base = devm_regmap_init_mmio(dev, base, &rockchip_regmap_config);
/* to check for the old dt-bindings */ info->reg_size = resource_size(res);// Check the old dt-bindings
/* Honor the old binding, with pull registers as 2nd resource */ // If the controller type is RK3188 and reg_size is less than 0x200, then obtain the second IORESOURCE_resource of type MEM as the base address of the pull register if (ctrl->type == RK3188 && info->reg_size < 0x200) { base = devm_platform_get_and_ioremap_resource(pdev, 1, &res); if (IS_ERR(base)) return PTR_ERR(base); // Configure the maximum register address and name for the pull register mapping rockchip_regmap_config.max_register = resource_size(res) - 4; rockchip_regmap_config.name = "rockchip,pinctrl-pull"; info->regmap_pull = devm_regmap_init_mmio(dev, base, &rockchip_regmap_config); } }
/* try to find the optional reference to the pmu syscon */ // Attempt to find the optional pmu syscon reference node = of_parse_phandle(np, "rockchip,pmu", 0); if (node) { // Call syscon_node_to_The regmap function converts the node to the base address of the register mapping and stores the result in info->regmap_in pmu info->regmap_pmu = syscon_node_to_regmap(node); of_node_put(node); if (IS_ERR(info->regmap_pmu)) return PTR_ERR(info->regmap_pmu); } // Perform special handling for certain SoCs if (IS_ENABLED(CONFIG_CPU_RK3308) && ctrl->type == RK3308) { ret = rk3308_soc_data_init(info); if (ret) return ret; } // Register the rockchip_pinctrl device ret = rockchip_pinctrl_register(pdev, info); if (ret) return ret; // Set the private data of pdev to info platform_set_drvdata(pdev, info); g_pctldev = info->pctl_dev;
// Register the GPIO device ret = of_platform_populate(np, NULL, NULL, &pdev->dev); if (ret) return dev_err_probe(dev, ret, "failed to register gpio device\n");
dev_info(dev, "probed %s\n", dev_name(dev));
return0; }
The role of the above probe function is to initialize and configure the Rockchip GPIO controller, and store relevant information instruct rockchip_pinctrl info, and finally register the relevant device and GPIO interface.
Line 26 callsctrl = rockchip_pinctrl_get_soc_data(info, pdev);Obtain the device-relatedrockchip_pin_ctrlstructure based on the device information.
Lines 34 to 70 handleTwo Device Tree binding methods
New-style binding: references a system controller (e.g., GRF) via phandle;
Old-style binding: directly declares memory resources in the pinctrl node (IORESOURCE_MEM)。
syscon_node_to_regmap(node)is a mechanism provided by the Linux kernel: converts a syscon node into aregmapinterface.regmapis a generic framework in the kernel for abstracting register read/write, supporting MMIO, I2C, SPI, etc.
Since the device tree contains:rockchip,grf = <&grf>;therefore the new-style binding is used
Line 89 callsret = rockchip_pinctrl_register(pdev, info);registersrockchip_pinctrldevice
probe function mind map
pinctrl
rockchip_pinctrl_get_soc_data()
According to the device treecompatible = "rockchip,rk3568-pinctrl"Match the pin description data of RK3568, and dynamically calculate the actual offset addresses of the multiplexing (iomux) and drive strength (drv) registers in each GPIO bank, preparing for subsequent pin configuration.
EachgpioXnode describes the GPIO controller hardware resources (address, interrupt, clock), but does not include pin multiplexing information — these are controlled by the pinctrl driver throughGRF/PMUGRFregisters.
RK3568 has 5 GPIO banks (gpio0~gpio4). Each GPIO bank has up to 32 pins, divided into 4 groups (8 pins per group), each group has its own:
iomuxRegister: controls multiplexing function (GPIO / I2C / UART…)
/* retrieve the soc specific data */ staticstruct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data( struct rockchip_pinctrl *d, struct platform_device *pdev) { structdevice *dev = &pdev->dev; structdevice_node *node = dev->of_node; conststructof_device_id *match; structrockchip_pin_ctrl *ctrl; structrockchip_pin_bank *bank; int grf_offs, pmu_offs, drv_grf_offs, drv_pmu_offs, i, j;
// Through of_match_node() in rockchip_pinctrl_dt_match table to find the SoC matching the current device node. match = of_match_node(rockchip_pinctrl_dt_match, node); ctrl = (struct rockchip_pin_ctrl *)match->data; // Specific SoC pin_banks replacement (variant support) if (IS_ENABLED(CONFIG_CPU_RK3308) && soc_is_rk3308bs()) ctrl->pin_banks = rk3308bs_pin_banks; if (IS_ENABLED(CONFIG_CPU_PX30) && soc_is_px30s()) ctrl->pin_banks = px30s_pin_banks;
// Initialize global register offsets grf_offs = ctrl->grf_mux_offset; pmu_offs = ctrl->pmu_mux_offset; drv_pmu_offs = ctrl->pmu_drv_offset; drv_grf_offs = ctrl->grf_drv_offset; bank = ctrl->pin_banks; // Traverse all pin banks (RK3568 has 5 in total: gpio0~gpio4) for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { int bank_pins = 0;
/* calculate iomux and drv offsets */ // Each GPIO bank of RK3568 has up to 32 pins, divided into 4 groups (8 pins per group), each group has its own: // iomux register: controls multiplexing function (GPIO / I2C / UART...) // drv register: controls drive strength (2mA / 4mA / 8mA...) for (j = 0; j < 4; j++) { structrockchip_iomux *iom = &bank->iomux[j]; structrockchip_drv *drv = &bank->drv[j]; int inc;
if (bank_pins >= bank->nr_pins) break;
/* preset iomux offset value, set new start value */ if (iom->offset >= 0) { if ((iom->type & IOMUX_SOURCE_PMU) || (iom->type & IOMUX_L_SOURCE_PMU)) pmu_offs = iom->offset; else grf_offs = iom->offset; } else { /* set current iomux offset */ iom->offset = ((iom->type & IOMUX_SOURCE_PMU) || (iom->type & IOMUX_L_SOURCE_PMU)) ? pmu_offs : grf_offs; }
/* preset drv offset value, set new start value */ if (drv->offset >= 0) { if (iom->type & IOMUX_SOURCE_PMU) drv_pmu_offs = drv->offset; else drv_grf_offs = drv->offset; } else { /* set current drv offset */ drv->offset = (iom->type & IOMUX_SOURCE_PMU) ? drv_pmu_offs : drv_grf_offs; }
dev_dbg(dev, "bank %d, iomux %d has iom_offset 0x%x drv_offset 0x%x\n", i, j, iom->offset, drv->offset);
/* * Increase offset according to iomux width. * 4bit iomux'es are spread over two registers. */ inc = (iom->type & (IOMUX_WIDTH_4BIT | IOMUX_WIDTH_3BIT | IOMUX_WIDTH_2BIT)) ? 8 : 4; if ((iom->type & IOMUX_SOURCE_PMU) || (iom->type & IOMUX_L_SOURCE_PMU)) pmu_offs += inc; else grf_offs += inc;
/* * Increase offset according to drv width. * 3bit drive-strenth'es are spread over two registers. */ if ((drv->drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) || (drv->drv_type == DRV_TYPE_IO_3V3_ONLY)) inc = 8; else inc = 4;
Callrockchip_pinctrl_get_soc_data()after: Each bank’siomux[j].offsetanddrv[j].offsetare correctly filled withGRForPMUGRFactual register offsets in . When the user sets a pin to I2C function via the pinctrl subsystem, the driver finds the corresponding bank and pin group and writes to the GRF register viaregmap_write(info->regmap_base, iomux_offset, value)to complete the multiplexing switch.
syscon
syscon= abbreviation for “system controller”.
It is essentially a:
lightweight platform driver
Used to map a segment ofmemory-mapped control register region(such as GRF, PMU, CRU, etc.)
encapsulated into a struct regmap object
for other drivers to safely read and write these registers via standard interfaces (such asregmap_read/write)
Compared to direct ioremap, why is thesyscon
method needed?
Issues
syscon Advantages
Each driver independentlyioremap
maps the same physical address multiple times, wasting resources
Global unique mapping
No synchronization mechanism
Multiple drivers writing to registers simultaneously → race condition
regmapBuilt-in lock
Code duplication
Each driver must handle offsets and bit operations
structregmap { union { structmutexmutex; struct { spinlock_t spinlock; unsignedlong spinlock_flags; }; }; regmap_lock lock; regmap_unlock unlock; void *lock_arg; /* This is passed to lock/unlock functions */ gfp_t alloc_flags;
structdevice *dev;/* Device we do I/O on */ void *work_buf; /* Scratch buffer used to format I/O */ structregmap_formatformat;/* Buffer format */ conststructregmap_bus *bus; void *bus_context; constchar *name;
bool async; spinlock_t async_lock; wait_queue_head_t async_waitq; structlist_headasync_list; structlist_headasync_free; int async_ret;
/* number of bits to (left) shift the reg value when formatting*/ int reg_shift; int reg_stride; int reg_stride_order;
/* regcache specific members */ conststructregcache_ops *cache_ops; enumregcache_typecache_type;
/* number of bytes in reg_defaults_raw */ unsignedint cache_size_raw; /* number of bytes per word in reg_defaults_raw */ unsignedint cache_word_size; /* number of entries in reg_defaults */ unsignedint num_reg_defaults; /* number of entries in reg_defaults_raw */ unsignedint num_reg_defaults_raw;
/* if set, only the cache is modified not the HW */ bool cache_only; /* if set, only the HW is modified not the cache */ bool cache_bypass; /* if set, remember to free reg_defaults_raw */ bool cache_free;
structreg_default *reg_defaults; constvoid *reg_defaults_raw; void *cache; /* if set, the cache contains newer data than the HW */ bool cache_dirty; /* if set, the HW registers are known to match map->reg_defaults */ bool no_sync_defaults;
structreg_sequence *patch; int patch_regs;
/* if set, converts bulk read to single read */ bool use_single_read; /* if set, converts bulk write to single write */ bool use_single_write; /* if set, the device supports multi write mode */ bool can_multi_write;
/* if set, raw reads/writes are limited to this size */ size_t max_raw_read; size_t max_raw_write;
structrb_rootrange_tree; void *selector_work_buf; /* Scratch buffer used for selector */
structhwspinlock *hwlock;
/* if set, the regmap core can sleep */ bool can_sleep; };
Common system controllers in Rockchip SoC include:
Controller
Full name
Function
GRF
General Register Files
Controls GPIO multiplexing, voltage domains, peripheral routing, etc.
PMU
Power Management Unit
Low-power pin control, power management
These registers typically:
isshared by multiple subsystems(e.g., pinctrl, USB, I2C all need to configure GRF)
cannot be exclusively occupied by a single driver
requiresatomic operations, bit operations, and lock protection
// Use the devm_kcalloc function to allocate a contiguous memory region on the device's memory for storing the pin descriptor structure. pindesc = devm_kcalloc(dev, info->ctrl->nr_pins, sizeof(*pindesc), GFP_KERNEL); if (!pindesc) return -ENOMEM;
ctrldesc->pins = pindesc;//By assigning the pointer pindesc of the pin descriptor structure to the pins member of the pinctrl descriptor structure ctrldesc->npins = info->ctrl->nr_pins;//Assign the pin count info->ctrl->nr_pins to the npins member of the pinctrl descriptor structure
pdesc = pindesc;// Define variable pdesc to point to the starting address of the pin descriptor structure // Iterate through each bank to which the pins belong, setting the number and name for each pin for (bank = 0, k = 0; bank < info->ctrl->nr_banks; bank++) { // The outer loop iterates through each bank to which the pins belong pin_bank = &info->ctrl->pin_banks[bank]; for (pin = 0; pin < pin_bank->nr_pins; pin++, k++) {//pin is the index of the current pin within the bank // The inner loop iterates through the pins in each bank. pdesc->number = k;//the number k of the current pin pdesc->name = kasprintf(GFP_KERNEL, "%s-%d", pin_bank->name, pin); pdesc++; }
INIT_LIST_HEAD(&pin_bank->deferred_pins); mutex_init(&pin_bank->deferred_lock); } // Parse the pinctrl information in the device tree; this function sets the default configuration of pins based on the description in the device tree. ret = rockchip_pinctrl_parse_dt(pdev, info); if (ret) return ret; // Register the pinctrl device, taking the pinctrl description structure, pinctrl-related operation functions, and private data as parameters to register the pinctrl device into the system. info->pctl_dev = devm_pinctrl_register(dev, ctrldesc, info); if (IS_ERR(info->pctl_dev)) return dev_err_probe(dev, PTR_ERR(info->pctl_dev), "could not register pinctrl driver\n");
return0; }
Lines 29 to 42 traverse the double for loop over the bank to which each pin belongs, setting the number and name for the pins of each bank.
Line 44ret = rockchip_pinctrl_parse_dt(pdev, info);Parse the pinctrl information in the device tree; this function sets the default configuration of pins based on the description in the device tree.
Line 48info->pctl_dev = devm_pinctrl_register(dev, ctrldesc, info);Passpdev->dev,info->pctlandinfoas parameters to register the pinctrl device, taking the pinctrl description structure, pinctrl-related operation functions, and private data as parameters to register the pinctrl device into the system.
rockchip_pinctrl_parse_dt()
rochip_pinctrl_register()called in the functionrockchip_pinctrl_register()Parse the pinctrl information in the device tree
staticintrockchip_pinctrl_parse_dt(struct platform_device *pdev, struct rockchip_pinctrl *info) { structdevice *dev = &pdev->dev; structdevice_node *np = dev->of_node; structdevice_node *child; int ret; int i; // Calculate the number of child nodes and update the counter in the info structure rockchip_pinctrl_child_count(info, np);
dev_dbg(dev, "nfunctions = %d\n", info->nfunctions); dev_dbg(dev, "ngroups = %d\n", info->ngroups); // Allocate memory space for functions and groups info->functions = devm_kcalloc(dev, info->nfunctions, sizeof(*info->functions), GFP_KERNEL); if (!info->functions) return -ENOMEM;
info->groups = devm_kcalloc(dev, info->ngroups, sizeof(*info->groups), GFP_KERNEL); if (!info->groups) return -ENOMEM;
i = 0; // Traverse each child node and parse function information for_each_child_of_node(np, child) { // If the node is not a function node, proceed to the next node if (of_match_node(rockchip_bank_match, child)) continue; // Parse function information and store it in the info structure ret = rockchip_pinctrl_parse_functions(child, info, i++); if (ret) { dev_err(dev, "failed to parse function\n"); of_node_put(child); return ret; } }
return0; }
Call at line 10rockchip_pinctrl_child_count(info, np);Count the number of child nodes and update the counter in the info structure
Through lines 25 to 36for_each_child_of_node(np, child)Traverse each child node and callret = rockchip_pinctrl_parse_functions(child, info, i++);Parse function information
rockchip_pinctrl_child_count()
rockchip_pinctrl_parse_dt()In the function, by callingrockchip_pinctrl_child_count(info, np);Count the number of child nodes and update the counter in the info structure
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
staticvoidrockchip_pinctrl_child_count(struct rockchip_pinctrl *info, struct device_node *np) { structdevice_node *child; // Traverse the child nodes of the device node for_each_child_of_node(np, child) { // If the child node is not a function node, skip the current node and continue to traverse the next node if (of_match_node(rockchip_bank_match, child)) continue;
// The child node is a function node, increment the function counter info->nfunctions++;
// Get the number of child nodes of the child node and add it to the group counter info->ngroups += of_get_child_count(child); } }
rockchip_pinctrl_parse_functions()
rockchip_pinctrl_parse_dt()In the function, by callingrockchip_pinctrl_parse_functions()Parse function information and store it into the info structure
staticintrockchip_pinctrl_parse_functions(struct device_node *np, struct rockchip_pinctrl *info, u32 index) { structdevice *dev = info->dev; structdevice_node *child; structrockchip_pmx_func *func;// Used to store function information structrockchip_pin_group *grp;// Used to store groups information int ret; static u32 grp_index; u32 i = 0; // Print debug information, showing the function node and index being parsed dev_dbg(dev, "parse function(%d): %pOFn\n", index, np);
// Get the pointer of the current function in the info->functions array func = &info->functions[index];
/* Initialise function */ /* Initialize function */ func->name = np->name; // Get the number of child nodes of the function node, i.e., the number of associated groups func->ngroups = of_get_child_count(np); if (func->ngroups <= 0) return0;
// Allocate memory space for the function's group pointer array func->groups = devm_kcalloc(dev, func->ngroups, sizeof(*func->groups), GFP_KERNEL); if (!func->groups) return -ENOMEM;
// Traverse each child node of the function node for_each_child_of_node(np, child) { // Store the name of the child node into the function's group pointer array func->groups[i] = child->name; // Get the corresponding group pointer in the info->groups array grp = &info->groups[grp_index++]; // Parse group information and store the result into the corresponding group pointer ret = rockchip_pinctrl_parse_groups(child, grp, info, i++); if (ret) { of_node_put(child); return ret; } }
return0; }
rockchip_pinctrl_parse_functionsIn:
func = &info->functions[index], sostruct rockchip_pinctrloffunctionsThe function of the parameter is to storepinctrlin the device treefunctioninformation.
grp = &info->groups[grp_index++];, sostruct rockchip_pinctrlofgroupsThe function of the parameter is to storepinctrlin the device treegroupsinformation.
rockchip_pinctrl_parse_groups()
rockchip_pinctrl_parse_functionsthroughret = rockchip_pinctrl_parse_groups(child, grp, info, i++);parse group information and store the result in the corresponding group pointer
staticintrockchip_pinctrl_parse_groups(struct device_node *np, struct rockchip_pin_group *grp, struct rockchip_pinctrl *info, u32 index) { structdevice *dev = info->dev; structrockchip_pin_bank *bank; int size; const __be32 *list; int num; int i, j; int ret; // print debug information, showing the group node and index being parsed dev_dbg(dev, "group(%d): %pOFn\n", index, np);
/* Initialise group */ // initialize group information, set the name of the pin group to the node's name grp->name = np->name;
/* * the binding format is rockchip,pins = <bank pin mux CONFIG>, * do sanity check and calculate pins number */ /* * The binding format is rockchip,pins = <bank pin mux CONFIG>, * perform validity check and calculate the number of pins */ list = of_get_property(np, "rockchip,pins", &size); /* we do not check return since it's safe node passed down */ size /= sizeof(*list); if (!size || size % 4)//If the attribute value is empty or the quantity is not a multiple of 4 return dev_err_probe(dev, -EINVAL, "wrong pins number or pins and configs should be by 4\n");
grp->npins = size / 4;// Calculate the number of pins in the group
// Allocate memory space for the pin array and data array based on the calculated number of pins; these arrays will be used to store pin numbers and related configuration information grp->pins = devm_kcalloc(dev, grp->npins, sizeof(*grp->pins), GFP_KERNEL); grp->data = devm_kcalloc(dev, grp->npins, sizeof(*grp->data), GFP_KERNEL); if (!grp->pins || !grp->data) return -ENOMEM; // Traverse each element in the list, where every 4 elements represent the information of one pin for (i = 0, j = 0; i < size; i += 4, j++) { const __be32 *phandle; structdevice_node *np_config; // Get the pin number num = be32_to_cpu(*list++); // Convert the pin number to the corresponding pin structure pointer bank = bank_num_to_bank(info, num); if (IS_ERR(bank)) return PTR_ERR(bank); // Calculate the pin number based on the pin base address (pin_base) in the pin structure and the value in the list, and store it in the pin array (grp->pins) grp->pins[j] = bank->pin_base + be32_to_cpu(*list++); // Get the function selection value related to the current pin from the list, and store it in the corresponding position in the data array (grp->data) grp->data[j].func = be32_to_cpu(*list++);
// Get the configuration information related to the pin phandle = list++; if (!phandle) return -EINVAL; // Get the handle of the configuration information related to the current pin from the list, and look up the corresponding configuration node (np_config) through this handle np_config = of_find_node_by_phandle(be32_to_cpup(phandle)); // Parse the configuration information and store the result in the group's data array ret = pinconf_generic_parse_dt_config(np_config, NULL, &grp->data[j].configs, &grp->data[j].nconfigs); if (ret) return ret; }
return0; }
devm_pinctrl_register()
rockchip_pinctrl_register()The function passesinfo->pctl_dev = devm_pinctrl_register(dev, ctrldesc, info);to register the pinctrl device. One parameter type isstruct device *, the second parameter type is struct pinctrl_desc *, the third parameter type isvoid *It is the private data of the pin controller.
// drivers/pinctrl/core.c /** * devm_pinctrl_register() - Resource managed version of pinctrl_register(). * @dev: parent device for this pin controller * @pctldesc: descriptor for this pin controller * @driver_data: private pin controller data for this pin controller * * Returns an error pointer if pincontrol register failed. Otherwise * it returns valid pinctrl handle. * * The pinctrl device will be automatically released when the device is unbound. */ struct pinctrl_dev *devm_pinctrl_register(struct device *dev, struct pinctrl_desc *pctldesc, void *driver_data) { structpinctrl_dev **ptr, *pctldev; // Use devres_The alloc function allocates memory for storing the pinctrl_variable ptr of the dev pointer ptr = devres_alloc(devm_pinctrl_dev_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return ERR_PTR(-ENOMEM); // Call pinctrl_The register function registers the pinctrl device. This function takes the pinctrl_desc structure, device pointer dev, and driver data driver_data as parameters, and returns the registered pinctrl_dev pointer pctldev = pinctrl_register(pctldesc, dev, driver_data); if (IS_ERR(pctldev)) { devres_free(ptr); return pctldev; } // Store the pinctrl_dev pointer to the memory location pointed to by ptr. *ptr = pctldev; // Use the devres_add function to add ptr to the device's resource list. This way, when the device is released, the previously allocated memory will be automatically freed. devres_add(dev, ptr);
devres_allocThe function is used to manage device resources, and it allocates memory in the device’s resource list. The size of memory allocated here issizeof(*ptr)bytes, which is the size of apinctrl_devpointer. If memory allocation fails, it returns-ENOMEM
pinctrl_register()
devm_pinctrl_register()By callingpctldev = pinctrl_register(pctldesc, dev, driver_data);Register the pinctrl device. This function takesstruct pinctrl_desc *pctldesc, device pointerstruct device *devand driver datavoid *driver_dataas parameters, and returns the registeredstruct pinctrl_dev*pointer
// drivers/pinctrl/core.c /** * pinctrl_register() - register a pin controller device * @pctldesc: descriptor for this pin controller * @dev: parent device for this pin controller * @driver_data: private pin controller data for this pin controller * * Note that pinctrl_register() is known to have problems as the pin * controller driver functions are called before the driver has a * struct pinctrl_dev handle. To avoid issues later on, please use the * new pinctrl_register_and_init() below instead. */ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, struct device *dev, void *driver_data) { structpinctrl_dev *pctldev; int error; // Initialize pinctrl controller pctldev = pinctrl_init_controller(pctldesc, dev, driver_data); if (IS_ERR(pctldev)) return pctldev; // Enable pinctrl controller error = pinctrl_enable(pctldev); if (error) return ERR_PTR(error);
/** * pinctrl_init_controller() - init a pin controller device * @pctldesc: descriptor for this pin controller * @dev: parent device for this pin controller * @driver_data: private pin controller data for this pin controller */ staticstruct pinctrl_dev * pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev, void *driver_data) { structpinctrl_dev *pctldev; int ret;
if (!pctldesc) return ERR_PTR(-EINVAL); if (!pctldesc->name) return ERR_PTR(-EINVAL);
pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL); if (!pctldev) return ERR_PTR(-ENOMEM);
/* Initialize pin control device struct */ /* Initialize pin control device structure */ pctldev->owner = pctldesc->owner;// Set owner pctldev->desc = pctldesc;// Set descriptor pctldev->driver_data = driver_data; // Set driver data INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL);// Initialize pin descriptor tree #ifdef CONFIG_GENERIC_PINCTRL_GROUPS INIT_RADIX_TREE(&pctldev->pin_group_tree, GFP_KERNEL);// Initialize pin group tree #endif #ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS INIT_RADIX_TREE(&pctldev->pin_function_tree, GFP_KERNEL);// Initialize pin function tree #endif INIT_LIST_HEAD(&pctldev->gpio_ranges);// Initialize GPIO range linked list INIT_LIST_HEAD(&pctldev->node);// Initialize node linked list pctldev->dev = dev;// Set device pointer mutex_init(&pctldev->mutex);// Initialize mutex lock
/* check core ops for sanity */ /* Check validity of core operation functions */ ret = pinctrl_check_ops(pctldev); if (ret) { dev_err(dev, "pinctrl ops lacks necessary functions\n"); goto out_err; }
/* If we're implementing pinmuxing, check the ops for sanity */ /* If pin multiplexing function is implemented, check validity of operation functions */ if (pctldesc->pmxops) { ret = pinmux_check_ops(pctldev); if (ret) goto out_err; }
/* If we're implementing pinconfig, check the ops for sanity */ /* If pin configuration function is implemented, check validity of operation functions */ if (pctldesc->confops) { ret = pinconf_check_ops(pctldev); if (ret) goto out_err; }
/* Register all the pins */ /* Register all pins */ dev_dbg(dev, "try to register %d pins ...\n", pctldesc->npins); ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins); if (ret) { dev_err(dev, "error during pin registration\n"); pinctrl_free_pindescs(pctldev, pctldesc->pins, pctldesc->npins); goto out_err; }
In this function, we need to focus on line 27pctldev->driver_data = driver_data, where the rvaluedriver_datais passed step by step from the pinctrl probe function, and is astruct rockchip_pinctrl *type structure pointer variable, the lvaluepctldevis the pin controller device to be registered. At this point, the two data structures are linked, and can **throughpctldevtorockchip_pinctrl**Access the data in.
pinctrl subsystem function operation set
1 2 3
conststructpinctrl_ops *pctlops;// pin control operation function pointer conststructpinmux_ops *pmxops;// pin multiplexing operation function pointer conststructpinconf_ops *confops;// pin configuration operation function pointer
/** * struct pinctrl_ops - global pin control operations, to be implemented by * pin controller drivers. * @get_groups_count: Returns the count of total number of groups registered. * @get_group_name: return the group name of the pin group * @get_group_pins: return an array of pins corresponding to a certain * group selector @pins, and the size of the array in @num_pins * @pin_dbg_show: optional debugfs display hook that will provide per-device * info for a certain pin in debugfs * @dt_node_to_map: parse a device tree "pin configuration node", and create * mapping table entries for it. These are returned through the @map and * @num_maps output parameters. This function is optional, and may be * omitted for pinctrl drivers that do not support device tree. * @dt_free_map: free mapping table entries created via @dt_node_to_map. The * top-level @map pointer must be freed, along with any dynamically * allocated members of the mapping table entries themselves. This * function is optional, and may be omitted for pinctrl drivers that do * not support device tree. */ structpinctrl_ops { int (*get_groups_count) (struct pinctrl_dev *pctldev);//Get the number of pin groups supported by the specified Pin Control device constchar *(*get_group_name) (struct pinctrl_dev *pctldev, unsigned selector);// Get the pin group name corresponding to the specified pin group selector int (*get_group_pins) (struct pinctrl_dev *pctldev, unsigned selector, constunsigned **pins, unsigned *num_pins);// Get the pin list in the pin group corresponding to the specified pin group selector void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset);//Output pin information corresponding to the specified pin selector in debug information int (*dt_node_to_map) (struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, unsigned *num_maps);// Create a Pin Control mapping associated with the given device tree node void (*dt_free_map) (struct pinctrl_dev *pctldev, struct pinctrl_map *map, unsigned num_maps);//Release the Pin Control mapping previously created via dt_node_to_map };
staticintrockchip_get_groups_count(struct pinctrl_dev *pctldev) { // From pinctrl_Get the private data pointer from the dev structure and cast it to rockchip_pinctrl structure structrockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); // Return the number of pin groups stored in the rockchip_pinctrl structure return info->ngroups; }
get_group_name
Get the name of the pin group.
1 2 3 4 5 6 7 8 9
staticconstchar *rockchip_get_group_name(struct pinctrl_dev *pctldev, unsigned selector) { // From pinctrl_Get the private data pointer from the dev structure and cast it to rockchip_pinctrl structure structrockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
// Return the name of the specified pin group return info->groups[selector].name; }
get_group_pins
Get the pin list of the pin group.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
staticintrockchip_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, constunsigned **pins, unsigned *npins) { // From pinctrl_Get the private data pointer from the dev structure and cast it to rockchip_pinctrl structure structrockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
// If the selector exceeds the range of pin groups, return error code -EINVAL if (selector >= info->ngroups) return -EINVAL;
// Assign the pointer to the pin array of the pin group to the passed pins pointer *pins = info->groups[selector].pins; // Assign the number of pins in the pin group to the passed npins variable *npins = info->groups[selector].npins;
return0; }
dt_node_to_map
Create the associated Pin Control mapping based on the device tree node. That is, convert a “pin configuration node” in the device tree (e.g.,i2c1_xfer: i2c1-xfer {...})into astruct pinctrl_mapmapping table that the kernel pinctrl system can understand, thereby telling the system: “When using a certain device, configure which pins to what function and electrical parameters.”
// include/linux/pinctrl/machine.h /** * struct pinctrl_map - boards/machines shall provide this map for devices * @dev_name: the name of the device using this specific mapping, the name * must be the same as in your struct device*. If this name is set to the * same name as the pin controllers own dev_name(), the map entry will be * hogged by the driver itself upon registration * @name: the name of this specific map entry for the particular machine. * This is the parameter passed to pinmux_lookup_state() * @type: the type of mapping table entry * @ctrl_dev_name: the name of the device controlling this specific mapping, * the name must be the same as in your struct device*. This field is not * used for PIN_MAP_TYPE_DUMMY_STATE * @data: Data specific to the mapping type */ structpinctrl_map { constchar *dev_name;// Device name constchar *name;// Mapping name enumpinctrl_map_typetype;// Mapping type constchar *ctrl_dev_name;// Control device name union { structpinctrl_map_muxmux;// Mux mapping data structpinctrl_map_configsconfigs;// Config mapping data } data; };
Field
Meaning
Description
dev_name
Device name using this pin configuration
Usually the peripheral name, such as"fe8a0000.i2c". If set to the pinctrl controller’s own name (e.g.,"pinctrl"), it is called a hogged pin (occupied at startup, independent of peripherals).
name
The state name of the mapping
corresponds to the one in the device treepinctrl-names = "xxx", for example"default"、"sleep". Called bypinctrl_lookup_state(pctl, "default")when used.
type
Mapping type
Enumeration values, common: PIN_MAP_TYPE_MUX_GROUP: configure pin multiplexing PIN_MAP_TYPE_CONFIGS_PIN: configure electrical parameters of a single pin PIN_MAP_TYPE_CONFIGS_GROUP: configure electrical parameters of the entire pin group
ctrl_dev_name
The pinctrl device name controlling this pin
is usually"pinctrl"or"rockchip-pinctrl", used to find the corresponding pinctrl driver.
data
Specific configuration content (union)
According totypedifferent, use.muxor.configs
struct pinctrl_map_mux
1 2 3 4 5 6 7 8 9 10 11
/** * struct pinctrl_map_mux - mapping table content for MAP_TYPE_MUX_GROUP * @group: the name of the group whose mux function is to be configured. This * field may be left NULL, and the first applicable group for the function * will be used. * @function: the mux function to select for the group */ structpinctrl_map_mux { constchar *group; constchar *function; };
field
Meaning
group
Pin group name, e.g.,"i2c1-xfer"、"uart2-m1". This group is predefined in the pinctrl driver and contains a set of physical pins.
function
Function name to switch to, e.g.,"i2c1"、"uart2". The pinctrl driver internally knows how to map this function to specific register values.
struct pinctrl_map_configs
Electrical parameter configuration mapping, used to set the pin’selectrical characteristics, such as pull-up/down, drive strength, Schmitt trigger, etc.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/** * struct pinctrl_map_configs - mapping table content for MAP_TYPE_CONFIGS_* * @group_or_pin: the name of the pin or group whose configuration parameters * are to be configured. * @configs: a pointer to an array of config parameters/values to program into * hardware. Each individual pin controller defines the format and meaning * of config parameters. * @num_configs: the number of entries in array @configs */ structpinctrl_map_configs { constchar *group_or_pin; unsignedlong *configs; unsigned num_configs; };
Field
Meaning
group_or_pin
The object to configure can be a pin name (e.g.,"gpio1-16") or a pin group name (e.g.,"i2c1-xfer")。
configs
Configuration parameter array, each element is aunsigned long, encoding configuration items and values (e.g.,PIN_CONFIG_BIAS_PULL_UP)。
num_configs
Number of configuration items, arrayconfigslength.
Example:
1 2 3 4 5 6 7 8 9 10 11 12
unsignedlong i2c_pull_up[] = { PIN_CONFIG_BIAS_PULL_UP, // Parameter type };
staticintrockchip_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **map, unsigned *num_maps) { // Get the private data pointer of the pin controller structrockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); // Pin group pointer conststructrockchip_pin_group *grp; // Device pointer structdevice *dev = info->dev; // New pin mapping array structpinctrl_map *new_map; // Parent node pointer structdevice_node *parent; // Number of mappings, default is 1 int map_num = 1; int i;
/* * first find the group of this node and check if we need to create * config maps for pins */ /* Find pin group */ grp = pinctrl_name_to_group(info, np->name);// Find the corresponding pin group by node name if (!grp) {// If the pin group is not found, print an error message dev_err(dev, "unable to find group for node %pOFn\n", np); return -EINVAL; }
map_num += grp->npins;// Calculate the number of mappings, including mux mappings and config mappings
new_map = kcalloc(map_num, sizeof(*new_map), GFP_KERNEL);// Allocate memory space for storing the mapping array if (!new_map) return -ENOMEM;
*map = new_map;// Assign the allocated mapping array to the output parameter *num_maps = map_num;// Assign the number of mappings to the output parameter
/* create mux map */ /* Create mux mapping */ parent = of_get_parent(np); // Get the parent node of the node if (!parent) { kfree(new_map);// If the parent node does not exist, free the allocated mapping array memory space return -EINVAL; }
new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;// Set the mapping type to mux mapping new_map[0].data.mux.function = parent->name;// The mux function name is the name of the parent node new_map[0].data.mux.group = np->name; // Use the device node name as the group name of the mapping of_node_put(parent);// Release the reference count of the parent node
/* create config map */ /* Create configuration mapping */ new_map++;// Move the mapping array pointer backward by one position for (i = 0; i < grp->npins; i++) { new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN;// Set the mapping type to configuration mapping new_map[i].data.configs.group_or_pin = pin_get_name(pctldev, grp->pins[i]);// The pin group or pin name is the pin name within the pin group new_map[i].data.configs.configs = grp->data[i].configs;// The configuration information array is the configuration information of that pin in the pin group new_map[i].data.configs.num_configs = grp->data[i].nconfigs;// The number of configuration information items is the configuration count of that pin in the pin group } // Print debug information, showing the function name, group name, and count of the created pin mapping dev_dbg(dev, "maps: function %s group %s num %d\n", (*map)->data.mux.function, (*map)->data.mux.group, map_num);
return0; }
rockchip_dt_node_to_mapThe function creates pin mappings based on device node information, including mux mappings and configuration mappings.
The mux mapping is used to associate the function of the pin group with the function of the parent node
The configuration mapping is used to associate the pin’s configuration information with the pin’s name
These mappings will be used to configure the pin controller, ensuring that pins are correctly configured and used in the system. This function is called during device tree parsing to create corresponding pin mappings for each device node.
dt_free_map
Release the Pin Control mapping created bydt_node_to_mapcreated Pin Control mapping.
struct pinconf_opsis the core operation interface in the pinctrl subsystem for pin electrical characteristic configuration (pin configuration).
If we say thatpinmux_opsis responsible for ‘which functional module the pin is connected to’ (such as I2C, UART), thenpinconf_opsis responsible for ‘how to set the electrical behavior of this pin’ (such as pull-up resistor, drive strength, Schmitt trigger, etc.).
/** * struct pinconf_ops - pin config operations, to be implemented by * pin configuration capable drivers. * @is_generic: for pin controllers that want to use the generic interface, * this flag tells the framework that it's generic. * @pin_config_get: get the config of a certain pin, if the requested config * is not available on this controller this should return -ENOTSUPP * and if it is available but disabled it should return -EINVAL * @pin_config_set: configure an individual pin * @pin_config_group_get: get configurations for an entire pin group; should * return -ENOTSUPP and -EINVAL using the same rules as pin_config_get. * @pin_config_group_set: configure all pins in a group * @pin_config_dbg_show: optional debugfs display hook that will provide * per-device info for a certain pin in debugfs * @pin_config_group_dbg_show: optional debugfs display hook that will provide * per-device info for a certain group in debugfs * @pin_config_config_dbg_show: optional debugfs display hook that will decode * and display a driver's pin configuration parameter */ structpinconf_ops { #ifdef CONFIG_GENERIC_PINCONF bool is_generic;// Whether it is a general pin configuration operation #endif // Get pin configuration information int (*pin_config_get) (struct pinctrl_dev *pctldev, unsigned pin, unsignedlong *config); // Set pin configuration information int (*pin_config_set) (struct pinctrl_dev *pctldev, unsigned pin, unsignedlong *configs, unsigned num_configs); // Get pin group configuration information int (*pin_config_group_get) (struct pinctrl_dev *pctldev, unsigned selector, unsignedlong *config); // Set pin group configuration information int (*pin_config_group_set) (struct pinctrl_dev *pctldev, unsigned selector, unsignedlong *configs, unsigned num_configs); // Debug function, display pin configuration information void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset);
// Debug function, display pin group configuration information void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s, unsigned selector); // Debug function, display specific pin configuration information void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s, unsignedlong config); };
Structurestruct pinconf_ops, used fordefining pin configuration operationsfunction pointers. Each function pointer corresponds to a specific operation, such as getting pin configuration, setting pin configuration, getting pin group configuration, etc. These functions are implemented in the driver to configure and control hardware pins.
1 2 3 4 5
staticconststructpinconf_opsrockchip_pinconf_ops = { .pin_config_get = rockchip_pinconf_get,// Function to get pin configuration .pin_config_set = rockchip_pinconf_set,// Function to set pin configuration .is_generic = true, };
/* get the pin config settings for a specified pin */ staticintrockchip_pinconf_get(struct pinctrl_dev *pctldev, unsignedint pin, unsignedlong *config) { structrockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); structrockchip_pin_bank *bank = pin_to_bank(info, pin); structgpio_chip *gpio = &bank->gpio_chip; enumpin_config_paramparam = pinconf_to_config_param(*config); u16 arg; int rc;
switch (param) { case PIN_CONFIG_BIAS_DISABLE:// Check if the pull-up/pull-down resistor is disabled if (rockchip_get_pull(bank, pin - bank->pin_base) != param) return -EINVAL;
arg = 0; break; case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: case PIN_CONFIG_BIAS_BUS_HOLD:// Check if the pull-up/pull-down resistor is valid and get the current pull-up/pull-down resistor configuration if (!rockchip_pinconf_pull_valid(info->ctrl, param)) return -ENOTSUPP;
if (rockchip_get_pull(bank, pin - bank->pin_base) != param) return -EINVAL;
arg = 1; break; case PIN_CONFIG_OUTPUT:// Check if the pin is configured as GPIO output mode rc = rockchip_get_mux(bank, pin - bank->pin_base); if (rc != RK_FUNC_GPIO) return -EINVAL;
if (!gpio || !gpio->get) { arg = 0; break; } // Get the output state of the pin rc = gpio->get(gpio, pin - bank->pin_base); if (rc < 0) return rc;
arg = rc ? 1 : 0; break; case PIN_CONFIG_DRIVE_STRENGTH: /* rk3288 is the first with per-pin drive-strength */ // Only supports per-pin independent drive strength settings for certain chips (e.g., rk3288) if (!info->ctrl->drv_calc_reg) return -ENOTSUPP; // Get the drive strength configuration of the pin rc = rockchip_get_drive_perpin(bank, pin - bank->pin_base); if (rc < 0) return rc;
arg = rc; break; case PIN_CONFIG_INPUT_SCHMITT_ENABLE: // Only supports Schmitt trigger settings for certain chips if (!info->ctrl->schmitt_calc_reg) return -ENOTSUPP; // Get the Schmitt trigger configuration of the pin rc = rockchip_get_schmitt(bank, pin - bank->pin_base); if (rc < 0) return rc;
arg = rc; break; case PIN_CONFIG_SLEW_RATE: // Only supports pin drive rate settings for certain chips if (!info->ctrl->slew_rate_calc_reg) return -ENOTSUPP; // Get the drive rate configuration of the pin rc = rockchip_get_slew_rate(bank, pin - bank->pin_base); if (rc < 0) return rc;
/* set the pin config settings for a specified pin */ staticintrockchip_pinconf_set(struct pinctrl_dev *pctldev, unsignedint pin, unsignedlong *configs, unsigned num_configs) { structrockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); structrockchip_pin_bank *bank = pin_to_bank(info, pin); structgpio_chip *gpio = &bank->gpio_chip; enumpin_config_paramparam; u32 arg; int i; int rc;
for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(configs[i]); arg = pinconf_to_config_argument(configs[i]);
if (param == PIN_CONFIG_OUTPUT || param == PIN_CONFIG_INPUT_ENABLE) { /* * Check for gpio driver not being probed yet. * The lock makes sure that either gpio-probe has completed * or the gpio driver hasn't probed yet. */ /* * Check GPIO Whether the driver has been probed。 * Lock to ensure GPIO Probe completed or GPIO Driver has not been probed。 */ mutex_lock(&bank->deferred_lock); if (!gpio || !gpio->direction_output) { // If the driver has not been probed, defer processing of the configuration information and return. rc = rockchip_pinconf_defer_pin(bank, pin - bank->pin_base, param, arg); mutex_unlock(&bank->deferred_lock); if (rc) return rc;
break; } mutex_unlock(&bank->deferred_lock); }
switch (param) { case PIN_CONFIG_BIAS_DISABLE: // Disable pull-up/pull-down resistors rc = rockchip_set_pull(bank, pin - bank->pin_base, param); if (rc) return rc; break; case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: case PIN_CONFIG_BIAS_BUS_HOLD: // Check if pull-up/pull-down resistors are valid if (!rockchip_pinconf_pull_valid(info->ctrl, param)) return -ENOTSUPP;
if (!arg) return -EINVAL; // Set pull-up/pull-down resistors rc = rockchip_set_pull(bank, pin - bank->pin_base, param); if (rc) return rc; break; case PIN_CONFIG_OUTPUT: // Set pin multiplexing function to GPIO rc = rockchip_set_mux(bank, pin - bank->pin_base, RK_FUNC_GPIO); if (rc != RK_FUNC_GPIO) return -EINVAL; // Set pin to output mode rc = gpio->direction_output(gpio, pin - bank->pin_base, arg); if (rc) return rc; break; case PIN_CONFIG_INPUT_ENABLE: // Set pin multiplexing function to GPIO rc = rockchip_set_mux(bank, pin - bank->pin_base, RK_FUNC_GPIO); if (rc != RK_FUNC_GPIO) return -EINVAL; // Set pin to input mode rc = gpio->direction_input(gpio, pin - bank->pin_base); if (rc) return rc; break; case PIN_CONFIG_DRIVE_STRENGTH: /* rk3288 is the first with per-pin drive-strength */ // Only supports independent drive strength settings for each pin on certain chips (e.g., rk3288) if (!info->ctrl->drv_calc_reg) return -ENOTSUPP; // Set pin drive strength rc = rockchip_set_drive_perpin(bank, pin - bank->pin_base, arg); if (rc < 0) return rc; break; case PIN_CONFIG_INPUT_SCHMITT_ENABLE: // Schmitt trigger settings supported only on certain chips if (!info->ctrl->schmitt_calc_reg) return -ENOTSUPP; // Set the Schmitt trigger mode of the pin rc = rockchip_set_schmitt(bank, pin - bank->pin_base, arg); if (rc < 0) return rc; break; case PIN_CONFIG_SLEW_RATE: // Pin drive rate settings supported only on certain chips if (!info->ctrl->slew_rate_calc_reg) return -ENOTSUPP; // Set the drive rate of the pin rc = rockchip_set_slew_rate(bank, pin - bank->pin_base, arg); if (rc < 0) return rc; break; default:// Unsupported configuration parameter return -ENOTSUPP; } } /* for each config */
/** * struct pinmux_ops - pinmux operations, to be implemented by pin controller * drivers that support pinmuxing * @request: called by the core to see if a certain pin can be made * available for muxing. This is called by the core to acquire the pins * before selecting any actual mux setting across a function. The driver * is allowed to answer "no" by returning a negative error code * @free: the reverse function of the request() callback, frees a pin after * being requested * @get_functions_count: returns number of selectable named functions available * in this pinmux driver * @get_function_name: return the function name of the muxing selector, * called by the core to figure out which mux setting it shall map a * certain device to * @get_function_groups: return an array of groups names (in turn * referencing pins) connected to a certain function selector. The group * name can be used with the generic @pinctrl_ops to retrieve the * actual pins affected. The applicable groups will be returned in * @groups and the number of groups in @num_groups * @set_mux: enable a certain muxing function with a certain pin group. The * driver does not need to figure out whether enabling this function * conflicts some other use of the pins in that group, such collisions * are handled by the pinmux subsystem. The @func_selector selects a * certain function whereas @group_selector selects a certain set of pins * to be used. On simple controllers the latter argument may be ignored * @gpio_request_enable: requests and enables GPIO on a certain pin. * Implement this only if you can mux every pin individually as GPIO. The * affected GPIO range is passed along with an offset(pin number) into that * specific GPIO range - function selectors and pin groups are orthogonal * to this, the core will however make sure the pins do not collide. * @gpio_disable_free: free up GPIO muxing on a certain pin, the reverse of * @gpio_request_enable * @gpio_set_direction: Since controllers may need different configurations * depending on whether the GPIO is configured as input or output, * a direction selector function may be implemented as a backing * to the GPIO controllers that need pin muxing. * @strict: do not allow simultaneous use of the same pin for GPIO and another * function. Check both gpio_owner and mux_owner strictly before approving * the pin request. */ structpinmux_ops { ` // Check if a pin can be set for multiplexing int (*request) (struct pinctrl_dev *pctldev, unsigned offset); // Reverse function of the request() callback, releases the pin after request int (*free) (struct pinctrl_dev *pctldev, unsigned offset); // Return the number of selectable named functions in this Pinmux driver int (*get_functions_count) (struct pinctrl_dev *pctldev); // Return the function name of the mux selector; the core calls this to determine which mux setting a device should be mapped to constchar *(*get_function_name) (struct pinctrl_dev *pctldev, unsigned selector); // Return a set of group names (referencing pins in order) associated with a function selector int (*get_function_groups) (struct pinctrl_dev *pctldev, unsigned selector, constchar * const **groups, unsigned *num_groups); // Enable a specific multiplexing function using a specific pin group int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector, unsigned group_selector); // Request and enable GPIO on a specific pin int (*gpio_request_enable) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset); // Release GPIO multiplexing on a specific pin void (*gpio_disable_free) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset); // Configure differently based on whether GPIO is set as input or output int (*gpio_set_direction) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset, bool input); // Do not allow the same pin to be used for both GPIO and other functions. Strictly check gpio before approving pin requests_owner and mux_owner bool strict; };
struct pinmux_opsis a structure used to describe pin multiplexing operations, a set of callback functions that the pinctrl driver must implement (if pin multiplexing is supported), enabling the pinctrl subsystem to:
Query which functions the SoC supports (e.g., I2C, UART, SPI…)
Query which pin groups can be used for each function
Dynamically switch pin functions at runtime (e.g., change GPIO1_B0 from a regular GPIO to I2C_SCL)
*groups = info->functions[selector].groups;// Returns the array of pin groups corresponding to the pin multiplexing function *num_groups = info->functions[selector].ngroups;// Returns the number of pin groups
dev_dbg(dev, "enable function %s group %s\n", info->functions[selector].name, info->groups[group].name);
/* * for each pin in the pin group selected, program the corresponding * pin function number in the config register. */ /* * For each pin in the selected pin group,Program the corresponding pin function numbers into the configuration register。 */ for (cnt = 0; cnt < info->groups[group].npins; cnt++) { bank = pin_to_bank(info, pins[cnt]); ret = rockchip_set_mux(bank, pins[cnt] - bank->pin_base, data[cnt].func); if (ret) break; }
if (ret && cnt) { /* revert the already done pin settings */ /* Restore the already set pin configuration */ for (cnt--; cnt >= 0 && !data[cnt].func; cnt--) rockchip_set_mux(bank, pins[cnt] - bank->pin_base, 0);
return ret; }
return0; }
When is the pin multiplexing relationship set
Conjecture 1
The first conjecture is that pinctrl pin multiplexing occurs when loading the LED driver. After the device tree and driver of the LED light match, the probe written in the driver is entered function, before whichdrivers/base/dd.cin the filereally_probesubfunction in the functionpinctrl_bind_pinswill be executed. This function binds pins for the given device and selects and sets the appropriate pinctrl state during the binding process. For specific binding details, refer to the previous chapters.
Conjecture 2
The second conjecture is that pin multiplexing is completed when loading the pinctrl driver. Since the pinctrl subsystem also conforms to the device model specification and executes the corresponding probe function, the same applies when loading the pinctrl driverdrivers/base/dd.cin the filereally_probesubfunction in the functionpinctrl_bind_pinswill be executed. Will the pinctrl pin multiplexing setting be performed at this time? Next, we will conduct an in-depth analysis of this.
Take the device tree node of the 485 control pin and the corresponding pinctrl device tree node as an example. The content of the 485 device tree to be added is as follows
After the 485 device tree written above successfully matches the driver, it enters the probe function in the corresponding driver. In the probe function, the 485 enable pin described in the device tree can be pulled high or low to control the reception and transmission of the 485.
Therefore, it can be inferred that the pins have already been multiplexed using the pinctrl subsystem before entering the driver’s probe function. In the device model, we already know that the probe function in the driver is located in the kernel source directorydrivers/base/dd.cloaded and executed in the file, and then findreally_probethe code related to the loading of the probe function in the function
staticintreally_probe(struct device *dev, struct device_driver *drv) { int ret = -EPROBE_DEFER; int local_trigger_count = atomic_read(&deferred_trigger_count); bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) && !drv->suppress_bind_attrs;
if (defer_all_probes) { /* * Value of defer_all_probes can be set only by * device_block_probing() which, in turn, will call * wait_for_device_probe() right after that to avoid any races. */ dev_dbg(dev, "Driver %s force probe deferral\n", drv->name); driver_deferred_probe_add(dev); return ret; }
ret = device_links_check_suppliers(dev); if (ret == -EPROBE_DEFER) driver_deferred_probe_add_trigger(dev, local_trigger_count); if (ret) return ret;
atomic_inc(&probe_count); pr_debug("bus: '%s': %s: probing driver %s with device %s\n", drv->bus->name, __func__, drv->name, dev_name(dev)); if (!list_empty(&dev->devres_head)) { dev_crit(dev, "Resources present before probing\n"); ret = -EBUSY; goto done; }
re_probe: dev->driver = drv;// Set the device's driver pointer to the current driver
/* If using pinctrl, bind pins now before probing */ /* If pinctrl is used, bind the pins before probing */ ret = pinctrl_bind_pins(dev);// Bind the device's pins if (ret) goto pinctrl_bind_failed;
... }
If pinctrl is enabled, it will call the function on line 39pinctrl_bind_pins()function to bind the device pins, and then jump topinctrl_bind_pinsthe function
/** * pinctrl_bind_pins() - called by the device core before probe * @dev: the device that is just about to probe */ intpinctrl_bind_pins(struct device *dev) { int ret; // Check if the device reuses a node if (dev->of_node_reused) return0; // Allocate memory space for the device's pins dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL); if (!dev->pins) return -ENOMEM;
// Get the pinctrl handle of the device dev->pins->p = devm_pinctrl_get(dev); if (IS_ERR(dev->pins->p)) { dev_dbg(dev, "no pinctrl handle\n"); ret = PTR_ERR(dev->pins->p); goto cleanup_alloc; } // Find the default pinctrl state of the device dev->pins->default_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_DEFAULT); if (IS_ERR(dev->pins->default_state)) { dev_dbg(dev, "no default pinctrl state\n"); ret = 0; goto cleanup_get; } // Find the initialization pinctrl state of the device dev->pins->init_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_INIT); if (IS_ERR(dev->pins->init_state)) { /* Not supplying this state is perfectly legal */ /* It is completely legal to not provide this state. */ dev_dbg(dev, "no init pinctrl state\n"); // Select the default pinctrl state. ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state); } else { // Select the initialized pinctrl state. ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state); }
if (ret) { dev_dbg(dev, "failed to activate initial pinctrl state\n"); goto cleanup_get; }
#ifdef CONFIG_PM /* * If power management is enabled, we also look for the optional * sleep and idle pin states, with semantics as defined in * <linux/pinctrl/pinctrl-state.h> */ /* * If power management is enabled,,we will also look for optional sleep and idle pin states.,Its semantics are defined in * <linux/pinctrl/pinctrl-state.h> defined in */ dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_SLEEP); if (IS_ERR(dev->pins->sleep_state)) /* Not supplying this state is perfectly legal */ /* It is completely legal to not provide this state. */ dev_dbg(dev, "no sleep pinctrl state\n");
dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_IDLE); if (IS_ERR(dev->pins->idle_state)) /* Not supplying this state is perfectly legal */ /* It is completely legal to not provide this state. */ dev_dbg(dev, "no idle pinctrl state\n"); #endif
return0;
/* * If no pinctrl handle or default state was found for this device, * let's explicitly free the pin container in the device, there is * no point in keeping it around. */ /* If no pinctrl handle or default state is found for this device, * let us explicitly release the pin container in the device,,because keeping it makes no sense.。 */ cleanup_get: devm_pinctrl_put(dev->pins->p); cleanup_alloc: devm_kfree(dev, dev->pins); dev->pins = NULL;
/* Return deferrals */ if (ret == -EPROBE_DEFER)/* Return deferred. */ return ret; /* Return serious errors */ if (ret == -EINVAL)/* Return a severe error. */ return ret; /* We ignore errors like -ENOENT meaning no pinctrl state */ /* We ignore errors such as -ENOENT, indicating no pinctrl state. */ return0; }
Line 19dev->pins->p = devm_pinctrl_get(dev);Get the pinctrl handle of the device,devthe type isstruct device, forstruct devicestructure: which containsstruct dev_pin_info *pins;a member representing the pin information of the device
structdevice { structkobjectkobj;// kernel object, used for device management structdevice *parent;// pointer to the parent device
structdevice_private *p;// private data pointer
constchar *init_name; /* initial name of the device */// initial name of the device conststructdevice_type *type;// device type
structbus_type *bus;/* type of bus device is on */// bus type of the device structdevice_driver *driver;/* which driver has allocated this device */// 分配该设备的驱动程序 void *platform_data; /* Platform specific data, device core doesn't touch it */// 平台特定的数据,设备核心不会修改它 void *driver_data; /* Driver data, set and get with dev_set_drvdata/dev_get_drvdata */// 驱动程序的数据,使用 dev_set/get_drvdata 来设置和获取 #ifdef CONFIG_PROVE_LOCKING structmutexlockdep_mutex; #endif structmutexmutex;/* mutex to synchronize calls to* its driver.*/
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN structirq_domain *msi_domain;// generic MSI IRQ domain of the device #endif #ifdef CONFIG_PINCTRL structdev_pin_info *pins;// pin information of the device #endif ... }
struct dev_pin_info
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/** * struct dev_pin_info - pin state container for devices * @p: pinctrl handle for the containing device * @default_state: the default state for the handle, if found * @init_state: the state at probe time, if found * @sleep_state: the state at suspend time, if found * @idle_state: the state at idle (runtime suspend) time, if found */ structdev_pin_info { structpinctrl *p;// pin controller pointer structpinctrl_state *default_state;// Default state pointer structpinctrl_state *init_state;// Initialization state pointer #ifdef CONFIG_PM structpinctrl_state *sleep_state;// Sleep state pointer (only available when power management is supported) structpinctrl_state *idle_state;// Idle state pointer (only available when power management is supported) #endif };
struct pinctrl *p: Pin controller pointer. This pointer points to the pin controller object used by the device, for controlling and configuring the device’s pins.
struct pinctrl_state *default_state: Default state pointer. This pointer points to the device’s default pin configuration state, representing the pin configuration during normal operation.
struct pinctrl_state *init_state: Initialization state pointer. This pointer points to the device’s initialization pin configuration state, representing the pin configuration during the initialization phase.
struct pinctrl_state *sleep_state: Sleep state pointer (only available when power management is supported). This pointer points to the device’s pin configuration state, representing the pin configuration when the device enters sleep mode.
struct pinctrl_state *idle_state: Idle state pointer (only available when power management is supported). This pointer points to the device’s pin configuration state, representing the pin configuration when the device is in idle state.
Thepinctrl-namesattribute specifies that the pin controller used by the device isdefault, which is thepinctrl-0in line 5, andpinctrl-0has the value of therk_485_gpio, sostruct pinctrl_state *default_stateThis default state structure pointer is used to store the pin multiplexing information of line 11, and in the previous chapter it was also mentioned that the pinctrl node in the device tree will be converted intopinctrl_mapstructure, thenstruct pinctrl_state *default_statewill inevitably be associated withpinctrl_mapstructure.
devm_pinctrl_get()
pinctrl_bind_pins()In the function, throughdev->pins->p = devm_pinctrl_get(dev);to obtain the device’s pinctrl handle, anddevm_pinctrl_get()the function is defined as follows:
// drivers/pinctrl/core.c /** * devm_pinctrl_get() - Resource managed pinctrl_get() * @dev: the device to obtain the handle for * * If there is a need to explicitly destroy the returned struct pinctrl, * devm_pinctrl_put() should be used, rather than plain pinctrl_put(). */ struct pinctrl *devm_pinctrl_get(struct device *dev) { structpinctrl **ptr, *p; // Allocate memory for the pointer storing the pin controller handle ptr = devres_alloc(devm_pinctrl_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return ERR_PTR(-ENOMEM);
// Obtain the device's pin controller handle p = pinctrl_get(dev); if (!IS_ERR(p)) { // If successful, store the pin controller handle in the pointer *ptr = p; // Add the pointer to the device resources devres_add(dev, ptr); } else { // If acquisition fails, free the previously allocated pointer memory devres_free(ptr); }
return p; } EXPORT_SYMBOL_GPL(devm_pinctrl_get);
Line 18, throughpinctrl_get()obtain the pin controller handle
pinctrl_get()
devm_pinctrl_get()In the function, throughpinctrl_get()the function obtains the pin controller handle
// drivers/pinctrl/core.c /** * pinctrl_get() - retrieves the pinctrl handle for a device * @dev: the device to obtain the handle for */ struct pinctrl *pinctrl_get(struct device *dev) { structpinctrl *p; // Check if the device pointer is null if (WARN_ON(!dev)) return ERR_PTR(-EINVAL);
/* * See if somebody else (such as the device core) has already * obtained a handle to the pinctrl for this device. In that case, * return another pointer to it. */ /* * Check if there are other components(such as the device core)has already obtained the pin controller handle of this device。 * In this case,return another pointer to that handle。 */ p = find_pinctrl(dev); if (p) { dev_dbg(dev, "obtain a copy of previously claimed pinctrl\n"); kref_get(&p->users); return p; } // Create and return the pin controller handle of the device return create_pinctrl(dev, NULL); } EXPORT_SYMBOL_GPL(pinctrl_get);
Use in the return valuecreate_pinctrlfunction, which creates and returns the pin controller handle of the device. Note that the second parameter is NULL
create_pinctrl()
pinctrl_get()Finally callcreate_pinctrl()to create and return the pin controller handle of the device, the first parameter passed is the device matched by the current driverstruct device, andstruct pinctrl_dev *pctldevthe value of is NULL
/* * create the state cookie holder struct pinctrl for each * mapping, this is what consumers will get when requesting * a pin control handle with pinctrl_get() */ /* * Create state holder allocation for each mapping struct pinctrl。 * This is when using pinctrl_get() the object that the consumer will obtain when requesting a pin control handle。 */ p = kzalloc(sizeof(*p), GFP_KERNEL); if (!p) return ERR_PTR(-ENOMEM); p->dev = dev; INIT_LIST_HEAD(&p->states); INIT_LIST_HEAD(&p->dt_maps);
ret = pinctrl_dt_to_map(p, pctldev); if (ret < 0) { kfree(p); return ERR_PTR(ret); }
devname = dev_name(dev);
mutex_lock(&pinctrl_maps_mutex); /* Iterate over the pin control maps to locate the right ones */ /* Traverse the pin control mapping to locate the correct mapping */ for_each_maps(maps_node, i, map) { /* Map must be for this device */ if (strcmp(map->dev_name, devname))/* The mapping must apply to this device */ continue; /* * If pctldev is not null, we are claiming hog for it, * that means, setting that is served by pctldev by itself. * * Thus we must skip map that is for this device but is served * by other device. */ if (pctldev && strcmp(dev_name(pctldev->dev), map->ctrl_dev_name)) continue;
ret = add_setting(p, pctldev, map); /* * At this point the adding of a setting may: * * - Defer, if the pinctrl device is not yet available * - Fail, if the pinctrl device is not yet available, * AND the setting is a hog. We cannot defer that, since * the hog will kick in immediately after the device * is registered. * * If the error returned was not -EPROBE_DEFER then we * accumulate the errors to see if we end up with * an -EPROBE_DEFER later, as that is the worst case. */ if (ret == -EPROBE_DEFER) { pinctrl_free(p, false); mutex_unlock(&pinctrl_maps_mutex); return ERR_PTR(ret); } } mutex_unlock(&pinctrl_maps_mutex);
if (ret < 0) { /* If some other error than deferral occurred, return here */ /* If an error other than deferral occurs, return here */ pinctrl_free(p, false); return ERR_PTR(ret); }
kref_init(&p->users);
/* Add the pinctrl handle to the global list */ /* Add the pin control handle to the global list */ mutex_lock(&pinctrl_list_mutex); list_add_tail(&p->node, &pinctrl_list); mutex_unlock(&pinctrl_list_mutex);
return p; }
Three important structures:
struct pinctrl
struct pinctrl_maps
struct pinctrl_map
Line 28 callsret = pinctrl_dt_to_map(p, pctldev);Convert the pin mapping information defined in the device tree intostruct pinctrl_mapstructure
for_each_mapsDefined as follows
1 2 3 4 5 6 7 8 9
// Macro definition: used to traverse each mapping table entry in the mapping table linked list // _maps_node_: mapping table node pointer used during traversal // _i_: counter variable used during traversal // _map_: Pointer to the mapping table entry used during traversal #define for_each_maps(_maps_node_, _i_, _map_) \ list_for_each_entry(_maps_node_, &pinctrl_maps, node) \ // Traverse each node in the mapping table linked list for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \ // Initialize the counter and the mapping table entry pointer _i_ < _maps_node_->num_maps; \ // Loop condition: counter is less than the number of mapping entries in the current node _i_++, _map_ = &_maps_node_->maps[_i_]) // Increment the counter and update the mapping table entry pointer each loop iteration
Important Data Structures
struct pinctrl
struct pinctrlStructure used to represent a pin controller.
A pin controller is a component in a hardware system that manages and controls pins (GPIO). It is responsible for configuring pin functions, electrical properties, etc. This structure is defined in the kernel source directory underdrivers/pinctrl/core.hthe file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/** * struct pinctrl - per-device pin control state holder * @node: global list node * @dev: the device using this pin control handle * @states: a list of states for this device * @state: the current state * @dt_maps: the mapping table chunks dynamically parsed from device tree for * this device, if any * @users: reference count */ structpinctrl { structlist_headnode;// Linked list node for adding the pin controller to the global list structdevice *dev;// Associated device structlist_headstates;// Linked list storing pin configuration states, used to track different pin configuration states structpinctrl_state *state;// Currently applied pin configuration state structlist_headdt_maps;// Linked list storing pin mapping information defined in the device tree structkrefusers;// Reference count of the pin controller, used to track the number of references to the pin controller };
struct pinctrl_maps
struct pinctrl_mapsvariable of typemaps_nodeused to traverse pin control mappings. Pin controller mappings describe the correspondence between the functions and configurations of different pin controllers and actual hardware pins. This structure is defined in the kernel source directory underdrivers/pinctrl/core.hfile.
1 2 3 4 5 6 7 8 9 10 11
/** * struct pinctrl_maps - a list item containing part of the mapping table * @node: mapping table list node * @maps: array of mapping table entries * @num_maps: the number of entries in @maps */ structpinctrl_maps { structlist_headnode;// pin controller mapping linked list node, used to add this mapping to the global list conststructpinctrl_map *maps;// pointer to the pin controller mapping array unsigned num_maps; // number of mappings in the pin controller mapping array };
wherepinctrl_mapthe structure is indt_node_to_mapcreated in
pinctrl_dt_to_map()
create_pinctrl()by callingret = pinctrl_dt_to_map(p, pctldev);function converts the pin mapping information defined in the device tree intostruct pinctrl_mapstructure, and adds it to thep->dt_mapslinked list.
// drivers/pinctrl/devicetree.c intpinctrl_dt_to_map(struct pinctrl *p, struct pinctrl_dev *pctldev) { structdevice_node *np = p->dev->of_node;// get the device tree node of the device associated with the pin controller int state, ret; char *propname; structproperty *prop; constchar *statename; const __be32 *list; int size, config; phandle phandle; structdevice_node *np_config;
/* CONFIG_OF enabled, p->dev not instantiated from DT */ /* if CONFIG_OF is enabled and p->dev is not instantiated from the device tree */ if (!np) { if (of_have_populated_dt()) dev_dbg(p->dev, "no of_node; not parsing pinctrl DT\n"); return0; }
/* We may store pointers to property names within the node */ /* Pointer to the property name stored inside the node */ of_node_get(np);//Increase the reference count of the device tree node to ensure the node is not freed during parsing
/* For each defined state ID */ /* For each defined state ID */ for (state = 0; ; state++) { /* Retrieve the pinctrl-* property */ /* Get the pinctrl-* property */ propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state); if (!propname) return -ENOMEM; prop = of_find_property(np, propname, &size); kfree(propname); if (!prop) { if (state == 0) { of_node_put(np); return -ENODEV; } break; } list = prop->value; size /= sizeof(*list);
/* Determine whether pinctrl-names property names the state */ /* Determine whether the pinctrl-names property names this state */ ret = of_property_read_string_index(np, "pinctrl-names", state, &statename); /* * If not, statename is just the integer state ID. But rather * than dynamically allocate it and have to free it later, * just point part way into the property name for the string. */ if (ret < 0) statename = prop->name + strlen("pinctrl-");
/* For every referenced pin configuration node in it */ /* For each referenced pin configuration node within it */ for (config = 0; config < size; config++) { phandle = be32_to_cpup(list++);
/* Look up the pin configuration node */ /* Find the pin configuration node */ np_config = of_find_node_by_phandle(phandle); if (!np_config) { dev_err(p->dev, "prop %s index %i invalid phandle\n", prop->name, config); ret = -EINVAL; goto err; }
/* Parse the node */ /* Parse the node */ ret = dt_to_map_one_config(p, pctldev, statename, np_config); of_node_put(np_config); if (ret < 0) goto err; }
/* No entries in DT? Generate a dummy state table entry */ /* If there is no entry in the device tree, generate a dummy state table entry */ if (!size) { ret = dt_remember_dummy_state(p, statename); if (ret < 0) goto err; } }
return0;
err: pinctrl_dt_free_maps(p); return ret; }
For each defined state ID, the loop to parse the pin controller mapping information specifically performs the following steps:
Construct the property name stringpropname, for example"pinctrl-0"、"pinctrl-1"and so on.
Useof_find_propertyfunction to get the property from the device tree node nppropnamevalue, and get the size of the property valuesize. If the property does not exist, check if it is the first state ID; if so, release the node reference and return -ENODEVindicates that there is no valid pinctrl description in the device tree node. Otherwise, break out of the loop.
Convert the property value into a pointer list and calculate the size of the list.
If thepinctrl-namesproperty names the state, then use theof_property_read_string_indexfunction to read the property value and store the state name in the statenamevariable. Otherwise, setstatenameto point to a part of the property name, i.e., remove the"pinctrl-"prefix.
For each referenced pin configuration node, perform the following steps:
Convert thelistvalue pointed to by thephandlepointer to native byte order.
Useof_find_node_by_phandleThe function, based onphandlefinds the pin configuration node and stores it innp_configthe variable. If the pin configuration node is not found, it prints an error message and returns-EINVAL。
Call thedt_to_map_one_configfunction to parse the pin configuration node information into a pinctrl mapping and store it inpctldev.
Decrement the reference count of the pin configuration node.
If there is no entry in the device tree, generate a dummy state table entry for subsequent processing.
Among them,dt_to_map_one_configthe function requires special attention. This function parses the pin controller mapping table from the device tree node and stores it. The specific content of this function is as follows:
/* Find the pin controller containing np_config */ /* Find the pin controller containing np_config */ np_pctldev = of_node_get(np_config); for (;;) { /* If default configuration is not allowed, read the pinctrl-use-default property */ if (!allow_default) allow_default = of_property_read_bool(np_pctldev, "pinctrl-use-default"); /* Get the parent node of np_pctldev */ np_pctldev = of_get_next_parent(np_pctldev); /* If there is no parent node or the parent node is the root node, release the np_pctldev reference and return */ if (!np_pctldev || of_node_is_root(np_pctldev)) { of_node_put(np_pctldev); /* Check whether to defer probing the driver state */ ret = driver_deferred_probe_check_state(p->dev); /* keep deferring if modules are enabled */ /* If the module is enabled and default configuration is not allowed, and the return value is -ENODEV, then defer probing */ if (IS_ENABLED(CONFIG_MODULES) && !allow_default && ret < 0) ret = -EPROBE_DEFER; return ret; } /* If we're creating a hog we can use the passed pctldev */ /* If a hog is being created, the passed pctldev can be used */ if (hog_pctldev && (np_pctldev == p->dev->of_node)) { pctldev = hog_pctldev; break; } /* Through np_pctldev gets pinctrl_dev structure */ pctldev = get_pinctrl_dev_from_of_node(np_pctldev); /* If the pinctrl_dev structure is obtained, break out of the loop */ if (pctldev) break; /* Do not defer probing of hogs (circular loop) */ /* Do not defer probing hogs (loop) */ if (np_pctldev == p->dev->of_node) { of_node_put(np_pctldev); return -ENODEV; } } of_node_put(np_pctldev);
/* * Call pinctrl driver to parse device tree node, and * generate mapping table entries */ // Call the pinctrl driver to parse device tree nodes and generate mapping table entries ops = pctldev->desc->pctlops; /* Check if the pinctrl driver supports device tree, i.e., whether it implements dt_node_to_map method. If not supported, return an error code. */ if (!ops->dt_node_to_map) { dev_err(p->dev, "pctldev %s doesn't support DT\n", dev_name(pctldev->dev)); return -ENODEV; } /* Call the pinctrl driver's dt_node_to_map method */ ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps); if (ret < 0) return ret; elseif (num_maps == 0) { /* * If we have no valid maps (maybe caused by empty pinctrl node * or typing error) ther is no need remember this, so just * return. */ dev_info(p->dev, "there is not valid maps for state %s\n", statename); return0; }
/* Stash the mapping table chunk away for later use */ /* Store the mapping table block for later use */ return dt_remember_or_free_map(p, statename, pctldev, map, num_maps); }
Line 70 calls the pin controller’sdt_node_to_mapmethod, converting the device tree nodenp_configConvert to mapping table entries. This method parses the device tree node and generates mapping table entries based on the node information. The specific conversion process is implemented by each pinctrl driver.
dt_remember_or_free_map()
dt_to_map_one_configThe function is used to parse the mapping table of the pin controller from the device tree node and store it, while the storage operation is performed by the functiondt_remember_or_free_mapis completed.
Function parameters:
p: Pointer tostruct pinctrlstructure pointer, representing the context of the pin controller.
statename: Pointer to the state name, indicating the name of the state to be set.
pctldev: Pointer tostruct pinctrl_devstructure pointer, representing the pin controller device.
/* Initialize common mapping table entry fields */ // Loop through the mapping table entry array and initialize the common fields for each entry. for (i = 0; i < num_maps; i++) { constchar *devname; // Copy the name of the pin controller device using the kstrdup_const function and assign the returned pointer to devname. devname = kstrdup_const(dev_name(p->dev), GFP_KERNEL); if (!devname) goto err_free_map; // Set the device name, state name, and controller device name of the mapping table entry. map[i].dev_name = devname; map[i].name = statename; if (pctldev) map[i].ctrl_dev_name = dev_name(pctldev->dev); }
/* Remember the converted mapping table entries */ /* Record the converted mapping table entry */ dt_map = kzalloc(sizeof(*dt_map), GFP_KERNEL);//Allocate memory space using kzalloc and assign the returned pointer to dt_map if (!dt_map) goto err_free_map; // Pass the incoming pctldev, map, and num_Assign maps to dt respectively_The corresponding fields of map dt_map->pctldev = pctldev; dt_map->map = map; dt_map->num_maps = num_maps; list_add_tail(&dt_map->node, &p->dt_maps);// Use list_add_The tail function adds dt_map to p->dt_in the maps linked list /* Register the mapping table entry */ return pinctrl_register_mappings(map, num_maps);
err_free_map: /* Free the memory of the mapping table entry */ dt_free_map(pctldev, map, num_maps); return -ENOMEM; }
struct pinctrl_dt_map
1 2 3 4 5 6
structpinctrl_dt_map { structlist_headnode;//Used to add the mapping table structure to the dt_maps linked list of pinctrl structpinctrl_dev *pctldev;// Pin controller device structpinctrl_map *map;// Mapping table entry array unsigned num_maps;//Number of mapping table entries };
pinctrl_register_mappings()
dt_remember_or_free_map()Finally, use thepinctrl_register_mappings()function to register the mapping table entries. This function registers the mapping table entries into the pinctrl subsystem, so that subsequent pin configuration and management can be performed through the relevant interfaces.
/** * pinctrl_register_mappings() - register a set of pin controller mappings * @maps: the pincontrol mappings table to register. Note the pinctrl-core * keeps a reference to the passed in maps, so they should _not_ be * marked with __initdata. * @num_maps: the number of maps in the mapping table */ // maps Pointer to the array of mapping table entries // num_maps Number of mapping table entries intpinctrl_register_mappings(conststruct pinctrl_map *maps, unsigned num_maps) { int i, ret; structpinctrl_maps *maps_node;
pr_debug("add %u pinctrl maps\n", num_maps);
/* First sanity check the new mapping */ /* First, perform a validity check on the new mapping table. */ for (i = 0; i < num_maps; i++) { // Check if the device name exists. if (!maps[i].dev_name) { pr_err("failed to register map %s (%d): no device given\n", maps[i].name, i); return -EINVAL; } // Check if the mapping table name exists. if (!maps[i].name) { pr_err("failed to register map %d: no map name given\n", i); return -EINVAL; } // For pin mapping type and configuration mapping type, check if the pin control device name exists. if (maps[i].type != PIN_MAP_TYPE_DUMMY_STATE && !maps[i].ctrl_dev_name) { pr_err("failed to register map %s (%d): no pin control device given\n", maps[i].name, i); return -EINVAL; }
switch (maps[i].type) { case PIN_MAP_TYPE_DUMMY_STATE:// For virtual state mapping type, no validation is performed. break; case PIN_MAP_TYPE_MUX_GROUP:// For mux group mapping type, perform pin mux mapping validation. ret = pinmux_validate_map(&maps[i], i); if (ret < 0) return ret; break; case PIN_MAP_TYPE_CONFIGS_PIN: case PIN_MAP_TYPE_CONFIGS_GROUP:// For configuration mapping type, perform pin configuration mapping validation. ret = pinconf_validate_map(&maps[i], i); if (ret < 0) return ret; break; default:// For invalid mapping types, return an error. pr_err("failed to register map %s (%d): invalid type given\n", maps[i].name, i); return -EINVAL; } } // Allocate memory for the mapping table node. maps_node = kzalloc(sizeof(*maps_node), GFP_KERNEL); if (!maps_node) return -ENOMEM; // Set the mapping table and the number of mapping table entries for the mapping table node. maps_node->maps = maps; maps_node->num_maps = num_maps; // Lock and insert the mapping table node at the end of the mapping table linked list. mutex_lock(&pinctrl_maps_mutex); list_add_tail(&maps_node->node, &pinctrl_maps); mutex_unlock(&pinctrl_maps_mutex);
visiblepinctrl_register_mappingsThe function’s purpose is to register a mapping table for a pin controllerpinctrl_maps, performs some parameter validity checks and verification, and inserts the mapping table node into the mapping table linked list.
add_setting()
create_pinctrlIn the function, throughret = add_setting(p, pctldev, map);add the mapping to the pin controller
// look up pinctrl_state, if it does not exist, create a new pinctrl_state state = find_state(p, map->name); if (!state) state = create_state(p, map->name); if (IS_ERR(state)) return PTR_ERR(state);
// If the mapping type is a virtual state mapping type, return directly if (map->type == PIN_MAP_TYPE_DUMMY_STATE) return0;
// allocate memory space for pinctrl_setting setting = kzalloc(sizeof(*setting), GFP_KERNEL); if (!setting) return -ENOMEM;
setting->type = map->type;// set the mapping type of pinctrl_setting
if (pctldev)// set the pin control device of pinctrl_setting setting->pctldev = pctldev; else setting->pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name); if (!setting->pctldev) { kfree(setting); /* Do not defer probing of hogs (circular loop) */ // if the pin control device does not exist, return an error if (!strcmp(map->ctrl_dev_name, map->dev_name)) return -ENODEV; /* * OK let us guess that the driver is not there yet, and * let's defer obtaining this pinctrl handle to later... */ dev_info(p->dev, "unknown pinctrl device %s in map entry, deferring probe", map->ctrl_dev_name); return -EPROBE_DEFER; } // set the device name of pinctrl_setting setting->dev_name = map->dev_name;
switch (map->type) { case PIN_MAP_TYPE_MUX_GROUP: // For the mux group mapping type, execute pinmux_map to pinctrl_setting conversion ret = pinmux_map_to_setting(map, setting); break; case PIN_MAP_TYPE_CONFIGS_PIN: case PIN_MAP_TYPE_CONFIGS_GROUP: // For the config mapping type, execute pinconf_map to pinctrl_setting conversion ret = pinconf_map_to_setting(map, setting); break; default: ret = -EINVAL; break; } if (ret < 0) { kfree(setting); return ret; } // Insert pinctrl_setting at the end of the state object's setting list list_add_tail(&setting->node, &state->settings);
return0; }
It can be seen:
For the mux group mapping type (PIN_MAP_TYPE_MUX_GROUP), callpinmux_map_to_settingfunction to perform the conversion from pin mux mapping to setting object.
For the config mapping type (PIN_MAP_TYPE_CONFIGS_PINorPIN_MAP_TYPE_CONFIGS_GROUP), callpinconf_map_to_settingfunction to perform the conversion from pin config mapping to setting object.
add_settingThe ultimate purpose of the function is to pass the incomingconst struct pinctrl_map *mapparameter values into thestruct pinctrl_settingtype variable, thereby further extractingpinctrl_mapthe content within the structure type variable.
struct pinctrl_state
1 2 3 4 5 6 7 8 9 10 11 12
/** * struct pinctrl_state - a pinctrl state for a device * @node: list node for struct pinctrl's @states field * @name: the name of this state * @settings: a list of settings for this state */ structpinctrl_state { structlist_headnode;// Linked list node, used to connect the state object to the state list of the pin controller object constchar *name;// Pointer to the name string of the state object structlist_headsettings;// Linked list of pinctrl_setting objects, containing all setting objects of this state };
// drivers/pinctrl/core.h /** * struct pinctrl_setting - an individual mux or config setting * @node: list node for struct pinctrl_settings's @settings field * @type: the type of setting * @pctldev: pin control device handling to be programmed. Not used for * PIN_MAP_TYPE_DUMMY_STATE. * @dev_name: the name of the device using this state * @data: Data specific to the setting type */ structpinctrl_setting { structlist_headnode;// Linked list node, used to connect the setting object to the setting list of the state object enumpinctrl_map_typetype;// Mapping type, indicating the type of the setting object structpinctrl_dev *pctldev;// Pointer to the pin control device object constchar *dev_name; // Pointer to the device name string union { structpinctrl_setting_muxmux;// Data structure for the mux group mapping type structpinctrl_setting_configsconfigs;// Data structure for the configuration mapping type } data; };
create_state()
add_setting()In the function, based on the name of the mapping table entry, usefind_state()function to find the corresponding state object in the pin controller object. Before this, we have not set the state object, so it will enter the second if judgment, throughstate = create_state(p, map->name)Create a state
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
staticstruct pinctrl_state *create_state(struct pinctrl *p, constchar *name) { structpinctrl_state *state; // Allocate memory for the pinctrl_state structure state = kzalloc(sizeof(*state), GFP_KERNEL); if (!state) return ERR_PTR(-ENOMEM); // Set the name of the state state->name = name; // Initialize the state's settings list INIT_LIST_HEAD(&state->settings); // Add the state to the pinctrl's state list list_add_tail(&state->node, &p->states);
return state; }
pinmux_map_to_setting()
add_setting()In the function, for the mux group mapping type (PIN_MAP_TYPE_MUX_GROUP), callpinmux_map_to_settingfunction to convert the pin mux mapping to a setting object.
// drivers/pinctrl/pinmux.c intpinmux_map_to_setting(conststruct pinctrl_map *map, struct pinctrl_setting *setting) { structpinctrl_dev *pctldev = setting->pctldev;// Get the pin control device pointer conststructpinmux_ops *pmxops = pctldev->desc->pmxops;// Get the pin mux operation pointer charconst * const *groups; // Pin mux group array unsigned num_groups; // Number of pin mux groups int ret; constchar *group; // Pin mux group name
if (!pmxops) {// Check if the pin control device supports pin mux operations dev_err(pctldev->dev, "does not support mux function\n"); return -EINVAL; } // Convert the mux function name in the mapping table to a mux function selector and save it in the data.mux.func field of the setting object ret = pinmux_func_name_to_selector(pctldev, map->data.mux.function); if (ret < 0) { dev_err(pctldev->dev, "invalid function %s in map table\n", map->data.mux.function); return ret; } setting->data.mux.func = ret;
// By calling the get method of the pin multiplexing operation object,_function_the groups function queries the multiplexing group information corresponding to the multiplexing function, // obtains the name array and count of the multiplexing groups, and saves them in the groups and num_groups variables. ret = pmxops->get_function_groups(pctldev, setting->data.mux.func, &groups, &num_groups); if (ret < 0) { dev_err(pctldev->dev, "can't query groups for function %s\n", map->data.mux.function); return ret; } if (!num_groups) { dev_err(pctldev->dev, "function %s can't be selected on any group\n", map->data.mux.function); return -EINVAL; } if (map->data.mux.group) {// Based on the multiplexing group name specified in the mapping table or by selecting the first multiplexing group name, it looks up the corresponding index in the multiplexing group array. group = map->data.mux.group; ret = match_string(groups, num_groups, group); if (ret < 0) { dev_err(pctldev->dev, "invalid group \"%s\" for function \"%s\"\n", group, map->data.mux.function); return ret; } } else { group = groups[0]; } // By calling the pinctrl method of the pin control device object,_get_group_selector // the function obtains the selector of the multiplexing group and saves it in the data.mux.group of the setting object. ret = pinctrl_get_group_selector(pctldev, group); if (ret < 0) { dev_err(pctldev->dev, "invalid group %s in map table\n", map->data.mux.group); return ret; } setting->data.mux.group = ret;// Set the multiplexing group selector of the setting object.
return0; }
pinconf_map_to_setting()
add_settingsIn the function, for the configuration mapping type (PIN_MAP_TYPE_CONFIGS_PINorPIN_MAP_TYPE_CONFIGS_GROUP), call thepinconf_map_to_settingfunction to perform the conversion of the pin configuration mapping to the setting object.
The function’s purpose is to convertpinctrl_mapthe pin configuration mapping intopinctrl_settinga setting object.
// drivers/pinctrl/pinconf.c intpinconf_map_to_setting(conststruct pinctrl_map *map, struct pinctrl_setting *setting) { structpinctrl_dev *pctldev = setting->pctldev;// Get the pin control device pointer. int pin;
switch (setting->type) { case PIN_MAP_TYPE_CONFIGS_PIN:// Configuration for a single pin pin = pin_get_from_name(pctldev, map->data.configs.group_or_pin);// Get pin number by pin name if (pin < 0) { dev_err(pctldev->dev, "could not map pin config for \"%s\"", map->data.configs.group_or_pin); return pin; } setting->data.configs.group_or_pin = pin;// Set the pin number of the settings object break; case PIN_MAP_TYPE_CONFIGS_GROUP:// Configuration for a pin group pin = pinctrl_get_group_selector(pctldev, map->data.configs.group_or_pin);// Get the selector of the pin group if (pin < 0) { dev_err(pctldev->dev, "could not map group config for \"%s\"", map->data.configs.group_or_pin); return pin; } setting->data.configs.group_or_pin = pin; // Set the pin group selector of the settings object break; default: return -EINVAL; }
setting->data.configs.num_configs = map->data.configs.num_configs;// Set the configuration count of the settings object setting->data.configs.configs = map->data.configs.configs;// Set the configuration pointer of the settings object
return0; }
For the configuration of a single pin, by calling thepin_get_from_namefunction, the pin number is obtained from the mapping table based on the pin name, and set into thedata.configs.group_or_pinfield of the settings object. If obtaining the pin number fails, an error is returned.
For the configuration of a pin group, it calls thepinctrl_get_group_selectorfunction to obtain the selector of the pin group from the mapping table based on the pin group name, and sets it into thedata.configs.group_or_pinfield of the settings object
pinctrl_lookup_state()
pinctrl_bind_pins()viadev->pins->default_state = pinctrl_lookup_state(dev->pins->p,PINCTRL_STATE_DEFAULT);Find the default pinctrl state of the device and assign it todev->pins->default_state. If the lookup fails, the function prints a debug message and sets the return value to 0, indicating continued execution.
/** * pinctrl_lookup_state() - retrieves a state handle from a pinctrl handle * @p: the pinctrl handle to retrieve the state from * @name: the state name to retrieve */ struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, constchar *name) { structpinctrl_state *state; // Find the state object with the specified name in the state linked list state = find_state(p, name); if (!state) { if (pinctrl_dummy_state) { /* create dummy state */ /* Create a dummy state */ dev_dbg(p->dev, "using pinctrl dummy state (%s)\n", name); // If the specified state object is not found and a dummy state exists, create a dummy state object state = create_state(p, name); } else // If the specified state object is not found and no dummy state exists, return the error pointer -ENODEV state = ERR_PTR(-ENODEV); }
pinctrl_bind_pins()Usepinctrl_select_state()functionret = pinctrl_select_state(dev->pins->p,dev->pins->default_state);Select and switch to the specifiedpinctrl_state(pin control state)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/** * pinctrl_select_state() - select/activate/program a pinctrl state to HW * @p: the pinctrl handle for the device that requests configuration * @state: the state handle to select/activate/program */ intpinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) { if (p->state == state)// If the current state is already the state to be selected, no operation is needed, and return 0 directly to indicate success return0; // Call pinctrl_commit_state function to apply and switch to the new state return pinctrl_commit_state(p, state); } EXPORT_SYMBOL_GPL(pinctrl_select_state);
/** * pinctrl_commit_state() - select/activate/program a pinctrl state to HW * @p: the pinctrl handle for the device that requests configuration * @state: the state handle to select/activate/program */ staticintpinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state) { structpinctrl_setting *setting, *setting2; structpinctrl_state *old_state = p->state; int ret;
if (p->state) { /* * For each pinmux setting in the old state, forget SW's record * of mux owner for that pingroup. Any pingroups which are * still owned by the new state will be re-acquired by the call * to pinmux_enable_setting() in the loop below. */ /* * For each pin mux setting in the old state,Cancel SW The recorded multiplexing owner of this pin group。 * Any pin groups still owned by the new state will be in the loop below pinmux_enable_setting() re-acquired in the call。 */ list_for_each_entry(setting, &p->state->settings, node) { if (setting->type != PIN_MAP_TYPE_MUX_GROUP) continue; pinmux_disable_setting(setting); } }
p->state = NULL;
/* Apply all the settings for the new state */ /* Apply all settings of the new state */ list_for_each_entry(setting, &state->settings, node) { switch (setting->type) { case PIN_MAP_TYPE_MUX_GROUP: // For pin multiplexing settings (PIN_MAP_TYPE_MUX_GROUP), call pinmux_enable_setting() function to enable the setting. ret = pinmux_enable_setting(setting); break; case PIN_MAP_TYPE_CONFIGS_PIN: case PIN_MAP_TYPE_CONFIGS_GROUP: // For pin configuration settings (PIN_MAP_TYPE_CONFIGS_PIN or PIN_MAP_TYPE_CONFIGS_GROUP), call pinconf_apply_setting() function to apply the setting ret = pinconf_apply_setting(setting); break; default: ret = -EINVAL; break; }
if (ret < 0) { // If applying the setting fails, roll back the settings of the new state goto unapply_new_state; }
/* Do not link hogs (circular dependency) */ if (p != setting->pctldev->p) pinctrl_link_add(setting->pctldev, p->dev); }
p->state = state;
return0;
unapply_new_state: // Roll back the settings of the new state dev_err(p->dev, "Error applying setting, reverse things back\n");
list_for_each_entry(setting2, &state->settings, node) { if (&setting2->node == &setting->node) break; /* * All we can do here is pinmux_disable_setting. * That means that some pins are muxed differently now * than they were before applying the setting (We can't * "unmux a pin"!), but it's not a big deal since the pins * are free to be muxed by another apply_setting. */ if (setting2->type == PIN_MAP_TYPE_MUX_GROUP) pinmux_disable_setting(setting2); }
/* There's no infinite recursive loop here because p->state is NULL */ if (old_state) pinctrl_select_state(p, old_state);
return ret; }
pinctrl_commit_state()The function iterates through all settings of the new state and performs corresponding actions based on the type of setting:
For pin multiplexing settings (PIN_MAP_TYPE_MUX_GROUP), call thepinmux_enable_setting()function to enable this setting.
For pin configuration settings (PIN_MAP_TYPE_CONFIGS_PINorPIN_MAP_TYPE_CONFIGS_GROUP), call thepinconf_apply_setting()function to apply this setting.
For other types of settings, an error code will be returned (-EINVAL)。
//drivers/pinctrl/pinmux.c intpinmux_enable_setting(conststruct pinctrl_setting *setting) { structpinctrl_dev *pctldev = setting->pctldev; conststructpinctrl_ops *pctlops = pctldev->desc->pctlops; conststructpinmux_ops *ops = pctldev->desc->pmxops; int ret = 0; constunsigned *pins = NULL; unsigned num_pins = 0; int i; structpin_desc *desc; // If pctlops->get_group_pins function exists, call it to obtain the pin information in the group, and store the pin information in the pins and num_pins variables if (pctlops->get_group_pins) ret = pctlops->get_group_pins(pctldev, setting->data.mux.group, &pins, &num_pins);
if (ret) {// If obtaining pin information fails, issue a warning and set num_pins to 0 constchar *gname;
/* errors only affect debug data, so just warn */ // The error only affects debug data, so only a warning is issued gname = pctlops->get_group_name(pctldev, setting->data.mux.group); dev_warn(pctldev->dev, "could not get pins for group %s\n", gname); num_pins = 0; }
/* Try to allocate all pins in this group, one by one */ // Request pins in the group one by one for (i = 0; i < num_pins; i++) { // Use the pin_request function to request pins, passing in the pin controller device, pin number, device name, and other parameters ret = pin_request(pctldev, pins[i], setting->dev_name, NULL); if (ret) { constchar *gname; constchar *pname; // After allocating pins, use the pin_desc_get function to obtain the pin descriptor, and point the mux setting pointer to the pin mux information. desc = pin_desc_get(pctldev, pins[i]); pname = desc ? desc->name : "non-existing"; gname = pctlops->get_group_name(pctldev, setting->data.mux.group); dev_err(pctldev->dev, "could not request pin %d (%s) from group %s " " on device %s\n", pins[i], pname, gname, pinctrl_dev_get_name(pctldev)); goto err_pin_request; } }
/* Now that we have acquired the pins, encode the mux setting */ // After pin allocation, configure multiplexing settings for (i = 0; i < num_pins; i++) { desc = pin_desc_get(pctldev, pins[i]); if (desc == NULL) { dev_warn(pctldev->dev, "could not get pin desc for pin %d\n", pins[i]); continue; } desc->mux_setting = &(setting->data.mux); } // Call the ops->set_mux function to set pin multiplexing, passing the pin controller device, multiplexing function, and group information to configure the pin multiplexing. ret = ops->set_mux(pctldev, setting->data.mux.func, setting->data.mux.group);
if (ret) goto err_set_mux;
return0;
err_set_mux: // Multiplexing setting failed, clear the multiplexing settings for (i = 0; i < num_pins; i++) { desc = pin_desc_get(pctldev, pins[i]); if (desc) desc->mux_setting = NULL; } err_pin_request: /* On error release all taken pins */ // Release the allocated pins when an error occurs while (--i >= 0) pin_free(pctldev, pins[i], NULL);
if (!ops) {// Check if the pinconf operation function set exists dev_err(pctldev->dev, "missing confops\n"); return -EINVAL; } // Select the corresponding operation based on the setting type switch (setting->type) { case PIN_MAP_TYPE_CONFIGS_PIN://Indicates configuration settings for a single pin if (!ops->pin_config_set) {// Check if the pin exists_config_set operation function dev_err(pctldev->dev, "missing pin_config_set op\n"); return -EINVAL; } // Call pin_config_set function configures a single pin ret = ops->pin_config_set(pctldev, setting->data.configs.group_or_pin, setting->data.configs.configs, setting->data.configs.num_configs); if (ret < 0) { dev_err(pctldev->dev, "pin_config_set op failed for pin %d\n", setting->data.configs.group_or_pin); return ret; } break; case PIN_MAP_TYPE_CONFIGS_GROUP:// Indicates configuration settings for a pin group if (!ops->pin_config_group_set) {// Check if the pin exists_config_group_set operation function dev_err(pctldev->dev, "missing pin_config_group_set op\n"); return -EINVAL; } // Call pin_config_The group_set function configures the pin group settings. ret = ops->pin_config_group_set(pctldev, setting->data.configs.group_or_pin, setting->data.configs.configs, setting->data.configs.num_configs); if (ret < 0) { dev_err(pctldev->dev, "pin_config_group_set op failed for group %d\n", setting->data.configs.group_or_pin); return ret; } break; default: return -EINVAL; }
return0; }
Mind Map
pinctrl
Conclusion
Hypothesis 1
The first hypothesis is that pinctrl pin multiplexing occurs when loading the LED driver. After the LED device tree matches the driver, it enters the probe function written in the driver. function, and before this, it will executedrivers/base/dd.cin the filereally_probesub-function within the functionpinctrl_bind_pins, which binds pins for the given device and selects and sets the appropriate pinctrl state during the binding process. Specific binding details can be found in previous chapters.
Hypothesis 2
The second hypothesis is that pin multiplexing is completed when loading the pinctrl driver. Since the pinctrl subsystem also conforms to the device model specification, it also executes the corresponding probe function, so similarly, when loading the pinctrl driver, it will also executedrivers/base/dd.cin the filereally_probesub-function within the functionpinctrl_bind_pins. Will the pinctrl pin multiplexing settings be performed at this point? Next, we will conduct an in-depth analysis of this.
Before the pinctrl probe function is executed, it will callpinctrl_bind_pinsfunction. Based on the previous content, according to the nesting of functions, the first to be called is thecreate_pinctrlfunction createsstruct pinctrltype of pin controller, and then increate_pinctrlcallingpinctrl_dt_to_mapfunction converts the pin mapping information defined in the device tree intostruct pinctrl_mapstructure, and adds it top->dt_mapslinked list
// drivers/pinctrl/devicetree.c intpinctrl_dt_to_map(struct pinctrl *p, struct pinctrl_dev *pctldev) { structdevice_node *np = p->dev->of_node;// Get the device tree node of the device associated with the pin controller int state, ret; char *propname; structproperty *prop; constchar *statename; const __be32 *list; int size, config; phandle phandle; structdevice_node *np_config;
/* CONFIG_OF enabled, p->dev not instantiated from DT */ /* If CONFIG_OF is enabled and p->dev is not instantiated from the device tree */ if (!np) { if (of_have_populated_dt()) dev_dbg(p->dev, "no of_node; not parsing pinctrl DT\n"); return0; }
/* We may store pointers to property names within the node */ /* Pointer to the property name stored inside the node */ of_node_get(np);//Increase the reference count of the device tree node to ensure the node is not freed during parsing
/* For each defined state ID */ /* For each defined state ID */ for (state = 0; ; state++) { /* Retrieve the pinctrl-* property */ /* Get the pinctrl-* property */ propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state); if (!propname) return -ENOMEM; prop = of_find_property(np, propname, &size); kfree(propname); if (!prop) { if (state == 0) { of_node_put(np); return -ENODEV; } break; } list = prop->value; size /= sizeof(*list);
/* Determine whether pinctrl-names property names the state */ /* Determine whether the pinctrl-names property has named this state */ ret = of_property_read_string_index(np, "pinctrl-names", state, &statename); /* * If not, statename is just the integer state ID. But rather * than dynamically allocate it and have to free it later, * just point part way into the property name for the string. */ /* * If not named,then statename only integer state ID。but,to avoid the trouble of dynamic allocation and subsequent deallocation, * can directly statename point to a part of the property name。 */ if (ret < 0) statename = prop->name + strlen("pinctrl-");
/* For every referenced pin configuration node in it */ /* for each referenced pin configuration node in it */ for (config = 0; config < size; config++) { phandle = be32_to_cpup(list++);
/* Look up the pin configuration node */ /* find the pin configuration node */ np_config = of_find_node_by_phandle(phandle); if (!np_config) { dev_err(p->dev, "prop %s index %i invalid phandle\n", prop->name, config); ret = -EINVAL; goto err; }
/* Parse the node */ /* parse the node */ ret = dt_to_map_one_config(p, pctldev, statename, np_config); of_node_put(np_config); if (ret < 0) goto err; }
/* No entries in DT? Generate a dummy state table entry */ /* if there is no entry in the device tree, generate a dummy state table entry */ if (!size) { ret = dt_remember_dummy_state(p, statename); if (ret < 0) goto err; } }
return0;
err: pinctrl_dt_free_maps(p); return ret; }
the key point is: what is passed here is the pinctrl device tree node, and in line 32, the for loop will obtainpinctrl-*property, but there is no such property in the pinctrl node,pinctrl-*property is added in a series of device nodes, so an error will be returned here, and the same error will be passed up to higher-level functions layer by layer, eventually causingpinctrl_bind_pins()function to return an error, thus failing to set the pin multiplexing, i.e.:
From the above call chain, it can be seen that the probe function of pinctrl will ultimately callcreate_pinctrlto createstruct pinctrltype of pin controller, thereby implementing pinctrl pin multiplexing settings. Similarly, this setting is also unsuccessful.pinctrl_claim_hogsThe function is defined in the kernel source directory underdrivers/pinctrl/core.cfile.