时间轴

2025-12-16

init


pinctrl设备树

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 引脚 ⊂ SoC 引脚,而SoC 引脚 由 pinctrl 统一管理,Linux 官方 pinctrl binding 推荐:GPIO controller 作为 pin controller 的子节点

GPIO bank 名称 管脚数 基地址
GPIO0 gpio0 32 0xfdd60000
GPIO1 gpio1 32 0xfe740000
GPIO2 gpio2 32 0xfe750000
GPIO3 gpio3 32 0xfe760000
GPIO4 gpio4 32 0xfe770000

每个 bank:

  • 一套寄存器
  • 一个中断号
  • 两个时钟(PCLK / DBCLK)
  • 控制一组物理引脚的:方向、电平、GPIO 中断

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

无论是 rk3568.dtsi 设备树中的 pinctrl 节点,还是上面 rk3568-pinctrl.dtsi 设备树中的一系列复用关系都是由瑞芯微原厂 BSP 工程师编写的,我们只需知道如何使用即可,而 pinctrl 客户端设备树是由我们自己根据特定需求来编写的

pinctrl驱动

设备树中存放的只是设备的描述信息,而具体的功能实现取决于相应的 pinctrl 驱动。

根据 rk3568.dtsi 设备树中 pinctrl 节点的 compatible 属性进行查找驱动,可以查找到 pinctrl 的驱动文件是内核源码的driver/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);

可以看到 pinctrl 驱动使用的是 platform 总线,使用postcore_initcall而非module_init

groups和function

pinctrl 子系统中,有两个关键概念:引脚组(groups)功能(function)

  • 引脚组(Groups): 引脚组是一组具有相似功能、约束条件或共同工作的引脚的集合。每个引脚组通常与特定的硬件功能或外设相关联。例如,一个引脚组可以用于控制串行通信接口(如 UART 或 SPI),另一个引脚组可以用于驱动 GPIO。
  • 功能(Function): 定义了芯片上具有外设功能的功能。每个功能节点对应于一个或多个 IO 组(group)的配置信息。这些功能可以是串口、SPI、I2C 等外设功能。

接下来以 rk3568-pinctrl.dtsi 设备树文件中的 can0can1 两个功能为例对上面的内容进行举例,具体内容如下所示:

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

在上面的设备树中,can0 和 can1 对应两个不同的 function分别为 CAN0 控制器和 CAN1 控制器每个控制器中又都有两个不同的 groups 引脚组

  • CAN0 控制器:
    • 引脚组 can0m0-pins:这是 CAN0 控制器的第一个引脚组,用于配置 CAN0 的引脚。它定义了两个引脚:RK_PB4RK_PB3
      • RK_PB4 用于 CAN0 的接收引脚(can0_rxm0
      • RK_PB3 用于 CAN0 的发送引脚(can0_txm0)。
    • 引脚组 can0m1-pins:这是 CAN0 控制器的第二个引脚组,也用于配置 CAN0 的引脚。它定义了两个引脚:RK_PA2RK_PA1
      • RK_PA2 用于 CAN0 的接收引脚(can0_rxm1
      • RK_PA1 用于 CAN0 的发送引脚(can0_txm1)。
  • CAN1 控制器:
    • 引脚组 can1m0-pins:这是 CAN1 控制器的第一个引脚组,用于配置 CAN1 的引脚。它定义了两个引脚:RK_PA0RK_PA1
      • RK_PA0 用于 CAN1 的接收引脚(can1_rxm0
      • RK_PA1 用于 CAN1 的发送引脚(can1_txm0)。
    • 引脚组 can1m1-pins:这是 CAN1 控制器的第二个引脚组,也用于配置 CAN1 的引脚。它定义了两个引脚:RK_PC2RK_PC3
      • RK_PC2 用于 CAN1 的接收引脚(can1_rxm1
      • RK_PC3 用于 CAN1 的发送引脚(can1_txm1)。

rockchip pinctrl 相关结构体

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;// 引脚控制器的名称
const struct pinctrl_pin_desc *pins;// 引脚描述符数组
unsigned int npins;// 引脚描述符数组的大小
const struct pinctrl_ops *pctlops;// 引脚控制操作函数指针
const struct pinmux_ops *pmxops;// 引脚复用操作函数指针
const struct pinconf_ops *confops;// 引脚配置操作函数指针
struct module *owner;// 拥有该结构体的模块
#ifdef CONFIG_GENERIC_PINCONF
unsigned int num_custom_params;// 自定义参数数量
const struct pinconf_generic_params *custom_params;// 自定义参数数组
const struct pin_config_item *custom_conf_items;// 自定义配置项数组
#endif
bool link_consumers;
};

  • const char *name: 引脚控制器的名称,用于标识引脚控制器的唯一性。

  • const struct pinctrl_pin_desc *pins: 引脚描述符数组,是一个指向引脚描述符的指针,用于描述引脚的属性和配置。每个引脚描述符包含了引脚的名称、编号、模式等信息。

  • unsigned int npins: 表示引脚描述符数组中元素的数量,用于确定引脚描述符数组的长度。

  • const struct pinctrl_ops *pctlops: 指向引脚控制操作函数的指针,用于定义引脚控制器的操作接口。通过这些操作函数,可以对引脚进行配置、使能、禁用等操作。

  • const struct pinmux_ops *pmxops: 指向引脚复用操作函数的指针,用于定义引脚的复用功能。复用功能允许将引脚的功能切换为不同的模式,以适应不同的设备需求

  • const struct pinconf_ops *confops: 指向引脚配置操作函数的指针,用于定义引脚的其他配置选项。这些配置选项可以包括引脚的上拉、下拉配置、电气特性等

  • struct module *owner: 指向拥有该引脚控制器结构体的模块的指针。这个字段用于跟踪引脚控制器结构体的所有者。

  • unsigned int num_custom_params: 表示自定义配置参数的数量,用于描述引脚控制器的自定义配置参数。

  • const struct pinconf_generic_params *custom_params: 指向自定义配置参数的指针,用于描述引脚控制器的自定义配置参数的属性。自定义配置参数可以根据具体需求定义,用于扩展引脚控制器的配置选项。

  • const struct pin_config_item *custom_conf_items: 指向自定义配置项的指针,用于描述引脚控制器的自定义配置项的属性。自定义配置项可以根据具体需求定义,用于扩展引脚控制器的配置选项。

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

rockchip_pinctrl_probe函数中通过info->pctl_dev = devm_pinctrl_register(dev, ctrldesc, 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
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;// pinctrl_dev链表中的节点
struct pinctrl_desc *desc; // 引脚控制器描述指针
struct radix_tree_root pin_desc_tree;// 引脚描述结构体的radix树
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
struct radix_tree_root pin_group_tree;// 引脚组结构体的radix树根
unsigned int num_groups;// 引脚组的数量
#endif
#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS
struct radix_tree_root pin_function_tree;// 引脚功能结构体的radix树根
unsigned int num_functions;// 引脚功能的数量
#endif
struct list_head gpio_ranges;// GPIO范围链表
struct device *dev;// 设备结构体指针
struct module *owner;// 拥有pinctrl_dev结构体的模块的指针
void *driver_data;// 驱动程序的私有数据指针
struct pinctrl *p;// pinctrl结构体指针
struct pinctrl_state *hog_default;// hog默认状态
struct pinctrl_state *hog_sleep;// hog睡眠状态
struct mutex mutex;// 互斥锁
#ifdef CONFIG_DEBUG_FS
struct dentry *device_root;// 调试文件系统的根节点
#endif
};

struct rockchip_pinctrl

瑞芯微为了适应瑞芯微芯片的特定需求和功能,对 struct pinctrl_desc 进行了再一次封装。封装后的 struct rockchip_pinctrl 结构体在 struct pinctrl_desc 的基础上增加了与瑞芯微芯片相关的字段和指针,这种封装可以提供更好的集成性、易用性和扩展性,同时保持与通用引脚控制器框架的兼容性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct rockchip_pinctrl {
struct regmap *regmap_base;// 基本寄存器映射指针
int reg_size; // 寄存器大小
struct regmap *regmap_pull;// 拉取寄存器映射指针
struct regmap *regmap_pmu;// 电源管理单元寄存器映射指针
struct device *dev; // 设备指针
struct rockchip_pin_ctrl *ctrl; // 瑞芯微芯片引脚控制器指针
struct pinctrl_desc pctl; // 引脚控制器描述符
struct pinctrl_dev *pctl_dev; // 引脚控制器设备指针
struct rockchip_pin_group *groups;// 瑞芯微芯片引脚组指针
unsigned int ngroups; // 引脚组数量
struct rockchip_pmx_func *functions;// 瑞芯微芯片引脚功能指针
unsigned int nfunctions;// 引脚功能数量
};
  • struct regmap *regmap_base:指向基本寄存器映射(regmap)的指针。基本寄存器映射是一个用于访问芯片寄存器的接口,它提供了对芯片寄存器的读写操作。

  • int reg_size:表示寄存器的字节大小,用于确定寄存器的地址范围。

  • struct regmap *regmap_pull:指向拉取寄存器映射的指针。拉取寄存器映射用于控制引脚上的上拉和下拉功能。

  • struct regmap *regmap_pmu:指向电源管理单元(PMU)寄存器映射的指针。PMU寄存器映射用于控制引脚的电源管理功能

  • struct device *dev:指向设备结构体的指针。设备结构体用于表示与硬件相关的设备,包括设备的物理地址、中断等信息

  • struct rockchip_pin_ctrl *ctrl:指向瑞芯微芯片引脚控制器的指针。这个结构体存储了瑞芯微芯片特定的引脚控制器的相关信息和操作。

  • struct pinctrl_desc pctl:包含了 struct pinctrl_desc 结构体的一个实例。用于描述引脚控制器的属性和操作,包括引脚控制器的名称、引脚描述符数组、函数指针等。

  • struct pinctrl_dev *pctl_dev:指向引脚控制器设备结构体的指针。引脚控制器设备结构体用于表示引脚控制器在系统中的设备实例,包含了与引脚控制器相关的设备信息和操作接口。

  • struct rockchip_pin_group *groups:指向瑞芯微芯片引脚组的指针。引脚组是一组相关的引脚,可以一起进行配置和管理。

  • unsigned int ngroups:表示引脚组数组的大小,用于确定引脚组数组的长度。

  • struct rockchip_pmx_func *functions:指向瑞芯微芯片引脚功能的指针。引脚功能定义了引脚可以承担的不同功能,例如 UART、SPI、I2C 等。

  • unsigned int nfunctions:引脚功能的数量。它表示引脚功能数组的大小,用于确定
    引脚功能数组的长度。

struct rockchip_pin_ctrl

rockchip_pinctrl_probe函数中通过ctrl = rockchip_pinctrl_get_soc_data(info, pdev)设置

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;// 引脚bank的数组指针
u32 nr_banks;// pin_bank的数量
u32 nr_pins;// 引脚的数量
char *label;// 引脚控制器标签
enum rockchip_pinctrl_type type;// 引脚控制器的类型
int grf_mux_offset;// GRF(Global Register File)复用寄存器的偏移量
int pmu_mux_offset;// PMU(电源管理单元)复用寄存器的偏移量
int grf_drv_offset;// GRF驱动寄存器的偏移量
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()

瑞芯微的 pinctrl 驱动函数中的 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
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; // Rockchip GPIO 控制器的信息结构体指针
struct device *dev = &pdev->dev; // 设备结构体指针
struct device_node *np = dev->of_node, *node; // 设备节点指针
struct rockchip_pin_ctrl *ctrl; // Rockchip GPIO 控制器的配置结构体指针
struct resource *res;// 设备资源指针
void __iomem *base;// 寄存器基地址指针
int ret;

// 检查设备结构体中的设备树节点是否存在,如果不存在则报错并返回错误码
if (!dev->of_node)
return dev_err_probe(dev, -ENODEV, "device tree node not found\n");

// 使用 devm_kzalloc 函数分配一个 rockchip_pinctrl 结构体的内存,并将其初始化为 0
// 分配并初始化一个 rockchip_pinctrl 结构体
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;

// 将设备结构体指针赋值给 info->dev,以便在后续代码中可以使用设备结构体的信息。
info->dev = dev;

// 调用 rockchip_pinctrl_get_soc_data 函数,根据设备信息获取与该设备相关的 rockchip_pin_ctrl 结构体。
// 如果获取失败,则报错并返回错误码。
ctrl = rockchip_pinctrl_get_soc_data(info, pdev);

if (!ctrl)
return dev_err_probe(dev, -EINVAL, "driver data not available\n");
// 将获取到的结构体指针赋值给 info->ctrl
info->ctrl = ctrl;

// 使用 of_parse_phandle 函数解析设备树中名为"rockchip,grf"的节点,获取寄存器映射基地址
node = of_parse_phandle(np, "rockchip,grf", 0);
if (node) {
// 如果解析成功,则调用 syscon_node_to_regmap 函数将节点转换为寄存器映射的基地址,并将结果存储在 info->regmap_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 {// 如果找不到"rockchip,grf"节点,则获取 IORESOURCE_MEM 类型的资源,得到寄存器基地址

// 通过 platform_get_resource 函数获取 IORESOURCE_MEM 类型的资源,以获取寄存器的基地址。
// 然后使用 devm_ioremap_resource 函数将资源映射到内存中,并将结果存储在 base中。
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
// 配置寄存器映射的最大寄存器地址和名称
rockchip_regmap_config.max_register = resource_size(res) - 4;
rockchip_regmap_config.name = "rockchip,pinctrl";
// 使用devm_regmap_init_mmio 函数初始化寄存器映射,将结果存储在 info->regmap_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);// 检查旧的 dt-bindings

/* Honor the old binding, with pull registers as 2nd resource */
// 如果控制器类型为 RK3188 且 reg_size 小于 0x200,则获取第二个 IORESOURCE_MEM 类型的资源,作为 pull 寄存器的基地址
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);
// 配置 pull 寄存器映射的最大寄存器地址和名称
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 */
// 尝试查找可选的 pmu syscon 引用
node = of_parse_phandle(np, "rockchip,pmu", 0);
if (node) {
// 调用 syscon_node_to_regmap 函数将节点转换为寄存器映射的基地址,并将结果存储在 info->regmap_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);
}
// 对于某些 SoC 进行特殊处理
if (IS_ENABLED(CONFIG_CPU_RK3308) && ctrl->type == RK3308) {
ret = rk3308_soc_data_init(info);
if (ret)
return ret;
}
// 注册 rockchip_pinctrl 设备
ret = rockchip_pinctrl_register(pdev, info);
if (ret)
return ret;
// 设置 pdev 的私有数据为 info
platform_set_drvdata(pdev, info);
g_pctldev = info->pctl_dev;

// 注册 GPIO 设备
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;
}

上面 probe 函数的作用是初始化和配置 Rockchip GPIO 控制器,并将相关信息存储在struct rockchip_pinctrl info 中,最后注册相关设备和 GPIO 接口。

  • 第 26 行调用ctrl = rockchip_pinctrl_get_soc_data(info, pdev);根据设备信息获取与该设备相关的 rockchip_pin_ctrl 结构体。

  • 第 34 ~ 70 行 处理了 两种设备树(Device Tree)绑定方式

    1. 新式绑定:通过 phandle 引用系统控制器(如 GRF);
    2. 旧式绑定:直接在 pinctrl 节点中声明内存资源(IORESOURCE_MEM)。

syscon_node_to_regmap(node)是Linux 内核提供的一种机制:将 syscon 节点转换为 regmap 接口。regmap 是内核中用于抽象寄存器读写的通用框架,支持 MMIO、I2C、SPI 等。

由于设备树中有:rockchip,grf = <&grf>; 因此使用的是新的绑定

  • 第 89 行调用ret = rockchip_pinctrl_register(pdev, info);注册 rockchip_pinctrl 设备

probe 函数思维导图

pinctrl

rockchip_pinctrl_get_soc_data()

根据设备树中 compatible = "rockchip,rk3568-pinctrl" 匹配到 RK3568 的引脚描述数据,并动态计算每个 GPIO bank 中复用(iomux)和驱动强度(drv)寄存器的实际偏移地址,为后续引脚配置做准备。

pinctrl 节点下各 gpioX 节点描述了 GPIO 控制器硬件资源(地址、中断、时钟),但 不包含引脚复用信息 —— 这些由 pinctrl 驱动通过 GRF/PMUGRF 寄存器控制。

RK3568共 5 个(gpio0~gpio4)GPIO bank, RK3568 每个 GPIO bank 最多 32 引脚,分成 4 组(每组 8 引脚),每组有自己的:

  • iomux 寄存器:控制复用功能(GPIO / I2C / UART…)
  • drv 寄存器:控制驱动强度(2mA / 4mA / 8mA…)

等等。

如RK3568 TRM中:

GPIO IOMUX 寄存器 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;

// 通过 of_match_node() 在 rockchip_pinctrl_dt_match 表中查找与当前设备节点匹配的 SoC。
match = of_match_node(rockchip_pinctrl_dt_match, node);
ctrl = (struct rockchip_pin_ctrl *)match->data;
// 特定 SoC 的 pin_banks 替换(变体支持)
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;

// 初始化全局寄存器偏移量
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;
// 遍历所有 pin bank(RK3568共 5 个: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 */
// RK3568 每个 GPIO bank 最多 32 引脚,分成 4 组(每组 8 引脚),每组有自己的:
// iomux 寄存器:控制复用功能(GPIO / I2C / UART...)
// drv 寄存器:控制驱动强度(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;
}

调用 rockchip_pinctrl_get_soc_data() 后:
每个 bank 的 iomux[j].offsetdrv[j].offset 都被正确填充为 GRFPMUGRF 中的实际寄存器偏移。当用户通过 pinctrl 子系统设置某个引脚为 I2C 功能时,驱动会找到对应 bank 和引脚组通过 regmap_write(info->regmap_base, iomux_offset, value)写入 GRF 寄存器,完成复用切换。

syscon

syscon = “system controller”(系统控制器)的缩写

它本质上是一个:

  • 轻量级平台驱动(platform driver)
  • 用于将一段内存映射的控制寄存器区域(如 GRF、PMU、CRU 等)
  • 封装成一个 struct regmap 对象
  • 供其他驱动通过标准接口(如 regmap_read/write)安全地读写这些寄存器

对比直接 ioremap 为什么需要 syscon

方式 问题 syscon 优势
每个驱动自己 ioremap 多次映射同一物理地址,浪费资源 全局唯一映射
无同步机制 多驱动同时写寄存器 → 竞争条件 regmap 自带锁
代码重复 每个驱动都要处理偏移、位操作 统一抽象接口
设备树耦合强 驱动需知道具体地址 通过 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;
};

Rockchip SoC 中常见的系统控制器包括:

控制器 全称 功能
GRF General Register Files 控制 GPIO 复用、电压域、外设路由等
PMU Power Management Unit 低功耗引脚控制、电源管理

这些寄存器通常:

  • 多个子系统共享(如 pinctrl、USB、I2C都要配置 GRF)
  • 不能被某个驱动独占
  • 需要 原子操作、位操作、锁保护

syscon 正好解决这些问题。

syscon 设备树

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 —— 主系统控制器,pinctrl 的核心依赖
  • 几乎所有外设的复用、使能、电压域都在这里配置。
  • 子功能:
    • io_domains:主 IO 电压域控制(如 HDMI、MIPI、SDIO 的电平)
    • lvds0 / lvds1:LVDS 显示输出控制器(连接屏幕)
    • rgb:RGB 并行显示接口(老式 LCD)

关键点:

  • 必须包含 "syscon"compatible 列表中
  • 内核中的 syscon 驱动会匹配所有带 "syscon" 的节点
  • 自动创建 regmap 并缓存,供其他驱动通过 of_xlatesyscon_node_to_regmap() 获取

内核 API 使用示例

1. 获取 syscon 的 regmap

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);
// 后续可调用 regmap_write(grf_regmap, offset, value);

2. 直接通过 phandle 获取

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

3. 全局查找

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

rockchip_pinctrl_register()

rockchip_pinctrl_probe()函数最后调用rockchip_pinctrl_register()函数注册 rockchip_pinctrl 设备

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的pctl即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;

// 初始化 pinctrl 描述结构体pinctrl_desc
ctrldesc->name = "rockchip-pinctrl";
ctrldesc->owner = THIS_MODULE;
ctrldesc->pctlops = &rockchip_pctrl_ops; // pinctrl 控制操作函数
ctrldesc->pmxops = &rockchip_pmx_ops; // pinctrl 引脚复用操作函数
ctrldesc->confops = &rockchip_pinconf_ops;// pinctrl 引脚配置操作函数

// 使用 devm_kcalloc 函数在设备的内存上分配一块连续的内存区域,用于存储引脚描述结构体。
pindesc = devm_kcalloc(dev, info->ctrl->nr_pins, sizeof(*pindesc), GFP_KERNEL);
if (!pindesc)
return -ENOMEM;

ctrldesc->pins = pindesc;//通过将引脚描述结构体的指针 pindesc 赋值给 pinctrl 描述结构体的 pins成员
ctrldesc->npins = info->ctrl->nr_pins;//将引脚数量 info->ctrl->nr_pins 赋值给 pinctrl 描述结构体的 npins 成员

pdesc = pindesc;// 定义变量 pdesc 指向引脚描述结构体的起始地址
// 遍历每个引脚所属的 bank,为每个引脚设置编号和名称
for (bank = 0, k = 0; bank < info->ctrl->nr_banks; bank++) {
// 外层循环遍历每个引脚所属的 bank
pin_bank = &info->ctrl->pin_banks[bank];
for (pin = 0; pin < pin_bank->nr_pins; pin++, k++) {//pin 是当前引脚在 bank 中的索引
// 内层循环遍历每个 bank 中的引脚。
pdesc->number = k;//当前引脚的编号 k
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);
}
// 解析设备树中的 pinctrl 信息,该函数根据设备树中的描述,设置引脚的默认配置
ret = rockchip_pinctrl_parse_dt(pdev, info);
if (ret)
return ret;
// 注册 pinctrl 设备,将 pinctrl 描述结构体、pinctrl 相关操作函数和私有数据作为参数,将 pinctrl 设备注册到系统中
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;
}
  • 第 29 ~ 42 行遍历双重 for 循环每个引脚所属 bank,为每个 bank 的引脚设置编号和名称
  • 第 44 行ret = rockchip_pinctrl_parse_dt(pdev, info);解析设备树中的 pinctrl 信息,该函数根据设备树中的描述,设置引脚的默认配置
  • 第 48 行info->pctl_dev = devm_pinctrl_register(dev, ctrldesc, info);pdev->devinfo->pctlinfo传入,注册 pinctrl 设备,将 pinctrl 描述结构体、pinctrl 相关操作函数和私有数据作为参数,将 pinctrl 设备注册到系统中

rockchip_pinctrl_parse_dt()

rochip_pinctrl_register() 函数中调用rockchip_pinctrl_register()解析设备树中的 pinctrl 信息

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;
// 计算子节点数量并更新 info 结构体中的计数器
rockchip_pinctrl_child_count(info, np);

dev_dbg(dev, "nfunctions = %d\n", info->nfunctions);
dev_dbg(dev, "ngroups = %d\n", info->ngroups);
// 为函数和组分配内存空间
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;
// 遍历每个子节点,解析function信息
for_each_child_of_node(np, child) {
// 如果节点不是函数节点,则继续下一个节点
if (of_match_node(rockchip_bank_match, child))
continue;
// 解析函数信息并存储到 info 结构体中
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;
}



  • 第 10 行调用rockchip_pinctrl_child_count(info, np);计算子节点数量并更新 info 结构体中的计数器
  • 第 25 ~ 36 行通过for_each_child_of_node(np, child)遍历每个子节点,调用ret = rockchip_pinctrl_parse_functions(child, info, i++);解析 function 信息

rockchip_pinctrl_child_count()

rockchip_pinctrl_parse_dt()函数中通过调用rockchip_pinctrl_child_count(info, np); 计算子节点数量并更新 info 结构体中的计数器

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;
// 遍历设备节点的子节点
for_each_child_of_node(np, child) {
// 如果子节点不是 function 节点,则跳过当前节点,继续遍历下一个节点
if (of_match_node(rockchip_bank_match, child))
continue;

// 子节点是 function 节点,增加 function 计数器
info->nfunctions++;

// 获取子节点的子节点数量,并增加到组计数器中
info->ngroups += of_get_child_count(child);
}
}

rockchip_pinctrl_parse_functions()

rockchip_pinctrl_parse_dt()函数中通过调用rockchip_pinctrl_parse_functions()解析函数信息并存储到 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
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;// 用来存放 function 信息
struct rockchip_pin_group *grp;// 用来存放 groups 信息
int ret;
static u32 grp_index;
u32 i = 0;
// 打印调试信息,显示正在解析的函数节点和索引
dev_dbg(dev, "parse function(%d): %pOFn\n", index, np);

// 获取当前函数在 info->functions 数组中的指针
func = &info->functions[index];

/* Initialise function */
/* 初始化函数 */
func->name = np->name;
// 获取函数节点的子节点数量,即关联的组数量
func->ngroups = of_get_child_count(np);
if (func->ngroups <= 0)
return 0;

// 为函数的组指针数组分配内存空间
func->groups = devm_kcalloc(dev, func->ngroups, sizeof(*func->groups), GFP_KERNEL);
if (!func->groups)
return -ENOMEM;

// 遍历函数节点的每个子节点
for_each_child_of_node(np, child) {
// 将子节点的名称存储到函数的组指针数组中
func->groups[i] = child->name;
// 获取 info->groups 数组中的对应组指针
grp = &info->groups[grp_index++];
// 解析组信息,并将结果存储到对应的组指针中
ret = rockchip_pinctrl_parse_groups(child, grp, info, i++);
if (ret) {
of_node_put(child);
return ret;
}
}

return 0;
}

rockchip_pinctrl_parse_functions中:

  • func = &info->functions[index],所以 struct rockchip_pinctrlfunctions 参数的作用就是用来存放 pinctrl 设备树中的 function 信息。
  • grp = &info->groups[grp_index++];,所以 struct rockchip_pinctrlgroups 参数的作用就是用来存放 pinctrl 设备树中的 groups 信息。
rockchip_pinctrl_parse_groups()

rockchip_pinctrl_parse_functions中通过ret = rockchip_pinctrl_parse_groups(child, grp, info, i++); 解析组信息,并将结果存储到对应的组指针中

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;
// 打印调试信息,显示正在解析的组节点和索引
dev_dbg(dev, "group(%d): %pOFn\n", index, np);

/* Initialise group */
// 初始化组信,将引脚组的名称设置为节点的名称
grp->name = np->name;

/*
* the binding format is rockchip,pins = <bank pin mux CONFIG>,
* do sanity check and calculate pins number
*/

/*
* 绑定格式为 rockchip,pins = <bank pin mux CONFIG>,
* 进行合法性检查并计算引脚数量
*/
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)//如果属性值为空或者数量不是 4 的倍数
return dev_err_probe(dev, -EINVAL, "wrong pins number or pins and configs should be by 4\n");

grp->npins = size / 4;// 计算组的引脚数量

// 根据计算得到的引脚数量为引脚数组和数据数组分配内存空间,这些数组将用于存储引脚的编号和相关的配置信息
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;
// 遍历列表中的每个元素,每 4 个元素表示一个引脚的信息
for (i = 0, j = 0; i < size; i += 4, j++) {
const __be32 *phandle;
struct device_node *np_config;
// 获取管脚号
num = be32_to_cpu(*list++);
// 引脚号转换为对应的引脚结构体指针
bank = bank_num_to_bank(info, num);
if (IS_ERR(bank))
return PTR_ERR(bank);
// 根据引脚结构体中的引脚基地址(pin_base)和列表中的值计算引脚的编号,并将其存储在引脚数组(grp->pins)中
grp->pins[j] = bank->pin_base + be32_to_cpu(*list++);
// 从列表中获取与当前引脚相关的功能选择值,并将其存储在数据数组(grp->data)中的相应位置
grp->data[j].func = be32_to_cpu(*list++);

// 获取与引脚相关的配置信息
phandle = list++;
if (!phandle)
return -EINVAL;
// 从列表中获取与当前引脚相关的配置信息的句柄,并通过该句柄查找对应的配置节点(np_config)
np_config = of_find_node_by_phandle(be32_to_cpup(phandle));
// 解析配置信息,并将结果存储到组的数据数组中
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()函数通过info->pctl_dev = devm_pinctrl_register(dev, ctrldesc, info); 注册 pinctrl 设备。一个参数类型为struct device *,第二个参数类型为 struct pinctrl_desc *,第三个参数类型为void *是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;
// 使用 devres_alloc 函数为存储 pinctrl_dev 指针的变量 ptr 分配内存
ptr = devres_alloc(devm_pinctrl_dev_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
// 调用 pinctrl_register 函数注册 pinctrl 设备。该函数将 pinctrl_desc 结构体、设备指针 dev 和驱动程序数据 driver_data 作为参数,并返回注册后的 pinctrl_dev 指针
pctldev = pinctrl_register(pctldesc, dev, driver_data);
if (IS_ERR(pctldev)) {
devres_free(ptr);
return pctldev;
}
// 将 pinctrl_dev 指针存储到 ptr 指向的内存位置。
*ptr = pctldev;
// 使用 devres_add 函数将 ptr 添加到设备的资源列表中。这样,在设备释放时,会自动释放之前分配的内存。
devres_add(dev, ptr);

return pctldev;
}
EXPORT_SYMBOL_GPL(devm_pinctrl_register);

devres_alloc 函数是用于管理设备资源的函数,它在设备的资源列表中分配内存。这里分配的内存大小为 sizeof(*ptr)字节,即一个 pinctrl_dev 指针的大小。如果内存分配失败,则返回-ENOMEM

pinctrl_register()

devm_pinctrl_register()通过调用pctldev = pinctrl_register(pctldesc, dev, driver_data);注册 pinctrl 设备。该函数将 struct pinctrl_desc *pctldesc、设备指针struct device *dev 和驱动程序数据 void *driver_data 作为参数,并返回注册后的 struct pinctrl_dev* 指针

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;
// 初始化 pinctrl 控制器
pctldev = pinctrl_init_controller(pctldesc, dev, driver_data);
if (IS_ERR(pctldev))
return pctldev;
// 启用 pinctrl 控制器
error = pinctrl_enable(pctldev);
if (error)
return ERR_PTR(error);

return pctldev;
}
EXPORT_SYMBOL_GPL(pinctrl_register);

pinctrl_init_controller()

pinctrl_register()中通过pctldev = pinctrl_init_controller(pctldesc, dev, driver_data);初始化 pinctrl 控制器

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 */
/* 初始化引脚控制设备结构体 */
pctldev->owner = pctldesc->owner;// 设置所有者
pctldev->desc = pctldesc;// 设置描述符
pctldev->driver_data = driver_data; // 设置驱动程序数据
INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL);// 初始化引脚描述符树
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
INIT_RADIX_TREE(&pctldev->pin_group_tree, GFP_KERNEL);// 初始化引脚组树
#endif
#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS
INIT_RADIX_TREE(&pctldev->pin_function_tree, GFP_KERNEL);// 初始化引脚功能树
#endif
INIT_LIST_HEAD(&pctldev->gpio_ranges);// 初始化 GPIO 范围链表
INIT_LIST_HEAD(&pctldev->node);// 初始化节点链表
pctldev->dev = dev;// 设置设备指针
mutex_init(&pctldev->mutex);// 初始化互斥锁

/* check core ops for sanity */
/* 检查核心操作函数的有效性 */
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 (pctldesc->pmxops) {
ret = pinmux_check_ops(pctldev);
if (ret)
goto out_err;
}

/* If we're implementing pinconfig, check the ops for sanity */
/* 如果实现了引脚配置功能,检查操作函数的有效性 */
if (pctldesc->confops) {
ret = pinconf_check_ops(pctldev);
if (ret)
goto out_err;
}

/* Register all the 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);
}

在该函数中我们要关注的是第 27 行内容 pctldev->driver_data = driver_data,其中右值 driver_data 是从 pinctrl 的 probe 函数中一步一步传递过来的,是一个 struct rockchip_pinctrl *类型的结构体指针变量,左值 pctldev 为要注册的引脚控制设备(pincontroller device),至此两个数据结构建立起了关联,可以通过 pctldev 来对 rockchip_pinctrl 中的数据进行访问

pinctrl子系统函数操作集

1
2
3
const struct pinctrl_ops *pctlops; // 引脚控制操作函数指针
const struct pinmux_ops *pmxops; // 引脚复用操作函数指针
const struct pinconf_ops *confops; // 引脚配置操作函数指针

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);//获取指定的 Pin Control 设备支持的引脚组数量
const char *(*get_group_name) (struct pinctrl_dev *pctldev,
unsigned selector);// 获取指定引脚组选择器对应的引脚组名称
int (*get_group_pins) (struct pinctrl_dev *pctldev,
unsigned selector,
const unsigned **pins,
unsigned *num_pins);// 获取指定引脚组选择器对应的引脚组中的引脚列表
void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,
unsigned offset);//在调试信息中输出指定引脚选择器对应的引脚信息
int (*dt_node_to_map) (struct pinctrl_dev *pctldev,
struct device_node *np_config,
struct pinctrl_map **map, unsigned *num_maps);// 根据给定的设备树节点,创建与之相关联的 Pin Control 映射
void (*dt_free_map) (struct pinctrl_dev *pctldev,
struct pinctrl_map *map, unsigned num_maps);//释放之前通过 dt_node_to_map 创建的Pin Control 映射
};

具体实现:

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

获取引脚组的数量。

1
2
3
4
5
6
7
static int rockchip_get_groups_count(struct pinctrl_dev *pctldev)
{
// 从 pinctrl_dev 结构中获取私有数据指针,将其转换为 rockchip_pinctrl 结构
struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
// 返回 rockchip_pinctrl 结构中存储的引脚组数量
return info->ngroups;
}

get_group_name

获取引脚组的名称。

1
2
3
4
5
6
7
8
9
static const char *rockchip_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
// 从 pinctrl_dev 结构中获取私有数据指针,将其转换为 rockchip_pinctrl 结构
struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

// 返回指定引脚组的名称
return info->groups[selector].name;
}

get_group_pins

获取引脚组的引脚列表。

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)
{
// 从 pinctrl_dev 结构中获取私有数据指针,将其转换为 rockchip_pinctrl 结构
struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

// 如果选择器超出引脚组的范围,则返回错误码 -EINVAL
if (selector >= info->ngroups)
return -EINVAL;

// 将指向引脚组的引脚数组的指针赋值给传入的 pins 指针
*pins = info->groups[selector].pins;
// 将引脚组中的引脚数量赋值给传入的 npins 变量
*npins = info->groups[selector].npins;

return 0;
}

dt_node_to_map

根据设备树节点创建与之相关联的 Pin Control 映射。即把设备树中一个“引脚配置节点”(如 i2c1_xfer: i2c1-xfer {...})转换成内核 pinctrl 系统能理解的 struct pinctrl_map 映射表,从而告诉系统:“当使用某个设备时,请把哪些引脚配置成什么功能 + 电气参数”。

例如:

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";
};
  • 定义了一个 引脚配置块i2c1_xfer
  • 当 I2C1 控制器工作时,需要:
    • GPIO1_B0 配置为 I2C1_SCL 功能(复用值=1),并启用上拉
    • GPIO1_B1 配置为 I2C1_SDA 功能(复用值=1),并启用上拉

pinctrl 驱动就靠 dt_node_to_map() 把这个 DTS 节点翻译成内核结构

相关结构体

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;// 设备名称
const char *name;// 映射名称
enum pinctrl_map_type type;// 映射类型
const char *ctrl_dev_name;// 控制设备名称
union {
struct pinctrl_map_mux mux;// 复用映射数据
struct pinctrl_map_configs configs; // 配置映射数据
} data;
};

字段 含义 说明
dev_name 使用该引脚配置的设备名 通常是外设的名字,如 "fe8a0000.i2c"。如果设为 pinctrl 控制器自己的名字(如 "pinctrl"),则称为 hogged pin(启动时就占用,不依赖外设)。
name 该映射的状态名 对应设备树中的 pinctrl-names = "xxx",例如 "default""sleep"。调用 pinctrl_lookup_state(pctl, "default") 时会用到。
type 映射类型 枚举值,常见:
PIN_MAP_TYPE_MUX_GROUP:配置引脚复用
PIN_MAP_TYPE_CONFIGS_PIN:配置单个引脚电气参数
PIN_MAP_TYPE_CONFIGS_GROUP:配置整个引脚组的电气参数
ctrl_dev_name 控制该引脚的 pinctrl 设备名 通常是 "pinctrl""rockchip-pinctrl",用于找到对应的 pinctrl 驱动。
data 具体配置内容(联合体) 根据 type 不同,使用 .mux.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;
};
字段 含义
group 引脚组名称 例如 "i2c1-xfer""uart2-m1"。这个组在 pinctrl 驱动中预定义,包含一组物理引脚。
function 要切换到的功能名称 例如 "i2c1""uart2"。pinctrl 驱动内部会知道如何将该功能映射到具体的寄存器值。
struct pinctrl_map_configs

电气参数配置映射,用于设置引脚的 电气特性,如上下拉、驱动强度、施密特触发等。

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;
};
字段 含义
group_or_pin 要配置的对象 可以是一个引脚名(如 "gpio1-16")或一个引脚组名(如 "i2c1-xfer")。
configs 配置参数数组 每个元素是一个 unsigned long,编码了配置项和值(如 PIN_CONFIG_BIAS_PULL_UP)。
num_configs 配置项数量 数组 configs 的长度。

示例:

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

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

含义:给引脚 gpio1-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)
{
// 获取引脚控制器的私有数据指针
struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
// 引脚组指针
const struct rockchip_pin_group *grp;
// 设备指针
struct device *dev = info->dev;
// 新的引脚映射数组
struct pinctrl_map *new_map;
// 父节点指针
struct device_node *parent;
// 映射数量,默认为 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
*/
/* 查找引脚组 */
grp = pinctrl_name_to_group(info, np->name);// 根据节点名称查找对应的引脚组
if (!grp) {// 如果找不到引脚组,打印错误信息
dev_err(dev, "unable to find group for node %pOFn\n", np);
return -EINVAL;
}

map_num += grp->npins;// 计算映射数量,包括复用映射和配置映射

new_map = kcalloc(map_num, sizeof(*new_map), GFP_KERNEL);// 分配内存空间用于存储映射数组
if (!new_map)
return -ENOMEM;

*map = new_map;// 将分配的映射数组赋值给输出参数
*num_maps = map_num;// 将映射数量赋值给输出参数

/* create mux map */
/* 创建复用映射 */
parent = of_get_parent(np); // 获取节点的父节点
if (!parent) {
kfree(new_map);// 如果父节点不存在,释放分配的映射数组内存空间
return -EINVAL;
}

new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;// 设置映射类型为复用映射
new_map[0].data.mux.function = parent->name;// 复用功能名称为父节点的名称
new_map[0].data.mux.group = np->name; // 将设备节点的名称作为映射的组名
of_node_put(parent);// 释放父节点的引用计数

/* create config map */
/* 创建配置映射 */
new_map++;// 映射数组指针向后移动一个位置
for (i = 0; i < grp->npins; i++) {
new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN;// 设置映射类型为配置映射
new_map[i].data.configs.group_or_pin =
pin_get_name(pctldev, grp->pins[i]);// 引脚组或引脚名称为引脚组中的引脚名称
new_map[i].data.configs.configs = grp->data[i].configs;// 配置信息数组为引脚组中该引脚的配置信息
new_map[i].data.configs.num_configs = grp->data[i].nconfigs;// 配置信息数量为引脚组中该引脚的配置数量
}
// 打印调试信息,显示创建的引脚映射的功能名称、组名和数量
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_map 函数根据设备节点的信息创建引脚映射,包括复用映射和配置映射。

  • 复用映射用于将引脚组的功能与父节点的功能关联起来
  • 配置映射用于将引脚的配置信息与引脚的名称关联起来

这些映射将用于配置引脚控制器,以确保引脚在系统中正确地配置和使用。这个函数在设备树解析过程中被调用,以便为每个设备节点创建相应的引脚映射。

dt_free_map

释放通过 dt_node_to_map 创建的 Pin Control 映射。

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_ops 是 pinctrl 子系统中用于引脚电气特性配置(pin configuration)的核心操作接口。

如果说 pinmux_ops 负责 “把引脚接到哪个功能模块”(如 I2C、UART),那么 pinconf_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
/**
* 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;// 是否为通用引脚配置操作
#endif
// 获取引脚配置信息
int (*pin_config_get) (struct pinctrl_dev *pctldev,
unsigned pin,
unsigned long *config);
// 设置引脚配置信息
int (*pin_config_set) (struct pinctrl_dev *pctldev,
unsigned pin,
unsigned long *configs,
unsigned num_configs);
// 获取引脚组配置信息
int (*pin_config_group_get) (struct pinctrl_dev *pctldev,
unsigned selector,
unsigned long *config);
// 设置引脚组配置信息
int (*pin_config_group_set) (struct pinctrl_dev *pctldev,
unsigned selector,
unsigned long *configs,
unsigned num_configs);
// 调试函数,显示引脚配置信息
void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev,
struct seq_file *s,
unsigned offset);

// 调试函数,显示引脚组配置信息
void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev,
struct seq_file *s,
unsigned selector);
// 调试函数,显示引脚配置的具体信息
void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,
struct seq_file *s,
unsigned long config);
};

结构体 struct pinconf_ops,用于定义引脚配置操作的函数指针。每个函数指针都对应了一个特定的操作,如获取引脚配置、设置引脚配置、获取引脚组配置等。这些函数在驱动程序中实现,用于对硬件引脚进行配置和控制

1
2
3
4
5
static const struct pinconf_ops rockchip_pinconf_ops = {
.pin_config_get = rockchip_pinconf_get,// 获取引脚配置的函数
.pin_config_set = rockchip_pinconf_set,// 设置引脚配置的函数
.is_generic = true,
};

pin_config_get

获取引脚配置信息。

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:// 检查上下拉电阻是否禁用
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:// 检查上下拉电阻是否有效,并获取当前的上下拉电阻配置
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:// 检查引脚是否配置为 GPIO 输出模式
rc = rockchip_get_mux(bank, pin - bank->pin_base);
if (rc != RK_FUNC_GPIO)
return -EINVAL;

if (!gpio || !gpio->get) {
arg = 0;
break;
}
// 获取引脚的输出状态
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 */
// 仅支持某些芯片(如 rk3288)的每个引脚独立的驱动强度设置
if (!info->ctrl->drv_calc_reg)
return -ENOTSUPP;
// 获取引脚的驱动强度配置
rc = rockchip_get_drive_perpin(bank, pin - bank->pin_base);
if (rc < 0)
return rc;

arg = rc;
break;
case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
// 仅支持某些芯片的施密特触发设置
if (!info->ctrl->schmitt_calc_reg)
return -ENOTSUPP;
// 获取引脚的施密特触发配置
rc = rockchip_get_schmitt(bank, pin - bank->pin_base);
if (rc < 0)
return rc;

arg = rc;
break;
case PIN_CONFIG_SLEW_RATE:
// 仅支持某些芯片的引脚驱动速率设置
if (!info->ctrl->slew_rate_calc_reg)
return -ENOTSUPP;
// 获取引脚的驱动速率配置
rc = rockchip_get_slew_rate(bank, pin - bank->pin_base);
if (rc < 0)
return rc;

arg = rc;
break;
default:// 不支持的配置参数
return -ENOTSUPP;
}

*config = pinconf_to_config_packed(param, arg);

return 0;
}

pin_config_set

设置引脚配置信息。

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.
*/
/*
* 检查 GPIO 驱动程序是否已经探测到。
* 锁定确保 GPIO 探测完成或者 GPIO 驱动程序尚未探测到。
*/
mutex_lock(&bank->deferred_lock);
if (!gpio || !gpio->direction_output) {
// 如果驱动程序尚未探测到,则将配置信息延迟处理并返回。
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:
// 禁用上下拉电阻
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:
// 检查上下拉电阻是否有效
if (!rockchip_pinconf_pull_valid(info->ctrl, param))
return -ENOTSUPP;

if (!arg)
return -EINVAL;
// 设置上下拉电阻
rc = rockchip_set_pull(bank, pin - bank->pin_base,
param);
if (rc)
return rc;
break;
case PIN_CONFIG_OUTPUT:
// 设置引脚复用功能为 GPIO
rc = rockchip_set_mux(bank, pin - bank->pin_base,
RK_FUNC_GPIO);
if (rc != RK_FUNC_GPIO)
return -EINVAL;
// 设置引脚为输出模式
rc = gpio->direction_output(gpio, pin - bank->pin_base,
arg);
if (rc)
return rc;
break;
case PIN_CONFIG_INPUT_ENABLE:
// 设置引脚复用功能为 GPIO
rc = rockchip_set_mux(bank, pin - bank->pin_base,
RK_FUNC_GPIO);
if (rc != RK_FUNC_GPIO)
return -EINVAL;
// 设置引脚为输入模式
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 */
// 仅支持某些芯片(如 rk3288)的每个引脚独立的驱动强度设置
if (!info->ctrl->drv_calc_reg)
return -ENOTSUPP;
// 设置引脚的驱动强度
rc = rockchip_set_drive_perpin(bank,
pin - bank->pin_base, arg);
if (rc < 0)
return rc;
break;
case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
// 仅支持某些芯片的施密特触发设置
if (!info->ctrl->schmitt_calc_reg)
return -ENOTSUPP;
// 设置引脚的施密特触发模式
rc = rockchip_set_schmitt(bank,
pin - bank->pin_base, arg);
if (rc < 0)
return rc;
break;
case PIN_CONFIG_SLEW_RATE:
// 仅支持某些芯片的引脚驱动速率设置
if (!info->ctrl->slew_rate_calc_reg)
return -ENOTSUPP;
// 设置引脚的驱动速率
rc = rockchip_set_slew_rate(bank,
pin - bank->pin_base, arg);
if (rc < 0)
return rc;
break;
default:// 不支持的配置参数
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 {
` // 查看是否可以将某个引脚设置为可用于复用
int (*request) (struct pinctrl_dev *pctldev, unsigned offset);
// request() 回调的反向函数,在请求后释放引脚
int (*free) (struct pinctrl_dev *pctldev, unsigned offset);
// 返回此 Pinmux 驱动程序中可选择的命名函数数量
int (*get_functions_count) (struct pinctrl_dev *pctldev);
// 返回复用选择器的函数名称,核心调用此函数来确定应将某个设备映射到哪个复用设置
const char *(*get_function_name) (struct pinctrl_dev *pctldev,
unsigned selector);
// 返回与某个函数选择器相关联的一组组名称(依次引用引脚)
int (*get_function_groups) (struct pinctrl_dev *pctldev,
unsigned selector,
const char * const **groups,
unsigned *num_groups);
// 使用特定引脚组启用特定复用功能
int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector,
unsigned group_selector);
// 在特定引脚上请求并启用 GPIO
int (*gpio_request_enable) (struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset);
// 在特定引脚上释放 GPIO 复用
void (*gpio_disable_free) (struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset);
// 根据 GPIO 配置为输入或输出而进行不同的配置
int (*gpio_set_direction) (struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset,
bool input);
// 不允许将同一引脚同时用于 GPIO 和其他功能。在批准引脚请求之前,严格检查 gpio_owner 和 mux_owner
bool strict;
};

struct pinmux_ops 是一个用于描述引脚复用操作的结构体,是 pinctrl 驱动必须实现的一组回调函数(如果支持引脚复用),用于让 pinctrl 子系统 能够:

  • 查询 SoC 支持哪些功能(如 I2C、UART、SPI…)
  • 查询每个功能可以用哪些引脚组(pin group)
  • 在运行时动态切换引脚的功能(如把 GPIO1_B0 从普通 GPIO 切成 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

返回在该引脚控制器驱动程序中可选择的命名函数的数量。

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;// 返回引脚复用功能的数量
}

get_function_name

返回复用选择器的函数名称

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;// 返回引脚复用功能的名称
}

get_function_groups

返回与某个函数选择器相关联的一组组名称(依次引用引脚)。

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;// 返回引脚复用功能对应的引脚组数组
*num_groups = info->functions[selector].ngroups;// 返回引脚组的数量

return 0;
}

set_mux

用于启用特定的复用功能。

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 (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 */
/* 恢复已经设置的引脚设置 */
for (cnt--; cnt >= 0 && !data[cnt].func; cnt--)
rockchip_set_mux(bank, pins[cnt] - bank->pin_base, 0);

return ret;
}

return 0;
}

引脚的复用关系是在什么时候被设置的

  • 猜想 1

第一个猜想是在加载 LED 驱动的时候进行的 pinctrl 引脚复用。当 LED 灯的设备树和驱动匹配之后,就会进入驱动中编写的 probe
函 数 , 在 此 之 前 会 执 行 drivers/base/dd.c 文 件 中 的really_probe 函 数 中 的 子 函 数pinctrl_bind_pins,该函数会为给定的设备绑定引脚,并在绑定过程中选择和设置适当的 pinctrl状态。具体的绑定细节可以去前面的章节中查找。

  • 猜想 2

第二种猜想是在加载 pinctrl 驱动的时候完成的引脚复用,因为 pinctrl 子系统也是符合设备模型的规范 ,也会 执 行 相 应的 probe 函数,所以同样的加在 pinctrl 驱动时也会执行drivers/base/dd.c文件中的 really_probe 函数中的子函数 pinctrl_bind_pins,那这时会进行pinctrl 管脚的复用设置吗,接下来我们对此进行深入的分析。

以 485 控制引脚的设备树节点和对应的 pinctrl 设备树节点进行举例,要添加的 485 设备树内容如下所示

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

当上面编写的 485 设备树跟驱动匹配成功之后,就会进入相应驱动中的 probe 函数,在probe 函数中就可以对设备树中描述的 485 使能引脚进行拉高和拉低的操作,从而控制 485 的接收和发送。

所以可以猜测在进入驱动的 probe 函数之前就已经使用 pinctrl 子系统对引脚进行了复用,在设备模型中,我们已经知道了驱动中的 probe 函数是在内核源码目录下的drivers/base/dd.c文件中加载执行的,然后找到 really_probe 函数中与 probe 函数加载相关的代码

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;// 将设备的驱动程序指针设置为当前驱动

/* If using pinctrl, bind pins now before probing */
/* 如果使用了 pinctrl,在探测之前绑定引脚 */
ret = pinctrl_bind_pins(dev);// 绑定设备的引脚
if (ret)
goto pinctrl_bind_failed;

...
}

如果使能了 pinctrl 就会调用第 39 行的 pinctrl_bind_pins()函数进行设备引脚的绑定,然后根据线索跳转到 pinctrl_bind_pins 函数

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;
// 检查设备是否重用了节点
if (dev->of_node_reused)
return 0;
// 为设备的引脚分配内存空间
dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
if (!dev->pins)
return -ENOMEM;

// 获取设备的 pinctrl 句柄
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;
}
// 查找设备的默认 pinctrl 状态
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;
}
// 查找设备的初始化 pinctrl 状态
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 */
/* 不提供此状态是完全合法的 */
dev_dbg(dev, "no init pinctrl state\n");
// 选择默认的 pinctrl 状态
ret = pinctrl_select_state(dev->pins->p,
dev->pins->default_state);
} else {
// 选择初始化的 pinctrl 状态
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>
*/
/*
* 如果启用了电源管理,我们还会寻找可选的睡眠和空闲的引脚状态,其语义在
* <linux/pinctrl/pinctrl-state.h> 中定义
*/
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 */
/* 不提供此状态是完全合法的 */
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 */
/* 不提供此状态是完全合法的 */
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.
*/
/* 如果对于此设备没有找到 pinctrl 句柄或默认状态,
* 让我们明确释放设备中的引脚容器,因为保留它没有意义。
*/
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 ret;
/* Return serious errors */
if (ret == -EINVAL)/* 返回严重错误 */
return ret;
/* We ignore errors like -ENOENT meaning no pinctrl state */
/* 我们忽略诸如 -ENOENT 的错误,表示没有 pinctrl 状态 */
return 0;
}

第 19 行dev->pins->p = devm_pinctrl_get(dev);获取设备的 pinctrl 句柄,dev的类型为struct device,对于struct device结构体: 其中含有struct dev_pin_info *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
struct device {
struct kobject kobj;// 内核对象,用于设备的管理
struct device *parent;// 指向父设备的指针

struct device_private *p;// 私有数据指针

const char *init_name; /* initial name of the device */ // 设备的初始名称
const struct device_type *type;// 设备类型

struct bus_type *bus; /* type of bus device is on */ // 设备所在的总线类型
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; // 设备的通用 MSI IRQ 域
#endif
#ifdef CONFIG_PINCTRL
struct dev_pin_info *pins;// 设备的引脚信息
#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; // 引脚控制器指针
struct pinctrl_state *default_state;// 默认状态指针
struct pinctrl_state *init_state;// 初始化状态指针
#ifdef CONFIG_PM
struct pinctrl_state *sleep_state;// 睡眠状态指针 仅在支持电源管理时可用
struct pinctrl_state *idle_state;// 空闲状态指针 仅在支持电源管理时可用
#endif
};

  • struct pinctrl *p:引脚控制器指针。该指针指向设备所使用的引脚控制器对象,用于对设备的引脚进行控制和配置。

  • struct pinctrl_state *default_state:默认状态指针。该指针指向设备的默认引脚配置状态,表示设备在正常操作时的引脚配置。

  • struct pinctrl_state *init_state:初始化状态指针。该指针指向设备的初始化引脚配置状态,表示设备在初始化阶段的引脚配置。

  • struct pinctrl_state *sleep_state:睡眠状态指针(仅在支持电源管理时可用)。该指针指向设备的引脚配置状态,表示设备在进入睡眠状态时的引脚配置。

  • struct pinctrl_state *idle_state:空闲状态指针(仅在支持电源管理时可用)。该指针指向设备的引脚配置状态,表示设备在空闲状态时的引脚配置。

仍以485控制引脚为例

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

其中第 4 行的 pinctrl-names 属性指定了设备所使用的引脚控制器为 default,即第 5 行的 pinctrl-0,而 pinctrl-0 的值为 pinctrl 节点中的 rk_485_gpio,所以 struct pinctrl_state *default_state 这一默认状态结构体指针会用来存放 11 行的引脚复用信息,而在上一章节中也提到了设备树中的 pinctrl 节点会转换为 pinctrl_map 结构体,那么 struct pinctrl_state *default_state 必然会跟 pinctrl_map 结构体建立联系。

devm_pinctrl_get()

pinctrl_bind_pins()函数中通过dev->pins->p = devm_pinctrl_get(dev);来获取设备的pinctrl句柄,而devm_pinctrl_get()函数定义如下:

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;
// 为存储引脚控制器句柄的指针分配内存
ptr = devres_alloc(devm_pinctrl_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);

// 获取设备的引脚控制器句柄
p = pinctrl_get(dev);
if (!IS_ERR(p)) {
// 如果获取成功,将引脚控制器句柄存储在指针中
*ptr = p;
// 将指针添加到设备资源中
devres_add(dev, ptr);
} else {
// 如果获取失败,释放之前分配的指针内存
devres_free(ptr);
}

return p;
}
EXPORT_SYMBOL_GPL(devm_pinctrl_get);

第 18 行通过pinctrl_get()获取引脚控制器句柄

pinctrl_get()

devm_pinctrl_get()函数中通过pinctrl_get()函数获取引脚控制器句柄

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;
// 检查设备指针是否为空
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.
*/
/*
* 查看是否有其他组件(如设备核心)已经获取了此设备的引脚控制器句柄。
* 在这种情况下,返回对该句柄的另一个指针。
*/
p = find_pinctrl(dev);
if (p) {
dev_dbg(dev, "obtain a copy of previously claimed pinctrl\n");
kref_get(&p->users);
return p;
}
// 创建并返回设备的引脚控制器句柄
return create_pinctrl(dev, NULL);
}
EXPORT_SYMBOL_GPL(pinctrl_get);

在返回值中使用 create_pinctrl 函数,该函数会创建并返回设备的引脚控制器句柄。注意第二个参数为 NULL

create_pinctrl()

pinctrl_get()最后调用create_pinctrl()创建并返回设备的引脚控制器句柄,传入的第一个参数为当前驱动匹配的设备的struct device,而struct pinctrl_dev *pctldev的值为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()
*/
/*
* 为每个映射创建状态持有者分配 struct pinctrl。
* 这是当使用 pinctrl_get() 请求引脚控制句柄时消费者将获得的对象。
*/
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 */
/* 遍历引脚控制映射以定位正确的映射 */
for_each_maps(maps_node, i, map) {
/* Map must be for this device */
if (strcmp(map->dev_name, devname))/* 映射必须适用于此设备 */
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 */
/* 如果发生了除推迟以外的其他错误,则在此处返回 */
pinctrl_free(p, false);
return ERR_PTR(ret);
}

kref_init(&p->users);

/* Add the pinctrl handle to the global list */
/* 将引脚控制句柄添加到全局列表 */
mutex_lock(&pinctrl_list_mutex);
list_add_tail(&p->node, &pinctrl_list);
mutex_unlock(&pinctrl_list_mutex);

return p;
}

三个重要结构体:

  • struct pinctrl
  • struct pinctrl_maps
  • struct pinctrl_map

第 28 行调用ret = pinctrl_dt_to_map(p, pctldev);将设备树中定义的引脚映射信息转换为 struct pinctrl_map 结构

for_each_maps定义如下

1
2
3
4
5
6
7
8
9
// 宏定义:用于遍历映射表链表中的每个映射表条目
// _maps_node_: 遍历时使用的映射表节点指针
// _i_: 遍历时使用的计数器变量
// _map_: 遍历时使用的映射表条目指针
#define for_each_maps(_maps_node_, _i_, _map_) \
list_for_each_entry(_maps_node_, &pinctrl_maps, node) \ // 遍历映射表链表中的每个节点
for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \ // 初始化计数器和映射表条目指针
_i_ < _maps_node_->num_maps; \ // 循环条件:计数器小于当前节点的映射表数量
_i_++, _map_ = &_maps_node_->maps[_i_]) // 每次循环增加计数器并更新映射表条目指针

重要数据结构

struct pinctrl

struct pinctrl 结构体用于表示引脚控制器。

引脚控制器是硬件系统中管理和控制引脚(GPIO)的组件,它负责配置引脚的功能、电气属性等,该结构体定义在内核源码目录下的drivers/pinctrl/core.h文件中。

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; // 用于将引脚控制器添加到全局列表的链表节点
struct device *dev; // 关联的设备
struct list_head states; // 存储引脚配置状态的链表,用于跟踪不同的引脚配置状态
struct pinctrl_state *state;// 当前应用的引脚配置状态
struct list_head dt_maps; // 存储设备树中定义的引脚映射信息的链表
struct kref users; // 引脚控制器的引用计数,用于跟踪引脚控制器的引用数量
};

struct pinctrl_maps

struct pinctrl_maps 类型的变量 maps_node 用于遍历引脚控制映射,引脚控制器映射描述了不同引脚控制器的功能和配置与实际硬件引脚之间的对应关系,该结构体定义在内核源码目录下的drivers/pinctrl/core.h文件中。

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; // 引脚控制器映射链表节点,用于将该映射添加到全局列表
const struct pinctrl_map *maps; // 指向引脚控制器映射数组的指针
unsigned num_maps; // 引脚控制器映射数组中的映射数量
};

其中pinctrl_map结构体在dt_node_to_map中创建

pinctrl_dt_to_map()

create_pinctrl()中通过调用 ret = pinctrl_dt_to_map(p, pctldev); 函数将设备树中定义的引脚映射信息转换为 struct pinctrl_map 结构,并将其添加到 p->dt_maps 链表中。

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;// 获取引脚控制器关联设备的设备树节点
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 */
/* 如果 CONFIG_OF 启用,且 p->dev 不是从设备树实例化而来 */
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 */
/* 节点内部存储属性名称的指针 */
of_node_get(np);//增加设备树节点的引用计数,以确保在解析过程中节点不会被释放

/* For each defined state ID */
/* 对于每个定义的状态 ID */
for (state = 0; ; state++) {
/* Retrieve the pinctrl-* property */
/* 获取 pinctrl-* 属性 */
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 */
/* 判断 pinctrl-names 属性是否命名了该状态 */
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 (config = 0; config < size; config++) {
phandle = be32_to_cpup(list++);

/* Look up 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 */
/* 解析节点 */
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 (!size) {
ret = dt_remember_dummy_state(p, statename);
if (ret < 0)
goto err;
}
}

return 0;

err:
pinctrl_dt_free_maps(p);
return ret;
}

对于每个定义的状态 ID,循环解析引脚控制器的映射信息具体会执行以下步骤:

  • 构造属性名称字符串 propname,例如 "pinctrl-0""pinctrl-1" 等等。
  • 使用 of_find_property 函数获取设备树节点 np 中的属性 propname 的值,并得到属性值的大小 size。如果属性不存在,则判断是否是第一个状态 ID,如果是,则释放节点引用并返回 -ENODEV 表示设备树节点中没有有效的 pinctrl 描述。否则,跳出循环。
  • 将属性值转换为指针列表 list,并计算列表的大小。
  • 如果设备树中的 pinctrl-names 属性命名了该状态,则使用 of_property_read_string_index 函数读取属性值,并将状态名称存储在 statename 变量中。否则,将 statename 指向属性名称的一部分,即去除 "pinctrl-" 前缀。
  • 对于每个引用的引脚配置节点,执行以下步骤:
    • list 指针指向的 phandle 值转换为本地字节序。
    • 使用 of_find_node_by_phandle 函数根据 phandle 查找引脚配置节点,并将其存储在 np_config 变量中。如果找不到引脚配置节点,则打印错误信息并返回 -EINVAL
    • 调用 dt_to_map_one_config 函数,将引脚配置节点的信息解析为 pinctrl 映射,并存储在 pctldev 中。
    • 递减引脚配置节点的引用计数。
    • 如果在设备树中没有条目,则生成一个虚拟状态表条目,以便后续处理。

其中dt_to_map_one_config 函数需要特别注意,该函数会从设备树节点中解析出引脚控制器的映射表,并将其存储起来,该函数的具体内容如下所示:

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 */
/* 查找包含 np_config 的引脚控制器 */
np_pctldev = of_node_get(np_config);
for (;;) {
/* 如果不允许默认配置,则读取 pinctrl-use-default 属性 */
if (!allow_default)
allow_default = of_property_read_bool(np_pctldev,
"pinctrl-use-default");
/* 获取 np_pctldev 的父节点 */
np_pctldev = of_get_next_parent(np_pctldev);
/* 如果没有父节点或者父节点是根节点,则释放 np_pctldev 引用并返回 */
if (!np_pctldev || of_node_is_root(np_pctldev)) {
of_node_put(np_pctldev);
/* 检查是否延迟探测驱动程序状态 */
ret = driver_deferred_probe_check_state(p->dev);
/* keep deferring if modules are enabled */
/* 如果启用了模块并且不允许默认配置,并且返回值是 -ENODEV,则延迟探测 */
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 */
/* 如果正在创建一个 hog,可以使用传递的 pctldev */
if (hog_pctldev && (np_pctldev == p->dev->of_node)) {
pctldev = hog_pctldev;
break;
}
/* 通过 np_pctldev 获取 pinctrl_dev 结构体 */
pctldev = get_pinctrl_dev_from_of_node(np_pctldev);
/* 如果获取到了 pinctrl_dev 结构体,则跳出循环 */
if (pctldev)
break;
/* Do not defer probing of hogs (circular loop) */
/* 不要推迟探测 hogs(循环) */
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
*/
// 调用 pinctrl 驱动程序解析设备树节点,并生成映射表条目
ops = pctldev->desc->pctlops;
/* 检查 pinctrl 驱动程序是否支持设备树, 即是否实现了 dt_node_to_map 方法。如果不支持,返回错误码。 */
if (!ops->dt_node_to_map) {
dev_err(p->dev, "pctldev %s doesn't support DT\n",
dev_name(pctldev->dev));
return -ENODEV;
}
/* 调用 pinctrl 驱动程序的 dt_node_to_map 方法 */
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 */
/* 将映射表块存储起来以供后续使用 */
return dt_remember_or_free_map(p, statename, pctldev, map, num_maps);
}

第 70 行调用引脚控制器的 dt_node_to_map 方法,将设备树节点 np_config 转换为映射表条目。该方法会解析设备树节点,并根据节点信息生成映射表条目。具体的转换过程由各个 pinctrl 的驱动程序实现。

dt_remember_or_free_map()

dt_to_map_one_config 函数用于从设备树节点中解析出引脚控制器的映射表并存储起来,而存储的操作由函数 dt_remember_or_free_map 完成。

函数传递参数:

  • p:指向 struct pinctrl 结构体的指针,表示引脚控制器的上下文。
  • statename:指向状态名称的指针,表示要设置的状态的名称。
  • pctldev:指向 struct pinctrl_dev 结构体的指针,表示引脚控制器设备。
  • map:指向 struct pinctrl_map 结构体数组的指针,表示解析得到的映射表条目。
  • num_maps:表示映射表条目的数量
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 */
// 循环遍历映射表条目数组,为每个条目初始化公共字段
for (i = 0; i < num_maps; i++) {
const char *devname;
// 通过 kstrdup_const 函数复制引脚控制器设备的名称,并将返回的指针赋值给 devname
devname = kstrdup_const(dev_name(p->dev), GFP_KERNEL);
if (!devname)
goto err_free_map;
// 设置映射表条目的设备名称、状态名称和控制器设备名称
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 */
/* 记录转换后的映射表条目 */
dt_map = kzalloc(sizeof(*dt_map), GFP_KERNEL);//使用 kzalloc 分配内存空间,并将返回的指针赋值给 dt_map
if (!dt_map)
goto err_free_map;
// 将传入的 pctldev、map 和 num_maps 分别赋值给 dt_map 的对应字段
dt_map->pctldev = pctldev;
dt_map->map = map;
dt_map->num_maps = num_maps;
list_add_tail(&dt_map->node, &p->dt_maps);// 使用 list_add_tail 函数将 dt_map 添加到 p->dt_maps 链表中
/* 注册映射表条目 */
return pinctrl_register_mappings(map, num_maps);

err_free_map:
/* 释放映射表条目内存 */
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;//用于将映射表结构体添加到 pinctrl 的 dt_maps 链表中
struct pinctrl_dev *pctldev;// 引脚控制器设备
struct pinctrl_map *map;// 映射表条目数组
unsigned num_maps;//映射表条目数量
};
pinctrl_register_mappings()

dt_remember_or_free_map()最后用 pinctrl_register_mappings() 函数注册映射表条目。该函数将映射表条目注册到 pinctrl 子系统,以便后续可以通过相关接口进行引脚配置和管理

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 指向映射表条目数组的指针
// num_maps 映射表条目数量
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 */
/* 首先对新映射表进行合法性检查 */
for (i = 0; i < num_maps; i++) {
// 检查设备名称是否存在
if (!maps[i].dev_name) {
pr_err("failed to register map %s (%d): no device given\n",
maps[i].name, i);
return -EINVAL;
}
// 检查映射表名称是否存在
if (!maps[i].name) {
pr_err("failed to register map %d: no map name given\n",
i);
return -EINVAL;
}
// 对于引脚映射类型和配置映射类型,检查引脚控制设备名称是否存在
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:// 对于虚拟状态映射类型,不进行验证
break;
case PIN_MAP_TYPE_MUX_GROUP:// 对于复用组映射类型,进行引脚复用映射验证
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:// 对于配置映射类型,进行引脚配置映射验证
ret = pinconf_validate_map(&maps[i], i);
if (ret < 0)
return ret;
break;
default:// 对于无效的映射类型,返回错误
pr_err("failed to register map %s (%d): invalid type given\n",
maps[i].name, i);
return -EINVAL;
}
}
// 分配映射表节点内存
maps_node = kzalloc(sizeof(*maps_node), GFP_KERNEL);
if (!maps_node)
return -ENOMEM;
// 设置映射表节点的映射表和映射表数量
maps_node->maps = maps;
maps_node->num_maps = num_maps;
// 加锁并将映射表节点插入映射表链表末尾
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);

可见pinctrl_register_mappings 函数的作用是注册一个引脚控制器的映射表pinctrl_maps,进行了一些参数合法性检查和验证,并将映射表节点插入到映射表链表中。

add_setting()

create_pinctrl函数中,通过 ret = add_setting(p, pctldev, 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
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对象指针
struct pinctrl_setting *setting;// pinctrl_setting对象指针
int ret;

// 查找pinctrl_state,如果不存在则创建新的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 (map->type == PIN_MAP_TYPE_DUMMY_STATE)
return 0;

// 分配pinctrl_setting的内存空间
setting = kzalloc(sizeof(*setting), GFP_KERNEL);
if (!setting)
return -ENOMEM;

setting->type = map->type;// 设置pinctrl_setting的映射类型

if (pctldev)// 设置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 (!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;
}
// 设置pinctrl_setting的设备名称
setting->dev_name = map->dev_name;

switch (map->type) {
case PIN_MAP_TYPE_MUX_GROUP:
// 对于复用组映射类型,执行pinmux_map到pinctrl_setting的转换
ret = pinmux_map_to_setting(map, setting);
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
// 对于配置映射类型,执行pinconf_map到pinctrl_setting的转换
ret = pinconf_map_to_setting(map, setting);
break;
default:
ret = -EINVAL;
break;
}
if (ret < 0) {
kfree(setting);
return ret;
}
// 将pinctrl_setting插入状态对象的设置链表末尾
list_add_tail(&setting->node, &state->settings);

return 0;
}

可以看到:

  • 对于复用组映射类型(PIN_MAP_TYPE_MUX_GROUP),调用 pinmux_map_to_setting 函数执行引脚复用映射到设置对象的转换。

  • 对于配置映射类型(PIN_MAP_TYPE_CONFIGS_PINPIN_MAP_TYPE_CONFIGS_GROUP),调用 pinconf_map_to_setting 函数执行引脚配置映射到设置对象的转换。

add_setting 函数的最终目的就是将传入的 const struct pinctrl_map *map 的参数值传入到 struct pinctrl_setting 类型的变量中,从而进一步提取 pinctrl_map 结构体类型变量中的内容。

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;// 链表节点,用于将状态对象连接到引脚控制器对象的状态链表
const char *name;// 状态对象的名称字符串指针
struct list_head settings;// pinctrl_setting对象链表,包含该状态的所有设置对象
};

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; // 链表节点,用于将设置对象连接到状态对象的设置链表
enum pinctrl_map_type type; // 映射类型,表示设置对象的类型
struct pinctrl_dev *pctldev; // 引脚控制设备对象指针
const char *dev_name; // 设备名称字符串指针
union {
struct pinctrl_setting_mux mux; // 复用组映射类型的数据结构
struct pinctrl_setting_configs configs; // 配置映射类型的数据结构
} data;
};
create_state()

add_setting()函数中,根据映射表条目的名称,使用 find_state() 函数在引脚控制器对象中查找对应的状态对象,在此之前我们并没有设置状态对象,所以会进入到第二个 if 判断,通过state = create_state(p, map->name)创建一个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;
// 为 pinctrl_state 结构体分配内存
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return ERR_PTR(-ENOMEM);
// 设置状态的名称
state->name = name;
// 初始化状态的设置列表
INIT_LIST_HEAD(&state->settings);
// 将状态添加到 pinctrl 的状态链表中
list_add_tail(&state->node, &p->states);

return state;
}

pinmux_map_to_setting()

add_setting()函数中对于复用组映射类型(PIN_MAP_TYPE_MUX_GROUP),调用 pinmux_map_to_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
// drivers/pinctrl/pinmux.c
int pinmux_map_to_setting(const struct pinctrl_map *map,
struct pinctrl_setting *setting)
{
struct pinctrl_dev *pctldev = setting->pctldev;// 获取引脚控制设备指针
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;// 获取引脚复用操作指针
char const * const *groups; // 引脚复用组数组
unsigned num_groups; // 引脚复用组数量
int ret;
const char *group; // 引脚复用组名称

if (!pmxops) {// 检查引脚控制设备是否支持引脚复用操作
dev_err(pctldev->dev, "does not support mux function\n");
return -EINVAL;
}
// 将映射表中的复用函数名称转换为复用函数的选择器,并将其保存在设置对象的 data.mux.func 字段中
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;

// 通过调用引脚复用操作对象的 get_function_groups 函数查询复用函数对应的复用组信息,
// 获取复用组的名称数组和数量,并将它们保存在 groups 和 num_groups 变量中。
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) {// 根据映射表中指定的复用组名称或者选择第一个复用组名称,并在复用组数组中查找对应的索引
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];
}
// 通过调用引脚控制设备对象的 pinctrl_get_group_selector
// 函数获取复用组的选择器,并将它保存在设置对象的 data.mux.group
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;// 设置设置对象的复用组选择器

return 0;
}

pinconf_map_to_setting()

add_settings函数中对于配置映射类型(PIN_MAP_TYPE_CONFIGS_PINPIN_MAP_TYPE_CONFIGS_GROUP),调用 pinconf_map_to_setting 函数执行引脚配置映射到设置对象的转换。

该函数的作用是将pinctrl_map引脚配置映射转换为pinctrl_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
// drivers/pinctrl/pinconf.c
int pinconf_map_to_setting(const struct pinctrl_map *map,
struct pinctrl_setting *setting)
{
struct pinctrl_dev *pctldev = setting->pctldev;// 获取引脚控制设备指针
int pin;

switch (setting->type) {
case PIN_MAP_TYPE_CONFIGS_PIN:// 针对单个引脚的配置
pin = pin_get_from_name(pctldev,
map->data.configs.group_or_pin);// 通过引脚名称获取引脚号
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;// 设置设置对象的引脚号
break;
case PIN_MAP_TYPE_CONFIGS_GROUP:// 针对引脚组的配置
pin = pinctrl_get_group_selector(pctldev,
map->data.configs.group_or_pin);// 获取引脚组的选择器
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; // 设置设置对象的引脚组选择器
break;
default:
return -EINVAL;
}

setting->data.configs.num_configs = map->data.configs.num_configs;// 设置设置对象的配置数量
setting->data.configs.configs = map->data.configs.configs;// 设置设置对象的配置指针

return 0;
}

对于针对单个引脚的配置,通过调用 pin_get_from_name 函数,根据映射表中的引脚名称获取引脚号,并将其设置到设置对象的 data.configs.group_or_pin 字段中。如果获取引脚号失败,则返回错误。

对于针对引脚组的配置,它通过调用 pinctrl_get_group_selector 函数,根据映射表中的引脚组名称获取引脚组的选择器,并将其设置到设置对象的 data.configs.group_or_pin 字段中

pinctrl_lookup_state()

pinctrl_bind_pins()通过dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,PINCTRL_STATE_DEFAULT); 查找设备的默认 pinctrl 状态,并将其赋值给 dev->pins->default_state。如果查找失败,函数会打印一条调试信息,并将返回值设置为 0,表示继续执行。

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;
// 在状态链表中查找指定名称的状态对象
state = find_state(p, name);
if (!state) {
if (pinctrl_dummy_state) {
/* create dummy state */
/* 创建虚拟状态 */
dev_dbg(p->dev, "using pinctrl dummy state (%s)\n",
name);
// 如果找不到 指定的状态对象,并且存在虚拟状态,则创建一个虚拟状态对象
state = create_state(p, name);
} else
// 如果找不到指定的状态对象,并且不存在虚拟状态,则返回错误指针 -ENODEV
state = ERR_PTR(-ENODEV);
}

return state;
}
EXPORT_SYMBOL_GPL(pinctrl_lookup_state);

pinctrl_select_state()

pinctrl_bind_pins()使用pinctrl_select_state()函数ret = pinctrl_select_state(dev->pins->p,dev->pins->default_state);选择并切换到指定的 pinctrl_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)// 如果当前状态已经是要选择的状态,则无需进行任何操作,直接返回 0 表示成功
return 0;
// 调用 pinctrl_commit_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.
*/
/*
* 对于旧状态中的每个引脚复用设置,取消 SW 记录的该引脚组的复用所有者。
* 任何仍由新状态拥有的引脚组将在下面循环中的 pinmux_enable_setting() 调用中重新获取。
*/
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 */
/* 应用新状态的所有设置 */
list_for_each_entry(setting, &state->settings, node) {
switch (setting->type) {
case PIN_MAP_TYPE_MUX_GROUP:
// 对于引脚复用设置(PIN_MAP_TYPE_MUX_GROUP),调用 pinmux_enable_setting()函数来启用该设置。
ret = pinmux_enable_setting(setting);
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
// 对于引脚配置设置(PIN_MAP_TYPE_CONFIGS_PIN 或 PIN_MAP_TYPE_CONFIGS_GROUP),调用 pinconf_apply_setting()函数来应用该设置
ret = pinconf_apply_setting(setting);
break;
default:
ret = -EINVAL;
break;
}

if (ret < 0) {
// 如果应用设置失败,则回滚新状态的设置
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:
// 回滚新状态的设置
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()函数遍历新状态的所有设置,并根据设置的类型执行相应的操作:

  • 对于引脚复用设置(PIN_MAP_TYPE_MUX_GROUP),调用 pinmux_enable_setting()函数来启用该设置。

  • 对于引脚配置设置(PIN_MAP_TYPE_CONFIGS_PINPIN_MAP_TYPE_CONFIGS_GROUP),调用 pinconf_apply_setting()函数来应用该设置。

  • 对于其他类型的设置,将返回一个错误码(-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;
// 如果 pctlops->get_group_pins 函数存在,则调用该函数获取组中的引脚信息,并将引脚信息存储在 pins 和 num_pins 变量中
if (pctlops->get_group_pins)
ret = pctlops->get_group_pins(pctldev, setting->data.mux.group,
&pins, &num_pins);

if (ret) {// 如果获取引脚信息失败,发出警告并将 num_pins 设置为 0
const char *gname;

/* errors only affect debug data, so just warn */
// 错误只影响调试数据,因此只发出警告
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 */
// 逐个申请组中的引脚
for (i = 0; i < num_pins; i++) {
// 使用 pin_request 函数申请引脚,并传入引脚控制器设备、引脚编号、设备名称和其他参数
ret = pin_request(pctldev, pins[i], setting->dev_name, NULL);
if (ret) {
const char *gname;
const char *pname;
// 分配引脚后,使用 pin_desc_get 函数获取引脚描述符,并将复用设置指针指向引脚复用信息。
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 */
// 分配引脚后,编码复用设置
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);
}
// 调用 ops->set_mux 函数设置引脚复用,传入引脚控制器设备、复用功能和组信息,以便设置引脚复用。
ret = ops->set_mux(pctldev, setting->data.mux.func,
setting->data.mux.group);

if (ret)
goto err_set_mux;

return 0;

err_set_mux:
// 复用设置失败,清除复用设置
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 */
// 在错误发生时释放已申请的引脚
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) {// 检查是否存在 pinconf 操作函数集
dev_err(pctldev->dev, "missing confops\n");
return -EINVAL;
}
// 根据设置类型选择相应的操作
switch (setting->type) {
case PIN_MAP_TYPE_CONFIGS_PIN://表示对单个引脚进行配置设置
if (!ops->pin_config_set) {// 检查是否存在 pin_config_set 操作函数
dev_err(pctldev->dev, "missing pin_config_set op\n");
return -EINVAL;
}
// 调用 pin_config_set 函数设置单个引脚的配置
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:// 表示对引脚组进行配置设置
if (!ops->pin_config_group_set) {// 检查是否存在 pin_config_group_set 操作函数
dev_err(pctldev->dev,
"missing pin_config_group_set op\n");
return -EINVAL;
}
// 调用 pin_config_group_set 函数设置引脚组的配置
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;
}

思维导图

pinctrl

结论

  • 猜想 1

第一个猜想是在加载 LED 驱动的时候进行的 pinctrl 引脚复用。当 LED 灯的设备树和驱动匹配之后,就会进入驱动中编写的 probe
函 数 , 在 此 之 前 会 执 行 drivers/base/dd.c 文 件 中 的really_probe 函 数 中 的 子 函 数pinctrl_bind_pins,该函数会为给定的设备绑定引脚,并在绑定过程中选择和设置适当的 pinctrl状态。具体的绑定细节可以去前面的章节中查找。

  • 猜想 2

第二种猜想是在加载 pinctrl 驱动的时候完成的引脚复用,因为 pinctrl 子系统也是符合设备模型的规范 ,也会 执 行 相 应的 probe 函数,所以同样的加在 pinctrl 驱动时也会执行drivers/base/dd.c文件中的 really_probe 函数中的子函数 pinctrl_bind_pins,那这时会进行pinctrl 管脚的复用设置吗,接下来我们对此进行深入的分析。

在 pinctrl 的 probe 函数执行之前,会调用 pinctrl_bind_pins 函数,根据前面的内容可知,根据函数的嵌套,首先会调用的 create_pinctrl 函数创建 struct pinctrl 类型的引脚控制器,然后在create_pinctrl调用 pinctrl_dt_to_map 函数将设备树中定义的引脚映射信息转换为 struct pinctrl_map 结构,并将其添加到 p->dt_maps 链表中

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;// 获取引脚控制器关联设备的设备树节点
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 */
/* 如果 CONFIG_OF 启用,且 p->dev 不是从设备树实例化而来 */
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 */
/* 节点内部存储属性名称的指针 */
of_node_get(np);//增加设备树节点的引用计数,以确保在解析过程中节点不会被释放

/* For each defined state ID */
/* 对于每个定义的状态 ID */
for (state = 0; ; state++) {
/* Retrieve the pinctrl-* property */
/* 获取 pinctrl-* 属性 */
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 */
/* 判断 pinctrl-names 属性是否命名了该状态 */
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.
*/
/*
* 如果未命名,则 statename 仅是整数状态 ID。但是,为了避免动态分配和之后要释放的麻烦,
* 可以直接将 statename 指向属性名称的一部分。
*/
if (ret < 0)
statename = prop->name + strlen("pinctrl-");

/* For every referenced pin configuration node in it */
/* 对于其中的每个引用的引脚配置节点 */
for (config = 0; config < size; config++) {
phandle = be32_to_cpup(list++);

/* Look up 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 */
/* 解析节点 */
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 (!size) {
ret = dt_remember_dummy_state(p, statename);
if (ret < 0)
goto err;
}
}

return 0;

err:
pinctrl_dt_free_maps(p);
return ret;
}

关键点在于:这里传递过来的是 pinctrl 的设备树节点,在 32 行在 for 循环中会获取 pinctrl-* 属性,而在 pinctrl 节点中并没有该属性,pinctrl-* 属性是在一系列的设备节点中添加的,所以会在这里返回错误,同样的错误会一层层的向上级函数传递,最终导致 pinctrl_bind_pins() 函数返回错误,从而不能设置引脚的复用,即:

1
2
3
pinctrl_bind_pins
create_pinctrl
pinctrl_dt_to_map -> 报错

所以猜想 2 是不正确的。

而瑞芯微的pinctrl驱动中的probe有这样一个调用关系:

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

从上面的调用关系可以得到 pinctrl 的 probe 函数最后也会调用 create_pinctrl 来创建 struct pinctrl 类型的引脚控制器,从而实现 pinctrl 引脚复用设置,同样的这是的设置也是不成功的,pinctrl_claim_hogs 函数定义在内核源码目录下的drivers/pinctrl/core.c文件中