字符设备驱动-pinctrl子系统

1 pinctrl和gpio subsystem引入#

上一节引入gpio了子系统:

linux内核驱动-gpio子系统 - fuzidage - 博客园 (cnblogs.com)

字符设备驱动-gpio子系统 | Hexo (fuzidage.github.io)

Linux 驱动讲究驱动分离与分层,pinctrl 和 gpio 子系统就是驱动分离与分层思想下的产物。
pinctrl顾名思义就是引脚控制,用来配置比如引脚mux复用信息,引脚电器属性(比如上/下拉、速度、驱动能力等)信息。
gpio顾名思义就是控制gpio的输入输出,以及高低电平。不过,大多数的芯片并没有单独的IOMUX模块,引脚的复用、配置等,而是在GPIO模块内部实现的。
image

2 pinctrl子系统原理介绍#

2.1 pinctrl子系统#

2.1.1 pinctrl子系统软件架构#

pinctrl子系统源码路径是linux/drivers/pinctrl:
image

image

1
2
3
4
其他驱动层(client):具体到使用系统pin资源的设备驱动程序
pinctrl核心层(core):内核抽象出来,向下为SoC pin controler drvier提供底层通信接口的能力,
向上为其他驱动提供了控制pin的能力,比如pin复用、配置引脚的电气特性,同时也为GPIO子系统提供pin操作。
pin控制器驱动层(pinctrl-driver): 提供了操作pin的具体方法。

pinctrl-driver主要为pinctrl-core提供pin的操作能力。把系统所有的pin以及对于pin的控制接口实例化成pinctrl_desc,并将pinctrl_desc注册到pinctrl-core中去。

2.1.2 Pinctrl重要概念#

Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt有介绍重要相关概念:

1. pin controller:
芯片手册里你找不到 pin controller,它是一个软件上的概念。对应 IOMUX──用来复用引脚,还可以配置引脚(比如上下拉电阻等)。
2. client device:
“客户设备”,客户是指Pinctrl系统的客户,即使用Pinctrl系统的设备,使用引脚的设备
image

    1. pin state
      对于一个"client device",如UART设备,它有多个“状态”default、sleep等,那么对应的引脚也有这些状态。

    比如,默认状态下,UART设备正常工作,那么所用的引脚就要复用为UART功能;
    休眠状态下,为了省电,可以把这些引脚复用为GPIO功能;或者直接把它们配置输出高电平。

    上图pinctrl-names定义2种状态:default,sleep
    第0种状态用到的引脚在pinctrl-0中定义,它是state_0_node_a,位于pincontroller节点中。
    第1种状态用到的引脚在Pinctrl-1中定义,它是state_1_node_a,位于pincontroller节点中。

    当UART设备处于default状态时,pinctrl子系统会自动根据上述信息将所用引脚复用为uart0功能。
    当UART设备处于sleep状态时,pinctrl子系统会自动根据上述信息将所用引脚配置为高电平。

    1. groups和function
      一个设备会用到一个或多个引脚,这些引脚可以归纳为一组(group)
      这些引脚可以复用为某个功能:function,如I2C功能,SPI功能,GPIO功能等。当然:一个设备可以用到多组引脚,比如A1、A2两组引脚,A1组复用为F1功能,A2组复用为F2功能:
      image
    1. Generic pin multiplexing node和Generic pin configuration node
      下图左边pin controller节点中,有子节点或孙节点,它们是给client device使用的。
      可用来描述复用信息:哪组(group)引脚复用为哪个功能(function)
      配置信息:哪组(group)引脚配置为哪个设置功能(setting),如上拉、下拉等;

image

2.1.3 pinctrl 子系统注册流程#

以imx6ull为例,drivers/pinctrl/freescale/pinctrl-imx6ul.c
image

驱动的入口是 arch_initcall 中声明的函数,类似于我们经常写的 module_init是动态ko加载,arch_initcall是编译进入内核镜像。可以看到是利用platform_device框架来写的。
image
根据 compatible设备树的compatible 字段进行匹配,匹配成功执行 probe 函数,调用imx_pinctrl_probe_dt解析dts中的pinctrl描述信息。调用pinctrl_register或者devm_pinctrl_register注册pinctl子系统。

3 soc pinctrl主要结构体(controller)#

3.0 数据结构关系图#

总框图记录数据结构之间的关联。
1876680-20240315174913445-1187095793

1
2
3
4
5
6
7
8
9
10
//pincontroller数据结构
drivers\pinctrl\core.h
include\linux\pinctrl\pinctrl.h
include\linux\pinctrl\pinmux.h
include\linux\pinctrl\pinconf.h
//client数据结构
drivers\pinctrl\core.h
include\linux\pinctrl\devinfo.h
include\linux\device.h
include\linux\pinctrl\machine.h

3.1 pinctrl_dev#

pinctrl_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
struct pinctrl_dev {
struct list_head node;
struct pinctrl_desc *desc; //提供具体操作方法和抽象包括pincrtl_ops函数,pinmux操作函数和pin的描述等
struct radix_tree_root pin_desc_tree;
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
struct radix_tree_root pin_group_tree;//存储group的描述信息
unsigned int num_groups;
#endif
#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS
struct radix_tree_root pin_function_tree;//存储function的描述信息
unsigned int num_functions;
#endif
struct list_head gpio_ranges;//链接gpio range 2 pin range相关的信息
struct device *dev;
struct module *owner;
void *driver_data;
struct pinctrl *p;//每个pinctrl都描述着一组gpio的复用和状态配置,
//如果这个pinctrl_dev是一个通过iic连接的,那么使用这个pinctrl_dev
//就需要配置其占用的gpio为iic功能,那么就要用这个来描述
struct pinctrl_state *hog_default;
struct pinctrl_state *hog_sleep;
struct mutex mutex;
#ifdef CONFIG_DEBUG_FS
struct dentry *device_root;
#endif
}

/driver/pinctrl/core.c中注册pinctrl时将soc中所有的pinctrl_dev挂载到pinctrl_dev_list链表中方便查询使用。

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

3.2 pinctrl_desc#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct pinctrl_desc {
const char *name;
const struct pinctrl_pin_desc *pins; //描述一个pin控制器的引脚,
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
};

image
image

三个ops:

  1. group操作接口对应数据结构struct pinctrl_ops,包含get_groups_countget_group_nameget_group_pins等接口;

  2. mux操作接口对应的数据结构为struct pinmux_ops,包含pin requestfreeset_muxget_functions_countget_function_groups等;

  3. function操作接口对应的数据结构为struct pinconf_ops,包含pin_config_setpin_config_getpin_config_group_getpin_config_group_set等接口;

3.2.1 pinctrl_pin_desc#

1
2
3
4
5
struct pinctrl_pin_desc {
unsigned number;//引脚序号
const char *name;//引脚名
void *drv_data;
};

pinctrl_pin_desc来描述一个引脚.

3.2.2 pin_desc#

1
2
3
4
5
6
7
8
9
10
11
12
13
struct pin_desc {
struct pinctrl_dev *pctldev;
const char *name;
bool dynamic_name;
void *drv_data;
/* These fields only added when supporting pinmux drivers */
#ifdef CONFIG_PINMUX
unsigned mux_usecount;
const char *mux_owner;
const struct pinctrl_setting_mux *mux_setting;
const char *gpio_owner;
#endif
};

记录引脚的使用计数、引脚当前所属的function、group信息(该数据结构主要是pinctrl子系统用于判断一个引脚是否被多次配置不同的复用情况使用(pin_request、pin_free

3.2.3 三个ops#

3.2.3.1 pinctrl_ops#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct pinctrl_ops {
//获取组数
int (*get_groups_count) ();
//获取组名
const char *(*get_group_name) ();
//获取某组的引脚
int (*get_group_pins) ();
//用以debugfs提供每个引脚的信息
void (*pin_dbg_show) ();
//解析设备树节点,转换成pinctrl_map,重点
int (*dt_node_to_map) ();
//释放map
void (*dt_free_map) ();
};
  • 来取出某组的引脚:get_groups_count、get_group_pins
  • 处理设备树中pin controller中的某个节点创建映射:dt_node_to_map,把device_node转换为一系列的pinctrl_map

3.2.3.2 pinmux_ops#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
bool is_generic;
#endif
//获取单个引脚配置
int (*pin_config_get) ();
//配置单个引脚
int (*pin_config_set) ();
//获取某组引脚配置
int (*pin_config_group_get) ();
//配置某组引脚
int (*pin_config_group_set) ();
//用以debugfs修改pin配置信息
int (*pin_config_dbg_parse_modify) ();
//用以debugfs提供pin配置信息
void (*pin_config_dbg_show) ();
//用以debugfs提供group配置信息
void (*pin_config_group_dbg_show) ();
//用以debugfs解析并显示pin的配置
void (*pin_config_config_dbg_show) (s);
};

3.2.3.3 pinconf_ops#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
bool is_generic;
#endif
//获取单个引脚配置
int (*pin_config_get) ();
//配置单个引脚
int (*pin_config_set) ();
//获取某组引脚配置
int (*pin_config_group_get) ();
//配置某组引脚
int (*pin_config_group_set) ();
//用以debugfs修改pin配置信息
int (*pin_config_dbg_parse_modify) ();
//用以debugfs提供pin配置信息
void (*pin_config_dbg_show) ();
//用以debugfs提供group配置信息
void (*pin_config_group_dbg_show) ();
//用以debugfs解析并显示pin的配置
void (*pin_config_config_dbg_show) (s);
};

4 board pinctrl相关结构体(使用者client)#

4.0 数据结构关系#

前面Soc pin描述相关的数据结构,已经搭建了该soc所支持的pinfunctiongroup以及相关操作接口等信息;而board pin描述相关的数据结构则描述一块board所使用到的function及相关的group
1876680-20240316150501629-222504717

4.1 pinctrl_map#

1
2
3
4
5
6
7
8
9
10
11
12
struct pinctrl_map {
const char *dev_name;//设备名称
const char *name;//该pinctrl_map对应的状态(default、idle、sleep等
enum pinctrl_map_type type;//pinctrl_map的类型,包括mux group、config group、config pins等
const char *ctrl_dev_name;//pinctrl device的名称,根据该名称可获取到soc pin controller对应的pinctrl device
union {
struct pinctrl_map_mux mux;//引脚复用的内容,该数据结构中包含function名称、group名称,
//通过function、group就可以确定进行引脚复用的引脚id与引脚复用值等信息;
struct pinctrl_map_configs configs;//引脚配置相关的内容,包括group或者pin的名称,
//以及该group、pin的配置信息,实现引脚配置操作。
} data;
};

该数据结构可以理解为一个function类型:

4.2 dev_pin_info#

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

1
2
3
4
5
6
7
8
9
10
struct dev_pin_info {    //该device对应引脚的配置与复用信息
struct pinctrl *p;//该设备支持引脚配置类型(包括支持的pinctrl状态,
//每一种pinctrl状态下的引脚复用以及引脚配置信息)
struct pinctrl_state *default_state;
struct pinctrl_state *init_state;
#ifdef CONFIG_PM
struct pinctrl_state *sleep_state;
struct pinctrl_state *idle_state;
#endif
};

4.2.1 pinctrl#

一个设备的所有引脚配置相关的信息:

1
2
3
4
5
6
7
8
struct pinctrl {
struct list_head node;
struct device *dev;
struct list_head states;
struct pinctrl_state *state;//states下链接了该设备支持的所有引脚配置状态
struct list_head dt_maps;
struct kref users;
};
4.2.1.1 pinctrl_state#
1
2
3
4
5
struct pinctrl_state {
struct list_head node;
const char *name;
struct list_head settings;
};

image

5 pinctrl实例化示例#

5.1 pin controller示例#

打开imx6ull的dtsi找到pin controller控制器节点,对比IMX6ULL参考手册可知:imx6ull一共3个IOMUX控制器
image

5.2 client示例#

evk公板为例:
image
imx6ull pinmux dts配置描述:
我们看到:

1
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19       0x17059 /* SD1 CD */

它是表示什么意思呢?配置了哪些信息呢?
image

UART1_RTS_B复用成GPIO1_IO19:
image

此宏定义在imx_6ull_pinfunc.h, 后面跟着 5 个数字,也就是这个宏定义的具体值,如下所示:

1
0x0090 0x031C 0x0000 0x5 0x0

这 5 个值的含义如下所示:

1
<mux_reg conf_reg input_reg mux_mode input_val>

0x0090表示UART1_RTS_B的iomux寄存器
0x031C表示UART1_RTS_B的电器属性寄存器
0x0000表示input寄存器
0x5表示复用成模式5
0x0表示input寄存器设置值为0x0

注意还少了一项电器属性的值啊?别急定义在dts中,0x17059就刚好是电器属性的值。

5.2.1 client如何使用pinctrl#

前面讲过client包含device结构体,每个device结构体里都有一个dev_pin_info结构体,用来保存设备的pinctrl信息。platform_device匹配driver会执行probe,probe前会进行pinctrl处理,处理函数为pinctrl_bind_pins

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
platform_device_register
platform_device_add
device_add
bus_probe_device;
device_initial_probe
__device_attach
bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver);
// 对于plarform_bus_type下的每一个driver, 调用__device_attach_driver

__device_attach_driver
driver_match_device(drv, dev);
drv->bus->match(dev, drv)// 调用platform_bus_type.match
driver_probe_device
really_probe
/* If using pinctrl, bind pins now before probing */
pinctrl_bind_pins
drv->probe //执行driver中的probe函数

5.2.1.1 pinctrl_bind_pins过程#

  • 构造pinctrl
    • 通过pinctrl_ops.dt_node_to_map将设备树节点转换成一系列pinctrl_map
    • pinctrl_map转换成pinctrl_setting,放入settings链表,记录在pinctrl_state
  • 选择state,遍历settings链表,进行pinctrl的mux和config
    image

函数调用过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pinctrl_bind_pins
/* 分配dev_pin_info结构体 */
devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
/* 获取pinctrl */
devm_pinctrl_get(dev);
pinctrl_get
/* 构建pinctrl */
create_pinctrl(dev);
/* 分配pinctrl */
p = kzalloc(sizeof(*p), GFP_KERNEL);
/* 设备树节点转换为pinctrl_map */
pinctrl_dt_to_map(p);
/* 每个pinctrl_map,又被转换为一个pinctrl_setting,添加到setting链表 */
for_each_maps(maps_node, i, map) {
ret = add_setting(p, map);
}

节点转换为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
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
pinctrl_dt_to_map(p);
for (state = 0; ; state++) {
propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);
/* 取出pinctrl-%d节点属性 */
prop = of_find_property(np, propname, &size);
list = prop->value;
size /= sizeof(*list);
/* 对pinctrl-%d中的每一个phandle进行pinctrl_map转换 */
/* 例如pinctrl-0 = <&state_0_node_a &state_0_node_b>有两个phandle */
for (config = 0; config < size; config++) {
/* 根据phandle找到对应节点 */
np_config = of_find_node_by_phandle(phandle);
/* Parse the node */
dt_to_map_one_config(p, statename, np_config);
/* 调用Pincontroller中dt_node_to_map函数,构造pinctrl_map */
ops->dt_node_to_map()
/* 将pinctrl_map添加到maps链表 */
dt_remember_or_free_map
pinctrl_register_map
list_add_tail
}
}

/* fy00的dt_node_to_map函数 */
mc_pctrl_dt_node_to_map()
/* 取出每一个子节点 */
for_each_child_of_node(np_config, np) {
mc_pctrl_dt_subnode_to_map
/* 获取设备树pinmux属性 */
pins = of_find_property(node, "pinmux", NULL);
num_pins = pins->length / sizeof(u32);
for (i = 0; i < num_pins; i++) {
of_property_read_u32_index(node, "pinmux",i, &pinfunc);
/* 解析设备树,将pinmux属性中每一个成员记录在pin和func中 */
pin = MC_GET_PIN_NO(pinfunc);
func = MC_GET_PIN_FUNC(pinfunc);
/* 设置复用的pinctrl_map */
mc_pctrl_dt_node_to_map_func();
(*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP;
(*map)[*num_maps].data.mux.group = grp->name;
(*map)[*num_maps].data.mux.function = mc_gpio_functions[fnum];
(*num_maps)++;
if (has_config) {
/* 设置配置pinctrl_map,fy00没有配置pinctrl_map */
pinctrl_utils_add_map_configs
(*map)[*num_maps].type = type;
(*map)[*num_maps].data.configs.group_or_pin = group;
(*map)[*num_maps].data.configs.configs = dup_configs;
(*map)[*num_maps].data.configs.num_configs = num_configs;
(*num_maps)++;
}
}
}

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
for_each_maps() {
add_setting();
find_state();
if (!state)
/* 第一次添加state到states链表 */
create_state(p, map->name);
list_add_tail(&state->node, &p->states);
/* 将map的name和tpye赋值给setting */
setting->type = map->type;
setting->dev_name = map->dev_name;
switch (map->type) {
/* MUX类型 */
case PIN_MAP_TYPE_MUX_GROUP:
pinmux_map_to_setting(map, setting);
/* 将pinctrl_map中的function字符串转换为序号,赋值给setting*/
pinmux_func_name_to_selector(pctldev, map->data.mux.function);
setting->data.mux.func = ret;
/* 将pinctrl_map中的group字符串转换为序号,赋值给setting */
ret = pinctrl_get_group_selector(pctldev, group);
setting->data.mux.group = ret;
/* CONFIGS类型 */
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
pinconf_map_to_setting(map, setting);
/* 从pinctrl_map取出pin或group赋值给setting */
setting->data.configs.group_or_pin = pin;
/* 将pinctrl_map的configs赋值给setting */
setting->data.configs.num_configs = map->data.configs.num_configs;
setting->data.configs.configs = map->data.configs.configs;
}
/* 添加到settings链表 */
list_add_tail(&setting->node, &state->settings);
}

pin脚的复用和配置过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pinctrl_bind_pins
/* 寻找state */
pinctrl_lookup_state
/* 选择state */
pinctrl_select_state
pinctrl_commit_state
/* 遍历settings链表 */
list_for_each_entry(setting, &state->settings, node) {
switch (setting->type) {
case PIN_MAP_TYPE_MUX_GROUP:
/* 设置复用 */
pinmux_enable_setting(setting);
ops->set_mux(...);
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
/* 设置配置 */
pinconf_apply_setting(setting);
ops->pin_config_group_set(...);
}