1. pinctrl Device Tree
    1. pinctrl driver
    2. groups and function
  2. Rockchip pinctrl related structure
    1. struct pinctrl_desc
    2. struct pinctrl_pin_desc
    3. struct pinctrl_dev
    4. struct rockchip_pinctrl
    5. struct rockchip_pin_ctrl
    6. struct rockchip_pin_bank
  3. rockchip_pinctrl_probe()
    1. probe function mind map
    2. rockchip_pinctrl_get_soc_data()
    3. syscon
      1. Compared to direct ioremap, why is thesyscon
      2. struct regmap
      3. syscon device tree
      4. Kernel API usage example
    4. rockchip_pinctrl_register()
      1. rockchip_pinctrl_parse_dt()
        1. rockchip_pinctrl_child_count()
        2. rockchip_pinctrl_parse_functions()
          1. rockchip_pinctrl_parse_groups()
      2. devm_pinctrl_register()
        1. pinctrl_register()
        2. pinctrl_init_controller()
  4. pinctrl subsystem function operation set
    1. pinctrl_ops
      1. get_groups_count
      2. get_group_name
      3. get_group_pins
      4. dt_node_to_map
        1. Related structure
          1. struct pinctrl_map
          2. struct pinctrl_map_mux
          3. struct pinctrl_map_configs
        2. rockchip_dt_node_to_map()
      5. dt_free_map
    2. pinconf_ops
      1. pin_config_get
      2. pin_config_set
    3. pinmux_ops
      1. get_functions_count
      2. get_function_name
      3. get_function_groups
      4. set_mux
  • When is the pin multiplexing relationship set
    1. really_probe()
    2. pinctrl_bind_pins()
      1. struct dev_pin_info
      2. devm_pinctrl_get()
      3. pinctrl_get()
      4. create_pinctrl()
        1. Important Data Structures
          1. struct pinctrl
          2. struct pinctrl_maps
        2. pinctrl_dt_to_map()
          1. dt_to_map_one_config()
          2. dt_remember_or_free_map()
          3. struct pinctrl_dt_map
          4. pinctrl_register_mappings()
        3. add_setting()
          1. struct pinctrl_state
          2. struct pinctrl_setting
          3. create_state()
          4. pinmux_map_to_setting()
          5. pinconf_map_to_setting()
      5. pinctrl_lookup_state()
      6. pinctrl_select_state()
        1. pinctrl_commit_state()
          1. pinmux_enable_setting()
          2. pinconf_apply_setting()
    3. Mind Map
    4. Conclusion
  • Cover image for Linux pinctrl subsystem

    Linux pinctrl subsystem


    Timeline

    Timeline

    2025-12-16

    init

    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.

    Linux Driver Notes

    Table of ContentsLinks
    1. Linux Driver Framework
    2. Linux Driver Loading Logic
    3. Character Device Basics
    4. Concurrency and Competition
    5. Advanced Character Device Progression
    6. Interrupts
    7. Platform Bus
    8. Device Tree
    9. Device Model
    10. Hot Plug
    11. pinctrl Subsystem
    12. gpio Subsystem
    13. Input Subsystem
    14. Single Bus
    15. I2C
    16. SPI
    17. UART
    18. PWM
    19. RTC
    20. Watchdog
    21. CAN
    22. Network Device
    23. ADC
    24. IIO
    25. USB
    26. LCD

    pinctrl Device Tree

    arch/arm64/boot/dts/rk3568.dtsi

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    pinctrl: pinctrl {
    compatible = "rockchip,rk3568-pinctrl";
    rockchip,grf = <&grf>;
    rockchip,pmu = <&pmugrf>;
    #address-cells = <2>;
    #size-cells = <2>;
    ranges;

    gpio0: gpio0@fdd60000 {
    compatible = "rockchip,gpio-bank";
    reg = <0x0 0xfdd60000 0x0 0x100>;
    interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&pmucru PCLK_GPIO0>, <&pmucru DBCLK_GPIO0>;

    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells = <2>;
    };

    gpio1: gpio1@fe740000 {
    compatible = "rockchip,gpio-bank";
    reg = <0x0 0xfe740000 0x0 0x100>;
    interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&cru PCLK_GPIO1>, <&cru DBCLK_GPIO1>;

    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells = <2>;
    };

    gpio2: gpio2@fe750000 {
    compatible = "rockchip,gpio-bank";
    reg = <0x0 0xfe750000 0x0 0x100>;
    interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&cru PCLK_GPIO2>, <&cru DBCLK_GPIO2>;

    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells = <2>;
    };

    gpio3: gpio3@fe760000 {
    compatible = "rockchip,gpio-bank";
    reg = <0x0 0xfe760000 0x0 0x100>;
    interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&cru PCLK_GPIO3>, <&cru DBCLK_GPIO3>;

    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells = <2>;
    };

    gpio4: gpio4@fe770000 {
    compatible = "rockchip,gpio-bank";
    reg = <0x0 0xfe770000 0x0 0x100>;
    interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&cru PCLK_GPIO4>, <&cru DBCLK_GPIO4>;

    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells = <2>;
    };
    };

    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 bankNamePin CountBase Address
    GPIO0gpio0320xfdd60000
    GPIO1gpio1320xfe740000
    GPIO2gpio2320xfe750000
    GPIO3gpio3320xfe760000
    GPIO4gpio4320xfe770000

    Each bank:

    • a set of registers
    • an interrupt number
    • two clocks (PCLK / DBCLK)
    • controls a set of physical pins: direction, level, GPIO interrupt

    arch/arm64/boot/dts/rockchip/rk3568-pinctrl.dtsi

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    &pinctrl {
    acodec {
    /omit-if-no-ref/
    acodec_pins: acodec-pins {
    rockchip,pins =
    /* acodec_adc_sync */
    <1 RK_PB1 5 &pcfg_pull_none>,
    /* acodec_adcclk */
    <1 RK_PA1 5 &pcfg_pull_none>,
    /* acodec_adcdata */
    <1 RK_PA0 5 &pcfg_pull_none>,
    /* acodec_dac_datal */
    <1 RK_PA7 5 &pcfg_pull_none>,
    /* acodec_dac_datar */
    <1 RK_PB0 5 &pcfg_pull_none>,
    /* acodec_dacclk */
    <1 RK_PA3 5 &pcfg_pull_none>,
    /* acodec_dacsync */
    <1 RK_PA5 5 &pcfg_pull_none>;
    };
    };

    audiopwm {
    /omit-if-no-ref/
    audiopwm_lout: audiopwm-lout {
    rockchip,pins =
    /* audiopwm_lout */
    <1 RK_PA0 4 &pcfg_pull_none>;
    };

    /omit-if-no-ref/
    audiopwm_loutn: audiopwm-loutn {
    rockchip,pins =
    /* audiopwm_loutn */
    <1 RK_PA1 6 &pcfg_pull_none>;
    };

    /omit-if-no-ref/
    audiopwm_loutp: audiopwm-loutp {
    rockchip,pins =
    /* audiopwm_loutp */
    <1 RK_PA0 6 &pcfg_pull_none>;
    };

    /omit-if-no-ref/
    audiopwm_rout: audiopwm-rout {
    rockchip,pins =
    /* audiopwm_rout */
    <1 RK_PA1 4 &pcfg_pull_none>;
    };

    /omit-if-no-ref/
    audiopwm_routn: audiopwm-routn {
    rockchip,pins =
    /* audiopwm_routn */
    <1 RK_PA7 4 &pcfg_pull_none>;
    };

    /omit-if-no-ref/
    audiopwm_routp: audiopwm-routp {
    rockchip,pins =
    /* audiopwm_routp */
    <1 RK_PA6 4 &pcfg_pull_none>;
    };
    };
    ...

    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    static const struct of_device_id rockchip_pinctrl_dt_match[] = {
    ...
    #ifdef CONFIG_CPU_RK3568
    { .compatible = "rockchip,rk3568-pinctrl",
    .data = &rk3568_pin_ctrl },
    #endif
    ...
    {},
    };

    static struct platform_driver rockchip_pinctrl_driver = {
    .probe = rockchip_pinctrl_probe,
    .remove = rockchip_pinctrl_remove,
    .driver = {
    .name = "rockchip-pinctrl",
    .pm = &rockchip_pinctrl_dev_pm_ops,
    .of_match_table = rockchip_pinctrl_dt_match,
    },
    };

    static int __init rockchip_pinctrl_drv_register(void)
    {
    return platform_driver_register(&rockchip_pinctrl_driver);
    }
    postcore_initcall(rockchip_pinctrl_drv_register);

    static void __exit rockchip_pinctrl_drv_unregister(void)
    {
    platform_driver_unregister(&rockchip_pinctrl_driver);
    }
    module_exit(rockchip_pinctrl_drv_unregister);

    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:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    can0 {
    /omit-if-no-ref/
    can0m0_pins: can0m0-pins {
    rockchip,pins =
    /* can0_rxm0 */
    <0 RK_PB4 2 &pcfg_pull_none>,
    /* can0_txm0 */
    <0 RK_PB3 2 &pcfg_pull_none>;
    };

    /omit-if-no-ref/
    can0m1_pins: can0m1-pins {
    rockchip,pins =
    /* can0_rxm1 */
    <2 RK_PA2 4 &pcfg_pull_none>,
    /* can0_txm1 */
    <2 RK_PA1 4 &pcfg_pull_none>;
    };
    };

    can1 {
    /omit-if-no-ref/
    can1m0_pins: can1m0-pins {
    rockchip,pins =
    /* can1_rxm0 */
    <1 RK_PA0 3 &pcfg_pull_none>,
    /* can1_txm0 */
    <1 RK_PA1 3 &pcfg_pull_none>;
    };

    /omit-if-no-ref/
    can1m1_pins: can1m1-pins {
    rockchip,pins =
    /* can1_rxm1 */
    <4 RK_PC2 3 &pcfg_pull_none>,
    /* can1_txm1 */
    <4 RK_PC3 3 &pcfg_pull_none>;
    };
    };

    In the device tree above,can0 and can1 correspond to two different functionswhich are the CAN0 controller and CAN1 controller respectivelyand 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
        • RK_PC2Receive pin for CAN1 (can1_rxm1
        • RK_PC3Transmit pin for CAN1 (can1_txm1)。

    Rockchip pinctrl related structure

    struct pinctrl_desc

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    /**
    * 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.
    */
    struct pinctrl_desc {
    const char *name;// Name of the pin controller
    const struct pinctrl_pin_desc *pins;// Pin descriptor array
    unsigned int npins;// Size of the pin descriptor array
    const struct pinctrl_ops *pctlops;// Pin control operation function pointer
    const struct pinmux_ops *pmxops;// Pin mux operation function pointer
    const struct pinconf_ops *confops;// Pin configuration operation function pointer
    struct module *owner;// Module that owns this structure
    #ifdef CONFIG_GENERIC_PINCONF
    unsigned int num_custom_params;// Custom parameter count
    const struct pinconf_generic_params *custom_params;// Custom parameter array
    const struct pin_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
    */
    struct pinctrl_pin_desc {
    unsigned number;
    const char *name;
    void *drv_data;
    };

    struct pinctrl_dev

    In therockchip_pinctrl_probefunction, throughinfo->pctl_dev = devm_pinctrl_register(dev, ctrldesc, info)setting

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    /**
    * 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
    */
    struct pinctrl_dev {
    struct list_head node;// the node in the pinctrl_dev linked list
    struct pinctrl_desc *desc; // the pin controller description pointer
    struct radix_tree_root pin_desc_tree;// Radix tree of pin descriptor structures
    #ifdef CONFIG_GENERIC_PINCTRL_GROUPS
    struct radix_tree_root pin_group_tree;// Root of radix tree for pin group structures
    unsigned int num_groups;// Number of pin groups
    #endif
    #ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS
    struct radix_tree_root pin_function_tree;// Root of radix tree for pin function structures
    unsigned int num_functions;// Number of pin functions
    #endif
    struct list_head gpio_ranges;// GPIO range linked list
    struct device *dev;// Device structure pointer
    struct module *owner;// Pointer to the module owning the pinctrl_dev structure
    void *driver_data;// Driver's private data pointer
    struct pinctrl *p;// Pointer to pinctrl structure
    struct pinctrl_state *hog_default;// Hog default state
    struct pinctrl_state *hog_sleep;// Hog sleep state
    struct mutex mutex;// Mutex lock
    #ifdef CONFIG_DEBUG_FS
    struct dentry *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
    struct rockchip_pinctrl {
    struct regmap *regmap_base;// Basic register mapping pointer
    int reg_size; // Register size
    struct regmap *regmap_pull;// Pull register mapping pointer
    struct regmap *regmap_pmu;// Power management unit register mapping pointer
    struct device *dev; // Device pointer
    struct rockchip_pin_ctrl *ctrl; // Rockchip chip pin controller pointer
    struct pinctrl_desc pctl; // Pin controller descriptor
    struct pinctrl_dev *pctl_dev; // Pin controller device pointer
    struct rockchip_pin_group *groups;// Rockchip chip pin group pointer
    unsigned int ngroups; // Number of pin groups
    struct rockchip_pmx_func *functions;// Rockchip chip pin function pointer
    unsigned int 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.

    struct rockchip_pin_ctrl

    Inrockchip_pinctrl_probethe function, throughctrl = rockchip_pinctrl_get_soc_data(info, pdev)Setting

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    struct rockchip_pin_ctrl {
    struct rockchip_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
    enum rockchip_pinctrl_type type;// 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;
    struct rockchip_mux_recalced_data *iomux_recalced;
    u32 niomux_recalced;
    struct rockchip_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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    /**
    * 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
    */
    struct rockchip_pin_bank {
    struct device *dev;
    void __iomem *reg_base;
    struct regmap *regmap_pull;
    struct clk *clk;
    struct clk *db_clk;
    int irq;
    u32 saved_masks;
    u32 pin_base;
    u8 nr_pins;
    char *name;
    u8 bank_num;
    struct rockchip_iomux iomux[4];
    struct rockchip_drv drv[4];
    enum rockchip_pin_pull_type pull_type[4];
    bool valid;
    struct device_node *of_node;
    struct rockchip_pinctrl *drvdata;
    struct irq_domain *domain;
    struct gpio_chip gpio_chip;
    struct pinctrl_gpio_range grange;
    raw_spinlock_t slock;
    const struct rockchip_gpio_regs *gpio_regs;
    u32 gpio_type;
    u32 toggle_edge_mode;
    u32 recalced_mask;
    u32 route_mask;
    struct list_head deferred_pins;
    struct mutex deferred_lock;
    };

    rockchip_pinctrl_probe()

    The probe function in Rockchip’s pinctrl driver is as follows:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    static int rockchip_pinctrl_probe(struct platform_device *pdev)
    {
    struct rockchip_pinctrl *info; // Pointer to Rockchip GPIO controller info structure
    struct device *dev = &pdev->dev; // Pointer to device structure
    struct device_node *np = dev->of_node, *node; // Pointer to device node
    struct rockchip_pin_ctrl *ctrl; // Pointer to Rockchip GPIO controller config structure
    struct resource *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));

    return 0;
    }

    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

      1. New-style binding: references a system controller (e.g., GRF) via phandle;
      2. 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
    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…)
    • drvRegister: controls drive strength (2mA / 4mA / 8mA…)

    etc.

    As in the RK3568 TRM:

    GPIO IOMUX register offset
    GPIO IOMUX register offset

    GPIO drive strength offset
    GPIO drive strength offset

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    /* retrieve the soc specific data */
    static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
    struct rockchip_pinctrl *d,
    struct platform_device *pdev)
    {
    struct device *dev = &pdev->dev;
    struct device_node *node = dev->of_node;
    const struct of_device_id *match;
    struct rockchip_pin_ctrl *ctrl;
    struct rockchip_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;

    raw_spin_lock_init(&bank->slock);
    bank->drvdata = d;
    bank->pin_base = ctrl->nr_pins;
    ctrl->nr_pins += bank->nr_pins;

    /* 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++) {
    struct rockchip_iomux *iom = &bank->iomux[j];
    struct rockchip_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;

    if (iom->type & IOMUX_SOURCE_PMU)
    drv_pmu_offs += inc;
    else
    drv_grf_offs += inc;

    bank_pins += 8;
    }

    /* calculate the per-bank recalced_mask */
    for (j = 0; j < ctrl->niomux_recalced; j++) {
    int pin = 0;

    if (ctrl->iomux_recalced[j].num == bank->bank_num) {
    pin = ctrl->iomux_recalced[j].pin;
    bank->recalced_mask |= BIT(pin);
    }
    }

    /* calculate the per-bank route_mask */
    for (j = 0; j < ctrl->niomux_routes; j++) {
    int pin = 0;

    if (ctrl->iomux_routes[j].bank_num == bank->bank_num) {
    pin = ctrl->iomux_routes[j].pin;
    bank->route_mask |= BIT(pin);
    }
    }
    }

    return ctrl;
    }

    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?Issuessyscon Advantages
    Each driver independentlyioremapmaps the same physical address multiple times, wasting resourcesGlobal unique mapping
    No synchronization mechanismMultiple drivers writing to registers simultaneously → race conditionregmapBuilt-in lock
    Code duplicationEach driver must handle offsets and bit operationsUnified abstract interface
    Strong device tree couplingDriver needs to know specific addressesDecoupling via phandle

    struct regmap

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    struct regmap {
    union {
    struct mutex mutex;
    struct {
    spinlock_t spinlock;
    unsigned long spinlock_flags;
    };
    };
    regmap_lock lock;
    regmap_unlock unlock;
    void *lock_arg; /* This is passed to lock/unlock functions */
    gfp_t alloc_flags;

    struct device *dev; /* Device we do I/O on */
    void *work_buf; /* Scratch buffer used to format I/O */
    struct regmap_format format; /* Buffer format */
    const struct regmap_bus *bus;
    void *bus_context;
    const char *name;

    bool async;
    spinlock_t async_lock;
    wait_queue_head_t async_waitq;
    struct list_head async_list;
    struct list_head async_free;
    int async_ret;

    #ifdef CONFIG_DEBUG_FS
    bool debugfs_disable;
    struct dentry *debugfs;
    const char *debugfs_name;

    unsigned int debugfs_reg_len;
    unsigned int debugfs_val_len;
    unsigned int debugfs_tot_len;

    struct list_head debugfs_off_cache;
    struct mutex cache_lock;
    #endif

    unsigned int max_register;
    bool (*writeable_reg)(struct device *dev, unsigned int reg);
    bool (*readable_reg)(struct device *dev, unsigned int reg);
    bool (*volatile_reg)(struct device *dev, unsigned int reg);
    bool (*precious_reg)(struct device *dev, unsigned int reg);
    bool (*writeable_noinc_reg)(struct device *dev, unsigned int reg);
    bool (*readable_noinc_reg)(struct device *dev, unsigned int reg);
    const struct regmap_access_table *wr_table;
    const struct regmap_access_table *rd_table;
    const struct regmap_access_table *volatile_table;
    const struct regmap_access_table *precious_table;
    const struct regmap_access_table *wr_noinc_table;
    const struct regmap_access_table *rd_noinc_table;

    int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
    int (*reg_write)(void *context, unsigned int reg, unsigned int val);
    int (*reg_update_bits)(void *context, unsigned int reg,
    unsigned int mask, unsigned int val);

    bool defer_caching;

    unsigned long read_flag_mask;
    unsigned long write_flag_mask;

    /* number of bits to (left) shift the reg value when formatting*/
    int reg_shift;
    int reg_stride;
    int reg_stride_order;

    /* regcache specific members */
    const struct regcache_ops *cache_ops;
    enum regcache_type cache_type;

    /* number of bytes in reg_defaults_raw */
    unsigned int cache_size_raw;
    /* number of bytes per word in reg_defaults_raw */
    unsigned int cache_word_size;
    /* number of entries in reg_defaults */
    unsigned int num_reg_defaults;
    /* number of entries in reg_defaults_raw */
    unsigned int 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;

    struct reg_default *reg_defaults;
    const void *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;

    struct reg_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;

    struct rb_root range_tree;
    void *selector_work_buf; /* Scratch buffer used for selector */

    struct hwspinlock *hwlock;

    /* if set, the regmap core can sleep */
    bool can_sleep;
    };

    Common system controllers in Rockchip SoC include:

    ControllerFull nameFunction
    GRFGeneral Register FilesControls GPIO multiplexing, voltage domains, peripheral routing, etc.
    PMUPower Management UnitLow-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

    sysconexactly solve these problems.

    syscon device tree

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    pmugrf: syscon@fdc20000 {
    compatible = "rockchip,rk3568-pmugrf", "syscon", "simple-mfd";
    reg = <0x0 0xfdc20000 0x0 0x10000>;

    pmu_io_domains: io-domains {
    compatible = "rockchip,rk3568-pmu-io-voltage-domain";
    status = "disabled";
    };

    reboot_mode: reboot-mode {
    compatible = "syscon-reboot-mode";
    offset = <0x200>;
    mode-bootloader = <BOOT_BL_DOWNLOAD>;
    mode-charge = <BOOT_CHARGING>;
    mode-fastboot = <BOOT_FASTBOOT>;
    mode-loader = <BOOT_BL_DOWNLOAD>;
    mode-normal = <BOOT_NORMAL>;
    mode-recovery = <BOOT_RECOVERY>;
    mode-ums = <BOOT_UMS>;
    mode-panic = <BOOT_PANIC>;
    mode-watchdog = <BOOT_WATCHDOG>;
    };
    };

    pipegrf: syscon@fdc50000 {
    compatible = "rockchip,rk3568-pipegrf", "syscon";
    reg = <0x0 0xfdc50000 0x0 0x1000>;
    };


    grf: syscon@fdc60000 {
    compatible = "rockchip,rk3568-grf", "syscon", "simple-mfd";
    reg = <0x0 0xfdc60000 0x0 0x10000>;

    io_domains: io-domains {
    compatible = "rockchip,rk3568-io-voltage-domain";
    status = "disabled";
    };

    lvds0: lvds: lvds {
    compatible = "rockchip,rk3568-lvds";
    phys = <&video_phy0>;
    phy-names = "phy";
    status = "disabled";

    ports {
    #address-cells = <1>;
    #size-cells = <0>;

    port@0 {
    reg = <0>;
    #address-cells = <1>;
    #size-cells = <0>;

    lvds0_in_vp1: lvds_in_vp1: endpoint@1 {
    reg = <1>;
    remote-endpoint = <&vp1_out_lvds>;
    status = "disabled";
    };

    lvds0_in_vp2: lvds_in_vp2: endpoint@2 {
    reg = <2>;
    remote-endpoint = <&vp2_out_lvds>;
    status = "disabled";
    };
    };
    };
    };

    lvds1: lvds1 {
    compatible = "rockchip,rk3568-lvds";
    phys = <&video_phy1>;
    phy-names = "phy";
    status = "disabled";

    ports {
    #address-cells = <1>;
    #size-cells = <0>;

    port@0 {
    reg = <0>;
    #address-cells = <1>;
    #size-cells = <0>;

    lvds1_in_vp1: endpoint@0 {
    reg = <0>;
    remote-endpoint = <&vp1_out_lvds1>;
    };

    lvds1_in_vp2: endpoint@1 {
    reg = <1>;
    remote-endpoint = <&vp2_out_lvds1>;
    };
    };
    };
    };

    rgb: rgb {
    compatible = "rockchip,rk3568-rgb";
    pinctrl-names = "default";
    pinctrl-0 = <&lcdc_ctl>;
    status = "disabled";

    ports {
    #address-cells = <1>;
    #size-cells = <0>;

    port@0 {
    reg = <0>;
    #address-cells = <1>;
    #size-cells = <0>;

    rgb_in_vp2: endpoint@2 {
    reg = <2>;
    remote-endpoint = <&vp2_out_rgb>;
    status = "disabled";
    };
    };
    };
    };

    };
    • GRFGeneral Register Files— main system controller,core dependency of pinctrl
    • Almost all peripheral multiplexing, enabling, and voltage domains are configured here.
    • Sub-functions:
      • io_domains: main IO voltage domain control (e.g., levels of HDMI, MIPI, SDIO)
      • lvds0 / lvds1: LVDS display output controller (connects to screen)
      • rgb: RGB parallel display interface (legacy LCD)

    Key points:

    • Must include"syscon"incompatiblelist
    • Thesyscondriver in the kernel will match all nodes with"syscon"node
    • Automatically createregmapand cache it for other drivers to access viaof_xlateorsyscon_node_to_regmap()get

    Kernel API usage example

    1. Get the regmap of syscon

    1
    2
    3
    struct device_node *np = of_parse_phandle(dev->of_node, "rockchip,grf", 0);
    struct regmap *grf_regmap = syscon_node_to_regmap(np);
    // Subsequently, you can call regmap_write(grf_regmap, offset, value);

    2. Get directly via phandle

    1
    struct regmap *grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");

    3. Global Search

    1
    struct regmap *grf = syscon_regmap_lookup_by_compatible("rockchip,rk3399-grf");

    rockchip_pinctrl_register()

    rockchip_pinctrl_probe()Function Last Callrockchip_pinctrl_register()Function Registrationrockchip_pinctrlDevice

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    static int rockchip_pinctrl_register(struct platform_device *pdev,
    struct rockchip_pinctrl *info)
    {
    // info's pctl is pinctrl_desc
    struct pinctrl_desc *ctrldesc = &info->pctl;
    struct pinctrl_pin_desc *pindesc, *pdesc;
    struct rockchip_pin_bank *pin_bank;
    struct device *dev = &pdev->dev;
    int pin, bank, ret;
    int k;

    // Initialize the pinctrl descriptor structure pinctrl_desc
    ctrldesc->name = "rockchip-pinctrl";
    ctrldesc->owner = THIS_MODULE;
    ctrldesc->pctlops = &rockchip_pctrl_ops; // pinctrl Control Operation Functions
    ctrldesc->pmxops = &rockchip_pmx_ops; // pinctrl Pin Mux Operation Functions
    ctrldesc->confops = &rockchip_pinconf_ops;// pinctrl Pin Configuration Operation Functions

    // 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");

    return 0;
    }
    • 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->devinfo->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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    static int rockchip_pinctrl_parse_dt(struct platform_device *pdev,
    struct rockchip_pinctrl *info)
    {
    struct device *dev = &pdev->dev;
    struct device_node *np = dev->of_node;
    struct device_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;
    }
    }

    return 0;
    }



    • 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
    static void rockchip_pinctrl_child_count(struct rockchip_pinctrl *info,
    struct device_node *np)
    {
    struct device_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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    static int rockchip_pinctrl_parse_functions(struct device_node *np,
    struct rockchip_pinctrl *info,
    u32 index)
    {
    struct device *dev = info->dev;
    struct device_node *child;
    struct rockchip_pmx_func *func;// Used to store function information
    struct rockchip_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)
    return 0;

    // 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;
    }
    }

    return 0;
    }

    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    static int rockchip_pinctrl_parse_groups(struct device_node *np,
    struct rockchip_pin_group *grp,
    struct rockchip_pinctrl *info,
    u32 index)
    {
    struct device *dev = info->dev;
    struct rockchip_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;
    struct device_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;
    }

    return 0;
    }

    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.

    1. struct device *dev = &pdev->dev;
    2. struct pinctrl_desc *ctrldesc = &info->pctl
    3. struct rockchip_pinctrl *info
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    // 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)
    {
    struct pinctrl_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);

    return pctldev;
    }
    EXPORT_SYMBOL_GPL(devm_pinctrl_register);

    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // 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)
    {
    struct pinctrl_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);

    return pctldev;
    }
    EXPORT_SYMBOL_GPL(pinctrl_register);

    pinctrl_init_controller()

    pinctrl_register()throughpctldev = pinctrl_init_controller(pctldesc, dev, driver_data);Initialize pinctrl controller

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    /**
    * 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
    */
    static struct pinctrl_dev *
    pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev,
    void *driver_data)
    {
    struct pinctrl_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;
    }

    return pctldev;

    out_err:
    mutex_destroy(&pctldev->mutex);
    kfree(pctldev);
    return ERR_PTR(ret);
    }

    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
    const struct pinctrl_ops *pctlops; // pin control operation function pointer
    const struct pinmux_ops *pmxops; // pin multiplexing operation function pointer
    const struct pinconf_ops *confops; // pin configuration operation function pointer

    pinctrl_ops

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    /**
    * 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.
    */
    struct pinctrl_ops {
    int (*get_groups_count) (struct pinctrl_dev *pctldev);//Get the number of pin groups supported by the specified Pin Control device
    const char *(*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,
    const unsigned **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
    };

    Specific implementation:

    1
    2
    3
    4
    5
    6
    7
    static const struct pinctrl_ops rockchip_pctrl_ops = {
    .get_groups_count = rockchip_get_groups_count,
    .get_group_name = rockchip_get_group_name,
    .get_group_pins = rockchip_get_group_pins,
    .dt_node_to_map = rockchip_dt_node_to_map,
    .dt_free_map = rockchip_dt_free_map,
    };

    get_groups_count

    Get the number of pin groups.

    1
    2
    3
    4
    5
    6
    7
    static int rockchip_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
    struct rockchip_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
    static const char *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
    struct rockchip_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
    static int rockchip_get_group_pins(struct pinctrl_dev *pctldev,
    unsigned selector, const unsigned **pins,
    unsigned *npins)
    {
    // From pinctrl_Get the private data pointer from the dev structure and cast it to rockchip_pinctrl structure
    struct rockchip_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;

    return 0;
    }

    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.”

    For example:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    &pinctrl {
    i2c1_xfer: i2c1-xfer {
    rockchip,pins =
    <1 RK_PB0 1 &pcfg_pull_up>, // GPIO1_B0 → I2C1_SCL
    <1 RK_PB1 1 &pcfg_pull_up>; // GPIO1_B1 → I2C1_SDA
    };
    };

    &i2c1 {
    pinctrl-names = "default";
    pinctrl-0 = <&i2c1_xfer>;
    status = "okay";
    };
    • defines apin configuration blocki2c1_xfer
    • When the I2C1 controller is working, it needs to:
      • ConfigureGPIO1_B0asthe I2C1_SCL function (mux value = 1), and enable pull-up
      • ConfigureGPIO1_B1asthe I2C1_SDA function (mux value = 1), and enable pull-up

    The pinctrl driver relies ondt_node_to_map()translating this DTS node into a kernel structure

    struct pinctrl_map
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    // 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
    */
    struct pinctrl_map {
    const char *dev_name;// Device name
    const char *name;// Mapping name
    enum pinctrl_map_type type;// Mapping type
    const char *ctrl_dev_name;// Control device name
    union {
    struct pinctrl_map_mux mux;// Mux mapping data
    struct pinctrl_map_configs configs; // Config mapping data
    } data;
    };

    FieldMeaningDescription
    dev_nameDevice name using this pin configurationUsually 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).
    nameThe state name of the mappingcorresponds to the one in the device treepinctrl-names = "xxx", for example"default""sleep". Called bypinctrl_lookup_state(pctl, "default")when used.
    typeMapping typeEnumeration 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_nameThe pinctrl device name controlling this pinis usually"pinctrl"or"rockchip-pinctrl", used to find the corresponding pinctrl driver.
    dataSpecific 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
    */
    struct pinctrl_map_mux {
    const char *group;
    const char *function;
    };
    fieldMeaning
    groupPin group name, e.g.,"i2c1-xfer""uart2-m1". This group is predefined in the pinctrl driver and contains a set of physical pins.
    functionFunction 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
    */
    struct pinctrl_map_configs {
    const char *group_or_pin;
    unsigned long *configs;
    unsigned num_configs;
    };
    FieldMeaning
    group_or_pinThe object to configure can be a pin name (e.g.,"gpio1-16") or a pin group name (e.g.,"i2c1-xfer")。
    configsConfiguration parameter array, each element is aunsigned long, encoding configuration items and values (e.g.,PIN_CONFIG_BIAS_PULL_UP)。
    num_configsNumber of configuration items, arrayconfigslength.

    Example:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    unsigned long i2c_pull_up[] = {
    PIN_CONFIG_BIAS_PULL_UP, // Parameter type
    };

    {
    .type = PIN_MAP_TYPE_CONFIGS_PIN,
    .data.configs = {
    .group_or_pin = "gpio1-16",
    .configs = i2c_pull_up,
    .num_configs = 1
    }
    }

    Meaning: Enable pull-up resistor for pingpio1-16

    rockchip_dt_node_to_map()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    static int rockchip_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
    struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
    // Pin group pointer
    const struct rockchip_pin_group *grp;
    // Device pointer
    struct device *dev = info->dev;
    // New pin mapping array
    struct pinctrl_map *new_map;
    // Parent node pointer
    struct device_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);

    return 0;
    }

    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.

    1
    2
    3
    4
    5
    static void rockchip_dt_free_map(struct pinctrl_dev *pctldev,
    struct pinctrl_map *map, unsigned num_maps)
    {
    kfree(map);
    }

    pinconf_ops

    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.).

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    /**
    * 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
    */
    struct pinconf_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,
    unsigned long *config);
    // Set pin configuration information
    int (*pin_config_set) (struct pinctrl_dev *pctldev,
    unsigned pin,
    unsigned long *configs,
    unsigned num_configs);
    // Get pin group configuration information
    int (*pin_config_group_get) (struct pinctrl_dev *pctldev,
    unsigned selector,
    unsigned long *config);
    // Set pin group configuration information
    int (*pin_config_group_set) (struct pinctrl_dev *pctldev,
    unsigned selector,
    unsigned long *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,
    unsigned long 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
    static const struct pinconf_ops rockchip_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,
    };

    pin_config_get

    Get pin configuration information.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    /* get the pin config settings for a specified pin */
    static int rockchip_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
    unsigned long *config)
    {
    struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
    struct rockchip_pin_bank *bank = pin_to_bank(info, pin);
    struct gpio_chip *gpio = &bank->gpio_chip;
    enum pin_config_param param = 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;

    arg = rc;
    break;
    default:// Unsupported configuration parameter
    return -ENOTSUPP;
    }

    *config = pinconf_to_config_packed(param, arg);

    return 0;
    }

    pin_config_set

    Set pin configuration information.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    /* set the pin config settings for a specified pin */
    static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
    unsigned long *configs, unsigned num_configs)
    {
    struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
    struct rockchip_pin_bank *bank = pin_to_bank(info, pin);
    struct gpio_chip *gpio = &bank->gpio_chip;
    enum pin_config_param param;
    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 */

    return 0;
    }

    pinmux_ops

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    /**
    * 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.
    */
    struct pinmux_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
    const char *(*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,
    const char * 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)
    1
    2
    3
    4
    5
    6
    static const struct pinmux_ops rockchip_pmx_ops = {
    .get_functions_count = rockchip_pmx_get_funcs_count,
    .get_function_name = rockchip_pmx_get_func_name,
    .get_function_groups = rockchip_pmx_get_groups,
    .set_mux = rockchip_pmx_set,
    };

    get_functions_count

    Returns the number of named functions selectable in the pin controller driver.

    1
    2
    3
    4
    5
    6
    static int rockchip_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
    {
    struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

    return info->nfunctions;// Returns the number of pin multiplexing functions
    }

    get_function_name

    Returns the function name of the multiplexer selector

    1
    2
    3
    4
    5
    6
    7
    static const char *rockchip_pmx_get_func_name(struct pinctrl_dev *pctldev,
    unsigned selector)
    {
    struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

    return info->functions[selector].name;// Returns the name of the pin multiplexing function
    }

    get_function_groups

    Returns a set of group names associated with a function selector (referencing pins in order).

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    static int rockchip_pmx_get_groups(struct pinctrl_dev *pctldev,
    unsigned selector, const char * const **groups,
    unsigned * const num_groups)
    {
    struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

    *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

    return 0;
    }

    set_mux

    Used to enable a specific multiplexing function.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    static int rockchip_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,
    unsigned group)
    {
    struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
    const unsigned int *pins = info->groups[group].pins;
    const struct rockchip_pin_config *data = info->groups[group].data;
    struct device *dev = info->dev;
    struct rockchip_pin_bank *bank;
    int cnt, ret = 0;

    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;
    }

    return 0;
    }

    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    	rk_485_ctl: rk-485-ctl {
    compatible = "topeet,rs485_ctl";
    gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>;
    pinctrl-names = "default";
    pinctrl-0 = <&rk_485_gpio>;
    };

    &pinctrl {
    rk_485{
    rk_485_gpio:rk-485-gpio {
    rockchip,pins = <3 13 RK_FUNC_GPIO &pcfg_pull_none>;
    };
    };
    };

    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

    really_probe()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    static int really_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()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    // drivers/base/pinctrl.c

    /**
    * pinctrl_bind_pins() - called by the device core before probe
    * @dev: the device that is just about to probe
    */
    int pinctrl_bind_pins(struct device *dev)
    {
    int ret;
    // Check if the device reuses a node
    if (dev->of_node_reused)
    return 0;
    // 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

    return 0;

    /*
    * 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. */
    return 0;
    }

    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    struct device {
    struct kobject kobj;// kernel object, used for device management
    struct device *parent;// pointer to the parent device

    struct device_private *p;// private data pointer

    const char *init_name; /* initial name of the device */ // initial name of the device
    const struct device_type *type;// device type

    struct bus_type *bus; /* type of bus device is on */ // bus type of the device
    struct device_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
    struct mutex lockdep_mutex;
    #endif
    struct mutex mutex; /* mutex to synchronize calls to* its driver.*/

    struct dev_links_info links;
    struct dev_pm_info power;
    struct dev_pm_domain *pm_domain;

    #ifdef CONFIG_ENERGY_MODEL
    struct em_perf_domain *em_pd;
    #endif

    #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
    struct irq_domain *msi_domain; // generic MSI IRQ domain of the device
    #endif
    #ifdef CONFIG_PINCTRL
    struct dev_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
    */
    struct dev_pin_info {
    struct pinctrl *p; // pin controller pointer
    struct pinctrl_state *default_state;// Default state pointer
    struct pinctrl_state *init_state;// Initialization state pointer
    #ifdef CONFIG_PM
    struct pinctrl_state *sleep_state;// Sleep state pointer (only available when power management is supported)
    struct pinctrl_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.

    Still taking the 485 control pin as an example

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    	rk_485_ctl: rk-485-ctl {
    compatible = "topeet,rs485_ctl";
    gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>;
    pinctrl-names = "default";
    pinctrl-0 = <&rk_485_gpio>;
    };

    &pinctrl {
    rk_485{
    rk_485_gpio:rk-485-gpio {
    rockchip,pins = <3 13 RK_FUNC_GPIO &pcfg_pull_none>;
    };
    };
    };

    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:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // 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)
    {
    struct pinctrl **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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    // 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)
    {
    struct pinctrl *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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    // drivers/pinctrl/core.c
    static struct pinctrl *create_pinctrl(struct device *dev,
    struct pinctrl_dev *pctldev)
    {
    struct pinctrl *p;
    const char *devname;
    struct pinctrl_maps *maps_node;
    int i;
    const struct pinctrl_map *map;
    int ret;

    /*
    * 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
    */
    struct pinctrl {
    struct list_head node; // Linked list node for adding the pin controller to the global list
    struct device *dev; // Associated device
    struct list_head states; // Linked list storing pin configuration states, used to track different pin configuration states
    struct pinctrl_state *state;// Currently applied pin configuration state
    struct list_head dt_maps; // Linked list storing pin mapping information defined in the device tree
    struct kref users; // 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
    */
    struct pinctrl_maps {
    struct list_head node; // pin controller mapping linked list node, used to add this mapping to the global list
    const struct pinctrl_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.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    // drivers/pinctrl/devicetree.c
    int pinctrl_dt_to_map(struct pinctrl *p, struct pinctrl_dev *pctldev)
    {
    struct device_node *np = p->dev->of_node;// get the device tree node of the device associated with the pin controller
    int state, ret;
    char *propname;
    struct property *prop;
    const char *statename;
    const __be32 *list;
    int size, config;
    phandle phandle;
    struct device_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");
    return 0;
    }

    /* 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;
    }
    }

    return 0;

    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:

    dt_to_map_one_config()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    static int dt_to_map_one_config(struct pinctrl *p,
    struct pinctrl_dev *hog_pctldev,
    const char *statename,
    struct device_node *np_config)
    {
    struct pinctrl_dev *pctldev = NULL;
    struct device_node *np_pctldev;
    const struct pinctrl_ops *ops;
    int ret;
    struct pinctrl_map *map;
    unsigned num_maps;
    bool allow_default = false;

    /* 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;
    else if (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);
    return 0;
    }

    /* 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.
    • map: Pointer tostruct pinctrl_mapstructure array pointer, representing the parsed mapping table entries.
    • num_maps: Indicates the number of mapping table entries.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
    struct pinctrl_dev *pctldev,
    struct pinctrl_map *map, unsigned num_maps)
    {
    int i;
    struct pinctrl_dt_map *dt_map;

    /* 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++) {
    const char *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
    struct pinctrl_dt_map {
    struct list_head node;//Used to add the mapping table structure to the dt_maps linked list of pinctrl
    struct pinctrl_dev *pctldev;// Pin controller device
    struct pinctrl_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.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    /**
    * 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
    int pinctrl_register_mappings(const struct pinctrl_map *maps,
    unsigned num_maps)
    {
    int i, ret;
    struct pinctrl_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);

    return 0;
    }
    EXPORT_SYMBOL_GPL(pinctrl_register_mappings);

    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    static int add_setting(struct pinctrl *p, struct pinctrl_dev *pctldev,
    const struct pinctrl_map *map)
    {
    struct pinctrl_state *state;// pinctrl_state object pointer
    struct pinctrl_setting *setting;// pinctrl_setting object pointer
    int ret;

    // 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)
    return 0;

    // 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);

    return 0;
    }

    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
    */
    struct pinctrl_state {
    struct list_head node;// Linked list node, used to connect the state object to the state list of the pin controller object
    const char *name;// Pointer to the name string of the state object
    struct list_head settings;// Linked list of pinctrl_setting objects, containing all setting objects of this state
    };

    struct pinctrl_setting
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 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
    */
    struct pinctrl_setting {
    struct list_head node; // Linked list node, used to connect the setting object to the setting list of the state object
    enum pinctrl_map_type type; // Mapping type, indicating the type of the setting object
    struct pinctrl_dev *pctldev; // Pointer to the pin control device object
    const char *dev_name; // Pointer to the device name string
    union {
    struct pinctrl_setting_mux mux; // Data structure for the mux group mapping type
    struct pinctrl_setting_configs configs; // 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
    static struct pinctrl_state *create_state(struct pinctrl *p,
    const char *name)
    {
    struct pinctrl_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.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    // drivers/pinctrl/pinmux.c
    int pinmux_map_to_setting(const struct pinctrl_map *map,
    struct pinctrl_setting *setting)
    {
    struct pinctrl_dev *pctldev = setting->pctldev;// Get the pin control device pointer
    const struct pinmux_ops *pmxops = pctldev->desc->pmxops;// Get the pin mux operation pointer
    char const * const *groups; // Pin mux group array
    unsigned num_groups; // Number of pin mux groups
    int ret;
    const char *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.

    return 0;
    }

    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.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    // drivers/pinctrl/pinconf.c
    int pinconf_map_to_setting(const struct pinctrl_map *map,
    struct pinctrl_setting *setting)
    {
    struct pinctrl_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

    return 0;
    }

    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.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    /**
    * 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,
    const char *name)
    {
    struct pinctrl_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);
    }

    return state;
    }
    EXPORT_SYMBOL_GPL(pinctrl_lookup_state);

    pinctrl_select_state()

    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
    */
    int pinctrl_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
    return 0;
    // 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()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    /**
    * 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
    */
    static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state)
    {
    struct pinctrl_setting *setting, *setting2;
    struct pinctrl_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;

    return 0;

    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)。

    pinmux_enable_setting()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    //drivers/pinctrl/pinmux.c
    int pinmux_enable_setting(const struct pinctrl_setting *setting)
    {
    struct pinctrl_dev *pctldev = setting->pctldev;
    const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
    const struct pinmux_ops *ops = pctldev->desc->pmxops;
    int ret = 0;
    const unsigned *pins = NULL;
    unsigned num_pins = 0;
    int i;
    struct pin_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
    const char *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) {
    const char *gname;
    const char *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;

    return 0;

    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);

    return ret;
    }

    pinconf_apply_setting()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    // drivers/pinctrl/pinconf.c
    int pinconf_apply_setting(const struct pinctrl_setting *setting)
    {
    struct pinctrl_dev *pctldev = setting->pctldev;
    const struct pinconf_ops *ops = pctldev->desc->confops;
    int ret;

    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;
    }

    return 0;
    }

    Mind Map

    pinctrl
    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    // drivers/pinctrl/devicetree.c
    int pinctrl_dt_to_map(struct pinctrl *p, struct pinctrl_dev *pctldev)
    {
    struct device_node *np = p->dev->of_node;// Get the device tree node of the device associated with the pin controller
    int state, ret;
    char *propname;
    struct property *prop;
    const char *statename;
    const __be32 *list;
    int size, config;
    phandle phandle;
    struct device_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");
    return 0;
    }

    /* 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;
    }
    }

    return 0;

    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.:

    1
    2
    3
    pinctrl_bind_pins
    create_pinctrl
    pinctrl_dt_to_map -> 报错

    so conjecture 2 is incorrect.

    The probe in Rockchip’s pinctrl driver has the following call chain:

    1
    2
    3
    4
    5
    6
    7
    rockchip_pinctrl_probe()
    rockchip_pinctrl_registér()
    devm_pinctrl_register()
    pinctrl_register()
    pinctrl_enable()
    pinctrl_claim_hogs()
    create_pinctrl()

    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.