基本完成的TQ2440 的按键驱动 key.c

代码如下:

#include "key.h"

// ref arch/arm/mach-s3c24xx/mach-mini2440.c how to set gpio 
MODULE_LICENSE("GPL");
/*
datasheet: page274
— Port A(GPA): 25-output port
— Port B(GPB): 11-input/out port
— Port C(GPC): 16-input/output por
— Port D(GPD): 16-input/output por
— Port E(GPE): 16-input/output por
— Port F(GPF): 8-input/output port
— Port G(GPG): 16-input/output po
— Port H(GPH): 9-input/output port
— Port J(GPJ): 13-input/output port
*/
/*
中断引脚:
GPF7 Input/output EINT7 – –
GPF6 Input/output EINT6 – –
GPF5 Input/output EINT5 – –
GPF4 Input/output EINT4 – –
GPF3 Input/output EINT3 – –
GPF2 Input/output EINT2
GPF1 Input/output EINT1
GPF0 Input/output EINT0
*/
/*
GPB5, GPB6, GPB7, GPB8 --- LED1, LED2, LED3, LED4  
K1: GPF1 -EINT1
K2: GPF4 -EINT4
K3: GPF2 -EINT2
K4: GPF0 -EINT0
*/
/*
	datasheet中关于中断的介绍澹?
The 24 external interrupts are requested by various signaling methods. The EXTINT register configures the signaling
method among the low level trigger, high level trigger, falling edge trigger, rising edge trigger, and both edge trigger
for the external interrupt request
Because each external interrupt pin has a digital filter, the interrupt controller can recognize the request signal that is
longer than 3 clocks.
*/
/*
GPF相关寄存器:

GPFCON 0x56000050 R/W Configures the pins of port F 0x0
GPFDAT 0x56000054 R/W The data register for port F Undef.
GPFUP  0x56000058 R/W Pull-up disable register for port F 0x000

K1: GPF1 -EINT1: GPF1 [3:2] 00 = Input  01 = Output 10 = EINT[1]  11 = Reserved
K2: GPF4 -EINT4: GPF4 [9:8] 00 = Input  01 = Output 10 = EINT[4]  11 = Reserved
K3: GPF2 -EINT2: GPF2 [5:4] 00 = Input  01 = Output 10 = EINT2]  11 = Reserved
K4: GPF0 -EINT0: GPF0 [1:0] 00 = Input  01 = Output 10 = EINT[0]  11 = Reserved
*/

#define S3C_ADDR_BASE	0xF6000000
#define S3C_ADDR(x)	(S3C_ADDR_BASE + (x))
#define S3C2410_PA_UART		(0x50000000)
#define S3C2410_PA_GPIO		(0x56000000)
#define S3C_VA_UART	S3C_ADDR(0x01000000)	/* UART */
#define S3C24XX_PA_UART		S3C2410_PA_UART
#define S3C24XX_VA_UART		S3C_VA_UART
#define S3C24XX_PA_GPIO		S3C2410_PA_GPIO
#define S3C24XX_VA_GPIO		((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)

#define S3C2410_GPIOREG(x) ((x) + S3C24XX_VA_GPIO)

#define S3C2410_GPBCON	   S3C2410_GPIOREG(0x10)
#define S3C2410_GPBDAT	   S3C2410_GPIOREG(0x14)
#define S3C2410_GPBUP	   S3C2410_GPIOREG(0x18)

#define S3C2410_GPFCON	   S3C2410_GPIOREG(0x50)
#define S3C2410_GPFDAT	   S3C2410_GPIOREG(0x54)
#define S3C2410_GPFUP	   S3C2410_GPIOREG(0x58)

#define S3C2410_EXTINT0	   S3C2410_GPIOREG(0x88)
#define S3C2410_EXTINT1	   S3C2410_GPIOREG(0x8C)
#define S3C2410_EXTINT2	   S3C2410_GPIOREG(0x90)


static int kobox_key_open(struct inode *inode, struct file *file)
{
	return 0;
}

static int kobox_key_release(struct inode *inode, struct file *file)
{
	return 0;
}

static long kobox_key_ioctl(struct file *file, unsigned int cmd,
			   unsigned long arg)
{
	return 0;
}

#if 0
unsigned uiVal = 0;
uiVal = readl(S3C2410_GPBCON);
uiVal |= (0x01<<(2*pin)); 
uiVal &= ~(0x01 <<((2*pin)+1)) ; 
writel(uiVal, S3C2410_GPBCON);
#endif
/*
GPF相关寄存器:

GPFCON 0x56000050 R/W Configures the pins of port F 0x0
GPFDAT 0x56000054 R/W The data register for port F Undef.
GPFUP  0x56000058 R/W Pull-up disable register for port F 0x000

K1: GPF1 -EINT1: GPF1 [3:2] 00 = Input  01 = Output 10 = EINT[1]  11 = Reserved
K2: GPF4 -EINT4: GPF4 [9:8] 00 = Input  01 = Output 10 = EINT[4]  11 = Reserved
K3: GPF2 -EINT2: GPF2 [5:4] 00 = Input  01 = Output 10 = EINT2]  11 = Reserved
K4: GPF0 -EINT0: GPF0 [1:0] 00 = Input  01 = Output 10 = EINT[0]  11 = Reserved
*/

static void set_gpio_as_eint(void)
{
	unsigned uiVal = 0;

	/* set gpf1 as eint1 */
	uiVal = readl(S3C2410_GPFCON);
	uiVal &= ~(0x01 << 2);
	uiVal |=  (0x01 << 3);
	writel(uiVal, S3C2410_GPFCON);

	/* set gpf4 as eint4 */
	uiVal = readl(S3C2410_GPFCON);
	uiVal &= ~(0x01 << 8);
	uiVal |=  (0x01 << 9);
	writel(uiVal, S3C2410_GPFCON);

	/* set gpf2 as eint2 */
	uiVal = readl(S3C2410_GPFCON);
	uiVal &= ~(0x01 << 4);
	uiVal |=  (0x01 << 5);
	writel(uiVal, S3C2410_GPFCON);

	/* set gpf0 as eint0 */
	uiVal = readl(S3C2410_GPFCON);
	uiVal &= ~(0x01 << 0);
	uiVal |=  (0x01 << 1);
	writel(uiVal, S3C2410_GPFCON);

	return;
}
/*
GPF相关寄存器:

GPFCON 0x56000050 R/W Configures the pins of port F 0x0
GPFDAT 0x56000054 R/W The data register for port F Undef.
GPFUP  0x56000058 R/W Pull-up disable register for port F 0x000

K1: GPF1 -EINT1: GPF1 [3:2] 00 = Input  01 = Output 10 = EINT[1]  11 = Reserved
K2: GPF4 -EINT4: GPF4 [9:8] 00 = Input  01 = Output 10 = EINT[4]  11 = Reserved
K3: GPF2 -EINT2: GPF2 [5:4] 00 = Input  01 = Output 10 = EINT2]  11 = Reserved
K4: GPF0 -EINT0: GPF0 [1:0] 00 = Input  01 = Output 10 = EINT[0]  11 = Reserved
*/

/*
K1:EINT1 External interrupt 1 ARB0
K2:EINT4_7 External interrupt 4 – 7 ARB1
K3:EINT2 External interrupt 2 ARB0
K4:EINT0 External interrupt 0 ARB0
*/

#define S3C2410_CPUIRQ_OFFSET	 (16)
#define S3C2410_IRQ(x) ((x) + S3C2410_CPUIRQ_OFFSET)
/* main cpu interrupts */
#define IRQ_EINT0      S3C2410_IRQ(0)	    /* 16 */
#define IRQ_EINT1      S3C2410_IRQ(1)		/* 17 */
#define IRQ_EINT2      S3C2410_IRQ(2)		/* 18 */
#define IRQ_EINT4t7    S3C2410_IRQ(4)	    /* 20 */
#define IRQ_EINT4      S3C2410_IRQ(36)	   /* 52 */

/*
* IRQF_DISABLED - keep irqs disabled when calling the action handler.
*				  DEPRECATED. This flag is a NOOP and scheduled to be removed
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)

*/

/*
EXTERNAL INTERRUPT CONTROL REGISTER
The 24 external interrupts are requested by various signaling methods. The EXTINT register configures the signaling
method among the low level trigger, high level trigger, falling edge trigger, rising edge trigger, and both edge trigger
for the external interrupt request
Because each external interrupt pin has a digital filter, the interrupt controller can recognize the request signal that is
longer than 3 clocks.
EINT[15:0] are used for wakeup sources.
中断触发方式

EXTINTn(External Interrupt Control Register n)
The 8 external interrupts can be requested by various signaling methods. The EXTINT register configures the
signaling method between the level trigger and edge trigger for the external interrupt request, and also configures the
signal polarity.

EXTINT0 0x56000088 R/W External interrupt control register 0 0x000000
EXTINT1 0x5600008c R/W External interrupt control register 1 0x000000
EXTINT2 0x56000090 R/W External interrupt control register 2 0x000000

EXTINT0:

K1:EINT1 [6:4] 000 = Low level 001 = High level 01x = Falling edge triggered, 10x = Rising edge triggered 11x = Both edge triggered
K2:EINT4[18:16]000 = Low level 001 = High level 01x = Falling edge triggered,10x = Rising edge triggered 11x = Both edge triggered
K3:EINT2 [10:8]000 = Low level 001 = High level 01x = Falling edge triggered,10x = Rising edge triggered 11x = Both edge triggered
K4:EINT0 [2:0] 000 = Low level 001 = High level 01x = Falling edge triggered, 10x = Rising edge triggered 11x = Both edge triggered

*/
static irqreturn_t kobox_gpio_irq_handle(int irq, void *dev_id)
{
	//disable_irq(IRQ_EINT0);
	printk("irq = %d\n", irq);
	 
	//enable_irq(IRQ_EINT0);
	return IRQ_RETVAL(IRQ_HANDLED);

}

/*
 * These flags used only by the kernel as part of the
 * irq handling routines.
 *
 * IRQF_DISABLED - keep irqs disabled when calling the action handler.
 *                 DEPRECATED. This flag is a NOOP and scheduled to be removed
 * IRQF_SHARED - allow sharing the irq among several devices
 * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
 * IRQF_TIMER - Flag to mark this interrupt as timer interrupt
 * IRQF_PERCPU - Interrupt is per cpu
 * IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
 * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
 *                registered first in an shared interrupt is considered for
 *                performance reasons)
 * IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
 *                Used by threaded interrupts which need to keep the
 *                irq line disabled until the threaded handler has been run.
 * IRQF_NO_SUSPEND - Do not disable this IRQ during suspend
 * IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set
 * IRQF_NO_THREAD - Interrupt cannot be threaded
 * IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device
 *                resume time.
 */
#define IRQF_DISABLED		0x00000020
#define IRQF_SHARED		0x00000080
#define IRQF_PROBE_SHARED	0x00000100
#define __IRQF_TIMER		0x00000200
#define IRQF_PERCPU		0x00000400
#define IRQF_NOBALANCING	0x00000800
#define IRQF_IRQPOLL		0x00001000
#define IRQF_ONESHOT		0x00002000
#define IRQF_NO_SUSPEND		0x00004000
#define IRQF_FORCE_RESUME	0x00008000
#define IRQF_NO_THREAD		0x00010000
#define IRQF_EARLY_RESUME	0x00020000

static int request_irq_for_gpio(void)
{
	int i;
	int ret;
	unsigned uiVal = 0;
	int nouse;

	/* set gpf1 as eint1, edge faling trigger */
	/* key1 */
	i = 1;
	uiVal = readl(S3C2410_EXTINT0);
	uiVal &= ~(0x1<<6);
	uiVal |= (0x1<<5);
	writel(uiVal, S3C2410_EXTINT0);
	
	ret = request_irq(IRQ_EINT1, kobox_gpio_irq_handle, NULL, "key1", (void *)i);
	if(ret)
	{
		printk("[func:%s][line:%d] request_irq failed, ret:%d!\n", __FUNCTION__,__LINE__,ret);
	}
	else
	{
		printk("[func:%s][line:%d] request_irq ok, irq:%d!\n", __FUNCTION__,__LINE__, IRQ_EINT1);
	}

	/*key2*/
	i = 2;

	uiVal = readl(S3C2410_EXTINT0);
	uiVal &= ~(0x1<<18);
//	uiVal |= (0x1<<18);
	uiVal |= (0x1<<17);
	writel(uiVal, S3C2410_EXTINT0);

//	ret = request_irq(IRQ_EINT4t7, kobox_gpio_irq_handle, IRQF_SHARED, "key2", (void *)&nouse);
	ret = request_irq(IRQ_EINT4, kobox_gpio_irq_handle, IRQF_SHARED, "key2", (void *)&nouse);
	if(ret)
	{
		printk("[func:%s][line:%d] request_irq failed, ret:%d!\n", __FUNCTION__,__LINE__,ret);
	}
	else
	{
		printk("[func:%s][line:%d] request_irq ok, irq:%d!\n", __FUNCTION__,__LINE__, IRQ_EINT4);
	}


	/*key3*/
	i = 3;
	uiVal = readl(S3C2410_EXTINT0);
	uiVal &= ~(0x1<<10);
	uiVal |= (0x1<<9);
	writel(uiVal, S3C2410_EXTINT0);
	ret = request_irq(IRQ_EINT2, kobox_gpio_irq_handle, NULL, "key3", (void *)i);
	if(ret)
	{
		printk("[func:%s][line:%d] request_irq failed, ret:%d!\n", __FUNCTION__,__LINE__,ret);

	}
	else
	{
		printk("[func:%s][line:%d] request_irq ok, irq:%d!\n", __FUNCTION__,__LINE__, IRQ_EINT2);
	}
	
	/*key4*/
	i = 4;
	uiVal = readl(S3C2410_EXTINT0);
	uiVal &= ~(0x1<<2);
	uiVal |= (0x1<<1);
	writel(uiVal, S3C2410_EXTINT0);
	ret = request_irq(IRQ_EINT0, kobox_gpio_irq_handle, NULL, "key4", (void *)i);
	if(ret)
	{
		printk("[func:%s][line:%d] request_irq failed, ret:%d!\n", __FUNCTION__,__LINE__,ret);

	}
	else
	{
		printk("[func:%s][line:%d] request_irq ok, irq:%d!\n", __FUNCTION__,__LINE__, IRQ_EINT0);
	}
	
	return 0;
}

struct file_operations kobox_key_operations = {
	.owner          = THIS_MODULE,
	.open           = kobox_key_open,
	.release        = kobox_key_release,
	.unlocked_ioctl = kobox_key_ioctl,
};

//GPB0
int major;
int minor;
struct cdev cdev;
struct class *kobox_key_class;
struct device *pstdev = NULL;
#define GPIO_KEY_NAME "kobox_key"

int __init key_drv_init(void)
{
	int error;
	dev_t dev;

	printk("#####enter key_drv_init!\n");

	major = register_chrdev(0, GPIO_KEY_NAME, &kobox_key_operations);
	if (major < 0)
	{
		printk(" can't register major number\n");
		return major;
	}

	/* create class */
	kobox_key_class = class_create(THIS_MODULE, GPIO_KEY_NAME);
	if(IS_ERR(kobox_key_class))
	{
		printk("class_create failed!\n");
		goto fail;
	}

	/* create /dev/kobox_gpio */
	pstdev = device_create(kobox_key_class, NULL, MKDEV(major, 0), NULL, GPIO_KEY_NAME);
	if(!pstdev)
	{
		printk("device_create failed!\n");
		goto fail1;
	}

	/* set gpf0/1/2/4 as extern interrupt pins */
	set_gpio_as_eint();

	request_irq_for_gpio();

	printk("#####key_drv_init ok!\n");
	
	return 0;
fail1:
	class_destroy(kobox_key_class);
fail:
	unregister_chrdev(major, GPIO_KEY_NAME);
	return -1;
}

void __exit key_drv_exit(void)
{
	printk("exit gpio drv!\n");	

	device_destroy(kobox_key_class, MKDEV(major, 0));
	class_destroy(kobox_key_class);
	unregister_chrdev(major, GPIO_KEY_NAME);

	return;
}

module_init(key_drv_init);
module_exit(key_drv_exit);


加载打印:

[\u@\h \W]# insmod  key.ko 
#####enter key_drv_init!
[func:request_irq_for_gpio][line:278] request_irq ok, irq:17!
[func:request_irq_for_gpio][line:298] request_irq ok, irq:52!
[func:request_irq_for_gpio][line:316] request_irq ok, irq:18!
[func:request_irq_for_gpio][line:333] request_irq ok, irq:16!
#####key_drv_init ok!
[\u@\h \W]# ls
key.ko
[\u@\h \W]# cat /proc/interrupts 
           CPU0       
 16:          0  s3c-ext0   0  key4
 17:          0  s3c-ext0   1  key1
 18:          0  s3c-ext0   2  key3
 24:          0       s3c   8  s3c2410-rtc tick
 29:      10767       s3c  13  samsung_time_irq
 32:          0       s3c  16  s3c2410-lcd
 37:          0       s3c  21  s3c-mci
 42:          0       s3c  26  ohci_hcd:usb1
 43:          0       s3c  27  s3c2440-i2c.0
 46:          0       s3c  30  s3c2410-rtc alarm
 52:          0   s3c-ext   4  key2
 55:        512   s3c-ext   7  eth0
 64:          0   s3c-ext  16  s3c-mci
 74:         69  s3c-level   0  s3c2440-uart
 75:        523  s3c-level   1  s3c2440-uart
 87:          0  s3c-level  13  s3c2410-wdt
Err:          0
[\u@\h \W]#

按四个按键打印:

irq = 18
irq = 17
irq = 16
irq = 16
irq = 52
irq = 52
irq = 52
irq = 52
irq = 52
irq = 52
irq = 52
irq = 18
irq = 17
irq = 16



你可能感兴趣的:(linux,中断)