Bootstrap

迅为嵌入式linux驱动开发笔记(五)—pinctl和gpio子系统

一、pinctl和gpio子系统

pinctrl子系统

设置引脚的复用关系和电气属性。

2、Linux Pinctrl 子系统提供的功能是什么?

( 1 ) 管理系统中所有的可以控制的 pin , 在系统初始化的时候,枚举所有可以控制的 pin , 并标识这些 pin 。

( 2 ) 管理这些 pin 的复用( Multiplexing )。对于 SOC 而言,其引脚除了配置成普通 的 GPIO 之外,若干个引脚还可以组成一个 pin group,形成特定的功能。

( 3 ) 配置这些 pin 的特性。例如使能或关闭引脚上的 pull-up 、pull-down 电阻,配置引脚的 driver strength 。

3、不同 soc 厂家的 pin controller 的节点这些节点里都是把某些引脚复用成某些功能。

4、不同 soc 厂家的 pin controller 的节点里面的属性都是什么意思?

可以通过 Linux 源码目录 /Documentation/devicetree/bindings 下的 txt 文档查看。

5、怎么在代码里面使用 pin controller 里面定义好的节点?
例 1 :

 pinctrl-names = "default"; 
 pinctrl-0 = <&pinctrl_hog_1>; 

含义:
<1>pinctrl-names = “default”;
设备的状态,可以有多个状态, default 为状态 0

<2>pinctrl-0 = <&pinctrl_hog_1>;
第 0 个状态所对应的引脚配置,也就是 default 状态对应的引脚在 pin controller 里面定义好 的节点 pinctrl_hog_1 里面的管脚配置

例 2 :

pinctrl-names = "default""wake up";
pinctrl-0 = <&pinctrl_hog_1>; 
pinctrl-1 = <&pinctrl_hog_2>; 

含义
<1>pinctrl-names = “default” , “wake up”;
设备的状态,可以有多个状态, default 为状态 0 , wake up 为状态 1

<2>pinctrl-0 = <&pinctrl_hog_1>;
第 0 个状态所对应的引脚配置,也就是 default 状态对应的引脚在 pin controller 里面定义好 的节点 pinctrl_hog_1 里面的管脚配置

<3>pinctrl-1 = <&pinctrl_hog_2>;
第 1 个状态所对应的引脚配置,也就是 wake up 状态对应的引脚在 pin controller 里面定义 好的节点 pinctrl_hog_2 里面的管脚配置

例 3 :

pinctrl-names = "default"; 
pinctrl-0 = <&pinctrl_hog_1 &pinctrl_hog_2>;

含义
<1>pinctrl-names = “default”; 设备的状态,可以有多个状态, default 为状态 0

<2>pinctrl-0 = <&pinctrl_hog_1 &pinctrl_hog_2>;
第 0 个状态所对应的引脚配置,也就是 default 状态对应的引脚在 pin controller 里面定义好 的节点 , 对应pinctrl_hog_1 和 pinctrl_hog_2 这俩个节点的管脚配置。

gpio 子系统

那么我们先来看一下怎么在设备树中 pinctrl 和 gpio 子系统描述一个 gpio 。

当pinctrl 把引脚设置为gpio子系统之后,就可以用gpio子系统来控制引脚了。

<1> 设备树使用 pinctrl 和 gpio 子系统描述一个 gpio 。

test1:test{ 
#address-cells = <1>; 
#size-cells = <1>; 

compatible = "test"; 
reg = <0x20ac000 0x0000004>; 
pinctrl-names = "default"; 
pinctrl-0 = <&pinctrl_test>; 
test-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
 };

<2> 常用 gpio 子系统提供的 api 函数

1 、 gpio_request 函数

作用: gpio_request 函数用于申请一个 GPIO 管脚
函数原型:

int gpio_request(unsigned gpio, const char *label) 

参数:
gpio : 要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属 性信息,此函数会返回这个 GPIO 的标号。

label : 给 gpio 设置个名字。 返回值: 0 , 申请成功;其他值,申请失败。

2 、 gpio_free 函数

作用:如果不使用某个 GPIO 了,那么就可以调用 gpio_free 函数进行释放。

函数原型: void gpio_free(unsigned gpio)

参数: gpio : 要释放的 gpio 标号。

返回值:无。

3 、 gpio_direction_input 函数

作用: 此函数用于设置某个 GPIO 为输入

函数原型:

int gpio_direction_input(unsigned gpio) 

参数 : gpio : 要设置为输入的 GPIO 标号。

返回值:设置成功返回 0 ; 设置失败返回负值

4 、 gpio_direction_output 函数
作用:此函数用于设置某个 GPIO 为输出,并且设置默认输出值

函数原型:

int gpio_direction_output(unsigned gpio, int value)

参数:

gpio : 要设置为输出的 GPIO 标号。

value : GPIO 默认输出值。 返回值:设置成功返回 0 ; 设置失败返回负值

5 、 gpio_get_value 函数

作用: 此函数用于获取某个 GPIO 的值 (0 或 1)

函数原型:

 int __gpio_get_value(unsigned gpio) 

参数:

gpio : 要获取的 GPIO 标号。

返回值:成功返回 GPIO 值,失败返回负值。

6 、 gpio_set_value 函数

作用: 此函数用于设置某个 GPIO 的值 函数原型:

void __gpio_set_value(unsigned gpio, int value) 

参数: gpio : 要设置的 GPIO 标号。

value : 要设置的值。 返回值:无

7 、 of_get_named_gpio

函数作用: 此函数获取 GPIO 编号,因为 Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编 号,此函数会将设备树中类似 <&gpio1 3 GPIO_ACTIVE_LOW> 的属性信息转换为对应的 GPIO 编号,函数原型如下:

int of_get_named_gpio(struct device_node *np,const char *propname,int index) 

参数:

np : 设备节点。

propname : 包含要获取 GPIO 信息的属性名。

index : 因为一个属性里面可能包含多个 GPIO ,此参数指定要获取哪个 GPIO 的编号,如果 只有一个 GPIO 信息的话此参数为 0 。

返回值:

成功返回到的 GPIO 编号,失败返回一个负数。

实践课

使用gpio子系统控制蜂鸣器,基本配置文件如下所示:

				.
				.
				.
				.
				.
				.
				.

    beep_gpio = of_get_named_gpio(test_device_node,"beep_gpio",0);
    if (beep_gpio < 0){
        printk("of_get_named_gpio is erron\n");
        return -1;
    }

    printk("beep_gpio is %d\n",beep_gpio);

    ret = gpio_request(beep_gpio,"beep_gpio");
    if (ret<0)
    {
        printk("gpio_request is erron\n");
        return -1;
    }
    
    gpio_direction_output(beep_gpio,1);//引脚配置

    ret = misc_register(&misc_dev);//注册杂项设备
    if (ret<0)
    {
        printk("misc_register is error\n");
        return -1;
    }

在杂项设备中,颠覆了以往使用蜂鸣器,而使用gpio子系统控制蜂鸣器。

 if (kbuf[0] == 1)
        gpio_set_value(beep_gpio,1);//open  寄存器操作换成了GPIO子系统的函数
    else if (kbuf[0] == 0)
        gpio_set_value(beep_gpio,0);//close

ioctl接口

unlocked_ioctl 接口

1、什么是 unlocked_ioctl 接口?

unlocked_ioctl 就是 ioctl 接口,但是功能和对应的系统调用均没有发生变化。

2、 unlocked_ioctl 和 read/write 函数有什么相同点和不同点?

相同点:都可以往内核写数据。

不同点: read 函数只能完成读的功能,write 只能完成写的功能。 但是读取大数据的时候效率高。

ioctl 既可以读也可以写。读取大数据的时候效率不高。

3、unlocked_ioctl 接口命令规则。
一共是32位。

第一个分区:0-7,命令的编号,范围是 0-255。

第二个分区:8-15 ,命令的幻数。

第一个分区和第二个分区主要作用是用来区分命令的。编号一样,幻数不能一样。

第三个分区: 16-29 表示传递的数据大小。

第四个分区: 30-31 代表读写的方向。因为ioctl既可以读也可以写。

00 : 表示用户程序和驱动程序没有数据传递
10 : 表示用户程序从驱动里面读数据
01 : 表示用户程序向驱动里面写数据
11 : 先写数据到驱动里面,然后在从驱动里面把数据读出来。(用得不多)

4、 命令的合成宏与分解宏
合成宏:

_IO(type,nr) : 用来定义没有数据传递的命令 

_IOR(type,nr,size) : 用来定义从驱动中读取数据的命令 

_IOW(type,nr,size) : 用来定义向驱动写入数据的命令

_IOWR(type,nr,size) :用来定义数据交换类型的命令,先写入数据,再读取数据这类命令。

参数: type : 表示命令组成的魔数,也就是 8~15 位 

nr : 表示命令组成的编号,也就是 0~7 位 

size : 表示命令组成的参数传递大小,注意这里不是传递数字,而是数据类型,如要传递 4 字节,就可以写成 int

分解宏:


_IOC_DIR(nr) 分解命令的方向,也就是上面说 30~31 位的值 

_IOC_TYPE(nr) 分解命令的魔数,也就是上面说 8~15 位的值 

_IOC_NR(nr) 分解命令的编号,也就是上面说 0~7_IOC_SIZE(nr) 分解命令的复制数据大小,也就是上面说的 16~29 位 

参数说明: nr : 要分解的命令

实践课

通过应用层传递参数到驱动层控制硬件io。
驱动层代码:

#define CMD_TEST0   _IO('L',0)
#define CMD_TEST1   _IO('L',1)
#define CMD_TEST2   _IOW('L',2,int)
#define CMD_TEST3   _IOW('L',3,int)
#define CMD_TEST4   _IOW('L',4,int)

long misc_ioctl(struct file *file,unsigned int cmd, unsigned long value)
{
    int val;

    switch(cmd){
        case CMD_TEST0:
            printk("LED ON!!!\n");
            printk("value is %ld\n",value);
            break;
        case CMD_TEST1:
            printk("LED OFF!!!\n");
            printk("value is %ld\n",value);
        break;
        case CMD_TEST4:
            val = 12;
            //从驱动中读取数据
            if(copy_to_user((int *)value,&val,sizeof(val))!=0)
            {
                printk("copy to user error\n");
                return -1;
            }  
        break;
        
    }
    return 0;
}

struct file_operations  misc_fops ={
    .owner = THIS_MODULE,
    .open = misc_open,
    .release = misc_close,
    .read = misc_read,
    .write = misc_write,
    
    .unlocked_ioctl = misc_ioctl//添加结构体成员
};

应用层代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define CMD_TEST0   _IO('L',0)
#define CMD_TEST1   _IO('L',1)
// #define CMD_TEST1   _IO('A',1)
#define CMD_TEST2   _IOW('L',2,int)
#define CMD_TEST3   _IOW('L',3,int)
#define CMD_TEST4   _IOW('L',4,int)

// #define CMD_TEST3   _IOR('L',3,int)

int main(int argc, char **argv)
{   
    int fd;
    int value;
    fd = open("/dev/hello_misc",O_RDWR);
    if (fd < 0) 
    {
        printf("open error\n");
        return fd;
    }
 
    while (1){
        ioctl(fd,CMD_TEST4,&value);

        printf("value is %d\n",value);

        sleep(2);
    }
    return 0;
}
;