目录
一、 pinctrl和gpio子系统
1.pinctrl子系统
1.1 pinctrl子系统简介
1.2 pinctrl子系统驱动
1.3 设备树中添加pinctrl节点模版
2. gpio子系统
2.1 gpio子系统简介
2.2 gpio子系统驱动
2.3 gpio子系统API函数
2.4 设备树中添加gpio节点模板
2.5 与gpio相关的OF函数
3. 驱动程序编写
3.1 驱动入口函数
3.2 驱动出口函数
Linux 是一个庞大而完善的系统,尤其是驱动框架,像 GPIO 这种最基本的驱动不可能采用“原始”的裸机驱动开发方式。Linux 内核提供了 pinctrl 和 gpio 子系统用于GPIO 驱动,
Linux 驱动讲究驱动分离与分层,pinctrl 和 gpio 子系统就是驱动分离与分层思想下的产物
要先设置某个 PIN 的复用功能、速度、上下拉等,然后再设置 PIN 所对应的 GPIO。其实对于大多数的 32 位 SOC 而言,引脚的设置基本都是这两方面,因此 Linux 内核针对 PIN 的配置推出了 pinctrl 子系统,对于 GPIO的配置推出了 gpio 子系统。
pinctrl 子系统主要工作内容如下:
①、获取设备树中 pin 信息。
②、根据获取到的 pin 信息来设置 pin 的复用功能
③、根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。
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. 创建对应的节点
同一个外设的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是具体设置值*/
>
}
pinctrl子系统重点是设置PIN(PAD)的复用和电气属性,若pinctrl子系统将一个PIN复用为GPIO的话,则接下来就要用到gpio子系统
gpio子系统的主要目的就是方便驱动开发者使用gpio,驱动开发者在设备树中添加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
对于驱动开发人员,设置好设备树以后就可以使用 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)
创建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>;
};
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)
test_gpio
int test_gpio; /* test 所使用的 GPIO 编号 */
在设备结构体test_dev中加入test_gpio这个成员变量,保存test设备所使用的的GPIO编号
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、创建设备*/
}
static void __exit led_exit(void)
{
/*注销字符设备驱动*/
/*1、删除cdev*/
/*2、取消注册字符设备*/
/*3、设备销毁*/
/*4、类销毁*/
}