linux驱动开发(一)—GPIO驱动框架

来源:互联网 发布:java二叉树的镜像树 编辑:程序博客网 时间:2024/05/22 09:04

转载地址:http://blog.csdn.net/zengxianyang/article/details/50589214

前言

         GPIO驱动是Linux驱动开发中最基础、但却是很常用、很重要的驱动。比如你要点亮一个LED灯、键盘扫描、输出高低电平等等。而Linux内核的强大之处在于对最底层的GPIO硬件操作层的基础上封装了一些统一的GPIO操作接口,也就是所谓的GPIO驱动框架。这样开发人员可以调用这些接口去操作设备的IO口,不需要担心硬件平台的不同导致IO口的不同。

         今天,我主要讲的就是如何使用Linux内核封装好的GPIO接口函数在驱动开发中需要操作GPIO时候的使用。

          文章部分内容参考他人博客,特此声明!

概述

       GPIO是与硬件体系密切相关的,linux提供一个模型来让驱动统一处理GPIO,即各个板卡都有实现自己的gpio_chip控制模块:request, free, input,output, get,set,irq...然后把控制模块注册到内核中,这时会改变全局gpio数组:gpio_desc[]. 当用户请求gpio时,就会到这个数组中找到,并调用这个GPIO对应的gpio_chip的处理函数。gpio实现为一组可用的 gpio_chip, 由驱动传入对应 gpio的全局序号去 request, dataout ,datain, free. 这时会调用gpio_chip中具体的实现。

        GPIO是一组可控件的脚,由多个寄存器同时控制。通过设置对应的寄存器可以达到设置GPIO口对应状态与功能。数据状态,输入输出方向,清零,中断(哪个边沿触发), 一般是一组(bank)一组的。寄存器读写函数: __raw_writel() __raw_writeb() __raw_readl() __raw_readb()。

1. Linux内核中GPIO模型的结构

1.1 struct gpio_desc

//表示一个gpio口,含对应的gpio_chip.
//对于每一个gpio,都有一个gpio描述符,这个描述符包含了这个gpio所属的控制器即chip和一些标志,label等。

[objc] view plain copy
  1. struct gpio_desc {  
  2.     struct gpio_chip    *chip;  
  3.     unsigned long       flags;  
  4. /* flag symbols are bit numbers */  
  5. #define FLAG_REQUESTED  0  
  6. #define FLAG_IS_OUT 1  
  7. #define FLAG_EXPORT 2   /* protected by sysfs_lock */  
  8. #define FLAG_SYSFS  3   /* exported via /sys/class/gpio/control */  
  9. #define FLAG_TRIG_FALL  4   /* trigger on falling edge */  
  10. #define FLAG_TRIG_RISE  5   /* trigger on rising edge */  
  11. #define FLAG_ACTIVE_LOW 6   /* value has active low */  
  12. #define FLAG_OPEN_DRAIN 7   /* Gpio is open drain type */  
  13. #define FLAG_OPEN_SOURCE 8  /* Gpio is open source type */  
  14. #define FLAG_USED_AS_IRQ 9  /* GPIO is connected to an IRQ */  
  15.   
  16. #define ID_SHIFT    16  /* add new flags before this one */  
  17.   
  18. #define GPIO_FLAGS_MASK     ((1 << ID_SHIFT) - 1)  
  19. #define GPIO_TRIGGER_MASK   (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))  
  20.   
  21. #ifdef CONFIG_DEBUG_FS  
  22.     const char      *label;  
  23. #endif  
  24. };  
  25. //采用了一个具有ARCH_NR_GPIOS大小的gpio描述符数组。这个描述符数组便代表了系统所有的gpio。  
[objc] view plain copy
  1. static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];  

1.2  struct davinci_gpio_controller

//一组GPIO控制器结构,例如GPIO0和GPIO1是一组(共32个GPIO口),共用一组寄存器,所以GPIO0和GPIO1荷载一起用chips[0]来控制
//假如有144个GPIO,分为4组(GPIO0~GPIO8),每组有2个banks(即GPIO0和GPIO1为1组),每组最多可以有32个GPIO,每组的控制寄存器空间有10个

[objc] view plain copy
  1. struct davinci_gpio_controller {  
  2.   
  3.     struct gpio_chip    chip;//每组对应的gpio_chip  
  4.   
  5.     int            irq_base;//每组对应的中断  
  6.   
  7.     spinlock_t        lock;//自旋锁  
  8.   
  9.     void __iomem        *regs;//每组的寄存器地址  
  10.   
  11.     void __iomem        *set_data;//设置数据寄存器地址  
  12.   
  13.     void __iomem        *clr_data;//清除数据寄存器地址  
  14.   
  15.     void __iomem        *in_data;//输入数据寄存器地址  
  16.   
  17. };   


1.3 struct gpio_chip

//每一个davinci_gpio_controller结构都对应于一个gpio_chip结构,gpio_chip既可看成是davinci_gpio_controller结构的补充
//表示一个gpio controller.通过这个结构抽象化所有的GPIO源,而让板上其它的模块可以用相同的接口调用使用这些GPIO。

[html] view plain copy
  1. struct gpio_chip {  
  2.   
  3.     const char    *label;  
  4.   
  5.     struct device    *dev;  
  6.   
  7.     struct module    *owner;   
  8.   
  9.     int    (*request)(struct gpio_chip *chip,unsigned offset);//请求gpio  
  10.   
  11.     void    *free)(struct gpio_chip *chip,unsigned offset);//释放gpio  
  12.   
  13.     int    (*get_direction)(struct gpio_chip *chip,unsigned offset);  
  14.   
  15.     int    (*direction_input)(struct gpio_chip *chip,unsigned offset);//配置gpio为输入,返回当前gpio状态  
  16.   
  17.     int    (*get)(struct gpio_chip *chip,unsigned offset);//获取gpio的状态  
  18.   
  19.     int    (*direction_output)(struct gpio_chip *chip,unsigned offset, int value);//配置gpio为输出,并设置为value  
  20.   
  21.     int    (*set_debounce)(struct gpio_chip *chip,unsigned offset, unsigned debounce);//设置消抖动时间,尤其是gpio按键时有用  
  22.   
  23.     void    (*set)(struct gpio_chip *chip,unsigned offset, int value);//设置gpio为value值  
  24.   
  25.     int    (*to_irq)(struct gpio_chip *chip,unsigned offset);//把gpio号转换为中断号  
  26.   
  27.     void    (*dbg_show)(struct seq_file *s,struct gpio_chip *chip);  
  28.   
  29.     int    base;// 这个gpio控制器的gpio开始编号  
  30.   
  31.     u16    ngpio;//这个gpio控制器说控制的gpio数  
  32.   
  33.     const char    *const *names;  
  34.   
  35.     unsigned    can_sleep:1;  
  36.   
  37.     unsigned    exported:1;  
  38.   
  39.    
  40.   
  41. #if defined(CONFIG_OF_GPIO)  
  42.   
  43.     struct device_node *of_node;  
  44.   
  45.     int of_gpio_n_cells;  
  46.   
  47.     int (*of_xlate)(struct gpio_chip *gc,const struct of_phandle_args *gpiospec, u32 *flags);  
  48.   
  49. #endif  
  50.   
  51. #ifdef CONFIG_PINCTRL  
  52.   
  53.     struct list_head pin_ranges;  
  54.   
  55. #endif  
  56.   
  57. };  


1.4 struct davinci_gpio_regs

//GPIO寄存器结构

[objc] view plain copy
  1. struct davinci_gpio_regs {  
  2.   
  3.     u32 dir; // gpio方向设置寄存器   
  4.   
  5.     u32 out_data; // gpio设置为输出时,表示输出状态(0或1)   
  6.   
  7.     u32 set_data; // gpio设置为输出时,用于输出高电平   
  8.   
  9.     u32 clr_data; // gpio设置为输出时,用于输出低电平   
  10.   
  11.     u32 in_data; // gpio设置为输入时,用于读取输入值   
  12.   
  13.     u32 set_rising; // gpio中断上升沿触发设置   
  14.   
  15.     u32 clr_rising; // gpio中断上升沿触发清除   
  16.   
  17.     u32 set_falling; // gpio中断下降沿触发设置   
  18.   
  19.     u32 clr_falling; // gpio中断下降沿触发清除   
  20.   
  21.     u32 intstat; // gpio中断状态位,由硬件设置,可读取,写1时清除。   
  22.   
  23. };  


1.5 struct gpio

struct gpio {

    unsigned gpio;//gpio号

    unsigned long flags;//gpio标志

    const char *label;//gpio名

};

2.  驱动开发中GPIO初始化操作

在实际的驱动开发中,根据板级资源和CPU手册,GPIO初始化一般需要以下三个步骤:

       1.设置IO口的复用模式,如果某个IO当作GPIO使用,那么就需要根据CPU手册去配置iomux(IO复用寄存器)为GPIO模式;

        2.设置IO口的输入输出方向,根据实际开发需求,将相应的GPIO配置为相应的输入输出方向;

        3.GPIO初始化赋值(输出高低电平)、拉高拉低操作;

2.1 GPIO申请

#########################################

#description:申请一个GPIO资源

#unsigned gpio:要申请的GPIO管脚号,为一个正整数

# const char *label:为申请的GPIO管脚取个名字                 

#########################################           

int gpio_request(unsigned gpio, const char *label);

int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);

2.2 GPIO输入输出设置

#########################################

#description:设置某个GPIO的输入输出方向

#unsigned gpio:要设置的GPIO管脚号,为一个正整数

# int value:设置的值                

#########################################           

int gpio_direction_input(unsigned gpio);
 int gpio_direction_output(unsigned gpio, int value);

2.3 获取GPIO管脚的值和设置GPIO管脚的值

#########################################

#description:获取、设置某个GPIO的值

#unsigned gpio:要获取、设置的GPIO管脚号

# int value:设置的值                

#########################################

int gpio_get_value(unsigned gpio);
void gpio_set_value(unsigned gpio, int value);

2.4 GPIO当作中断口使用

#########################################

#description:设置某个GPIO为中断口

#unsigned gpio:要设置中断的GPIO管脚号            

#########################################

int gpio_to_irq(unsigned gpio);

返回的值即中断编号可以传给request_irq()和free_irq(),内核通过调用该函数将gpio端口转换为中断,在用户空间也有类似方法。

3. GPIO驱动实例

3.1 以下GPIO驱动例子为矩阵键盘中对GPIO的操作

//设置某个管脚为输入

[objc] view plain copy
  1. int set_key_input(unsigned int gpio)  
  2. {  
  3.     char name[32];  
  4.     sprintf(name, "GPIO%d", gpio);  
  5.       
  6.     if(gpio_request(gpio,NULL) != 0)  
  7.     {  
  8.         printk("gpio request error!\n");  
  9.         return -1;  
  10.     }  
  11.     gpio_direction_input(gpio);  
  12.     gpio_free(gpio);  
  13.     return 0;  
  14. }  

//设置某个管脚为输出

[objc] view plain copy
  1. int set_key_output(unsigned int gpio,int value)  
  2. {  
  3.     char name[32];  
  4.     sprintf(name, "GPIO%d", gpio);  
  5.   
  6.     if(gpio_request(gpio,NULL) != 0)  
  7.     {  
  8.         printk("gpio request error!\n");  
  9.         return -1;  
  10.     }  
  11.     gpio_direction_output(gpio,value);  
  12.     gpio_free(gpio);  
  13.     return 0;  
  14. }  


//获取某个GPIO管脚的值

[objc] view plain copy
  1. int get_key_value(unsigned int gpio)  
  2. {  
  3.     int value= -1;  
  4.     if (gpio_request(gpio, NULL) != 0)  
  5.     {  
  6.         printk("get_key_value err\n");          
  7.         return -1;  
  8.     }  
  9.     value = gpio_get_value(gpio);  
  10.     gpio_free(gpio);  
  11.   
  12.     return value;  
  13. }  


//设置某个GPIO输出为低电平

[objc] view plain copy
  1. int set_key_low(unsigned int gpio)  
  2. {  
  3.   
  4.     if (gpio_request(gpio, NULL) != 0) {  
  5.         //printk("set_key_low request err\n");  
  6.         return -1;  
  7.     }  
  8.     gpio_direction_output(gpio, 0);  
  9.     __gpio_set_value(gpio, 0);  
  10.     gpio_free(gpio);  
  11.   
  12.     return 0;  
  13. }  

//拉高、拉低某个GPIO

//GPIO的拉高拉低操作内核没有提供通用的接口函数,这个需要驱动开发人员根据CPU手册的寄存器配置去封装拉高拉低函数,以下给出一个伪代码的例子:

//假设拉高GPIO1_IO01这个IO:

[objc] view plain copy
  1. <span style="font-size:12px;">#define SET_PULL_UP 0x01  
  2. #define SET_PULL_DOWN 0x00  
  3. #define REG_GPIO_BASE 0x8e000000  
  4. #define GPIO1_IO01_OFFSET 0x400  
  5.   
  6. int set_pull_up(unsigned int reg_base,unsigned int offset,int up)  
  7. {  
  8.      unsigned int gpio_base;  
  9.      unsigned int gpio;  
  10.   
  11.      gpio_base = ioreamap(reg_base,SIZE_4K);//调用ioreamap映射GPIO空间到内存,映射大小根据实际需求而定  
  12.      gpio = gpio_base + offset;   
  13.      gpio |= up;        //将某个GPIO拉高,根据具体的寄存器操作而定   
  14.      __raw_writel(gpio,gpio_base + offset);  
  15.      return 0;    
  16. }  
  17.   
  18. set_pull_up(REG_GPIO_BASE,GPIO1_IO01_OFFSET,SET_PULL_UP);</span>  


//拉低某个GPIO

[objc] view plain copy
  1. #define SET_PULL_UP 0x01  
  2. #define SET_PULL_DOWN 0x00  
  3. #define REG_GPIO_BASE 0x8e000000  
  4. #define GPIO1_IO01_OFFSET 0x400  
  5.   
  6. int set_pull_down(unsigned int reg_base,unsigned int offset,int down)  
  7. {  
  8.      unsigned int gpio_base;  
  9.      unsigned int gpio;  
  10.   
  11.      gpio_base = ioreamap(reg_base,SIZE_4K);//调用ioreamap映射GPIO空间到内存,映射大小根据实际需求而定  
  12.      gpio = gpio_base + offset;   
  13.      gpio &= down;      //将某个GPIO拉低,根据具体的寄存器操作而定   
  14.      __raw_writel(gpio,gpio_base + offset);  
  15.      return 0;    
  16. }  
  17.   
  18. set_pull_up(REG_GPIO_BASE,GPIO1_IO01_OFFSET,SET_PULL_DOWN);  

 

3.2 总结

         3.1中展示了基本的GPIO操作函数的编写,在实际的驱动开发中,比如对某个连接到CPU的GPIO管脚的外设模块需要初始化的时候,一般都是调用GPIO接口函数进行输入输出、拉高、拉低设置,读者可以参考3.1的例子根据实际开发需求进行修改。

原创粉丝点击