我是靠谱客的博主 秀丽金针菇,这篇文章主要介绍gpio子系统与pinctrl子系统三:pinctrl,现在分享给大家,希望可以做个参考。

以全志A40i芯片+Linux-3.10内核为例分析。

对于A40i+Linux-3.10代码中已经没有单独的drivers/gpio/gpio-*.c驱动。主要是因为gpio驱动和

pinctrl驱动有着紧密联系,所以在pinctrl驱动里会同时向gpio子系统注册struct gpio_chip

因此对于A40i而言,我们只要分析pinctrl驱动就够了。

文件与代码

此处主要针对于平台的gpio控制器,一般这部分的代码都是由芯片或者方案厂家完成。

  • 设备端(device),主要是设备树文件:

  linux-3.10archarmbootdtssun8iw11p1-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
soc@01c00000{ pio: pinctrl@01c20800 { compatible = "allwinner,sun8iw11p1-pinctrl"; reg = <0x0 0x01c20800 0x0 0x400>; interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>; device_type = "pio"; clocks = <&clk_pio>; gpio-controller; interrupt-controller; #interrupt-cells = <2>; #size-cells = <0>; #gpio-cells = <6>; ...... uart0_pins_b: uart0@1 { allwinner,pins = "PB22", "PB23"; allwinner,function = "io_disabled"; allwinner,muxsel = <7>; allwinner,drive = <1>; allwinner,pull = <1>; }; ...... }
  • 在驱动端(driver)文件:

linux-3.10driverspinctrlsunxipinctrl-sun8iw11p1.c

linux-3.10driverspinctrlsunxipinctrl-sunxi.c

  • pinctrl-sun8iw11p1.c 文件

<1> —>  入口:postcore_initcall(sun8iw11p1_pio_init);        // 与module_init类似的调用接口  

复制代码
1
2
3
4
5
6
7
8
9
10
11
static int __init sun8iw11p1_pio_init(void) { int ret; ret = platform_driver_register(&sun8iw11p1_pinctrl_driver); if (IS_ERR_VALUE(ret)) { pr_debug("register sun8iw11p1 pio controller failedn"); return -EINVAL; } return 0; }

<2> —>关注其中的 sun8iw11p1_pinctrl_driver 结构体。

复制代码
1
2
3
4
5
6
7
8
static struct platform_driver sun8iw11p1_pinctrl_driver = { .probe = sun8iw11p1_pinctrl_probe, .driver = { .name = "sun8iw11p1-pinctrl", .owner = THIS_MODULE, .of_match_table = sun8iw11p1_pinctrl_match, }, };

其中的 sun8iw11p1_pinctrl_match 中的 "allwinner,sun8iw11p1-pinctrl"与设备树中的匹配一致.

复制代码
1
2
3
4
5
static struct of_device_id sun8iw11p1_pinctrl_match[] = { { .compatible = "allwinner,sun8iw11p1-pinctrl", }, {} };

<3> —> 关注其中的 sun8iw11p1_pinctrl_probe 回调函数。

复制代码
1
2
3
4
5
static int sun8iw11p1_pinctrl_probe(struct platform_device *pdev) { return sunxi_pinctrl_init(pdev, &sun8iw11p1_pinctrl_data); }

该回调函数只是二次封装了下 pinctrl-sunxi.c 文件中的 sunxi_pinctrl_init 函数。

而 sunxi_pinctrl_init 函数有个参数 const struct sunxi_pinctrl_desc *desc 在此处定义:

复制代码
1
2
3
4
5
6
static const struct sunxi_pinctrl_desc sun8iw11p1_pinctrl_data = { .pins = sun8iw11p1_pins, .npins = ARRAY_SIZE(sun8iw11p1_pins), .pin_base = 0, .irq_banks = 1, };

<4> —> 关注其中的 sun8iw11p1_pins结构体数组。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static const struct sunxi_desc_pin sun8iw11p1_pins[] = { SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 0), SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), SUNXI_FUNCTION(0x2, "emac0"), /* ERXD3 */ SUNXI_FUNCTION(0x3, "spi1"), /* CS0 */ SUNXI_FUNCTION(0x4, "uart2"), /* RTS */ SUNXI_FUNCTION(0x5, "gmac0"), /* GRXD3 */ SUNXI_FUNCTION(0x7, "io_disabled")), SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 1), SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), SUNXI_FUNCTION(0x2, "emac0"), /* ERXD2 */ SUNXI_FUNCTION(0x3, "spi1"), /* CLK */ SUNXI_FUNCTION(0x4, "uart2"), /* CTS */ SUNXI_FUNCTION(0x5, "gmac0"), /* GRXD2 */ SUNXI_FUNCTION(0x7, "io_disabled")), ...... };

该数组基本按照芯片手册上的定义,将PA到PI的引脚和功能都定义了一遍。

<5> —> 另外就是该文件中的sunxi_pinctrl_desc结构体: 

复制代码
1
2
3
4
5
6
struct sunxi_pinctrl_desc { const struct sunxi_desc_pin *pins; int npins; unsigned pin_base; unsigned irq_banks; };

赋值的就是“sun8iw11p1_pins”数组。

复制代码
1
2
3
4
5
6
static const struct sunxi_pinctrl_desc sun8iw11p1_pinctrl_data = { .pins = sun8iw11p1_pins, .npins = ARRAY_SIZE(sun8iw11p1_pins), .pin_base = 0, .irq_banks = 1, };
  • 在该sunxi_pinctrl_desc结构体中包含了一个结构体变量: struct pinctrl_pin_desc

       该变量的定义类型就是所有pinctrl驱动通用的 “pinctrl_pin_desc”结构体(☆关键结构体)

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
/** * struct pinctrl_pin_desc - boards/machines provide information on their * pins, pads or other muxable units in this struct * @number: unique pin number from the global pin number space * @name: a name for this pin * @drv_data: driver-defined per-pin data. pinctrl core does not touch this */ struct pinctrl_pin_desc { unsigned number; const char *name; void *drv_data; };

       引用一段网友的解释:

* struct pinctrl_pin_desc用于描述一个引脚,例如:

对于PA0引脚,成员number=0,成员name="PA0",类似一个map(key=0,value="PA0");

  • 在该sunxi_pinctrl_desc结构体中包含了另一个结构体变量: struct sunxi_desc_function

        再引用一段网友的解释: 

struct sunxi_desc_function用于描述一个引脚功能,对于PA0引脚,共有4个function(gpio_in、gpio_out、uart2[看来只是功能分组,并没有指定具体是哪一个引脚功能]、jtag),第一个function的muxval=0x0(寄存器里的偏移值),name="gpio_in";

因此 pinctrl-sun8iw11p1.c 文件的主要作用就是:

  1.  驱动入口:postcore_initcall(sun8iw11p1_pio_init),该接口与module_init类似。
  2. 利用 pinctrl_pin_desc(通用)结构体 和 全志自定义 sunxi_desc_function 结构体,定义了所有 pin引脚 和 引脚功能(sunxi_desc_pin sun8iw11p1_pins[] 数组)。
  3. 与设备树文件匹配:"allwinner,sun8iw11p1-pinctrl" 名称。
  4. sun8iw11p1_pinctrl_probe 函数调用了 pinctrl-sunxi.c 文件的 sunxi_pinctrl_init函数,并传入sun8iw11p1_pins[] 数组,开始初始化流程。

  • pinctrl-sunxi.c 文件

        从sunxi_pinctrl_init函数开始,该函数中主要做了几件事: 

  1. 申请platform_device资源并初始化。
  2. 初始化 struct pinctrl_desc *pctrl_desc 结构体,并调用pinctrl_register()接口注册进pinctrl子系统。
  3. 初始化struct gpio_chip *chip结构体(☆关键结构体),并调用gpiochip_add()接口,向Gpiolib注册gpio_chip(一个该结构体代表一个控制器)。注册后内核的Gpiolib就有了控制引脚的能力(设置输入输出/读写高低电平),但是还无法感知CPU有多少引脚可用。
  4. 调用gpiochip_add_pin_range函数感知所有引脚,之后CPU就知道有多少pin引脚可用。
  5. 准备并使能clk时钟。
  6. 获取并注册中断资源,并创建irq号与硬件号的对应关系。
  7. 最后调用sunxi_pinctrl_debugfs(),创建debugfs接口。

        该文件中的其他部分,基本上是内部函数与debugfs的实现。

复制代码
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// A40i板子GPIO控制函数入口, sunxi_pinctrl初始化函数 int sunxi_pinctrl_init(struct platform_device *pdev, const struct sunxi_pinctrl_desc *desc) { struct device_node *node = pdev->dev.of_node; struct pinctrl_desc *pctrl_desc;// pinctrl_desc用于表示一个pin控制器,可通过[devm_] struct pinctrl_pin_desc *pins; struct sunxi_pinctrl *pctl; struct resource *res; int i, ret, last_pin; struct clk *clk; pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL); if (!pctl) return -ENOMEM; platform_set_drvdata(pdev, pctl); spin_lock_init(&pctl->lock); // 1: platform_device res = platform_get_resource(pdev, IORESOURCE_MEM, 0); pctl->membase = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(pctl->membase)) return PTR_ERR(pctl->membase); pctl->dev = &pdev->dev; pctl->desc = desc; pctl->irq_array = devm_kcalloc(&pdev->dev, IRQ_PER_BANK * pctl->desc->irq_banks, sizeof(*pctl->irq_array), GFP_KERNEL); if (!pctl->irq_array) return -ENOMEM; ret = sunxi_pinctrl_build_state(pdev); if (ret) { dev_err(&pdev->dev, "dt probe failed: %dn", ret); return ret; } pins = devm_kzalloc(&pdev->dev, pctl->desc->npins * sizeof(*pins), GFP_KERNEL); if (!pins) return -ENOMEM; // 2: pctrl_desc // 初始化pctrl_desc结构体 for (i = 0; i < pctl->desc->npins; i++) pins[i] = pctl->desc->pins[i].pin; pctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctrl_desc), GFP_KERNEL); if (!pctrl_desc) return -ENOMEM; if (pctl->desc->pin_base < PL_BASE) pctrl_desc->name = SUNXI_PINCTRL; else pctrl_desc->name = SUNXI_R_PINCTRL; pctrl_desc->owner = THIS_MODULE; pctrl_desc->pins = pins; pctrl_desc->npins = pctl->desc->npins; pctrl_desc->confops = &sunxi_pconf_ops; pctrl_desc->pctlops = &sunxi_pctrl_ops; pctrl_desc->pmxops = &sunxi_pmx_ops; // 注册进pinctrl子系统 pctl->pctl_dev = pinctrl_register(pctrl_desc, &pdev->dev, pctl); if (!pctl->pctl_dev) { dev_err(&pdev->dev, "couldn't register pinctrl drivern"); return -EINVAL; } // 3: gpio_chip // 初始化gpio_chip结构体 pctl->chip = devm_kzalloc(&pdev->dev, sizeof(*pctl->chip), GFP_KERNEL); if (!pctl->chip) { ret = -ENOMEM; goto pinctrl_error; } last_pin = pctl->desc->pins[pctl->desc->npins - 1].pin.number; pctl->chip->owner = THIS_MODULE; pctl->chip->request = sunxi_pinctrl_gpio_request, pctl->chip->free = sunxi_pinctrl_gpio_free, pctl->chip->direction_input = sunxi_pinctrl_gpio_direction_input, pctl->chip->direction_output = sunxi_pinctrl_gpio_direction_output, pctl->chip->get = sunxi_pinctrl_gpio_get, pctl->chip->set = sunxi_pinctrl_gpio_set, pctl->chip->set_debounce = sunxi_pinctrl_gpio_set_debounce, pctl->chip->of_xlate = sunxi_pinctrl_gpio_of_xlate, pctl->chip->to_irq = sunxi_pinctrl_gpio_to_irq, pctl->chip->of_gpio_n_cells = 6, pctl->chip->can_sleep = false, pctl->chip->ngpio = round_up(last_pin + 1, PINS_PER_BANK) - pctl->desc->pin_base; pctl->chip->label = dev_name(&pdev->dev); pctl->chip->dev = &pdev->dev; pctl->chip->base = pctl->desc->pin_base; // 调用gpiochip_add()接口,向Gpiolib注册gpio_chip // 将struct gpio_chip注册给gpio子系统后,内核就有了控制引脚的能力(设置输入输出/读写高低电平) // 但是还无法感知CPU有多少引脚可用. ret = gpiochip_add(pctl->chip); if (ret) goto pinctrl_error; //4: 感知所有引脚 for (i = 0; i < pctl->desc->npins; i++) { const struct sunxi_desc_pin *pin = pctl->desc->pins + i; ret = gpiochip_add_pin_range(pctl->chip, dev_name(&pdev->dev), pin->pin.number - pctl->desc->pin_base, pin->pin.number, 1); if (ret) goto gpiochip_error; } // clk相关 clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { ret = PTR_ERR(clk); goto gpiochip_error; } ret = clk_prepare_enable(clk); if (ret) goto gpiochip_error; // 中断相关 pctl->irq = devm_kcalloc(&pdev->dev, pctl->desc->irq_banks, sizeof(*pctl->irq), GFP_KERNEL); if (!pctl->irq) { ret = -ENOMEM; goto clk_error; } for (i = 0; i < pctl->desc->irq_banks; i++) { pctl->irq[i] = platform_get_irq(pdev, i); if (pctl->irq[i] < 0) { ret = pctl->irq[i]; goto clk_error; } } pctl->domain = irq_domain_add_linear(node, pctl->desc->irq_banks * IRQ_PER_BANK, &irq_domain_simple_ops, NULL); if (!pctl->domain) { dev_err(&pdev->dev, "Couldn't register IRQ domainn"); ret = -ENOMEM; goto clk_error; } for (i = 0; i < (pctl->desc->irq_banks * IRQ_PER_BANK); i++) { int irqno = irq_create_mapping(pctl->domain, i);// 创建irq号与硬件号的对应关系 irq_set_chip_and_handler(irqno, &sunxi_pinctrl_edge_irq_chip, handle_edge_irq); irq_set_chip_data(irqno, pctl); }; for (i = 0; i < pctl->desc->irq_banks; i++) { /* Mask and clear all IRQs before registering a handler */ writel(0, pctl->membase + sunxi_irq_ctrl_reg_from_bank(i)); writel(0xffffffff, pctl->membase + sunxi_irq_status_reg_from_bank(i)); if(pctl->desc->pin_base >= PL_BASE){ ret = devm_request_irq(&pdev->dev, pctl->irq[i], sunxi_pinctrl_irq_handler, IRQF_SHARED | IRQF_NO_SUSPEND, "PIN_GRP", pctl); }else{ ret = devm_request_irq(&pdev->dev, pctl->irq[i], sunxi_pinctrl_irq_handler, IRQF_SHARED, "PIN_GRP", pctl); } if (IS_ERR_VALUE(ret)) { pr_err("unable to request eint irq %dn", pctl->irq[i]); return ret; } } #ifdef CONFIG_DEBUG_FS sunxi_pinctrl_debugfs(); #endif dev_info(&pdev->dev, "initialized sunXi PIO drivern"); return 0; clk_error: clk_disable_unprepare(clk); gpiochip_error: if (gpiochip_remove(pctl->chip)) dev_err(&pdev->dev, "failed to remove gpio chipn"); pinctrl_error: pinctrl_unregister(pctl->pctl_dev); return ret; }

最后

以上就是秀丽金针菇最近收集整理的关于gpio子系统与pinctrl子系统三:pinctrl的全部内容,更多相关gpio子系统与pinctrl子系统三内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(86)

评论列表共有 0 条评论

立即
投稿
返回
顶部