【Linux 驱动专栏】Linux 设备树下 pinctrl 和 gpio 子系统用和不用的区别
不用设备树 pinctrl 子系统下——使能GPIO的办法:
1. 定义相关宏——各寄存器的物理地址
2. 进行重映射——各寄存器的虚拟地址
3. 为各个寄存器写入参数
使用设备树 pinctrl 子系统下——使能GPIO的办法:
1. 添加pinctrl 信息
2. 添加设备节点信息:
注意:设备节点中pinctrl-0 要用到添加的 pinctrl 信息,名字应该相同(这里不同,是错误示范)。
3. 驱动文件中:找到设备树节点
4. 驱动文件中:确定GPIO编号
5. 由上一步得到的GPIO编号,设置GPIO的输入输出模式
对比之下:
1. 设备树方式不需要定义地址宏,不需要查找和设置其时钟寄存器、GDIR、DR寄存器的地址,需要在设备树文件中添加 pinctrl 信息,再在相应的节点添加相应的属性。
2. 设备树方式不需要通过 writel() 写入寄存器值,而是通过 GPIO 编号和Linux 提供的 API 函数来设置GPIO的输入输出模式,输出电平。
最重要的是两个函数:of_find_node_by_path() 和 of_get_named_gpio()
其中of_find_node_by_path() 在其中起着连接驱动文件和设备树的作用;而 of_get_named_gpio() 则为驱动文件用到的 Linux API 提供基础。
个人感觉:不用 pinctrl 的方式,程序更容易编写,
用 pinctrl 的方式,程序编写方面,虽然设置MUX设置输入输出模式比不用 pinctrl 的方式简单的多,但是要改设备树,要查imx6ul-pinfunc.h,还要用 Linux 特定的 OF 函数(挺不好记忆的)。
因此程序更难编写,但更容易阅读。
GPIO编号 与 of_get_named_gpio()函数详解
GPIO编号是什么呢?
Linux内核中关于 GPIO的 API函数都要使用 GPIO编号。
举控制LED的例子来说,原来控制 LED 的亮灭是通过 GPIO_DR 寄存器来实现的,如下所示:
if(writebuf[0] == LED_ON) { //led_value |= (1<<4); led_value &= ~(1<<4); writel(led_value, GPIO1_DR); } else if(writebuf[0] == LED_OFF) { //led_value &= !(1<<4); led_value |= (1<<4); writel(led_value, GPIO1_DR); }
而现在,由于用了 Linux 的 pinctrl 和 gpio 子系统,因此对于 GPIO 的操作,可以调用其系统函数( API函数)实现,其中就用到了 GPIO 编号:
if(ledstat == LEDON) { gpio_set_value(dev->led_gpio, 0); /* 打开LED灯 */ } else if(ledstat == LEDOFF) { gpio_set_value(dev->led_gpio, 1); /* 关闭LED灯 */ }
代码中的 gpio_set_value用于设置某个 GPIO的值,其函数原型为:
/*
gpio:要设置的 GPIO标号。
value 要设置的值。
返回值: 无
*/
void __gpio_set_value(unsigned gpio, int value)
其中的第一个参数——unsigned gpio 就是需要用到的 GPIO 编号。
那么如何获取这个编号呢?
of_get_named_gpio()
函数用于获取 GPIO编号,因为
此函数会将设备树中类似 <&gpio5 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO编
号,此函数在驱动中使用很频繁!函数原型如下:
1 int of_get_named_gpio 2 ( struct device_node *np, 3 const char *propname, 4 int index
)
函数参数和返回值含义如下:
np:设备节点。
propname:要获取的GPIO的属性名。
index GPIO索引,因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO的编号,如果只有一个 GPIO信息的话此参数为 0。
返回值: 正值,获取到的 GPIO编号;负值,失败。
其中,propname 为设备树中特定节点的某一个属性,其参数格式应该如 <&gpio1 4 GPIO_ACTIVE_LOW> 。
然后 of_get_named_gpio() 函数可以将 <&gpio1 4 >这样的格式转换为一个GPIO编号。
gpioled{ #address-cells = <1>; #size-cells = <1>; compatible = "fire-gpioled"; pinctrl-names = "default"; status = "okay"; pinctrl-0 = <&pinctrl_led>; led-gpio = <&gpio1 4 GPIO_ACTIVE_LOW>; status = "okay"; };