1.简介
在如今的驱动开发工作中,实际上已经很少去对着寄存器手册进行驱动开发了,一般板子拿到手,已经有原厂的驱动开发工程师,在gpio子系统、pinctrl子系统中将自家芯片的引脚适配好了。
我们直接基于设备树已配置好的寄存器值,去使用子系统对应的API函数,就能快速完成驱动开发,不需要再那么关心IO寄存器的值,借助这种驱动分层的思想,快速完成驱动开发。
其中配置一个GPIO最重要的几点就是配置IO的MUX复用属性,PAD电气属性,输入及输出
pinctrl 子系统作用:从设备树中获取PIN 的复用(MUX)和电气属性(PAD),并完成初始化等,PIN 可复用为 I2C、SPI、GPIO,当复用为gpio的时候,就需要用到gpio子系统
gpio 子系统作用:方便开发者使用gpio,负责初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO 为输入输出,读取 GPIO 的值
1.1 pinctrl和gpio子系统分层思想
在加入gpio子系统和pinctrl系统后,对gpio的操作,将通过pinctrl子系统设置IO复用及电气属性配置,gpio子系统控制输入/输出,读取gpio值等。当原厂bsp工程师适配好设备树后,借助子系统去完成驱动开发将变的十分简单。
其中gpio子系统和pinctrl子系统的关系如下图,相互依赖密不可分。
2.pinctrl子系统
2.1驱动源码分析
那么以MX6UL_PAD_UART1_RTS_B__GPIO1_IO19这个引脚为例子,来解析如何使用设备树+设备驱动完成引脚配置。
例如,arch/arm/boot/dts/imx6ull.dtsi中,子节点iomuxc为:
iomuxc: iomuxc@020e0000 {compatible = "fsl,imx6ul-iomuxc";reg = <0x020e0000 0x4000>;
};
而在arch/arm/boot/dts/imx6ull-alientek-emmc.dts中,对iomuxc子节点进行修改
&iomuxc {pinctrl-names = "default";pinctrl-0 = <&pinctrl_hog_1>;imx6ul-evk {pinctrl_hog_1: hoggrp-1 {fsl,pins = <MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059 /* SD1 VSELECT */MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058 /* USB_OTG1_ID */>;};
其中MX6UL_PAD_UART1_RTS_B__GPIO1_IO19宏定义的具体含义,可以参考下下面的pinctrl配置信息,先不放这个章节
其中的compatible属性为fsl,imx6ul-iomuxc,那么Linux内核就会根据这个字段,查找相应的驱动文件。
全局搜索后找到drivers/pinctrl/freescale/pinctrl-imx6ul.c中的OF表有匹配的属性
static struct of_device_id imx6ul_pinctrl_of_match[] = {{ .compatible = "fsl,imx6ul-iomuxc", .data = &imx6ul_pinctrl_info, },{ .compatible = "fsl,imx6ull-iomuxc-snvs", .data = &imx6ull_snvs_pinctrl_info, },{ /* sentinel */ }
};
当设备和驱动匹配的时候,就会调用对应的probe成员函数,在其中完成PIN配置
随后调用pinctrl_register向Linux内核注册一个PIN控制器
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);
if (ret) {dev_err(&pdev->dev, "fail to probe dt properties\n");return ret;
}ipctl->info = info;
ipctl->dev = info->dev;
platform_set_drvdata(pdev, ipctl);
ipctl->pctl = pinctrl_register(imx_pinctrl_desc, &pdev->dev, ipctl);
if (!ipctl->pctl) {dev_err(&pdev->dev, "could not register IMX pinctrl driver\n");return -EINVAL;
}
在其中的pctlops,pmxops,confops都是PIN的配置函数,借助这些函数来完成PIN 配置,其他的就留着之后再分析啦~
2.2pinctrl配置信息
宏定义MX6UL_PAD_UART1_RTS_B__GPIO1_IO19为:
#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0
分别对应的值为:<mux_reg conf_reg input_reg mux_mode input_val>
则代表:
mux_reg:IO复用寄存器地址(MUX类) = 0x0090
conf_reg:io配置寄存器地址(PAD类)= 0x031C
input_reg:输入寄存器地址 = 0x0000
mux_mode:mux_reg寄存器值 = 0x5
input_val:input_reg值 = 0x0
0x17059:conf_reg寄存器值
如上面iomuxc节点的reg地址为0x020e0000,则代表MX6UL_PAD_UART1_RTS_B__GPIO1_IO19的复用寄存器地址为0x020e0000+0x0090=0x020e0090
mux_mode = 0x5 则代表io复用为GPIO1_IO19
conf_reg = 0x020e031C,寄存器地址为0x020e031C,值为0x17059
2.3添加pinctrl节点过程
多说不如多做,实战添加外设的pin信息。
iomuxc下imx6ul-evk节点添加pinctrl test子节点
pinctrl_test : testgrp {fsl,pins = <MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config //待结合gpio子系统来添加具体值,这里先不写>;
};
这样就完成了一个gpio的pinctrl子系统配置。这里因为复用为gpio,所以需要用到gpio子系统,gpio子系统中再继续完成这个实战~
3.最后
哈喽~我是徐章鑫,沪漂嵌入式开发工程师一枚,立志成为嵌入式全栈开发工程师,成为优秀博客创作者,共同学习进步。
以上代码全部放在我私人的github地址,其中有许多自己辛苦敲的例程源码,供大家参考、批评指正,有兴趣还可以直接提patch修改我的仓库~:
https://github.com/Xuzhangxin
觉得不错的话可以点个收藏和star~