pinctrl子系统和GPIO子系统的匹配过程(imx6ull)
个人声明:转发请注明出处,个人原创,实属不易。本人水平有限,文章若有不妥之处,还请留言批评指正,不胜感激。
pinctrl子系统和GPIO子系统是驱动分层下的产物
- pinctrl子系统
1.1 借助pinctrl来设置PIN的复用和电气属性
1.2 PIN信息的添加:在.dtsi文件中添加,以宏的方式添加,格式为<引脚宏定义 电气属性值>
比如: MX6UL_PAD_UART1_RTS_B__GPIO1_IO09 0x17059
1.3 PIN宏定义在imx6ul-pinfunc.h或在imx6ull-pinfunc.h
比如: MX6UL_PAD_UART1_RTS_B__GPIO1_IO09 0x0090 0x031C 0x0000 0x5 0x0
• <mux_reg conf_reg input_reg mux_mode input_val>
0x0090 0x031C 0x0000 0x5 0x0
• 含义
mux_reg:复用寄存器偏移地址
conf_reg: 电气属性配置寄存器偏移地址
input_reg:输入寄存器偏移(偏移为0表示此PIN没有input功能)
mux_mode:复用模式
input_val:写入input_reg的值
PIN的电气属性配置寄存器的值:0x17059,即是conf_reg要设置的值
-
GPIO子系统
2.1 初始化GPIO和提供对GPIO操作的API函数 -
匹配
3.1 imx6ull.dtsi文件下的
iomuxc: iomuxc@020e0000 {
compatible = "fsl,imx6ul-iomuxc"; /* compatible属性非常重要 */
reg = <0x020e0000 0x4000>;
};
3.2 pinctrl-imx6ul.c文件下的匹配表
static struct of_device_id imx6ul_pinctrl_of_match[] = {
/* 这个匹配表的compatible值和设备树imx6ull.dtsi下iomuxc节点的compatible属性值一致 */
{ .compatible = "fsl,imx6ul-iomuxc", .data = &imx6ul_pinctrl_info, },
{ .compatible = "fsl,imx6ull-iomuxc-snvs", .data = &imx6ull_snvs_pinctrl_info, },
{ /* sentinel */ }
};
3.2.1 驱动文件下的platform_driver结构体初始化
static struct platform_driver imx6ul_pinctrl_driver = {
.driver = {
.name = "imx6ul-pinctrl",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(imx6ul_pinctrl_of_match),
},
.probe = imx6ul_pinctrl_probe,
.remove = imx_pinctrl_remove,
};
3.2.2 imx6ul_pinctrl_probe函数
static int imx6ul_pinctrl_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
/*pinctrl_info信息就会在pinctrl_register向系统注册结构体之前的imx_pinctrl_desc->pctlops使用
pinctrl_info保存了platform_device下的设备和imx6ul_pinctrl_of_match匹配表的匹配结果,即最佳匹配的of_device_id
而of_device_id就是platform驱动框架下开发人员必须定义的设备匹配表
*/
struct imx_pinctrl_soc_info *pinctrl_info;
match = of_match_device(imx6ul_pinctrl_of_match, &pdev->dev);
if (!match)
return -ENODEV;
pinctrl_info = (struct imx_pinctrl_soc_info *) match->data;
return imx_pinctrl_probe(pdev, pinctrl_info);
}
- 最终调用pinctrl-imx.c文件下的imx_pinctrl_probe函数
int imx_pinctrl_probe(struct platform_device *pdev, struct imx_pinctrl_soc_info *info)
{
……
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ipctl->base = devm_ioremap_resource(&pdev->dev, res);
……
imx_pinctrl_desc->name = dev_name(&pdev->dev);
imx_pinctrl_desc->pins = info->pins;
imx_pinctrl_desc->npins = info->npins;
/* 三个重要的ops结构体 */
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->info = info;
ipctl->dev = info->dev;
ipctl->pctl = pinctrl_register(imx_pinctrl_desc, &pdev->dev, ipctl);
}
pinctrl_register向系统注册imx_pinctrl_desc结构体和platform_device结构体下的设备&pdev->dev。
4.1三个重要的ops结构体
4.1.1 imx_pctrl_ops:
static const struct pinctrl_ops imx_pctrl_ops = {
……
.dt_node_to_map = imx_dt_node_to_map,
……
};
imx_pctrl_ops结构体下的imx_dt_node_to_map成员,首先通过imx_pinctrl_find_group_by_name(info, np->name);在对应的组中查找节点(通过节点名字查找)
static const inline struct imx_pin_group *imx_pinctrl_find_group_by_name(
const struct imx_pinctrl_soc_info *info,
const char *name)
函数的第一个*info参数非常重要,有没有发现imx_pinctrl_soc_info此结构体很熟悉啊!!!此结构体就是imx6ul_pinctrl_probe函数中获取到的匹配设备信息。用户向内核注册驱动时,将platform驱动结构体通过platform_driver_register()函数传递给内核,其中这个结构体就包括了驱动的匹配列表!!!
4.1.2 imx_pmx_ops:设置PIN的复用
static const struct pinmux_ops imx_pmx_ops = {
……
.set_mux = imx_pmx_set,
……
};
static int imx_pmx_set(struct pinctrl_dev *pctldev, unsigned selector, unsigned group){
……
/* 配置PIN的复用模式 */
……
}
4.1.3 imx_pinconf_ops:设置PIN的电气属性
static const struct pinconf_ops imx_pinconf_ops = {
……
.pin_config_set = imx_pinconf_set,
……
for (i = 0; i < num_configs; i++) {
if (info->flags & SHARE_MUX_CONF_REG) {
/* 共享复用配置寄存器???所以采样 "读->改->写" 的方式 ?*/
u32 reg;
reg = readl(ipctl->base + pin_reg->conf_reg); /* 读出寄存器的值 */
reg &= ~0xffff;
reg |= configs[i]; /* 修改寄存器的值 */
writel(reg, ipctl->base + pin_reg->conf_reg); /* 将修改的结果写入寄存器 */
} else {
writel(configs[i], ipctl->base + pin_reg->conf_reg);
}
}
- imx_pinctrl_probe中的imx_pinctrl_probe_dt函数
imx_pinctrl_probe_dt(struct platform_device *pdev,struct imx_pinctrl_soc_info *info)
->for_each_child_of_node(np, child) /* 解析每个子节点 */
imx_pinctrl_parse_functions(child, info, i++);
->imx_pinctrl_parse_groups(child, grp, info, i++);
pin_reg->mux_reg = mux_reg;
pin_reg->conf_reg = conf_reg;
pin->input_reg = be32_to_cpu(*list++);
pin->mux_mode = be32_to_cpu(*list++);
pin->input_val = be32_to_cpu(*list++);
......
<mux_reg conf_reg input_reg mux_mode input_val>
这五个变量的值是不是很熟悉!!!这不就是在imx6ul-pinfunc.h或在imx6ull-pinfunc.h文件下PIN的宏定义吗!!至此,pinctrl完成了PIN的复用和电气属性值的获取。
imx_pinctrl_probe中的三个ops结构体完成PIN的复用设置和电气属性的设置。
总结:
- 驱动文件下的platform驱动结构体通过platform_driver_register()函数传递给内核,而platform驱动结构体包含了匹配列表成员,此匹配列表的compatible属性就是通过总线的设备和驱动匹配中找到对应的设备,此设备的描述信息就在设备树对应的节点下;
- 匹配在文中的3.2.2小节的imx6ul_pinctrl_probe函数中已经完成;
- 完成匹配之后,调用imx_pinctrl_probe用于获取和设置PIN的复用与电气属性。
<下一篇将介绍具体的匹配过程:of_match_device>