📄 tsc2003.c
字号:
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <asm/irq.h>
#include <linux/signal.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/cdev.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <asm/semaphore.h>
#include <asm/atomic.h>
#define USE_ASYNC
#define IIC_MAJOR 0 /*the predicted master device number*/
#define I2C_START 0x80
#define I2C_STOP 0x40
#define I2C_READ 0x20
#define I2C_WRITE 0x10
#define I2C_WACK 0x8
#define I2C_IACK 0x1
#define I2C_RUN 0x2
#define I2C_BUSY 0x40
#define I2C_RACK 0x80
#define GC_SOC_I2C_BASE 0xbf0040d0
#define GC_SOC_I2C_PRER_LO (volatile unsigned char *)(GC_SOC_I2C_BASE + 0x0)
#define GC_SOC_I2C_PRER_HI (volatile unsigned char *)(GC_SOC_I2C_BASE + 0x1)
#define GC_SOC_I2C_CTR (volatile unsigned char *)(GC_SOC_I2C_BASE + 0x2)
#define GC_SOC_I2C_TXR (volatile unsigned char *)(GC_SOC_I2C_BASE + 0x3)
#define GC_SOC_I2C_RXR (volatile unsigned char *)(GC_SOC_I2C_BASE + 0x3)
#define GC_SOC_I2C_CR (volatile unsigned char *)(GC_SOC_I2C_BASE + 0x4)
#define GC_SOC_I2C_SR (volatile unsigned char *)(GC_SOC_I2C_BASE + 0x4)
#define GPIO_OE_15_8 (volatile unsigned char *)( 0xbF004100 + 0x01 )
//for LPB MISC
#define GPIO_OE_15_8 (volatile unsigned char *)( 0xbF004100 + 0x01 )
#define GPIO_I_15_8 (volatile unsigned char *)( 0xbf004100 + 0x11 )
//for IIC
#define HSB_MISC_REG_BASE 0xbF003200
#define INT_EDGE (volatile unsigned int *)(HSB_MISC_REG_BASE+0x04)
#define INT_STEER (volatile unsigned int *)(HSB_MISC_REG_BASE+0x08)
#define INT_POL (volatile unsigned int *)(HSB_MISC_REG_BASE+0x0c)
#define INT_SET (volatile unsigned int *)(HSB_MISC_REG_BASE+0x10)
#define INT_CLR (volatile unsigned int *)(HSB_MISC_REG_BASE+0x14)
#define INT_EN (volatile unsigned int *)(HSB_MISC_REG_BASE+0x18)
#define INT_ISR (volatile unsigned int *)(HSB_MISC_REG_BASE+0x1c)
#define INT_EDGE_GPIO13 22//interupt number
#define u8 unsigned char
static int tsc2003_major = IIC_MAJOR;
static struct cdev tsc2003_cdev;/*global variable for device*/
static char *DEVICE_NAME = "h3600_tsraw";
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("TSC2003 touchscreen driver");
MODULE_LICENSE("GPL");
//COMMAND
#define TSC_ADDR 0x90
#define INIT_TSC 0xB6
#define MEASURE_X 0xC0
#define MEASURE_Y 0xD0
#define RESERV_CMD 0x70
static unsigned short pos_x ,pos_y;
typedef struct {
unsigned short pressure;
unsigned short x;
unsigned short y;
unsigned short pad;
} TS_RET;
#define MAX_TS_BUF 16 /* how many do we want to buffer */
static atomic_t timer_sw;
typedef struct {
volatile unsigned int penStatus; /* PEN_UP, PEN_DOWN, PEN_SAMPLE */
TS_RET buf[MAX_TS_BUF]; /* protect against overrun */
unsigned int head, tail; /* head and tail for queued events */
wait_queue_head_t wq;
struct semaphore sem;
spinlock_t lock;
#ifdef USE_ASYNC
struct fasync_struct *aq;
#endif
} TS_DEV;
enum PEN_Stat{
PEN_UP = 0,
PEN_DOWN = 1,//
PEN_PROCESS,
};
static TS_DEV tsdev;
#define BUF_HEAD (tsdev.buf[tsdev.head])
#define BUF_TAIL (tsdev.buf[tsdev.tail])
#define INCBUF(x,mod) ((++(x)) & ((mod) - 1))
static struct timer_list ts_timer;
//#define TS_TIMER_DELAY (HZ/100) //10ms
#define TS_TIMER_DELAY (HZ/100) //10ms
static int myi2c_init(int v)
{
static int inited=0;
if(inited)return 0;
inited=1;
* GC_SOC_I2C_PRER_LO = 0x63;//50K
* GC_SOC_I2C_PRER_HI = 0x0;
* GC_SOC_I2C_CTR = 0x80;
if(v)printk("* GC_SOC_I2C_PRER_LO=0x%x\n",* GC_SOC_I2C_PRER_LO);
if(v)printk("* GC_SOC_I2C_PRER_HI=0x%x\n",* GC_SOC_I2C_PRER_HI);
if(v)printk("* GC_SOC_I2C_CTR=0x%x\n",* GC_SOC_I2C_CTR);
if((* GC_SOC_I2C_PRER_LO != 0x63) || (* GC_SOC_I2C_PRER_HI != 0x0))
return -1;
else return 0;
}
static int myi2c_write(u8 cmd,int v)
{
spin_lock_irq(&(tsdev.lock));
unsigned char tmp;
myi2c_init(v);
* GC_SOC_I2C_TXR = 0x90;
* GC_SOC_I2C_CR = 0x90;
do tmp = * GC_SOC_I2C_SR;
while( tmp & 0x02);
if((* GC_SOC_I2C_SR) & 0x80)
{
if(v)printk("TSC2003 write slave addresss dnot receive ack\n");
return 1;
}
else
if(v)printk("TSC2003 write slave addresss receive ack\n");
* GC_SOC_I2C_TXR = cmd;//
* GC_SOC_I2C_CR = 0x50;//contain stop signal
do tmp = * GC_SOC_I2C_SR;
while( tmp & 0x02);
if((* GC_SOC_I2C_SR) & 0x80)
{
if(v)printk("TSC2003 write slave addresss dnot receive ack\n");
return 2;
}
else
if(v)printk("TSC2003 write slave command receive ack\n");
if((* GC_SOC_I2C_SR) & 0x40)
{
if(v)printk("bus is busy\n");
return 3;
}
else
if(v)printk("i2c bus is idle\n");
udelay(20);
spin_unlock_irq(&(tsdev.lock));
return 0;
}
static int myi2c_read(u8 cmd,int v)
{
spin_lock_irq(&(tsdev.lock));
unsigned short val;
unsigned char tmp;
unsigned char result[2];
myi2c_init(v);
* GC_SOC_I2C_TXR = 0x91;
*GC_SOC_I2C_CR = 0x90;
do tmp = * GC_SOC_I2C_SR;
while( tmp & 0x02);
if((* GC_SOC_I2C_SR) & 0x80)
{
if(v)printk("TSC2003 read slave addresss dnot receive ack\n");
}
else
if(v)printk("TSC2003 read slave addresss receive ack\n");
* GC_SOC_I2C_CR = 0x20;
do tmp = * GC_SOC_I2C_SR;
while( tmp & 0x02);
result[1] = * GC_SOC_I2C_TXR;
* GC_SOC_I2C_CR = 0x48;//STOP
result[0] = * GC_SOC_I2C_TXR;
val = result[1];
val=val<<4;
val+=result[0]>>4;
if(cmd == MEASURE_X)
{
pos_x = val;
}
else
{
pos_y = val;
}
if(v) printk("GPIO_I_15_8=%x\n", * GPIO_I_15_8);
spin_unlock_irq(&(tsdev.lock));
return 0;
}
static int measure_pos(u8 cmd ,int v)
{
if ( myi2c_write(cmd,v))
{
return 1;
}
myi2c_read(cmd,v);
return 0;
}
static int IsPen_down(void)
{
if( (*GPIO_I_15_8) & (1<<5))
return 0;
else
return 1;//pen down
}
static int measure_xy(void)
{
/*if(measure_pos(MEASURE_X ,1))
{
return 1;
}
if(measure_pos(MEASURE_Y ,1))
{
return 2;
}*/
int i=0;
do{
i++;
}while((measure_pos(MEASURE_X ,0)|measure_pos(MEASURE_Y ,0)) && (i<5) );
//printk("measurn_xy\n");
return 0;
}
static void ts_timer_handler(unsigned long data)
{
//spin_lock_irq(&(tsdev.lock));//add for test
if(down_trylock(&tsdev.sem))
{
printk("handler can't get sem\n");
return;
}
if(IsPen_down())
{
measure_xy();
BUF_HEAD.x = pos_x;
BUF_HEAD.y = pos_y;
BUF_HEAD.pressure = PEN_DOWN;
tsdev.penStatus = PEN_DOWN;
ts_timer.expires = jiffies + TS_TIMER_DELAY;
add_timer(&ts_timer);
tsdev.head = INCBUF(tsdev.head, MAX_TS_BUF);
wake_up_interruptible(&(tsdev.wq));
}
else
{
tsdev.penStatus = PEN_UP;
BUF_HEAD.x = pos_x ;
BUF_HEAD.y = pos_y ;
BUF_HEAD.pressure = PEN_UP;
tsdev.head = INCBUF(tsdev.head, MAX_TS_BUF);
wake_up_interruptible(&(tsdev.wq));
tsdev.penStatus = PEN_UP;
if(atomic_read(&timer_sw) == 1)
{
atomic_set(&timer_sw , 0);
del_timer(&ts_timer);
}
//printk("timer_list =0x%0x\n",*INT_POL);
}
#ifdef USE_ASYNC
if(tsdev.aq)
kill_fasync(&(tsdev.wq),SIGIO,POLL_IN);
#endif
//spin_unlock_irq(&(tsdev.lock));//add for test
up(&tsdev.sem);
}
//timer for release touch screen
static int tsRead(TS_RET * ts_ret)
{
ts_ret->x = BUF_TAIL.x;
ts_ret->y = BUF_TAIL.y;
ts_ret->pressure = BUF_TAIL.pressure;
//printk("\nx=%d,y=%d,press = %d \n",ts_ret->x ,ts_ret->y,ts_ret->pressure );
tsdev.tail = INCBUF(tsdev.tail, MAX_TS_BUF);
//spin_unlock_irq(&(tsdev.lock));
return sizeof(TS_RET);
}
static ssize_t pm3210_ts_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
//printk("\n=============tsc2003 read================\n");
TS_RET ts_ret;
retry:
if (tsdev.head != tsdev.tail) {
int count;
count = tsRead(&ts_ret);
if (count) copy_to_user(buffer, (char *)&ts_ret, count);
return count;
} else {
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
interruptible_sleep_on(&(tsdev.wq));
if (signal_pending(current))
return -ERESTARTSYS;
goto retry;
}
return sizeof(TS_RET);
}
static int pm3210_ts_open(struct inode *inode, struct file *filp)
{
//printk("\n=============tsc2003 open================\n");
tsdev.head = tsdev.tail = 0;
tsdev.penStatus = PEN_UP;
init_timer(&ts_timer);
ts_timer.function = ts_timer_handler;
// hardware initalize
init_waitqueue_head(&(tsdev.wq));
return 0;
}
static unsigned int pm3210_ts_poll(struct file *filp,struct poll_table_struct *wait)
{
poll_wait(filp,&(tsdev.wq),wait);
return (tsdev.head==tsdev.tail)?0:(POLLIN|POLLRDNORM);
}
#ifdef USE_ASYNC
static int pm3210_ts_fasync(int fd,struct file *filp,int mode)
{
return fasync_helper(fd,filp,mode,&(tsdev.aq));
}
#endif
static int pm3210_ts_release(struct inode *inode,struct file *filp)
{
del_timer(&ts_timer);
return 0;
}
static struct file_operations pm3210_fops = {
owner: THIS_MODULE,
open: pm3210_ts_open,
read: pm3210_ts_read,
poll: pm3210_ts_poll,
release:pm3210_ts_release,
#ifdef USE_ASYNC
fasync:pm3210_ts_fasync,
#endif
};
static irqreturn_t tsc2003_pen_isr(int irq,void *dev_id,struct pt_regs *regs)
{
//printk("irq\n");
if(IsPen_down())
{
tsdev.penStatus = PEN_DOWN;
*INT_POL |= (1<<INT_EDGE_GPIO13);//high voltage triggle
//printk("irq down =0x%0x\n",*INT_POL);
if(atomic_read(&timer_sw) == 0)
{
atomic_set(&timer_sw , 1);
ts_timer.expires = jiffies + TS_TIMER_DELAY;
add_timer(&ts_timer);
}
}
else
{
*INT_POL &= ~(1<<INT_EDGE_GPIO13);//low voltage triggle
//printk("irq up =0x%0x\n",*INT_POL);
}
return IRQ_HANDLED;
}
static void tsc2003_setup_cdev()
{
int err;
int tsc2003_devno = MKDEV(tsc2003_major,0);
cdev_init(&tsc2003_cdev,&pm3210_fops);
tsc2003_cdev.owner = THIS_MODULE;
tsc2003_cdev.ops = &pm3210_fops;
err = cdev_add(&tsc2003_cdev,tsc2003_devno,1);
if(err)
{
printk(KERN_NOTICE "Error %d adding tsc2003",err);
}
}
static int __init pm3210_ts_init(void)
{
int ret;
*GPIO_OE_15_8 &= (unsigned char) ~(1<<5);//GPIO13 output disable
*INT_POL &= ~(1<<INT_EDGE_GPIO13);//low voltage to triggle the interrupt
*INT_EN |=1<<INT_EDGE_GPIO13;
myi2c_write(RESERV_CMD,1);
atomic_set(&timer_sw,0);
init_MUTEX(&tsdev.sem);
ret=request_irq(INT_EDGE_GPIO13,tsc2003_pen_isr,SA_INTERRUPT,DEVICE_NAME,tsc2003_pen_isr);
/* Get irqs */
if (ret) {
printk(KERN_ERR "TSC2003_ts.c: Could not allocate ts INT_EDGE_GPIO13 !\n");
goto tc_failed;
}
printk("DEVICE_NAME initialized\n");
int register_result=0;
dev_t tsc2003_devno = MKDEV(tsc2003_major,0);
printk("\ntsc2003 driver\n");
//begin applying for the device number for registration
if(tsc2003_major)
{
register_result = register_chrdev_region(tsc2003_devno,1,DEVICE_NAME);
}
else
{
register_result = alloc_chrdev_region(&tsc2003_devno,0,1,DEVICE_NAME);
tsc2003_major = MAJOR(tsc2003_devno);
}
if(register_result < 0)
{
return register_result;
}
//begin registering
tsc2003_setup_cdev();
return 0;
tc_failed:
free_irq(INT_EDGE_GPIO13, tsc2003_pen_isr);
return ret;
}
static void __exit pm3210_ts_exit(void)
{
cdev_del(&tsc2003_cdev);
printk("tsc2003 exit\n");
unregister_chrdev_region(MKDEV(tsc2003_major,0),1);
free_irq(INT_EDGE_GPIO13, tsc2003_pen_isr);
}
module_init(pm3210_ts_init);
module_exit(pm3210_ts_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -