**目的:**简化GPIO驱动开发。
***注意点:***每当配置一个PIN为GPIO时,需要确定该GPIO是否有被别的外设使用。-----------在vscode里对DTS搜索
1. pinctrl子系统
**作用:**设置一个PIN的复用和电气属性。
主要工作内容:
①获取设备树中 pin 信息。
②根据获取到的 pin 信息来设置 pin 的复用功能
③根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。
1.1 pinctrl子系统驱动
1)PIN配置信息:
imx6ull.dtsi中iomuxc的节点以及dts中&iomuxc的数据。
复用可以在imx6ul和6ull-pinfunc.h中查询。
以MX6UL_PAD_UART1_RTS_B_GPIO1_IO09这个复用为例:
//imx6ul-pinfunc.h
MX6UL_PAD_UART1_RTS_B_GPIO1_IO09 0X0090 0X031C 0X0000 0X5 0X0
<mux_reg=0X0090 conf_reg=0X031C input_reg=0X0000 mux_mode=0X5 input_val=0X0>
/*
---------------------相关GPIO复用及电气设置请查阅手册中的IOMUXC章节---------------------
mux_reg是复用寄存器的偏移,iomuxc父节点地址0X020e0000,所以复用寄存器地址为0X020e 0090
conf_reg:0X020e0000 + 0X031C,电气配置寄存器的地址。
input_reg:偏移为0,表示UART1_RTS_B这个PIN没有input功能。
input_val:写入input_reg寄存器的值。
mux_mode:0X5表示复用为GPIO1_IO09
*/
//imx6ull-alientek-emmc.dts
MX6UL_PAD_UART1_RTS_B_GPIO1_IO09 0X17059
//0X17059是写给电气配置寄存器的
2)pinctrl驱动:
**找对应设备驱动文件的方法:**用compatible的值,在vscode中全局搜索,找到的对应.c文件就是驱动文件。
在设备树中添加pinctrl节点模板–如何在设备树中添加某个外设PIN信息:
假设一个test设备,使用了GPIO1_IO00这个PIN的GPIO功能。
①创建对应节点:
同一个外设的PIN都放在一个节点中,打开对应的dts,在iomuxc节点中imx6ul-evk子节点下添加pinctrl_test节点。
②添加"fsl,pins"属性:
I.MX系列的SOC,pinctrl驱动程序通过读取该属性值来获取PIN配置信息。
③添加PIN配置信息:
pinctrl_test: testgrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config /*config 是具体设置值*/
>;
};
2. gpio子系统
如果pinctrl子系统将一个PIN复用为GPIO,接下来就需要调用gpio子系统,初始化GPIO并提供相应的API。
在设备树中添加GPIO相关信息,然后在驱动程序中使用gpio子系统提供的API来操作GPIO。
1)设备树中的gpio信息:
前面将UART1_RTS_B复用为GPIO1_IO19,作为SD的CD引脚。那么SD卡驱动程序需要建立这个映射关系,因此需要在dts中,SD卡对应的节点“usdhc1”下添加一个属性描述这个映射。
&usdhc1 {
...
/* pinctrl-3 = <&pinctrl_hog_1>; */
cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>; //低电平有效
...
}
2)gpio子系统API:
①gpio_requst:用于申请一个GPIO管脚。
int gpio_request(unsigned gpio, const char *label)
/*
para:要申请的gpio标号,给gpio设置的名字。
return:0表示成功
*/
②gpio_free:用于释放一个GPIO管脚。
void gpio_free(unsigned gpio)
//para:要申请的gpio标号
③gpio_direction_input:用于设置某GPIO为输入。
int gpio_direction_input(unsigned gpio)
/*
para:要设置输入的gpio标号
return:0表示成功
*/
④gpio_direction_output:用于设置某GPIO为输出。
int gpio_direction_output(unsigned gpio, int value)
/*
para:要设置输出的gpio标号,gpio默认输出值
return:0表示成功
*/
⑤gpio_get_value:用于获取某个 GPIO 的值(0 或 1),此函数是个宏。
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)
/*
para:要获取的gpio标号
return:0表示成功
*/
⑥gpio_set_value:用于设置某个 GPIO 的值,此函数是个宏。
#define gpio_set_value __gpio_set_value
int __gpio_set_value(unsigned gpio, int value)
/*
para:要设置的gpio标号,设置的值
return:0表示成功
*/
3)设备树中添加gpio节点模板:
①在根节点下创建test设备节点。
②添加pinctrl信息,表示test设备的PIN信息在pincttrl_test节点中。
③添加GPIO属性信息。
test {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_test>;
gpio = <&gpio1 0 GPIO_ACTIVE_LOW>;
};
4)与gpio相关的OF函数:
**①of_gpio_named_count:**用于获取设备树某个属性(任意属性)里面定义了几个 GPIO 信息。
int of_gpio_named_count(struct device_node *np, const char *propname)
/*
para:np设备节点,propname要统计的GPIO属性
return:正值表示统计到的GPIO数量,负值表示失败
*/
**②of_gpio_count:**统计的是“gpios”这个属性的 GPIO 数量。
int of_gpio_count(struct device_node *np)
/*
para:np设备节点
return:正值表示统计到的GPIO数量,负值表示失败
*/
**③of_get_named_gpio:**获取GPIO编号。
此函数会将设备树中类似<&gpio5 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号
int of_get_named_gpio(struct device_node *np, const char *propname, int index)
/*
para:np设备节点,propname要统计的GPIO属性
index:GPIO 索引,一个属性里面可能包含多个 GPIO,此参数指定获取哪个 GPIO的编号,如果只有一个 GPIO 信息的话此参数为 0。
return:正值表示获取到的GPIO编号
*/