时间轴

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),在介绍 pinctrl子系统函数操作集之前,首先对 groups 和 function 进行讲解。

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

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

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_PB4 和 RK_PB3
      • RK_PB4 用于 CAN0 的接收引脚(can0_rxm0)
      • RK_PB3 用于 CAN0 的发送引脚(can0_txm0)。
    • 引脚组 can0m1-pins:这是 CAN0 控制器的第二个引脚组,也用于配置 CAN0 的引脚。它定义了两个引脚:RK_PA2 和 RK_PA1。
      • RK_PA2 用于 CAN0 的接收引脚(can0_rxm1)
      • RK_PA1 用于 CAN0 的发送引脚(can0_txm1)。
  • CAN1 控制器:
    • 引脚组 can1m0-pins:这是 CAN1 控制器的第一个引脚组,用于配置 CAN1 的引脚。它定义了两个引脚:RK_PA0 和 RK_PA1。
      • RK_PA0 用于 CAN1 的接收引脚(can1_rxm0)
      • RK_PA1 用于 CAN1 的发送引脚(can1_txm0)。
    • 引脚组 can1m1-pins:这是 CAN1 控制器的第二个引脚组,也用于配置 CAN1 的引脚。它定义了两个引脚:RK_PC2 和 RK_PC3。
      • RK_PC2 用于 CAN1 的接收引脚(can1_rxm1)
      • RK_PC3 用于 CAN1 的发送引脚(can1_txm1)。

struct rockchip_pinctrl结构体中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;// 引脚功能数量
};

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
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
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);// 分配并初始化一个 rockchip_pinctrl 结构体
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); // 获取并设置与 pdev 相关的 rockchip_pin_ctrl 结构体

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

/* 使用 of_parse_phandle 函数解析设备树中名为"rockchip,grf"的节点。 */
node = of_parse_phandle(np, "rockchip,grf", 0);// 解析设备树中的"rockchip,grf"节点,获取寄存器映射基地址
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 控制器,并将相关信息存储在rockchip_pinctrl 结构体中,最后注册相关设备和 GPIO 接口。

需要注意的是rockchip_pinctrl_register函数,传入参数为platform_device和rockchip_pinctrl类型

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

最后调用devm_pinctrl_register,将pdev->devinfo->pctlinfo传入

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;
// 遍历每个子节点,解析函数信息
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;
}



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

  • 将 info 的 functions 地址赋值给了 func,所以 rockchip_pinctrl 的 functions 参数的作用就是用来存放 pinctrl 设备树中的 function 信息。
  • info 的 groups 地址赋值给了 grp,所以 rockchip_pinctrl 的 groups 参数的作用就是用来存放 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
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 设备,将 pinctrl 描述结构体、pinctrl 相关操作函数和私有数据作为参数,将 pinctrl 设备注册到系统中

一个参数类型为struct device *,第二个参数类型为struct pinctrl_desc *,第三个参数类型为void *是pin controller的私有数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 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 设备。该函数将 pinctrl_desc 结构体、设备指针 dev 和驱动程序数据 driver_data 作为参数,并返回注册后的 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
30
// 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
82
/**
* 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_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: 指向自定义配置项的指针,用于描述引脚控制器的自定义配置项的属性。自定义配置项可以根据具体需求定义,用于扩展引脚控制器的配置选项。

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:引脚功能的数量。它表示引脚功能数组的大小,用于确定
    引脚功能数组的长度。

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

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

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 映射。

该结构体用于在引脚控制器中定义引脚的映射关系。通过映射类型的不同,可以将引脚与具 体 的 复 用 功 能 或 配 置 信 息 关 联 起 来 , 从 而 实 现 引 脚 的 配 置 和 控 制

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

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

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 是一个用于描述引脚复用操作的结构体。它定义了一组函数指针,这些函数指针指向了引脚控制器驱动程序中实现的具体功能。通过实现这些函数,引脚控制器驱动程序可以与核心交互,并提供引脚复用的功能。核心可以通过调用这些函数来请求、释放引脚,设置复用功能,操作 GPIO 等。这个结构体的设计允许引脚控制器驱动程序根据具体的硬件需求和功能定义自己的操作。

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

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

结构体 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;
}

思维导图

pinctrl

pinctrl_bind_pins()

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

以 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 函数加载相
关的代码,

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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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;

if (dev->bus->dma_configure) {// 配置 DMA
ret = dev->bus->dma_configure(dev);
if (ret)
goto probe_failed;
}

ret = driver_sysfs_add(dev);// 添加驱动程序的 sysfs 接口
if (ret) {
pr_err("%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}

if (dev->pm_domain && dev->pm_domain->activate) {
ret = dev->pm_domain->activate(dev);// 激活电源管理域
if (ret)
goto probe_failed;
}

if (dev->bus->probe) {
ret = dev->bus->probe(dev);// 如果总线的探测函数存在,则调用总线的探测函数
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);// 否则调用驱动程序的探测函数
if (ret)
goto probe_failed;
}

ret = device_add_groups(dev, drv->dev_groups);
if (ret) {
dev_err(dev, "device_add_groups() failed\n");
goto dev_groups_failed;
}

if (dev_has_sync_state(dev)) {
ret = device_create_file(dev, &dev_attr_state_synced);
if (ret) {
dev_err(dev, "state_synced sysfs add failed\n");
goto dev_sysfs_state_synced_failed;
}
}

if (test_remove) {
test_remove = false;

device_remove_file(dev, &dev_attr_state_synced);
device_remove_groups(dev, drv->dev_groups);

if (dev->bus->remove)// 如果总线的移除函数存在,则调用总线的移除函数
dev->bus->remove(dev);
else if (drv->remove)// 否则调用驱动程序的移除函数
drv->remove(dev);

devres_release_all(dev);// 释放设备资源
arch_teardown_dma_ops(dev);
kfree(dev->dma_range_map);
dev->dma_range_map = NULL;
driver_sysfs_remove(dev);// 移除驱动程序的 sysfs 接口
dev->driver = NULL;
dev_set_drvdata(dev, NULL);
if (dev->pm_domain && dev->pm_domain->dismiss)
dev->pm_domain->dismiss(dev);// 解除电源管理域的激活状态
pm_runtime_reinit(dev);// 重新初始化电源管理运行时

goto re_probe;// 重新进行设备的探测
}

pinctrl_init_done(dev);// 完成引脚控制器的初始化

if (dev->pm_domain && dev->pm_domain->sync)
dev->pm_domain->sync(dev);// 同步电源管理域

driver_bound(dev);// 驱动程序与设备绑定成功
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;

dev_sysfs_state_synced_failed:
device_remove_groups(dev, drv->dev_groups);
dev_groups_failed:
if (dev->bus->remove)
dev->bus->remove(dev);
else if (drv->remove)
drv->remove(dev);
probe_failed:
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
pinctrl_bind_failed:
device_links_no_driver(dev);
devres_release_all(dev);
arch_teardown_dma_ops(dev);
kfree(dev->dma_range_map);
dev->dma_range_map = NULL;
driver_sysfs_remove(dev);
dev->driver = NULL;
dev_set_drvdata(dev, NULL);
if (dev->pm_domain && dev->pm_domain->dismiss)
dev->pm_domain->dismiss(dev);
pm_runtime_reinit(dev);
dev_pm_set_driver_flags(dev, 0);

switch (ret) {
case -EPROBE_DEFER:
/* Driver requested deferred probing */
dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name);
driver_deferred_probe_add_trigger(dev, local_trigger_count);
break;
case -ENODEV:
case -ENXIO:
pr_debug("%s: probe of %s rejects match %d\n",
drv->name, dev_name(dev), ret);
break;
default:
/* driver matched but the probe failed */
pr_warn("%s: probe of %s failed with error %d\n",
drv->name, dev_name(dev), ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
* its luck.
*/
ret = 0;
done:// 执行完成后的处理逻辑
atomic_dec(&probe_count);
wake_up_all(&probe_waitqueue);
return ret;
}

使用了 pinctrl 就会调用第 39 行的 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
104
// 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;
}

对于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
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句柄

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

可见通过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 函数,该函数会创建并返回设备的引脚控制器句柄

create_pinctrl()

pinctrl_get()最后调用create_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
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
// 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()
*/
/*
* 为每个映射创建状态 cookie 持有者 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.
*/
/*
* 如果 pctldev 不为空,我们正在声明它的独占使用权,
* 这意味着它自己提供了该设置。
*
* 因此,我们必须跳过适用于此设备但由其他设备提供的映射。
*/
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.
*/
/*
* 在这一点上,添加设置可能会导致:
*
* - 延迟,如果引脚控制设备尚不可用
* - 失败,如果引脚控制设备尚不可用,
* 并且该设置是一个独占设置。我们不能推迟它,因为
* 该独占设置会在设备注册后立即生效。
*
* 如果返回的错误不是 -EPROBE_DEFER,则我们将
* 累积错误,以查看是否最终得到 -EPROBE_DEFER,
* 因为那是最糟糕的情况。
*/
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

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_]) // 每次循环增加计数器并更新映射表条目指针

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; // 引脚控制器的引用计数,用于跟踪引脚控制器的引用数量
};

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()中通过调用 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;
}

对于每个定义的状态 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
87
88
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 完成。

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

函数传递参数:

  • p:指向 struct pinctrl 结构体的指针,表示引脚控制器的上下文。
  • statename:指向状态名称的指针,表示要设置的状态的名称。
  • pctldev:指向 struct pinctrl_dev 结构体的指针,表示引脚控制器设备。
  • map:指向 struct pinctrl_map 结构体数组的指针,表示解析得到的映射表条目。
  • num_maps:表示映射表条目的数量
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:
// 对于复用组映射类型,执行pinctrl_map到pinctrl_setting的转换
ret = pinmux_map_to_setting(map, setting);
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
// 对于配置映射类型,执行pinctrl_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 结构体类型变量中的内容。

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对象链表,包含该状态的所有设置对象
};

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
38
// 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
88
/**
* 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_PIN 或 PIN_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_pinctr
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_pinctr

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