Pinctrl子系统
1. Pinctrl引入
参考资料:
- Linux 5.x内核文档
- Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt
- Linux 4.x内核文档
- Documentation\pinctrl.txt
- Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt
1.1 Pinctrl作用
无论是哪种芯片,都有类似下图的结构:
想要pinA、B用于GPIO,需要设置IOMUX让它们连接到GPIO模块;要想让pinA、B用于I2C,需要设置IOMUX让它们连接到I2C模块,这里GPIO、I2C应该是并列的关系,它们能够使用之前,需要设置复用关系IOMUX,有时还要配置引脚,比如上拉、下拉、开漏等等。现在芯片一般动辄几百个引脚,在使用到GPIO、I2C等功能时,若一个引脚一个引脚去找对应的寄存器进行配置非常浪费时间和精力,所以内核引入了Pinctrl子系统,把引脚的复用和配置抽象出来,只需要芯片厂商把自家芯片的支持进去,就可以很方便的配置引脚。
Pinctrl:Pin Controller,顾名思义,就是用来控制引脚的:
- 引脚枚举与命名(Enumerating and naming)
- 引脚复用(Multiplexing):比如用作GPIO、I2C或其他功能
- 引脚配置(Configuration):比如上拉、下拉、开漏、驱动强度等
1.2 Pinctrl重要概念
从设备树开始学习Pinctrl会比较容易。参考内核Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt,Pinctrl子系统涉及2个对象:Pin controller devices、Pinctrl client devices。
- Pin controller devices:提供服务,可以用它来复用引脚、配置引脚,是一个软件上的概念。
注意:Pin controller和GPIO Controller不同,前者控制的引脚可用于GPIO功能、I2C功能;后者只是把引脚配置为输入、输出等简单的功能。两者的关系是先用Pin controller把引脚配置为GPIO,再用GPIO Controler把引脚配置为输入或输出。 - Pinctrl client devices:使用服务,Pinctrl系统的客户,即使用Pinctrl系统的设备。声明自己要使用哪些引脚的哪些功能,怎么配置它们。
在设备树中,上述两个对象被定义成两个节点,如下图所示。左边是Pin controller节点,右边是client device节点:
a. 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节点中。
当这个设备处于default状态时,pinctrl子系统会自动根据上述信息把所用引脚复用为uart0功能。
当这个设备处于sleep状态时,pinctrl子系统会自动根据上述信息把所用引脚配置为高电平。
b. groups、function和pins:一个设备会用到一个或多个引脚pin,这些引脚就可以归为一组group,复用为某个功能function;当然:一个设备可以用到多组引脚,比如A1、A2两组引脚,A1组复用为F1功能,A2组复用为F2功能。
c. pin multiplexing node和pin configuration node:在上图Pin controller节点中,有子节点或孙节点,它们是给client device使用的。
可以用来描述复用信息:哪组(group)引脚复用为哪个功能(function);
可以用来描述配置信息:哪组(group)引脚配置为哪个设置功能(setting),比如上拉、下拉等。
注意:Pin controller节点的格式,没有统一的标准!!!!每家芯片都不一样,甚至上面的group、function关键字也不一定有,但是概念是有的。而client device节点的格式是统一的。
这是由于怎么去解析Pincontroller节点下的子节点完全是有芯片厂商决定,内核Pinctrl驱动框架只定义了解析节点的接口而已,而Client端是由内核代码统一管理的。
2. Pinctrl子系统主要数据结构
参考资料
- 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
2.1 设备树
2.1.1 理想模型
2.1.2 实际例子
- IMX6ULL
- fy00
2.2 pincontroller的数据结构
记住pinctrl的三大作用,有助于理解所涉及的数据结构:
- 引脚枚举与命名(Enumerating and naming)
- 引脚复用(Multiplexing):比如用作GPIO、I2C或其他功能
- 引脚配置(Configuration):比如上拉、下来、open drain、驱动强度等
2.2.1 pinctrl_desc和pinctrl_dev
1. 结构体引入
Pincontroller虽然是一个软件P的概念,但是它背后是有硬件支持的,所以可以使用一个结构体来表示它:pinctrl_dev
怎么构造出pinctrl_dev?我们只需要描述它:提供一个pinctrl_desc,然后调用pinctrl_register就可以:
struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
struct device *dev, void *driver_data);
怎么使用pinctrl_desc、pinctrl_dev来描述一个pin controller?这两个结构体定义如下
pinctrl_desc示例如下:
- pinctrl_pin_desc:描述引脚
- pinctrl_ops:引脚控制操作,用来获取某组引脚,解析设备树节点创建映射
- pinmux_ops:复用操作
- pinconf_ops:配置操作
2. 作用1:描述、获取、解析引脚
使用结构体pinctrl_pin_desc来描述一个引脚,一个pin controller有多个引脚:
/**
* struct pinctrl_pin_desc - boards/machines provide information on their
*/
struct pinctrl_pin_desc {
unsigned number; //引脚序号
const char *name; //引脚名
void *drv_data;
};
使用pinctrl_ops来操作引脚,主要功能如下:
/**
* struct pinctrl_ops - global pin control operations, to be implemented by
* pin controller drivers.
*/
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:引脚复用
/**
* struct pinmux_ops - pinmux operations, to be implemented by pin controller
* drivers that support pinmuxing
*/
struct pinmux_ops {
int (*request) ();
int (*free) ();
//获取func数量
int (*get_functions_count) ();
//获取func名
const char *(*get_function_name) ();
//获取func下的组
int (*get_function_groups) ();
//设置复用
int (*set_mux) ();
int (*gpio_request_enable) ();
void (*gpio_disable_free) ();
int (*gpio_set_direction) ();
bool strict;
};
4. 作用3:引脚配置
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);
};
5. 使用pinctrl_desc注册得到pinctrl_dev
调用devm_pinctrl_register或pinctrl_register,就可以根据pinctrl_desc构造出pinctrl_dev,并且把pinctrl_dev放入链表:
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时格式如下:
/* 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>;
};
2.3.1 device结构体
设备节点要么被转换为platform_device,或者其他结构体(比如i2c_client),但是里面都会有一个device结构体:
2.3.2 dev_pin_info、pinctrl、pinctrl_map和pinctrl_setting
每个device结构体里都有一个dev_pin_info结构体,用来保存设备的pinctrl信息
假设芯片上有多个pin controller,那么这个设备使用哪个pin controller ?
这需要通过设备树来确定:
- 分析设备树,找到pin controller
- 对于每个状态,比如default、init,去分析pin controller中的设备树节点
- 使用pin controller的pinctrl_ops.dt_node_to_map来处理设备树的pinctrl节点信息,例如
pinctrl-0 = <&state_0_node_a>
,state_0_node_a节点被解析为一系列的pinctrl_map - 这些pinctrl_map放在pinctrl.dt_maps链表中
- 每个pinctrl_map都被转换为pinctrl_setting,放在对应的pinctrl_state.settings链表中
- 使用pin controller的pinctrl_ops.dt_node_to_map来处理设备树的pinctrl节点信息,例如
- 选择状态时,遍历settings链表,执行pin controller复用或配置函数
3. Pincontroller构造过程情景分析
参考资料
- drivers\pinctrl\molchip\pinctrl-mcfy00.c
- drivers\pinctrl\molchip\pinctrl-mc-common.c
- drivers\pinctrl\core.c
- drivers\base\dd.c
- drivers\base\pinctrl.c
- drivers\pinctrl\devicetree.c
- drivers\pinctrl\pinmux.c
- drivers\pinctrl\pinconf.c
3.1 设备树节点
3.2 驱动代码执行流程
调用过程:
drivers\pinctrl\molchip\pinctrl-mcfy00.c
drivers\pinctrl\molchip\pinctrl-mc-common.c
mcfy00_pinctrl_probe
mc_pctrl_init
pctl->pctl_desc.name = dev_name(&pdev->dev);
pctl->pctl_desc.owner = THIS_MODULE;
pctl->pctl_desc.pins = pins;
pctl->pctl_desc.npins = pctl->devdata->npins;
pctl->pctl_desc.confops = &mc_pconf_ops;
pctl->pctl_desc.pctlops = &mc_pctrl_ops;
pctl->pctl_desc.pmxops = &mc_pmx_ops;
pctl->dev = &pdev->dev;
pctl->pctl_dev = devm_pinctrl_register(&pdev->dev, &pctl->pctl_desc,
pctl);
3.3 作用1:描述、获取、解析引脚
3.3.1 单个引脚
//debugfs查看pin信息:cat /sys/kernel/debug/pinctrl/28200000.pinctrl/pins
pctl->pctl_desc.pins = pins;
pctl->pctl_desc.npins = pctl->devdata->npins;
3.3.2 某组引脚
//开发板查看 cat /sys/kernel/debug/pinctrl/28200000.pinctrl/pingroups
//FY001是把一个管脚当做一个组处理,见mc_pctrl_build_state
pctl->pctl_desc.pctlops = &mc_pctrl_ops;
static const struct pinctrl_ops mc_pctrl_ops = {
.dt_node_to_map = mc_pctrl_dt_node_to_map,
.dt_free_map = pinctrl_utils_free_map,
.get_groups_count = mc_pctrl_get_groups_count,
.get_group_name = mc_pctrl_get_group_name,
.get_group_pins = mc_pctrl_get_group_pins,
};
3.3.3 设备树节点解析 - 节点转换为pinctrl-map
详见第4节
static int mc_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
struct device_node *np_config,
struct pinctrl_map **map, unsigned *num_maps)
3.4 作用2:引脚复用、配置
详见第4节
3.4.1 引脚复用
static const struct pinmux_ops mc_pmx_ops = {
.get_functions_count = mc_pmx_get_funcs_cnt,
.get_function_name = mc_pmx_get_func_name,
.get_function_groups = mc_pmx_get_func_groups,
.set_mux = mc_pmx_set_mux,
};
3.4.2 引脚配置
//FY00无引脚配置
static const struct pinconf_ops mc_pconf_ops = {
.pin_config_group_get = mc_pconf_group_get,
.pin_config_group_set = mc_pconf_group_set,
};
4. client端使用pinctrl过程的情景分析
4.1 设备树节点
4.2 client节点如何使用pinctrl
设备节点要么被转换为platform_device,或者其他结构体(比如i2c_client),但是里面都会有一个device结构体,每个device结构体里都有一个dev_pin_info结构体,用来保存设备的pinctrl信息。
platform_device匹配driver会执行probe探测函数,执行到驱动中真正的probe函数之前,会进行pinctrl的处理,处理函数为pinctrl_bind_pins
,调用过程如下
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函数
pinctrl_bind_pins做了什么?
- 构造pinctrl
- 通过pinctrl_ops.dt_node_to_map将设备树节点转换成一系列pinctrl_map
- pinctrl_map转换成pinctrl_setting,放入settings链表,记录在pinctrl_state中
- 选择state,遍历settings链表,进行pinctrl的mux和config
4.2.1 client节点如何构造pinctrl
设备引用pin controller中的某个节点时,这个节点会被转换为一些列的pinctrl_map:
- 转换为多少个pinctrl_map,完全由具体的驱动决定
- 每个pinctrl_map,又被转换为一个pinctrl_setting
- 举例,设备节点里有:
pinctrl-0 = <&state_0_node_a>
pinctrl-0 = <&state_0_node_a &state_0_node_b>
- pinctrl-0对应一个状态,会得到一个pinctrl_state
- state_0_node_a节点被解析为一系列的pinctrl_map
- 这一系列的pinctrl_map被转换为一系列的pinctrl_setting
- 这些pinctrl_setting被放入pinctrl_state的settings链表
函数调用过程
really_probe
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) {
add_setting(p, map);
}
1. 设备树节点转换为pinctrl_map
这些设备树节点信息会被转换为一些列的pinctrl_map,以fy00的uart2_pins节点举例
函数调用过程
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)++;
}
}
}
2. pinctrl_map转换为pinctrl_setting
函数调用过程
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);
}
4.2.2 client节点如何使用设置管脚
涉及pinctrl子系统的其他2个作用:引脚复用、引脚配置
切换state情景分析
really_probe
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(...);
}
}
1. 复用设置
与具体芯片有关,以fy00为例
mc_pmx_set_mux()
pinctrl_params_data.mc_pmx_set_mode();
//寄存器操作
fy00_pmx_set_mode
2. 配置设置
与具体芯片有关,fy00无配置函数,实际也是通过dt_node_to_map拿到设备树中的配置数据,在pin_config_group_set完成寄存器操作。
5. 海思平台上编写Pinctrl驱动程序
5.1 设备树
对于hisi_i2c这个设备,将AT26、AN26两个引脚复用成i2c功能,对应寄存器配置成0x1403
5.2 驱动代码
5.2.1 pincontroller端
/* pin属性 */
static const struct pinctrl_pin_desc pins[] = {
{0, "AT26", &AT26_REG},
{1, "AN26", &AN26_REG},
{2, "AN28", &AN28_REG},
{3, "AR27", &AR27_REG},
{4, "AP27", &AP27_REG},
};
struct hisi_functions_desc {
const char *func_name;
const char **groups;
int num_groups;
};
static const char *i2c0_grps[] = {"AT26", "AN26"};
static const char *gpio_grps[] = {"AT26", "AN26", "AN28", "AR27", "AP27"};
static const char *spi0_grps[] = {"AT26", "AN26", "AN28", "AR27", "AP27"};
/* func和group属性,本驱动中pin即group */
static struct hisi_functions_desc g_funcs_des[] = {
{"gpio", gpio_grps, 5},
{"i2c", i2c0_grps, 2},
{"spi", spi0_grps, 5},
};
5.2.2 client device端
5.3 使用debugfs调试
加载驱动insmod
查看当前pinctrl控制设备cat /sys/kernel/debug/pinctrl/pinctrl-devices
查看设备树节点转换而来的pinctrl-mapcat /sys/kernel/debug/pinctrl/pinctrl-maps
查看当前pinctrl的处理cat /sys/kernel/debug/pinctrl/pinctrl-handles
进入控制器cd soc\:amba\:hisi_pincontroller/
查看配置的groupscat pinconf-groups
查看groupscat pingroups
查看复用的pinscat pinmux-pins
查看配置的pinscat pinconf-pins
查看复用功能cat pinmux-functions
查看所有注册的pinscat pins
6. GPIO子系统与Pinctrl子系统的交互
参考资料
- drivers\gpio\gpiolib.h
- drivers\gpio\gpiolib.c
- drivers\gpio\gpiolib-of.c
- include\linux\gpio\driver.h
- include\linux\pinctrl\pinctrl.h
- drivers\pinctrl\core.c
- drivers\pinctrl\pinmux.c
- include\linux\pinctrl\pinmux.h
要使用pinA来控制LED,首先要通过Pinctrl子系统把它设置为GPIO功能,然后才能设置它为输出引脚、设置它的输出值。
所以在设备树文件里,应该添加Pinctrl的内容:
pincontroller {
compatible = "mypinctrl";
myled_pin: myled_pin {
functions = "gpio";
groups = "pin0";
configs = <0x11223344>;
};
};
mygpio: gpiocontroller {
compatible = "mygpioctrl";
gpio-controller;
#gpio-cells = <2>;
ngpios = <4>;
};
myled {
compatible = "myleddrv";
led-gpios = <&mygpio 0 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&myled_pin>;
};
但是很多芯片,并不要求在设备树中把把引脚复用为GPIO功能,即不需要添加Pinctrl的相关属性。原因在于:Pinctrl是一个软件虚拟处理的概念,它的实现本来就跟GPIO密切相关,并给GPIO留了后门。当你使用gpiod_get获得GPIO引脚时,它就偷偷地通过Pinctrl把引脚复用为GPIO功能了。
从上图可知:
-
左边的Pinctrl支持8个引脚,在Pinctrl的内部编号为0~7
-
图中有2个GPIO控制器
- GPIO0内部引脚编号为0-3,假设在GPIO子系统中全局编号为100~103
- GPIO1内部引脚编号为0-3,假设在GPIO子系统中全局编号为104~107
-
假设我们要使用pin1_1,应该这样做:
- 根据GPIO1的内部编号1,可以换算为Pinctrl子系统中的编号5
- 使用Pinctrl的函数,把第5个引脚配置为GPIO功能
6.1 数据结构
6.2 GPIO调用Pinctrl的过程
GPIO子系统中的request函数,用来申请某个GPIO引脚
它会导致Pinctrl子系统中的这2个函数之一被调用:pmxops->gpio_request_enable
或pmxops->request
调用关系如下:
gpiod_get
gpiod_get_index
desc = of_find_gpio(dev, con_id, idx, &lookupflags);
gpiod_request(desc, con_id ? con_id : devname);
gpiod_request_commit(desc, label);
if (chip->request) {
ret = chip->request(chip, offset);
}
编写GPIO驱动程序时,所设置chip->request
函数,一般直接调用gpiochip_generic_request
,它导致Pinctrl把引脚复用为GPIO功能
gpiochip_generic_request(struct gpio_chip *chip, unsigned offset)
pinctrl_request_gpio(chip->gpiodev->base + offset)
pinctrl_get_device_gpio_range(gpio, &pctldev, &range); // gpio是引脚的全局编号
/* Convert to the pin controllers number space */
/* gpio号转换为pincontroller中的pin序号 */
pin = gpio_to_pin(range, gpio);
/* 请求gpio */
pinmux_request_gpio(pctldev, range, pin, gpio);
pin_request(pctldev, pin, owner, range);
Pinctrl子系统中的pin_request函数就会把引脚配置为GPIO功能,执行pmxops->gpio_request_enable
或pmxops->request
static int pin_request(struct pinctrl_dev *pctldev,
int pin, const char *owner,
struct pinctrl_gpio_range *gpio_range)
{
const struct pinmux_ops *ops = pctldev->desc->pmxops;
/*
* If there is no kind of request function for the pin we just assume
* we got it by default and proceed.
*/
if (gpio_range && ops->gpio_request_enable)
/* This requests and enables a single GPIO pin */
status = ops->gpio_request_enable(pctldev, gpio_range, pin);
else if (ops->request)
status = ops->request(pctldev, pin);
else
status = 0;
}
6.3 如何建立GPIO和Pinctrl间的联系
6.3.1 表明GPIO和Pinctrl间的联系
在GPIO设备树中使用gpio-ranges
来描述它们之间的联系:
- GPIO系统中有引脚号
- Pinctrl子系统中也有自己的引脚号
- 2个号码要建立映射关系
- 在GPIO设备树中使用如下代码建立映射关系
// 当前GPIO控制器的0号引脚, 对应pinctrlA中的128号引脚, 数量为12
gpio-ranges = <&pinctrlA 0 128 12>;
6.3.2 如何解析这些联系
在GPIO驱动程序中,解析跟Pinctrl之间的联系:处理gpio-ranges
:
- 这不需要我们自己写代码
- 注册gpio_chip时会自动调用
- gpio_chip->request(即
gpiochip_generic_request
)进行gpio与pinctrl号转换
int gpiochip_add_data(struct gpio_chip *chip, void *data)
of_gpiochip_add(chip);
of_gpiochip_add_pin_range(chip);
of_gpiochip_add_pin_range
for (;; index++) {
of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, index, &pinspec);
/* 根据gpio-ranges的第1个参数找到pinctrl_dev */
pctldev = of_pinctrl_get(pinspec.np);
// 增加映射关系
/* npins != 0: linear range */
ret = gpiochip_add_pin_range(chip,
pinctrl_dev_get_devname(pctldev),
pinspec.args[0],
pinspec.args[1],
pinspec.args[2]);
}
gpiochip_add_pin_range()
{
/* Use local offset as range ID */
pin_range->range.id = gpio_offset;
pin_range->range.gc = chip;
pin_range->range.name = chip->label;
pin_range->range.base = gdev->base + gpio_offset;
pin_range->range.pin_base = pin_offset;
pin_range->range.npins = npins;
pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name,
&pin_range->range);
}
gpiochip_generic_request
pinctrl_request_gpio
/* 获取pin_range */
pinctrl_get_device_gpio_range
/* gpio与pinctrl号转换 */
pin = gpio_to_pin(range, gpio);
static inline int gpio_to_pin()
{
unsigned int offset = gpio - range->base;
if (range->pins)
return range->pins[offset];
else
return range->pin_base + offset;
}
6.3.3 编程
- 在GPIO驱动程序中,提供
gpio_chip->request
- 在Pinctrl驱动程序中,提供
pmxops->gpio_request_enable
或pmxops->request