【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); 
        }
View Code

而现在,由于用了 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";
};        

 

posted @ 2023-06-16 09:41  FBshark  阅读(199)  评论(0编辑  收藏  举报