目录
概述
设备树表示
内核中操作
总结
在前面的学习过程中操作了很多的IO引脚,那么,使用一个IO引脚最重要的三部分
1、配置IO的复用,主要是MUX类的寄存器
2、配置IO的电气属性,主要配置PAD类的寄存器
3、配置IO的输入输出
设备树的引入方便开发者对设备进行描述,对于外设的一些引脚的使用,在设备树中也有描述,称为pinctrl子系统,内核根据设备树中的描述来完成对管脚的配置,这篇随着imx6ull平台的设备树分析来简单了解一些内核的pinctrl子系统,打开设备树,找到引脚复用的节点,
iomuxc: iomuxc@020e0000 {
compatible = "fsl,imx6ul-iomuxc";
reg = <0x020e0000 0x4000>;
};
0x020e0000就是芯片的管脚复用控制寄存器的基地址,这个是在dtsi文件中描述的,为芯片的公共资源,接下来在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_IO09__GPIO1_IO09 0x17059 SD1 RESET */
MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058 /* USB_OTG1_ID */
>;
};
}
内容有很多,写法都是类似,这些都是对具体的外设所使用的GPIO进行描述,找到一个
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
由两部分组成,前面是一个宏,后面为一个数字,首先从宏的名字来看就知道它是UART1_RST复用为GPIO1_IO19,在设备树的引入文件中都已找到宏对应的含义
/*
* The pin function ID is a tuple of
*
*/
#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0
看到这里就知道它的含义了,这个宏对应5个值,分别为注释中的5个寄存器的值,
mux_reg 0x0090 io复用寄存器偏移地址
conf_reg 0x031C io电气属性寄存器偏移地址
input_reg 0x0000 input寄存器偏移地址
mux_mode 0x5 复用模式
input_val 0x0 输入值
找到UART1_RST管脚的相关寄存器
这个寄存器的地址是20E_0090h,上面的mux_reg的值是偏移值,加上最前面的iomuxc寄存器的基地址不就是这个寄存器的真实地址了吗,其他几个寄存器也是一样的,就不去验证了
这里着重看一下mux_mode的值,表示复用模式,查看IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B寄存器
UART1_RST_B复用模式为5的话,就是将这个引脚复用为GPIO1_IO19,和宏的名字是一致的。IO的复用就是这样表示的
那么,还有一个表示电气属性的值,在这个宏里只有电气属性寄存器的地址,一般电气属性可以随时修改,所以不能放在宏里表示,还记得上面宏后面还跟了一个数字吗?那个就是电气属性的值
配置一个IO所必须的在这里都可以找出来,,接下来就是看内核是如何把这些值关联起来,来配置具体的IO
根据iomuxc节点的compatible属性就可以找到相关的c文件
具体的代码也比较多,这里就不去全部分析出来了,主要过程总结为
1、申请一个struct pinctrl_desc *imx_pinctrl_desc;结构体类型变量,对结构体进行赋值,主要是一些操作函数集
2、然后就是去解析设备树,分析iomux节点的内容,获取它的引脚
这些信息都记录在了imx_pinctrl_desc这个结构体当中,
3、使用pinctrl_register函数,把结构体注册到系统当中,并通过厂商自己提供的操作函数来配置GPIO
这一部分我也没有深入的去分析,但是如果我们熟悉驱动或者内核开发的流程,就可以看出它的套路
1、首先,内核定义出了一套关于GPIO配置的结构,但是它不可能去涉及到具体的单板,具体单板的GPIO的配置肯定是由厂商自己来定义的
2、厂商根据内核的框架,实现一个结构体,并根据自己的单板填充结构体的操作函数集,这个操作函数无疑就是具体的GPIO的配置,
比如imx6ull,它就会获取设备树中的那5个值和管脚的电气属性值,然后把值写到对应的寄存器中,其他的都是类似
3、使用内核提供函数,把这个结构体注册到系统中,这样,系统跑起来的时候,只需要找到结构体,然后执行其中的函数,就可以从公共执行到具体,最终完成某款单板的IO配置
不仅仅是pinctrl子系统,内核的代码框架都是采用这种注册回调的方式来完成的。