/* to compile this modules, do arm-linux-gcc -O2 -D__KERNEL__ -DMODULE -I"your kernel include path" -c ads.c this driver is for ADS7846 touch-panel controller on pxa255 In my board, we use GPIO22 as pen interrupt. modify GPIO_NUM if you use different GPIO sam.hsu@southpro.com.tw or joepasscheng@so-net.net.tw */ #include #include #include #include #include #include #include #include #include #include #include #define GPIO_NUM 22 #define SPP_BSY 0x10 #define SPP_RNE 0x08 #define ADS7846_MINOR 16 #define DIFFMODE_X_POSI 0x00d0 #define DIFFMODE_Y_POSI 0x0090 static int mouse_users = 0; typedef struct { int state; int offx; int offy; } ADS_TS_Event; typedef struct { int ref_x; int ref_y; int orig_x; int orig_y; } TSRef; typedef struct { int ri, wi; int x, y; int new_x, new_y; TSRef r; unsigned long lock; struct timeval start; wait_queue_head_t ts_irqwait; struct fasync_struct *ts_fasync; } TSDev; static TSDev ads_device; static int fifo_empty(void) { if( SSSR & 0x8) return 0; // is not empty else return 1; // empty } void interrupt_ads7846_handler(int irq, void *dev_id, struct pt_regs *regs) { unsigned long r; int x, y; printk("interrupt occur\n"); SSDR = DIFFMODE_X_POSI; while (fifo_empty()); x = SSDR; ads_device.new_x = (x - 141)*800/3662; SSDR = DIFFMODE_Y_POSI; while (fifo_empty()); y = SSDR; ads_device.new_y = 600 - (y - 197)*600/3630; printk("offset : %d %d\n", ads_device.new_x - ads_device.x, ads_device.new_y - ads_device.y); ads_device.wi = 0; // wake_up_interruptible(&ts_irqwait); } static int ads_open(struct inode *inode, struct file *filp) { int rv = 0; if(mouse_users++) return 0; rv = request_irq(GPIO_2_80_TO_IRQ(GPIO_NUM), interrupt_ads7846_handler, 0, "ADS7846 interrupt", NULL); if (rv) { printk("can't get interrupt\n"); return 0; } filp->private_data = &ads_device; MOD_INC_USE_COUNT; return 0; } static int ads_release(struct inode *inode, struct file *filp) { if(--mouse_users) return 0; free_irq(GPIO_2_80_TO_IRQ(GPIO_NUM), NULL); MOD_DEC_USE_COUNT; return 0; } static int ads_fasync(int inode, struct file *filp, int mode) { printk("ads fasync function\n"); return 0; } static int ads_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { printk("ads ioctl command: %d\n", cmd); return 0; } static ssize_t ads_read(struct file *filp, char *buf, size_t count, loff_t * offp) { TSDev *ts; ADS_TS_Event e; printk("ads read function start: %d\n", count); ts = filp->private_data; ts->wi = 1; e.state = 0x04; e.offx = ts->new_x - ts->x; ts->x = ts->new_x; e.offy = ts->new_y - ts->y; ts->y = ts->new_y; copy_to_user(buf, &e, sizeof(ADS_TS_Event)); /* DECLARE_WAITQUEUE(wait, current); add_wait_queue(&ts_irqwait, &wait); current->state = TASK_INTERRUPTIBLE; schedule(); current->state = TASK_RUNNING; printk("task wake up\n"); remove_wait_queue(&ts_irqwait, &wait); */ return sizeof(ADS_TS_Event); } static unsigned int ads_poll(struct file *filp, poll_table * table) { TSDev *ts; // printk("ads poll function\n"); ts = filp->private_data; if (!ts) return -ENODEV; poll_wait(filp, &ts->ts_irqwait, table); if (ts->wi == 0) return POLLIN | POLLRDNORM; return 0; } static struct file_operations ads_fops = { owner:THIS_MODULE, poll:ads_poll, read:ads_read, ioctl:ads_ioctl, fasync:ads_fasync, release:ads_release, open:ads_open, }; static struct miscdevice ads_ts = { ADS7846_MINOR, "ADS7846", &ads_fops }; static int __init init_ads7846(void) { unsigned long r; int rv = 0; rv = misc_register(&ads_ts); if (rv < 0) { printk("can't register misc device\n"); goto get_no_interrupt; } memset(&ads_device, 0, sizeof(TSDev)); ads_device.r.ref_x = 3662; ads_device.r.ref_y = 3630; ads_device.r.orig_x = 141; ads_device.r.orig_y = 197; ads_device.x = 0; ads_device.y = 0; SSCR0 = 0x0000472B; SSCR1 = 0x00000440; CKEN |= CKEN3_SSP; SSCR0 |= 0x00000080; GPDR(GPIO_NUM) &= ~GPIO_bit(GPIO_NUM); set_GPIO_IRQ_edge(GPIO_NUM, GPIO_FALLING_EDGE); SSDR = DIFFMODE_X_POSI; while (fifo_empty()); r = SSDR; get_no_interrupt: return 0; } static void __exit cleanup_ads7846(void) { misc_deregister(&ads_ts); printk(KERN_INFO "ads7846 remove\n"); } MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sam Hsu"); MODULE_DESCRIPTION("ADS7846 touchscreen driver for the MidasPad"); module_init(init_ads7846); module_exit(cleanup_ads7846); EXPORT_NO_SYMBOLS;