目录
一、 pinctrl和gpio子系统
Linux 是一个庞大而完善的系统,尤其是驱动框架,像 GPIO 这种最基本的驱动不可能采用“原始”的裸机驱动开发方式。Linux 内核提供了 pinctrl 和 gpio 子系统用于GPIO 驱动,
Linux 驱动讲究驱动分离与分层,pinctrl 和 gpio 子系统就是驱动分离与分层思想下的产物
要先设置某个 PIN 的复用功能、速度、上下拉等,然后再设置 PIN 所对应的 GPIO。其实对于大多数的 32 位 SOC 而言,引脚的设置基本都是这两方面,因此 Linux 内核针对 PIN 的配置推出了 pinctrl 子系统,对于 GPIO的配置推出了 gpio 子系统。
1.pinctrl子系统
1.1 pinctrl子系统简介
-
pinctrl 子系统主要工作内容如下:
①、获取设备树中 pin 信息。
②、根据获取到的 pin 信息来设置 pin 的复用功能
③、根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。
1.2 pinctrl子系统驱动
-
PIN配置信息详解
要使用pinctrl子系统,需要在设备树里设置PIN的配置信息,pinctrl子系统会根据提供的信息去配置PIN功能,一般会在设备树里创建一个节点来描述PIN的配置信息
如何向iomuxc(描述IOMUXC设备的节点)追加数据?
不同的外设使用的PIN不同,配置也不同,因此一个萝卜一个坑,将某个外设所使用的的所有PIN都组织在一个子节点里
例如:
iomuxc: iomuxc@020e0000 {
compatible = "fsl,imx6ul-iomuxc";
reg = <0x020e0000 0x4000>;
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
MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059
MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059
MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058
>;
......
};
};
};
"fsl,pins"为pinctrl_hog_1 子节点所使用的 PIN 配置信息,对于一个 PIN 的配置主要包括两方面,一个是设置这个 PIN 的复用功能,另一个就是设置这个 PIN 的电气特性
-
PIN 驱动程序讲解
《正点驱动文档》P1163
1.3 设备树中添加pinctrl节点模版
1. 创建对应的节点
同一个外设的PIN都放到一个节点里,如在iomuxc节点中的imx6ul-evk子节点下添加"pinctrl_test"节点
pinctrl_test:testgrp
{
/*具体的PIN信息*/
}
2, 添加"fsl,pins"属性
设备树通过属性来保存信息,对于I.MX系列SOC而言,pinctrl驱动程序是通过读取"fsl,pins"属性值来获取PIN的配置信息
pinctrl_test:testrp
{
fsl,pins=<
/*设备所使用的PIN配置信息*/
>
}
3. 在“fsl,pins”属性中添加具体的PIN配置信息
pinctrl_test:testrp
{
fsl,pins=<
MX6UL_PAD_GPIO1_IO00_GPIO1_IO00 config/*config是具体设置值*/
>
}
2. gpio子系统
2.1 gpio子系统简介
pinctrl子系统重点是设置PIN(PAD)的复用和电气属性,若pinctrl子系统将一个PIN复用为GPIO的话,则接下来就要用到gpio子系统
gpio子系统的主要目的就是方便驱动开发者使用gpio,驱动开发者在设备树中添加gpio相关信息
2.2 gpio子系统驱动
-
设备树中的gpio信息
pinctrl配置好以后就是设置gpio了,驱动程序通过相关GPIO进行判断处理等
-
如何让驱动程序知道相应引脚连接的是相关的GPIO呢
需要设备树来告诉驱动,在设备树设备节点下添加一个属性来描述设备引脚,设备驱动直接读取这个属性值就知道相应引脚使用哪个GPIO
示例代码 45.2.2.2 设备树中 SD 卡节点
&usdhc1 {
pinctrl-names = "default", "state_100mhz", "state_200mhz";
pinctrl-0 = <&pinctrl_usdhc1>;
pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
/* pinctrl-3 = <&pinctrl_hog_1>; */
cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
keep-power-in-suspend;
enable-sdio-wakeup;
vmmc-supply = <®_sd1_vmmc>;
status = "okay";
};
-
"cd-gpios"属性
cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
描述了SD卡的CD引脚使用的哪个IO,属性值一共有三个:
-
“&gpio1”:表示CD引脚所使用的的IO属于GPIO1组
-
“19”:表示GPIO1组的第19号IO
通过这两个值SD卡驱动程序就知道CD引脚使用了GPIO1_IO19这个GPIO
-
“GPIO_ACTIVE_LOW”:表示低电平有效
-
gpio1节点
描述了GPIO1控制器的所有信息,重点就是GPIO1外设寄存器基地址以及兼容属性
-
GPIO驱动程序简介
《正点驱动文档》P1171
2.3 gpio子系统API函数
对于驱动开发人员,设置好设备树以后就可以使用 gpio 子系统提供的 API 函数来操作指定的 GPIO,gpio 子系统向驱动开发人员屏蔽了具体的读写寄存器过程。这就是驱动分层与分离的好处
-
gpio_request函数
用于申请一个GPIO管脚,在使用一个GPIO之前一定要使用gpio_request进行申请
int gpio_request(unsigned gpio, const char *label)
-
gpio_free函数
如果不使用某个GPIO了,就可以调用gpio_free函数进行释放
void gpio_free(unsigned gpio)
-
gpio_direction_input函数
此函数用于设置某个GPIO为输入
int gpio_direction_input(unsigned gpio)
-
gpio_direction_output函数
此函数用于设置某个GPIO为输出,并且设置默认输出值
int gpio_direction_output(unsigned gpio, int value)
-
gpio_get_value函数
此函数用于获取某个GPIO的值
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)
-
gpio_set_value函数
此函数用于设置某个GPIO的值
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
2.4 设备树中添加gpio节点模板
-
创建test设备节点
在根节点“/”下创建test设备子节点
test{
/*节点内容*/
}
-
添加pinctrl信息
将描述test设备所使用的的GPIOx_IOxx这个PIN的信息的pinctrl_test节点添加到test设备节点中
test{
pinctrl-names="default";
pinctrl-0=<&pinctrl_test>;
/*其他节点内容*/
};
3.添加GPIO属性信息
最后需要在test节点中添加GPIO属性信息,表明test设备所使用的的GPIO是哪个引脚
test{
pinctrl-names="default";
pinctrl-0=<&pinctrl_test>;
gpio=<&gpio1 0 GPIO_ACTIVE_LOW>;
};
2.5 与gpio相关的OF函数
-
of_gpio_named_count函数
用于获取设备树某个属性里面定义了几个GPIO信息,空的GPIO信息也会被统计到
int of_gpio_named_count(struct device_node *np, const char *propname)
-
of_gpio_count函数
和of_gpio_named_count函数一样,但是不同的地方在于,此函数统计的是"gpios"这个属性的GPIO数量,而of_gpio_named_count函数可以统计任意属性的GPIO信息
int of_gpio_count(struct device_node *np)
-
of_get_named_gpio函数
获取GPIO编号,Linux内核关于GPIO的API函数都需要GPIO编号,此函数会将设备树中类似<&gpio5 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号,此函数很重要
int of_get_named_gpio(struct device_node *np,const char *propname, int index)
3. 驱动程序编写
-
test_gpio
int test_gpio; /* test 所使用的 GPIO 编号 */
在设备结构体test_dev中加入test_gpio这个成员变量,保存test设备所使用的的GPIO编号
3.1 驱动入口函数
static int __init led_init(void)
{
/*设置test设备所使用的的GPIO*/
/*1、获取设备节点:gpiotest (通过设备树中的路径/xxx)*/
/*2、获取gpio属性内容,得到test要使用的gpio编号*/
/*初始化LED*/
/*1、设置GPIO引脚的功能*/
/*2、默认设置*/
/*注册字符设备驱动*/
/*1、创建设备号*/
定义了设备号:register_chrdev_region()
没有定义设备号:alloc_chrdev_region()
/*2、初始化cdev*/
/*3、添加一个cdev*/
/*4、创建类*/
/*5、创建设备*/
}
3.2 驱动出口函数
static void __exit led_exit(void)
{
/*注销字符设备驱动*/
/*1、删除cdev*/
/*2、取消注册字符设备*/
/*3、设备销毁*/
/*4、类销毁*/
}