📄 utu2440_buttons.w
字号:
/* * * A button driver for UTU2410 a board based on s3c2440 * * The source code in this file can be freely used, adapted, * and redistributed in source or binary form, so long as an * acknowledgment appears in derived source files.No warranty * is attached;we cannot take responsibility for errors or * fitness for use. * * */#include <linux/autoconf.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/kernel.h> /* printk() */#include <linux/fs.h> /* everything... */#include <linux/cdev.h>#include <linux/interrupt.h> /* request_irq() */#include <asm/arch/regs-gpio.h>#include <asm/arch/regs-irq.h>#include <asm/io.h>#include <asm/uaccess.h> /* copy_to_user() */#include <linux/delay.h> /* mdelay() */#include <linux/irq.h>#include <linux/input.h>#ifdef CONFIG_DEVFS_FS//#include <linux/devfs_fs_kernel.h>#endif#define VERSION_STRING "Button driver for utu2440"#define DEVICE_NAME "utu2440-buttons"#define EXTINT_OFF (IRQ_EINT4 - 4)#define S3C2410_GND 5MODULE_AUTHOR("lili <bit.lili@gmail.com>");MODULE_LICENSE("Dual BSD/GPL");MODULE_DESCRIPTION(VERSION_STRING);unsigned int type=IRQT_BOTHEDGE;unsigned int buttons_major_number=0;struct cdev buttons_dev;static int ready = 0;static int key_value = 0;static struct key_info { unsigned int irq_no; unsigned int gpio_port; int key_no; unsigned int IN; unsigned int EINT;} key_info_tab[6] = { { IRQ_EINT0, S3C2410_GPF0, 1 ,S3C2410_GPF0_INP,S3C2410_GPF0_EINT0}, { IRQ_EINT1, S3C2410_GPF1, 2 ,S3C2410_GPF1_INP,S3C2410_GPF1_EINT1}, { IRQ_EINT2, S3C2410_GPF2, 3 ,S3C2410_GPF2_INP,S3C2410_GPF2_EINT2}, { IRQ_EINT3, S3C2410_GPF3, 4 ,S3C2410_GPF3_INP,S3C2410_GPF3_EINT3}, { IRQ_EINT11, S3C2410_GPG3, 5 ,S3C2410_GPG3_INP,S3C2410_GPG3_EINT11}, { IRQ_EINT19, S3C2410_GPG11, 6 ,S3C2410_GPG11_INP,S3C2410_GPG11_EINT19},};static struct scan_gpio { unsigned int gpio_scan; unsigned int IN; unsigned int OUT;} scan_tab[4] = { { S3C2410_GPD13, S3C2410_GPD13_INP,S3C2410_GPD13_OUTP}, { S3C2410_GPD14, S3C2410_GPD14_INP,S3C2410_GPD14_OUTP}, { S3C2410_GPD15, S3C2410_GPD15_INP,S3C2410_GPD15_OUTP}, { S3C2410_GPG12, S3C2410_GPG12_INP,S3C2410_GPG12_OUTP},};static struct key_code { unsigned int key_no; unsigned int key_code;} code_tab[22] = { { 1, KEY_ENTER}, { 2, KEY_ENTER}, { 3, KEY_ENTER}, { 4, KEY_ENTER}, { 5, KEY_ENTER}, { 6, KEY_ENTER}, { 7, KEY_ENTER}, { 8, KEY_ENTER}, { 9, KEY_ENTER}, { 10, KEY_ENTER}, { 11, KEY_ENTER}, { 12, KEY_ENTER}, { 13, KEY_ENTER}, { 14, KEY_ENTER}, { 15, KEY_ENTER}, { 16, KEY_ENTER}, { 17, KEY_ENTER}, { 18, KEY_ENTER}, { 19, KEY_ENTER}, { 20, KEY_ENTER}, { 21, KEY_ENTER}, { 22, KEY_ENTER},};DECLARE_WAIT_QUEUE_HEAD(buttons_wait);static inline void s3c_irq_ack(unsigned int irqno){ unsigned long bitval = 1UL << (irqno - IRQ_EINT0); __raw_writel(bitval, S3C2410_SRCPND); __raw_writel(bitval, S3C2410_INTPND);} static void s3c_irqext_ack(unsigned int irqno){ unsigned long req; unsigned long bit; unsigned long mask; bit = 1UL << (irqno - EXTINT_OFF); mask = __raw_readl(S3C2410_EINTMASK); __raw_writel(bit, S3C2410_EINTPEND); req = __raw_readl(S3C2410_EINTPEND); req &= ~mask; /* not sure if we should be acking the parent irq... */ if (irqno <= IRQ_EINT7 ) { if ((req & 0xf0) == 0) s3c_irq_ack(IRQ_EINT4t7); } else { if ((req >> 8) == 0) s3c_irq_ack(IRQ_EINT8t23); }}static int s3c_irqext_type(unsigned int irq, unsigned int type){ unsigned long extint_reg; unsigned long gpcon_reg; unsigned long gpcon_offset, extint_offset; unsigned long newvalue = 0, value; if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3)) { gpcon_reg = (unsigned long)S3C2410_GPFCON; extint_reg = (unsigned long)S3C2410_EXTINT0; gpcon_offset = (irq - IRQ_EINT0) * 2; extint_offset = (irq - IRQ_EINT0) * 4; } else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7)) { gpcon_reg = (unsigned long)S3C2410_GPFCON; extint_reg = (unsigned long)S3C2410_EXTINT0; gpcon_offset = (irq - (EXTINT_OFF)) * 2; extint_offset = (irq - (EXTINT_OFF)) * 4; } else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15)) { gpcon_reg = (unsigned long)S3C2410_GPGCON; extint_reg = (unsigned long)S3C2410_EXTINT1; gpcon_offset = (irq - IRQ_EINT8) * 2; extint_offset = (irq - IRQ_EINT8) * 4; } else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23)) { gpcon_reg = (unsigned long)S3C2410_GPGCON; extint_reg = (unsigned long)S3C2410_EXTINT2; gpcon_offset = (irq - IRQ_EINT8) * 2; extint_offset = (irq - IRQ_EINT16) * 4; } else return -1; /* Set the GPIO to external interrupt mode */ value = __raw_readl(gpcon_reg); value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset); __raw_writel(value, gpcon_reg); /* Set the external interrupt to pointed trigger type */ switch (type) { case IRQT_NOEDGE: printk(KERN_WARNING "No edge setting!\n"); break; case IRQT_RISING: newvalue = S3C2410_EXTINT_RISEEDGE; break; case IRQT_FALLING: newvalue = S3C2410_EXTINT_FALLEDGE; break; case IRQT_BOTHEDGE: newvalue = S3C2410_EXTINT_BOTHEDGE; break; case IRQT_LOW: newvalue = S3C2410_EXTINT_LOWLEV; break; case IRQT_HIGH: newvalue = S3C2410_EXTINT_HILEV; break; default: printk(KERN_ERR "No such irq type %d", type); return -1; } value = __raw_readl(extint_reg); value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset); __raw_writel(value, extint_reg); return 0;}static irqreturn_t buttons_irq(int irq,void *dev_id,struct pt_regs *req){ struct key_info *k;// struct scan_gpio *scan; int i,j; int found = 0; int up ; unsigned long flags; for (i = 0; i < sizeof key_info_tab / sizeof key_info_tab[1]; i++) { k = key_info_tab + i; if (k->irq_no == irq) { found = 1; break; } } if (!found) { printk("bad irq %d in button\n", irq); return IRQ_NONE; } local_irq_save(flags); mdelay(1);//为ebook 项目修改, 2009.03 cywang s3c2410_gpio_cfgpin(k->gpio_port, k->IN); for(j=0;j<4;j++) s3c2410_gpio_setpin(scan_tab[j].gpio_scan, 1); if(!s3c2410_gpio_getpin(k->gpio_port)) key_value = code_tab[i*5+4].key_no; else for(j=0;j<4;j++) { s3c2410_gpio_setpin(scan_tab[j].gpio_scan, 0); s3c2410_gpio_setpin(scan_tab[(j+1)%4].gpio_scan, 1); s3c2410_gpio_setpin(scan_tab[(j+2)%4].gpio_scan, 1); s3c2410_gpio_setpin(scan_tab[(j+3)%4].gpio_scan, 1); up = s3c2410_gpio_getpin(k->gpio_port); if(!up) { key_value = code_tab[i*5+j].key_no; break;} } for(j=0;j<4;j++) s3c2410_gpio_setpin(scan_tab[j].gpio_scan, 0); // up = s3c2410_gpio_getpin(k->gpio_port); local_irq_restore(flags); s3c_irqext_type(irq, type); if(irq<IRQ_EINT7) s3c_irq_ack(irq); else s3c_irqext_ack(irq);/* if (!up) { key_value = k->key_no; ready=1; }*/ wake_up_interruptible(&buttons_wait);// printk("buttons_irq"); if((key_value == 1)) { s3c2410_gpio_setpin(S3C2410_GPF4, 0); printk(KERN_INFO "Buttons Value 1\n"); } if((key_value == 2)) { s3c2410_gpio_setpin(S3C2410_GPF5, 0); printk(KERN_INFO "Buttons Value 2\n"); } if((key_value == 3)) { s3c2410_gpio_setpin(S3C2410_GPF6, 0); printk(KERN_INFO "Buttons Value 3\n"); } if((key_value == 4)) { s3c2410_gpio_setpin(S3C2410_GPF7, 0); printk(KERN_INFO "Buttons Value 4\n"); } if((key_value == 5)) { s3c2410_gpio_setpin(S3C2410_GPF4, 0); s3c2410_gpio_setpin(S3C2410_GPF5, 0); s3c2410_gpio_setpin(S3C2410_GPF6, 0); s3c2410_gpio_setpin(S3C2410_GPF7, 0); printk(KERN_INFO "Buttons Value 5\n"); } if((key_value == 6)) { s3c2410_gpio_setpin(S3C2410_GPF4, 1); s3c2410_gpio_setpin(S3C2410_GPF5, 1); s3c2410_gpio_setpin(S3C2410_GPF6, 1); s3c2410_gpio_setpin(S3C2410_GPF7, 1); printk(KERN_INFO "Buttons Value 6\n"); } return IRQ_HANDLED;}static int request_irqs(void){ struct key_info *k; int i,request; unsigned int irq; for (i = 0; i < sizeof key_info_tab / sizeof key_info_tab[1]; i++) { k = key_info_tab + i; irq=k->irq_no; s3c_irqext_type(irq, type); if(irq<IRQ_EINT7) s3c_irq_ack(irq); else s3c_irqext_ack(irq); request =request_irq(k->irq_no,&buttons_irq,SA_INTERRUPT,DEVICE_NAME,NULL); if (request) { printk(KERN_WARNING "buttons:can't get irq no.\n"); return -1; } } return 0;}static void free_irqs(void){ struct key_info *k; int i; for (i = 0; i < sizeof key_info_tab / sizeof key_info_tab[1]; i++) { k = key_info_tab + i; free_irq(k->irq_no, NULL); }}static ssize_t buttons_read(struct file *filp,char __user *buffer,size_t count,loff_t *ppos){ static int key; unsigned long flags; if (!ready) { return -EAGAIN; } if (count != sizeof key_value) return -EINVAL; local_irq_save(flags); key = key_value; local_irq_restore(flags); copy_to_user(buffer, &key, sizeof key); ready = 0; return sizeof key_value;}static struct file_operations buttons_fops = { .owner = THIS_MODULE, .read = buttons_read,};static int __init buttons_init(void){ int ret,devno; dev_t dev;// 初始化GPIO printk(KERN_INFO "Buttons key gpio init!\n"); s3c2410_gpio_cfgpin(S3C2410_GPD13,S3C2410_GPD13_OUTP); s3c2410_gpio_setpin(S3C2410_GPD13, 0); s3c2410_gpio_cfgpin(S3C2410_GPD14,S3C2410_GPD14_OUTP); s3c2410_gpio_setpin(S3C2410_GPD14, 0); s3c2410_gpio_cfgpin(S3C2410_GPD15,S3C2410_GPD15_OUTP); s3c2410_gpio_setpin(S3C2410_GPD15, 0); s3c2410_gpio_cfgpin(S3C2410_GPG12,S3C2410_GPG12_OUTP); s3c2410_gpio_setpin(S3C2410_GPG12, 1); s3c2410_gpio_cfgpin(S3C2410_GPG5,S3C2410_GPG5_OUTP); s3c2410_gpio_setpin(S3C2410_GPG5, 1); ret = alloc_chrdev_region(&dev,0,1,DEVICE_NAME); buttons_major_number = MAJOR(dev); printk(KERN_INFO "Initial utulinux 2440 Buttons driver!\n"); if (ret<0) { printk(KERN_WARNING "button:can't get major number %d\n",buttons_major_number); return ret; } ret = request_irqs(); if (ret) { unregister_chrdev_region(dev,1); printk(KERN_WARNING "button:can't request irqs\n"); return ret; } devno = MKDEV(buttons_major_number,0); cdev_init(&buttons_dev,&buttons_fops); buttons_dev.owner = THIS_MODULE; buttons_dev.ops = &buttons_fops; ret = cdev_add(&buttons_dev,devno,1); if (ret) { free_irqs(); unregister_chrdev_region(dev,1); printk(KERN_NOTICE "Error %d adding buttons device\n",ret); return ret; } printk(KERN_INFO "Todo: mknod c /dev/%s %d 0\n",DEVICE_NAME,buttons_major_number); return 0;}static void __exit buttons_cleanup(void){ dev_t dev=MKDEV(buttons_major_number,0); free_irqs(); cdev_del(&buttons_dev); unregister_chrdev_region(dev,1); printk(KERN_INFO "unregistered the %s\n",DEVICE_NAME);}module_init(buttons_init);module_exit(buttons_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -