#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/ioport.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <mach/irqs.h>
#include <asm/io.h>
#include "ypublic.h"
MODULE_LICENSE("Dual BSD/GPL");
#define YMAJOR 100
#define YMINOR 1
#define YDEVICENAME "uartdriver"
#define YBUFSIZE 10
//io regs
#define GPABASE 0x56000000 //0
#define GPBBASE 0x56000010 //1
#define GPCBASE 0x56000020 //2
#define GPDBASE 0x56000030 //3
#define GPEDASE 0x56000040 //4
#define GPFBASE 0x56000050 //5
#define GPGBASE 0x56000060 //6
#define GPHBASE 0x56000070 //7
#define IOA 0
#define IOB 1
#define IOC 2
#define IOD 3
#define IOE 4
#define IOF 5
#define IOG 6
#define IOH 7
#define GPIOCONF(b) (b)
#define GPIODAT(b) ((b)+4)
#define GPIOUP(b) ((b)+8)
typedef struct _YCDEV{
struct cdev dev;
uchar x,y; //记录当前按键的位置
uchar pre_states[20];//记录按键的之前的状态
uchar cur_states[20];
}ycdev;
typedef struct _YIO{
uint regbasenum;//该IO对应寄存器虚拟地址在_io_reg_base数组的序列号,可在"//io regs"出查
uint num; //io在该端口的序列号
}yio;
typedef struct _YIRQIO{
yio io;
int irqnum; //irq号
char irqname[4];//irq命名
}yirqio;
yio _keyio[4]={
{IOF,3},
{IOF,4},
{IOG,1},
{IOG,11}};
yirqio _keyirqio[5]={
{{IOG,6},IRQ_EINT14,"k0"},
{{IOF,2},IRQ_EINT2,"k1"},
{{IOF,5},IRQ_EINT5,"k2"},
{{IOF,6},IRQ_EINT6,"k3"},
{{IOG,8},IRQ_EINT16,"k4"}};
ycdev *_pdev;
static int _major=YMAJOR;
static struct timer_list _tm;
uint _io_reg_base[8];
int _cur_irq;
void init_virtual_address(void)
{
uint i=0;
char name[10];
do{
sprintf(name,"keyreg%d",i);
_io_reg_base[i]=(uint)yioremap((0x56000000+i*0x00000010),12,name);
if(_io_reg_base[i]==0)printk(KERN_ERR"CALL init_virtual_address failed\n");
//else printk(KERN_INFO"v%u:0x%X\t",i,_io_reg_base[i]);
}while((++i)<8);
//printk(KERN_INFO"\n");
}
void enable_ios_irq(uint en)
{
//en=0,disable irqs; en=1 enable irqs
uint n=sizeof(_keyirqio)/sizeof(_keyirqio[0]);
if(en==1){
while(n--){
enable_irq(_keyirqio[n].irqnum);
}
}
else {
while(n--){
disable_irq_nosync(_keyirqio[n].irqnum);
}
}
}
static irqreturn_t onIrq(int irq,void *dev_id)
{
//interrupt service function,disable all irq,and elimination of button's jitter
//disable_irq_nosync((uint)dev_id);
_cur_irq=(int)dev_id;
_pdev->x=(int)dev_id;
_pdev->y=255;
enable_ios_irq(0);
//printk(KERN_INFO"call onIrq:%d %d\n",irq,_cur_irq);
_tm.expires=MS(25);
add_timer(&_tm);
return IRQ_HANDLED;
}
void setIoConf(uint func,uint ionum,uint reg)
{
//set gpio function model
uint dat;
//printk(KERN_INFO"CALL setIoConf: 0x%X\n",reg);
dat =ioread32(reg);
dat=setNB(func,dat,ionum*2,2);
iowrite32(dat,reg);
}
void set_io_conf(yio io,uint func)
{
//set gpio function model for yio type
//printk(KERN_INFO"CALL set_io_conf: %u 0x%X 0x%X\n",io.regbasenum,_io_reg_base[io.regbasenum],GPIOCONF(_io_reg_base[io.regbasenum]));
setIoConf(func,io.num,GPIOCONF(_io_reg_base[io.regbasenum]));
}
void set_irqios(uint func)
{
//set _keyirqio to func model
uint n=sizeof(_keyirqio)/sizeof(_keyirqio[0]);
while(n--){
set_io_conf(_keyirqio[n].io,func);
}
}
void irqios_input(void)
{
//disable irqs,and set _keyirqio to input model
enable_ios_irq(0);
set_irqios(Y2440_IO_IN);
}
void irqios_input2irq(void)
{
//from input model to irq model
set_irqios(Y2440_IO_EINT);
enable_ios_irq(1);
}
uint init_irqio(void)
{
//initiality _keyirqio, disable pull and request irq
uint n=sizeof(_keyio)/sizeof(_keyio[0]);
uint i;
int rs;
for(i=0;i<n;i++){
iowrite32(SETB(_keyirqio[i].io.num)|ioread32(GPIOUP(_io_reg_base[_keyirqio[i].io.regbasenum])),GPIOUP(_io_reg_base[_keyirqio[i].io.regbasenum]));
set_irq_type( _keyirqio[i].irqnum ,IRQ_TYPE_EDGE_FALLING);
rs=request_irq(_keyirqio[i].irqnum,onIrq,IRQF_DISABLED,_keyirqio[i].irqname,(void *)i);
if(rs)return rs;
}
return 0;
}
void ios_output0(void)
{
//set _keyio to output model ,disable pull and output 0
uint n=sizeof(_keyio)/sizeof(_keyio[0]);
while(n--){
//printk(KERN_INFO"io_output0:%u %u\n",n,_keyio[n].regbasenum);
//printk(KERN_INFO"io_output0:d0 %u\n",_io_reg_base[_keyio[n].regbasenum]);
iowrite32(SETB(_keyio[n].num)|ioread32(GPIOUP(_io_reg_base[_keyio[n].regbasenum])),GPIOUP(_io_reg_base[_keyio[n].regbasenum]));
//printk(KERN_INFO"io_output0:d1\n");
set_io_conf(_keyio[n],Y2440_IO_OUT);
//printk(KERN_INFO"io_output0:d2\n");
iowrite32(CLRB(_keyio[n].num)&ioread32(GPIODAT(_io_reg_base[_keyio[n].regbasenum])),GPIODAT(_io_reg_base[_keyio[n].regbasenum]));
//printk(KERN_INFO"io_output0:d3\n");
}
}
static void onTime(ulong arg)
{
int i,n=ARRAYSIZE(_keyio);
uint regdat1=GPIODAT(_io_reg_base[_keyirqio[_cur_irq].io.regbasenum]),regdat2;
//printk(KERN_INFO"call onTimer\n");
set_io_conf(_keyirqio[_cur_irq].io,Y2440_IO_IN);
if((ioread32(regdat1)&SETB(_keyirqio[_cur_irq].io.num))==0){
//have a key down
for(i=0;i<n;i++){
//set another io output height
regdat2=GPIODAT(_io_reg_base[_keyio[i].regbasenum]);
iowrite32(SETB(_keyio[i].num)|ioread32(regdat2),regdat2);
//should be have one true
if(ioread32(regdat1)&SETB(_keyio[i].num)){
if(_pdev->pre_states[_cur_irq*5+i]==0){
_pdev->pre_states[_cur_irq*5+i]=1;
_pdev->cur_states[_cur_irq*5+i]=1;
_pdev->y=(uchar)i;
printk(KERN_INFO "have key down:(%d,%d)\n",_cur_irq,i);
}
else printk(KERN_INFO "keep key down:(%d,%d)\n",_cur_irq,i);
iowrite32(CLRB(_keyio[i].num)&ioread32(regdat2),regdat2);
_tm.expires=MS(100);
add_timer(&_tm);
break;
}
//have a expect
//else printk(KERN_ERR "error 2\n");
//set another io output low
iowrite32(CLRB(_keyio[i].num)&ioread32(regdat2),regdat2);
}
}
else{
//key up
if(_pdev->y!=255){
_pdev->pre_states[_pdev->x*5+_pdev->y]=0;
printk(KERN_INFO "have key up:(%u,%u)\n",_pdev->x,_pdev->y);
_pdev->y=255;
}
else printk(KERN_INFO "error 3\n");
irqios_input2irq();
}
}
int yopen(struct inode *pinode,struct file *pfile)
{
return 0;
}
int yrelease(struct inode *pinode,struct file *pfile)
{
return 0;
}
static struct file_operations _fops={
.open=yopen,
.release=yrelease,
};
int __init yinit(void)
{
dev_t devno=MKDEV(YMAJOR,YMINOR);
int rs;
//uint tmp;
if(_major){
rs=register_chrdev_region(devno,1,YDEVICENAME);
}
else{
rs=alloc_chrdev_region(&devno,YMINOR,1,YDEVICENAME);
_major=MAJOR(devno);
}
if(rs<0)return rs;
_pdev=kmalloc(sizeof(ycdev),GFP_KERNEL);
if(!_pdev){
rs= -ENOMEM;
goto fail1;
}
cdev_init(&_pdev->dev,&_fops);
_pdev->dev.owner=THIS_MODULE;
rs=cdev_add(&_pdev->dev,MKDEV(_major,YMINOR),0);
if(rs){
rs=-EBUSY;
goto fail2;
}
init_virtual_address();
ios_output0();
if(init_irqio())printk(KERN_INFO"irq failed");
//init io's pre-state and timer
memset(_pdev->pre_states,0,sizeof(_pdev->pre_states));
memset(_pdev->cur_states,0,sizeof(_pdev->cur_states));
setup_timer(&_tm,onTime,IRQ_EINT16);
_pdev->x=_pdev->y=255;
printk(KERN_INFO "driver ok\n");
return 0;
fail2:
printk(KERN_ERR "fail2\n");
kfree(_pdev);
fail1:
printk(KERN_ERR "fail1\n");
unregister_chrdev_region(devno,1);
return -EBUSY;
}
void __exit yexit(void)
{
del_timer(&_tm);
yiounmap(GPBBASE,12);
cdev_del(&_pdev->dev);
kfree(_pdev);
unregister_chrdev_region(MKDEV(_major,YMINOR),1);
}
MODULE_AUTHOR("MrY");
module_init(yinit);
module_exit(yexit);
|