Linux GPIO & IRQ Driver

本文主要描述了GPIO口的申請和高低電平輸出、輸入,以及中斷的申請和調用等

Linux Kernel 中使用GPIO,不能直接引用和操作GPIO的物理地址,而需要把物理地址映射爲內存中的虛擬地址,然後對映射的虛擬地址進行操作。

大致情況是這樣的,以下通過實際代碼分析linux GPIO的簡單的驅動分析。

一、一種情況的GPIO驅動和IRQ中斷服務申請

1.1、源碼實例:

#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/serial.h>

#include <asm/irq.h>
#include <mach/hardware.h>
#include <plat/regs-serial.h>
#include "samsung.h"

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/clk.h>

#include <linux/io.h>
#include <mach/hardware.h>
#include <linux/delay.h>
#include <asm/irq.h>

#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>

#include <plat/s5pv210.h>
#include <plat/gpio-cfg.h>
#include <linux/gpio.h>
struct scan_button_irq_desc
{	
	int irq;
	int pin;
	int pin_setting;
//	int number;
	char *name;
};

static struct scan_button_irq_desc scan_button_irq[] =
{
	{IRQ_EINT7,S5PV210_GPH0(7),IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,"scan_trig"},
};

static irqreturn_t  s3c_gpio_scan_isr(int irq,void* dev_id)
{

	unsigned int key_status;	
	u32 cfg;

	//void __iomem *base = S5PV210_GPG1DAT;
	cfg = readl(S5PV210_GPG1CON);
	cfg &= (0xf<<20); //clear gpg1_5 con
	cfg |= (1 << 20); //out put cfg
	writel(cfg,S5PV210_GPG1CON);
	printk("scanmodule pwr ctrl con: %d",readl(S5PV210_GPG1CON));
	
	cfg = readl(S5PV210_GPG1DAT);
	cfg |= (1 << 5);
	writel(cfg,S5PV210_GPG1DAT);
	printk("scanmodule pwr ctrl dat: %d",readl(S5PV210_GPG1DAT));

	key_status = (readl(S5PV210_GPH0DAT)) & (1 << 7);
	printk("scan_button,keyval:%d : %s\n", GPIO_KEY, key_status?"released":"pressed");
	
	return IRQ_HANDLED;
}

static int s3c_gpio_scan_isr_setup(void)
{
	int ret;
~~~~
	s3c_gpio_cfgpin(S5PV210_GPH0(7), S3C_GPIO_SPECIAL(0xf)); //配置gpio爲中斷輸出,Eint7  
	s3c_gpio_setpull(S5PV210_GPH0(7), S3C_GPIO_PULL_UP);  //S3C_GPIO_PULL_NONE
	
	set_irq_wake(scan_button_irq[0].irq, 1);
	ret = request_irq(scan_button_irq[0].irq, s3c_gpio_scan_isr,  
		IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING,
		scan_button_irq[0].name, (void *)&scan_button_irq);
	if (ret) {
		printk("request IRQ for scan triger failed\n");
		ret = -EIO;
	}
~~~~
	return ret;
}

static int __init XXX_init(void)
{
~~~~
	//中斷申請
	ret = s3c_gpio_scan_isr_setup();
	if(!ret)
		printk("IRQ request successfully\n");
	else
		printk("IRQ request failed");
~~~~
	return 0;
}

static void __exit XXX_exit(void)
{

~~~~
        //IRQ srv free
	free_irq(scan_button_irq[0].irq,(void *)&scan_button_irq);

~~~~

}

1.2、中斷申請函數原型及分析:

1.2.1、原型

int request_irq(unsigned int irqirqreturn_t (*handler)(int, void *,struct pt_regs *),unsigned long flags, const char *dev_name, void *dev_id);
void free_irq(unsigned int irq, void *dev_id);

1.2.2、分析

1)、unsigned int irq:所要註冊的中斷號
2)、irqreturn_t (*handler)(int, void *, struct pt_regs *):中斷服務程序的入口地址。
3)、unsigned long flags:與中斷管理有關的位掩碼選項,有三組值:
          SA_INTERRUPT : 快速中斷處理程序,當使用它的是後處理器上所有的其他中斷都被禁用;
          SA_SHIRQ :           該中斷是在設備之間可共享的;
          SA_SAMPLE_RANDOM :   這個位表示產生的中斷能夠有貢獻給 /dev/random和 /dev/urandom 使用的加密池。
4)、const char *dev_name:設備描述,表示那一個設備在使用這個中斷。
5)、void *dev_id:用作共享中斷線的指針. 它是一個獨特的標識, 用在當釋放中斷線時以及可能還被驅動用來指向它自己的私有數據區(來標識哪個設備在中斷) 。這個參數在真正的驅動程序中一般是指向設備數據結構的指針.在調用中斷處理程序的時候它就會傳遞給中斷處理程序的void *dev_id。(這是我的理解)如果中斷沒有被共享, dev_id 可以設置爲 NULL, 但是使用這個項指向設備結構不管如何是個好主意. 我們將在"實現一個處理"一節中看到 dev_id 的一個實際應用。

中斷號的查看可以使用下面的命令:“cat /proc/interrupts”。

# cat /proc/interrupts                                                          
           CPU0                                                                 
 24:        109    s3c-uart  s5pv210-uart                                       
 26:        164    s3c-uart  s5pv210-uart                                       
 35:          0  s5p_vic_eint  key pgio                                         
 36:          0  s5p_vic_eint  pmic Tick                                        
 38:          1  s5p_vic_eint  rt5625 HP Hotplug                                
 39:         15  s5p_vic_eint  scan_trig                                        
 45:          0  s5p_vic_eint  hpd                                              
 58:          1         VIC  System timer                                       
 59:          0         VIC  s3c2410-wdt                                        
 61:     271883         VIC  rtc-tick                                           
 78:       1992         VIC  s3c2410-i2c.0                                      
 83:          0         VIC  s3c2410-i2c.2                                      
 87:          0         VIC  ehci_hcd:usb1                                      
 88:       5180         VIC  s3c-udc                                            
 90:       4528         VIC  mmc0                                               
 91:          0         VIC  mmc1                                               
 92:          0         VIC  mmc2                                               
 97:          1         VIC  s3cfb, s3cfb                                       
101:          0         VIC  s3c-fimc0                                          
102:          0         VIC  s3c-fimc1                                          
103:         90         VIC  s3c-fimc2                                          
104:          0         VIC  s3c-jpg                                            
105:          0         VIC  s5p-g2d                                            
106:        740         VIC  PowerVR                                            
107:          0         VIC  s5p-tvout                                          
108:          0         VIC  s5p-tvout                                          
109:         57         VIC  s3c2410-i2c.1                                      
110:          0         VIC  s3c-mfc                                            
111:          0         VIC  s5p-tvout                                          
121:          1         VIC  s3c-keypad                                         
130:         16         VIC  mmc3                                               
131:          0         VIC  s5p-cec                                            
137:          0         VIC  s3c_action                                         
138:          0         VIC  s3c_updown                                         
Err:          0                                                                 
#

/proc/stat 記錄了幾個關於系統活動的低級統計量, 包括(但是不限於)自系統啓動以來收到的中斷數. stat 的每一行以一個文本字串開始, 是該行的關鍵詞; intr 標誌是我們在找的.

第一個數是所有中斷的總數, 而其他每一個代表一個單個 IRQ 線, 從中斷 0 開始. 所有的計數跨系統中所有處理器而彙總的. 這個快照顯示, 中斷號 4 已使用 1 次, 儘管當前沒有安裝處理. 如果你在測試的驅動請求並釋放中斷在每個打開和關閉循環, 你可能發現 /proc/stat 比 /proc/interrupts 更加有用.


二、GPIO Driver 另外一種申請方法

~~~~

#include <linux/gpio.h>
#include <linux/spi/spi.h>

#include <plat/gpio-cfg.h>
#include <plat/regs-fb.h>

#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-mem.h>
#include <mach/regs-gpio.h>
#include <mach/gpio.h> //s5pv210 base address define
#include <mach/gpio-bank.h>

~~~~
	if(gpio_request(S5PV210_GPJ1(3), "GPJ1_3"))  //S5PV210_GPJ1(3)爲GPIO的編號
		printk("S5PV210_GPJ1(3) ERR!\n");
	
	if(gpio_request(S5PV210_GPJ1(4), "GPJ1_4"))
		printk("S5PV210_GPJ1(4) ERR!\n");
	
	if(gpio_request(S5PV210_GPJ1(5), "GPJ1_5"))
		printk("S5PV210_GPJ1(5) ERR!\n");
	
	if(gpio_request(S5PV210_GPJ0(5), "GPJ0_5"))
		printk("S5PV210_GPJ0(5) ERR!\n");

	gpio_direction_output(S5PV210_GPJ1(3), 0); //直接設置爲輸出功能,並且直接輸出對應的高低電平
	gpio_direction_output(S5PV210_GPJ1(4), 0);
	gpio_direction_output(S5PV210_GPJ1(5), 0);
	gpio_direction_output(S5PV210_GPJ0(5), 0);

~~~~

	gpio_free(S5PV210_GPJ1(3));
	gpio_free(S5PV210_GPJ1(4));
	gpio_free(S5PV210_GPJ1(5));
	gpio_free(S5PV210_GPJ0(5));	

~~~~

gpio_set_value(port_num,0/1) 一般只是在這個GPIO口的寄存器上寫上某個值,至於這個端口是否設置爲輸出,它就管不了!而gpio_direction_output (port_num,0/1),在某個GPIO口寫上某個值之後,還會把這個端口設置爲輸出模式。 因此,有人也許就會建議,把gpio_set_value這個函數直接去掉不用,是否可以,顯然是可以的。 但是爲什麼系統還要用呢, 我個人分析是, 系統開發人員在要結合這兩者來使用,以便提高效率。 一般某個端口設置好了輸入與輸出模式後,最好不要經常變動。 首先要調用gpio_direction_output(),以後要設置高低電平時,直接使用gpio_set_value()就可以了,這樣可以省卻再次調用設置輸出模式的操作,從而提高運行效率!

發佈了27 篇原創文章 · 獲贊 6 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章