07、引脚的复用关系的设置pinctrl_bind_pins
问题:引脚的复用关系是在什么时候被设置的 ?
一、Overview
在Linux设备驱动模型中,当设备与驱动匹配成功后,系统会进入设备初始化流程。具体来说:
- 在驱动探测设备(driver_probe_device)的过程中,系统会执行really_probe函数;
- 在调用总线(bus)的probe接口之前,系统会先执行pinctrl_bind_pins操作;
- 这个操作会把设备与引脚控制子系统(pinctrl)绑定,并完成引脚功能的切换和配置。
简单来说就是:设备和驱动匹配成功后,系统在正式初始化设备前,会先通过pinctrl子系统完成设备所需引脚的配置工作。
二、really_probe
在驱动的probe函数执行前,系统已经通过pinctrl引脚控制子系统完成了引脚功能的切换配置。根据设备模型的学习,驱动的probe函数实际是在内核源码的drivers/base/dd.c文件中被调用的,具体位置是在really_probe函数里处理驱动加载时触发的。 具体内容如下所示:
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;
...
re_probe:
dev->driver = drv;
/* If using pinctrl, bind pins now before probing */
ret = pinctrl_bind_pins(dev); // 绑定设备的引脚
if (ret)
goto pinctrl_bind_failed;
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
三、pinctrl_bind_pins
文件目录:kernel\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)) {
/* 不提供此状态是完全合法的 */
dev_dbg(dev, "no init pinctrl state\n");
// 选择默认的 pinctrl 状态
ret = pinctrl_select_state(dev->pins->p,
dev->pins->default_state);
} else {
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
/*
* 如果启用了电源管理, 我们还会寻找可选的睡眠和空闲的引脚状态, 其语义在
* sleep and idle pin states, with semantics as defined in
* <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))
/* 不提供此状态是完全合法的 */
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))
/* 不提供此状态是完全合法的 */
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.
*/
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 */
return 0;
}
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
- 检查设备是否已复用某个节点。如果已经复用,直接返回0(表示绑定成功)。
- 若未复用节点,则为设备的引脚信息分配内存。使用系统函数动态申请一块与设备引脚结构体等大的内存空间。若内存分配失败,返回-ENOMEM(代表内存不足)。
- 获取设备的引脚控制器(pinctrl)句柄。通过系统函数尝试获取设备的引脚控制信息。若获取失败,会记录错误日志并返回对应的错误码。
3.1、dev_pin_info
struct device {
struct device *parent; // 指向父设备的指针
struct device_private *p; // 私有数据指针
struct kobject kobj; // 内核对象, 用于设备的管理
const char *init_name; // 设备的初始名称
const struct device_type *type; // 设备类型
struct mutex mutex; // 互斥锁, 用于同步对驱动程序的调用
struct bus_type *bus; // 设备所在的总线类型
struct device_driver *driver; // 分配该设备的驱动程序
void *platform_data; // 平台特定的数据, 设备核心不会修改它
void *driver_data; // 驱动程序的数据, 使用 dev_set/get_drvdata 来设置和获取
....
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
struct irq_domain *msi_domain; // 设备的通用 MSI IRQ 域
#endif
#ifdef CONFIG_PINCTRL
struct dev_pin_info *pins; // 设备的引脚信息
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
如果定义了 CONFIG_PINCTRL 那记录设备引脚信息的结构体 struct dev_pin_info *pins 就会被使能, 到这里本小节要讲解的重点终于出现了, 接下来跳转到 struct dev_pin_info 结构体定义, 该结构体的具体内容如下所示:
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
};
2
3
4
5
6
7
8
9
以下是结构体dev_pin_info成员变量的解释:
- struct pinctrl *p: 设备的引脚控制器指针,指向控制设备引脚的管理对象,用来设置和管理引脚。
- struct pinctrl_state *default_state: 默认引脚配置指针,指向设备正常工作时的默认引脚设置。
- struct pinctrl_state *init_state: 初始化引脚配置指针,指向设备启动时需要的初始引脚设置。
- struct pinctrl_state *sleep_state: 睡眠状态引脚配置指针(仅支持电源管理时有效),指向设备进入休眠时的引脚设置。
- struct pinctrl_state *idle_state: 空闲状态引脚配置指针(仅支持电源管理时有效),指向设备空闲时的引脚设置。
3.2、获取设备的 pinctrl 句柄
在 pinctrl_bind_pins 函数中使用 devm_pinctrl_get 函数获取设备的 pinctrl 句柄, 该函数定义早内核源码目录下的“drivers/pinctrl/core.c” 文件中具体内容如下所示:
/**
* struct 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);
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
使用 pinctrl_get 函数获取引脚控制器句柄, 我们继续跳转到该函数, 该函数的具体内容如下所示:
/**
* 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);
/*
* 查看是否有其他组件(如设备核心) 已经获取了此设备的引脚控制器句柄。
* 在这种情况下, 返回对该句柄的另一个指针。
*/
p = find_pinctrl(dev);
if (p != NULL) {
dev_dbg(dev, "obtain a copy of previously claimed pinctrl\n");
kref_get(&p->users);
return p;
}
// 创建并返回设备的引脚控制器句柄
return create_pinctrl(dev);
}
EXPORT_SYMBOL_GPL(pinctrl_get);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
返回值中使用的 cr eate_pinctrl 函数, 该函数会创建并返回设设备的引脚控制器句柄
目录: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;
/*
* 为每个映射创建状态 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);
/* 遍历引脚控制映射以定位正确的映射 */
for_each_maps(maps_node, i, map) {
/* 映射必须适用于此设备 */
if (strcmp(map->dev_name, devname))
continue;
/*
* 如果 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.
*/
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);
/* 将引脚控制句柄添加到全局列表 */
mutex_lock(&pinctrl_list_mutex);
list_add_tail(&p->node, &pinctrl_list);
mutex_unlock(&pinctrl_list_mutex);
return p;
}
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
其中 struct pinctrl 类型的变量 p 用于表示创建的引脚控制器句柄, struct pinctrl 结构体用于表示引脚控制器。 引脚控制器是硬件系统中管理和控制引脚(GPIO) 的组件, 它负责配置引脚的功能、 电气属性等, 该结构体定义在内核源码目录下的“drivers/pinctrl/core.h” 文件中, 具体内容如下所示:
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; // 引脚控制器的引用计数, 用于跟踪引脚控制器的引用数量
};
2
3
4
5
6
7
8
struct pinctrl_maps 类型的变量 maps_node 用于遍历引脚控制映射, 引脚控制器映射描述了不同引脚控制器的功能和配置与实际硬件引脚之间的对应关系, 该结构体定义在内核源码目录下的“drivers/pinctrl/core.h” 文件中, 具体内容如下所示:
struct pinctrl_maps {
struct list_head node; // 引脚控制器映射链表节点, 用于将该映射添加到全局列表
const struct pinctrl_map *maps; // 指向引脚控制器映射数组的指针
unsigned num_maps; // 引脚控制器映射数组中的映射数量
};
2
3
4
5
3.3、pinctrl_dt_to_map
调用 pinctrl_dt_to_map 函数将设备树中定义的引脚映射信息转换为 stru ct pinctrl_map 结构, 并将其添加到 p->dt_maps 链表中。 如果转换过程中出现错误(返回值小于 0) , 则释放之前分配的内存, 并返回对应的错误码
文件:kernel\drivers\pinctrl\devicetree.c
int pinctrl_dt_to_map(struct pinctrl *p)
{
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 启用, 且 p->dev 不是从设备树实例化而来 */
if (!np) {
if (of_have_populated_dt())
dev_dbg(p->dev,
"no of_node; not parsing pinctrl DT\n");
return 0;
}
/* 节点内部存储属性名称的指针 */
of_node_get(np);
/* 对于每个定义的状态 ID */
for (state = 0; ; state++) {
/* 获取 pinctrl-* 属性 */
propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);
prop = of_find_property(np, propname, &size);
kfree(propname);
if (!prop) {
if (!state) {
ret = -EINVAL;
goto err;
}
break;
}
list = prop->value;
size /= sizeof(*list);
/* 判断 pinctrl-names 属性是否命名了该状态 */
ret = of_property_read_string_index(np, "pinctrl-names",
state, &statename);
/*
* If not, statename is just the integer state ID. But rather
* than dynamically allocate it and have to free it later,
* just point part way into the property name for the string.
*/
if (ret < 0) {
/* strlen("pinctrl-") == 8 */
statename = prop->name + 8;
}
/* 对于其中的每个引用的引脚配置节点 */
for (config = 0; config < size; config++) {
phandle = be32_to_cpup(list++);
/* 查找引脚配置节点 */
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;
}
/* 解析节点 */
ret = dt_to_map_one_config(p, pctldev, statename,
np_config);
of_node_put(np_config);
if (ret < 0)
goto err;
}
/* 如果在设备树中没有条目, 则生成一个虚拟状态表条目 */
if (!size) {
ret = dt_remember_dummy_state(p, statename);
if (ret < 0)
goto err;
}
}
return 0;
err:
pinctrl_dt_free_maps(p);
return ret;
}
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
dt_to_map_one_config 函数需要特别注意, 该函数会从设备树节点中解析出引脚控制器的映射表, 并将其存储起来, 该函数的具体内容如下所示: