一、Pinctrl子系统使用示例

1.1 要做什么

1.1_i2c_example

  • 查看原理图确定使用哪些引脚:比如pinA、pinB
  • 生成pincontroller设备树信息
    • 选择功能:比如把pinA配置为I2C_SCL、把pinB配置为I2C_SDA
    • 配置:比如把pinA、pinB配置为open drain
  • 使用pincontroller设备树信息:比如在i2c节点里定义”pinctrl-names”、”pinctrl-0”

1.2 pincontroller设备树

生成pincontroller设备树信息,有3中方法:

  • 有些芯片有图形化的工具,可以点点鼠标就可以配置引脚信息,得到pincontroller中的信息
  • 有些芯片,只能看厂家给的设备树文档或者参考设备树的例子
  • 最差的就是需要阅读驱动代码才能构造设备树信息。
1
2
3
4
5
6
7
8
9
10
11
12
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&BOARD_InitPins>;
imx6ull-board {
i2c1_pins: i2c1_pins {
fsl,pins = <
MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x000018B0
MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x000018B0
>;
};
};
};

1.3 client节点使用pincontroller

1
2
3
4
5
6
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&i2c1_pins>;
status = "okay";
};

1.4 使用过程

设备驱动基本不用关心流程。当设备切换状态时,对应的pinctrl就会被调用。

比如在platform_device和platform_driver的probe过程中,流程如下:

1.4_pinctrl_really_probe

二、Pinctrl子系统主要数据结构

2.1 设备树

理想模型:

2.1_pinctrl_dts_modules

实际的例子(imx6ull):

2.1_pinctrl_dts_imx6ull

2.2 pincontroller的数据结构

记住pinctrl的三大作用,有助于理解所涉及的数据结构:

  1. 引脚枚举与命名(Enumerating and naming)
  2. 引脚复用(Multiplexing):比如用作GPIO、I2C或其他功能
  3. 引脚配置(Configuration):比如上拉、下拉、open drain、驱动强度等

2.2.1 pinctrl_desc和pinctrl_dev

pincontroller虽然是一个软件的概念,但是它也用一个结构体来表示:pinctrl_dev。

怎么构造出pinctrl_dev?我们只需要描述它:提供一个pinctrl_desc,然后调用pinctrl_register就可以:

1
struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, struct device *dev, void *driver_data);

怎么使用pinctrl_desc、pinctrl_dev来描述一个pincontroller?这两个结构体定义如下:

2.2.1_pinctrl_desc_and_pinctrl_dev

pinctrl_desc示例如下:

2.2.1_pinctrl_desc_example

2.2.2 作用1:引脚枚举与命名

分为2部分:

  • 描述、获得单个引脚的信息
  • 描述、获得某组引脚的信息

2.2.2_pinctrl_driver_1_pin_and_group

使用结构体pinctrl_pin_desc来描述一个引脚,一个pincontroller有多个引脚:

2.2.2_pinctrl_pin_desc

单个pins 的信息在 imx6ull 是代码写死的,info中的pins是在pincontroller构造时赋值的,后文有分析。

1
2
imx_pinctrl_desc->pins  = info->pins;
imx_pinctrl_desc->npins = info->npins;

2.2.2

使用pinctrl_ops来操作引脚,主要功能有二:

  1. 取出某组的引脚的信息:get_groups_count、get_group_pins
  2. 处理设备树中pin controller中的某个节点:client pinctrl端回调pincontroller端的dt_node_to_map,把device_node转换为一系列的pinctrl_map

2.2.2_pinctrl_ops

2.2.3 作用2:引脚复用

用来把某组引脚(group)复用为某个功能(function)。

2.2.3_pinmux_ops

2.2.4 作用3:引脚配置

用来配置某个引脚(pin)或某组引脚(group)。

2.2.4_pinconf_ops

2.2.5 pinctrl_desc注册得到pinctrl_dev

调用devm_pinctrl_register或pinctrl_register,就可以根据pinctrl_desc构造出pinctrl_dev,并且把pinctrl_dev放入链表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
devm_pinctrl_register
pinctrl_register
struct pinctrl_dev *pctldev;
pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);

pctldev->owner = pctldesc->owner;
pctldev->desc = pctldesc;
pctldev->driver_data = driver_data;

/* check core ops for sanity */
ret = pinctrl_check_ops(pctldev);

/* If we're implementing pinmuxing, check the ops for sanity */
ret = pinmux_check_ops(pctldev);

/* If we're implementing pinconfig, check the ops for sanity */
ret = pinconf_check_ops(pctldev);

/* Register all the pins */
ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);

list_add_tail(&pctldev->node, &pinctrldev_list);

2.3 client的数据结构

在设备树中,使用pinctrl时格式如下:

1
2
3
4
5
6
/* For a client device requiring named states */
device {
pinctrl-names = "active", "idle";
pinctrl-0 = <&state_0_node_a>;
pinctrl-1 = <&state_1_node_a &state_1_node_b>;
};

设备节点要么被转换为platform_device,或者其他结构体(比如i2c_client),但是里面都会有一个device结构体,比如:

2.3.0_pinctrl_in_device

2.3.1 dev_pin_info

每个device结构体里都有一个dev_pin_info结构体,用来保存设备的pinctrl信息:

2.3.1_dev_pin_info

2.3.2 pinctrl

假设芯片上有多个pincontroller,那么这个设备使用哪个pincontroller?这需要通过设备树来确定:

  • 分析设备树,找到pincontroller
  • 对于每个状态,比如default、init,去分析pincontroller中的设备树节点
    • 使用pincontroller的pinctrl_ops.dt_node_to_map来处理设备树的pinctrl节点信息,得到一系列的pinctrl_map
    • 这些pinctrl_map放在pinctrl.dt_maps链表中
    • 每个pinctrl_map都被转换为pinctrl_setting,放在对应的pinctrl_state.settings链表中

2.3.2_pinctrl_maps

2.3.3 pinctrl_map和pinctrl_setting

设备引用pincontroller中的某个节点时,这个节点会被转换为一些列的pinctrl_map:

  • 转换为多少个pinctrl_map,完全由具体的驱动决定
  • 每个pinctrl_map,又被转换为一个pinctrl_setting,里面保存有引脚的mux和config
  • 举例,设备节点里有:pinctrl-0 = <&state_0_node_a>
    • pinctrl-0对应一个状态,会得到一个pinctrl_state
    • state_0_node_a节点被解析为一系列的pinctrl_map
    • 这一系列的pinctrl_map被转换为一系列的pinctrl_setting
    • 这些pinctrl_setting被放入pinctrl_state的settings链表

2.3.3_dt_to_map

2.3.4 使用pinctrl_setting

调用过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
really_probe
pinctrl_bind_pins
pinctrl_select_state
/* 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:
ret = pinmux_enable_setting(setting);
ret = ops->set_mux(...);
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
ret = pinconf_apply_setting(setting);
ret = ops->pin_config_group_set(...);
break;
default:
ret = -EINVAL;
break;
}

2.3.4_apply_setting

三、Pincontroller构造情景分析

3.1 整体执行流程

驱动程序位置:

1
2
drivers\pinctrl\freescale\pinctrl-imx6ul.c
drivers\pinctrl\freescale\pinctrl-imx.c

调用过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
imx6ul_pinctrl_probe
imx_pinctrl_probe(pdev, pinctrl_info);
imx_pinctrl_desc->name = dev_name(&pdev->dev);
imx_pinctrl_desc->pins = info->pins;
imx_pinctrl_desc->npins = info->npins;
imx_pinctrl_desc->pctlops = &imx_pctrl_ops;
imx_pinctrl_desc->pmxops = &imx_pmx_ops;
imx_pinctrl_desc->confops = &imx_pinconf_ops;
imx_pinctrl_desc->owner = THIS_MODULE;

ret = imx_pinctrl_probe_dt(pdev, info);

ipctl->pctl = devm_pinctrl_register(&pdev->dev, imx_pinctrl_desc, ipctl);

3.2 构造pinctrl_desc

pincontroller的最终目的:提供一个pinctrl_desc,然后调用pinctrl_register注册。所以我们要完善pinctrl_desc。

  1. 引脚枚举与命名(单个引脚):info的pins在imx6ull是代码写死的:
1
2
3
4
//[root@qing:/sys/kernel/debug/pinctrl/20e0000.iomuxc]# cat pins
imx_pinctrl_probe(struct platform_device *pdev, struct imx_pinctrl_soc_info *info)
imx_pinctrl_desc->pins = info->pins;
imx_pinctrl_desc->npins = info->npins;

3.2.0

  1. 引脚枚举与命名(某组引脚):某组功能中有哪些引脚?这要解析设备树,由imx_pinctrl_probe_dt函数解析设备树后放在imx_pinctrl_soc_info结构体中作为私有数据传入注册。具体解析流程省略,调用如下:
1
2
//[root@qing:/sys/kernel/debug/pinctrl/20e0000.iomuxc]# cat pingroups
static int imx_pinctrl_probe_dt(struct platform_device *pdev, struct imx_pinctrl_soc_info *info)

3.2.1

imx_pinctrl_soc_info:

3.2.2

imx_pin_group:存放各组信息

3.2.3

imx_pin:存放各组引脚信息,reg addr,reg val

3.2.4

  1. 引脚复用、引脚配置、client端回调获取引脚组信息和设备树引脚节点转换成map
    1
    2
    3
    imx_pinctrl_desc->pctlops = &imx_pctrl_ops; //client端回调
    imx_pinctrl_desc->pmxops = &imx_pmx_ops; //引脚复用
    imx_pinctrl_desc->confops = &imx_pinconf_ops; //引脚配置

整体框图如下:

3.2.5

四、client pinctrl构造情景分析

client pinctrl的整体流程:

  1. 设备树转换为pinctrl_map;使用pincontroller的pinctrl_ops.dt_node_to_map来处理设备pinctrl节点信息,得到一系列的pinctrl_map。
  2. pinctrl_map转换为pinctrl_setting;每个pinctrl_map被转换为pinctrl_setting,里面保存有引脚的mux和config。
  3. 切换state;在platform_device和platform_driver的设备probe过程前,也即really_probe中使用pincontroller相关ops函数,将引脚的pinctrl_setting都进行设置。

第三点切换state的调用流程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
really_probe
pinctrl_bind_pins
pinctrl_select_state
/* 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:
ret = pinmux_enable_setting(setting);
ret = ops->set_mux(...);
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
ret = pinconf_apply_setting(setting);
ret = ops->pin_config_group_set(...);
break;
}

总结如下图:

4.0

五、编写虚拟的pinctrl子系统

5.1 整体步骤

  • pin controller:
    • 创建设备树节点
    • 编写驱动程序
  • 测试:
    • 创建client设备树节点
    • 编写驱动程序

5.2 硬件功能

假设这个虚拟的pin controller有4个引脚:

  • pin0,1,2,3都可以配置为GPIO功能
  • pin0,1还可以配置为I2C功能
  • pin2,3还可以配置为UART功能

1.1_i2c_example

5.3 编写设备树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
virtual_pincontroller {
compatible = "100ask,virtual_pinctrl";
i2cgrp: i2cgrp {
functions = "i2c", "i2c";
groups = "pin0", "pin1";
configs = <0x11223344 0x55667788>;
};
};

virtual_i2c {
compatible = "100ask,virtual_i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2cgrp>;
};

5.4 编写pinctrl驱动

核心:pinctrl_desc

  • 分配pinctrl_desc结构体
  • 设置pinctrl_desc结构体
  • 注册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
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
#include <linux/module.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/slab.h>
#include <linux/regmap.h>

#include "core.h"

static struct pinctrl_dev *g_pinctrl_dev;

//把它作为pins的config register,config val是由virtual_dt_node_to_map解析设备树得来
static unsigned long g_configs[4];

/******************************************* 引脚枚举与命名(单个引脚)******************************************************************/

// 单个pins的信息直接在代码中定义
static const struct pinctrl_pin_desc pins[] = {
{0, "pin0", NULL},
{1, "pin1", NULL},
{2, "pin2", NULL},
{3, "pin3", NULL},
};

/******************************************* 引脚枚举与命名(各个function功能中有哪些引脚)******************************************************************/

struct virtual_functions_desc {
const char *func_name; // 这个功能的名字
const char **groups; // 支持这个功能的pins是哪几组(我们现在每个pin都当作一组)
int num_groups; // 这个功能有多少个组,也就是有多少个pins(我们现在每个pin都当作一组)
};

// 各个function的group pins本应在设备树中定义,然后解析出来,我们此处也直接在代码中定义
static const char *func0_grps[] = {"pin0", "pin1", "pin2", "pin3"};
static const char *func1_grps[] = {"pin0", "pin1"};
static const char *func2_grps[] = {"pin2", "pin3"};

static struct virtual_functions_desc g_funcs_des[] = {
{"gpio", func0_grps, 4},
{"i2c", func1_grps, 2},
{"uart", func2_grps, 2},
};

/***************************************** pctlops,client端回调获取组的信息和设备树引脚节点转换成map ****************************************/

// 模仿stm32mp157的每个pin都当作一个group
static int virtual_get_groups_count(struct pinctrl_dev *pctldev)
{
return pctldev->desc->npins;
}

static const char *virtual_get_group_name(struct pinctrl_dev *pctldev, unsigned selector)
{
return pctldev->desc->pins[selector].name;
}

static int virtual_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
const unsigned **pins,
unsigned *npins)
{
if (selector >= pctldev->desc->npins)
return -EINVAL;

*pins = &pctldev->desc->pins[selector].number;
*npins = 1;

return 0;
}

static void virtual_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
unsigned offset)
{
seq_printf(s, "%s", dev_name(pctldev->dev));
}

/*
virtual_pincontroller {
compatible = "100ask,virtual_pinctrl";
i2cgrp {
functions = "i2c", "i2c";
groups = "pin0", "pin1";
configs = <0x11223344 0x55667788>;
};
};

virtual_i2c {
compatible = "100ask,virtual_i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2cgrp>;
};

one pin ==> two pinctrl_map (one for mux, one for config)
*/

static int virtual_dt_node_to_map(struct pinctrl_dev *pctldev,
struct device_node *np,
struct pinctrl_map **map, unsigned *num_maps)
{
int i;
int num_pins = 0;
const char *pin;
const char *function;
unsigned int config;
struct pinctrl_map *new_map;
unsigned long *configs;

/* 1. 确定pin个数/分配pinctrl_map */
while (1)
{
if (of_property_read_string_index(np, "groups", num_pins, &pin) == 0)
num_pins++;
else
break;
}
printk("num_pins = %d\n", num_pins);

new_map = kmalloc(sizeof(struct pinctrl_map) * num_pins * 2, GFP_KERNEL);

for (i = 0; i < num_pins; i++)
{
/* 2. get pin/function/config */
of_property_read_string_index(np, "groups", i, &pin);
of_property_read_string_index(np, "functions", i, &function);
of_property_read_u32_index(np, "configs", i, &config);
printk("num_pins[%d]->groups = %s, num_pins[%d]->function = %s\n", i, pin, i, function);

/* 3. 存入pinctrl_map */
configs = kmalloc(sizeof(*configs), GFP_KERNEL);

new_map[i*2].type = PIN_MAP_TYPE_MUX_GROUP;
new_map[i*2].data.mux.function = function;
new_map[i*2].data.mux.group = pin;

new_map[i*2+1].type = PIN_MAP_TYPE_CONFIGS_PIN;
new_map[i*2+1].data.configs.group_or_pin = pin;
new_map[i*2+1].data.configs.configs = configs;
configs[0] = config;
new_map[i*2+1].data.configs.num_configs = 1;

}

*map = new_map;
*num_maps = num_pins * 2;

return 0;
}

static void virtual_dt_free_map(struct pinctrl_dev *pctldev,
struct pinctrl_map *map, unsigned num_maps)
{
while (num_maps--)
{
if (map->type == PIN_MAP_TYPE_CONFIGS_PIN)
kfree(map->data.configs.configs);

kfree(map);
map++;
}
}

static const struct pinctrl_ops virtual_pctrl_ops = {
.get_groups_count = virtual_get_groups_count,
.get_group_name = virtual_get_group_name,
.get_group_pins = virtual_get_group_pins,
.pin_dbg_show = virtual_pin_dbg_show,
.dt_node_to_map = virtual_dt_node_to_map,
.dt_free_map = virtual_dt_free_map,

};

/************************************************** pmxops 引脚复用 ****************************************************************/

static int virtual_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
{
return ARRAY_SIZE(g_funcs_des);
}

static const char *virtual_pmx_get_func_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
return g_funcs_des[selector].func_name;
}

static int virtual_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector,
const char * const **groups,
unsigned * const num_groups)
{
*groups = g_funcs_des[selector].groups;
*num_groups = g_funcs_des[selector].num_groups;

return 0;
}

static int virtual_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,
unsigned group)
{
//真实情况这里是去操作寄存器设置复用,我们这做个打印模拟即可
printk("set %s as %s\n", pctldev->desc->pins[group].name, g_funcs_des[selector].func_name);
return 0;
}

static const struct pinmux_ops virtual_pmx_ops = {
.get_functions_count = virtual_pmx_get_funcs_count,
.get_function_name = virtual_pmx_get_func_name,
.get_function_groups = virtual_pmx_get_groups,
.set_mux = virtual_pmx_set,
};

/***************************************** pinconf_ops 引脚配置 ****************************************/

static int virtual_pinconf_get(struct pinctrl_dev *pctldev,
unsigned pin_id, unsigned long *config)
{
*config = g_configs[pin_id];
return 0;
}

static int virtual_pinconf_set(struct pinctrl_dev *pctldev,
unsigned pin_id, unsigned long *configs,
unsigned num_configs)
{
if (num_configs != 1)
return -EINVAL;

g_configs[pin_id] = *configs;

printk("config %s as 0x%lx\n", pctldev->desc->pins[pin_id].name, *configs);

return 0;
}

static void virtual_pinconf_dbg_show(struct pinctrl_dev *pctldev,
struct seq_file *s, unsigned pin_id)
{
seq_printf(s, "0x%lx", g_configs[pin_id]);
}

//我们的一个pin就是一组,所以使用上面一样的函数即可
static void virtual_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
struct seq_file *s, unsigned pin_id)
{
seq_printf(s, "0x%lx", g_configs[pin_id]);
}

static const struct pinconf_ops virtual_pinconf_ops = {
.pin_config_get = virtual_pinconf_get,
.pin_config_set = virtual_pinconf_set,
.pin_config_dbg_show = virtual_pinconf_dbg_show,
.pin_config_group_dbg_show = virtual_pinconf_group_dbg_show,
};

/******************************************************************************************************************/

static int virtual_pinctrl_probe(struct platform_device *pdev)
{
struct pinctrl_desc *pictrl;

printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

/* a. 分配pinctrl_desc */
pictrl = devm_kzalloc(&pdev->dev, sizeof(*pictrl), GFP_KERNEL);

/* b. 设置pinctrl_desc */
pictrl->name = dev_name(&pdev->dev);
pictrl->owner = THIS_MODULE;

/* b.1 pins and group */
pictrl->pins = pins;
pictrl->npins = ARRAY_SIZE(pins);

pictrl->pctlops = &virtual_pctrl_ops;

/* b.2 pin mux */
pictrl->pmxops = &virtual_pmx_ops;

/* b.3 pin config */
pictrl->confops = &virtual_pinconf_ops;

/* c. 注册pinctrl_desc */
g_pinctrl_dev = devm_pinctrl_register(&pdev->dev, pictrl, NULL);

return 0;
}
static int virtual_pinctrl_remove(struct platform_device *pdev)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}

static const struct of_device_id virtual_pinctrl_of_match[] = {
{ .compatible = "100ask,virtual_pinctrl", },
{ },
};

static struct platform_driver virtual_pinctrl_driver = {
.probe = virtual_pinctrl_probe,
.remove = virtual_pinctrl_remove,
.driver = {
.name = "100ask_virtual_pinctrl",
.of_match_table = of_match_ptr(virtual_pinctrl_of_match),
}
};

/* 1. 入口函数 */
static int __init virtual_pinctrl_init(void)
{
/* 1.1 注册一个platform_driver */
return platform_driver_register(&virtual_pinctrl_driver);
}

/* 2. 出口函数 */
static void __exit virtual_pinctrl_exit(void)
{
/* 2.1 注销platform_driver */
platform_driver_unregister(&virtual_pinctrl_driver);
}

module_init(virtual_pinctrl_init);
module_exit(virtual_pinctrl_exit);

MODULE_LICENSE("GPL");

5.5 编写client驱动

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
#include <linux/module.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/slab.h>
#include <linux/regmap.h>

static const struct of_device_id virtual_client_of_match[] = {
{ .compatible = "100ask,virtual_i2c", },
{ },
};

static int virtual_client_probe(struct platform_device *pdev)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}

static int virtual_client_remove(struct platform_device *pdev)
{

printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}

static struct platform_driver virtual_client_driver = {
.probe = virtual_client_probe,
.remove = virtual_client_remove,
.driver = {
.name = "100ask_virtual_client",
.of_match_table = of_match_ptr(virtual_client_of_match),
}
};

/* 1. 入口函数 */
static int __init virtual_client_init(void)
{
/* 1.1 注册一个platform_driver */
return platform_driver_register(&virtual_client_driver);
}

/* 2. 出口函数 */
static void __exit virtual_client_exit(void)
{
/* 2.1 注销platform_driver */
platform_driver_unregister(&virtual_client_driver);
}

module_init(virtual_client_init);
module_exit(virtual_client_exit);

MODULE_LICENSE("GPL");

5.6 使用方法

依次装载驱动

  1. virtual_pinctrl_driver.ko 注册了我们的虚拟的pinctrl控制器,里面有我们的虚拟pinctrl的操作函数。
  2. virtual_pinctrl_client.ko 这个client的pinctrl控制器在设备树中指定为了我们的虚拟的pinctrl控制器,在执行他的probe函数之前,系统会先执行really_probe函数,会用这个驱动对应的pinctrl控制器的操作函数设置成设备树中指定的配置。

开发板的/sys/kernel/debug/pinctrl/目录下,每一个pin controller都有一个目录,比如virtual_pincontroller。里面有很多文件,作用如下:

Pinctrl的虚拟文件 作用 解释
pins 单个引脚信息
pingroups 引脚的组信息
pinmux-pins 单个引脚的复用信息
pinmux-functions function下的group(支持该function的group)
pinconf-pins 单个引脚的配置
pinconf-groups 引脚组的配置
pinconf-config 可以通过写它修改指定设备、指定状态下、指定(组)引脚的config值
  • 单个引脚信息
1
2
3
4
5
6
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pins
registered pins: 4
pin 0 (pin0) virtual_pincontroller
pin 1 (pin1) virtual_pincontroller
pin 2 (pin2) virtual_pincontroller
pin 3 (pin3) virtual_pincontroller
  • 引脚的组信息(模仿stm32mp157的每个pin都当作一个group)
1
2
3
4
5
6
7
8
9
10
11
12
13
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pingroups
registered pin groups:
group: pin0
pin 0 (pin0)

group: pin1
pin 1 (pin1)

group: pin2
pin 2 (pin2)

group: pin3
pin 3 (pin3)
  • 单个引脚的复用信息
1
2
3
4
5
6
7
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinmux-pins
Pinmux settings per pin
Format: pin (name): mux_owner gpio_owner hog?
pin 0 (pin0): virtual_i2c (GPIO UNCLAIMED) function i2c group pin0
pin 1 (pin1): virtual_i2c (GPIO UNCLAIMED) function i2c group pin1
pin 2 (pin2): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 3 (pin3): (MUX UNCLAIMED) (GPIO UNCLAIMED)
  • function下的group(支持该function的group,模仿stm32mp157的每个pin都当作一个group)
1
2
3
4
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinmux-functions
function: gpio, groups = [ pin0 pin1 pin2 pin3 ]
function: i2c, groups = [ pin0 pin1 ]
function: uart, groups = [ pin2 pin3 ]
  • 单个引脚的配置
1
2
3
4
5
6
7
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinconf-pins
Pin config settings per pin
Format: pin (name): configs
pin 0 (pin0): 0x11223344
pin 1 (pin1): 0x55667788
pin 2 (pin2): 0x0
pin 3 (pin3): 0x0
  • 引脚组的配置(模仿stm32mp157的每个pin都当作一个group)
1
2
3
4
5
6
7
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinconf-groups
Pin config settings per pin group
Format: group (name): configs
0 (pin0): 0x11223344
1 (pin1): 0x55667788
2 (pin2): 0x0
3 (pin3): 0x0

我们可以在pinctrl驱动的ops组中加入打印信息,这样就可以确认这些cat操作分别对应的ops函数。

六、相关疑惑与调试

6.0