/*
* FileName main.c
* Author wangzhenhui
* Date 09/18/06
* Board SESI_AT91RM9200EDUKIT
* Desc touchscreen driver ads7843
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_*_user */
#include <asm/arch/board.h>
#include <asm/arch/gpio.h>
#include <asm/hardware.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include "ads7843.h"
//#undef DEBUG_ADS7843
#define DEBUG_ADS7843 1
#ifdef DEBUG_ADS7843
#define DBG_ADS7843(fmt, args...) printk(fmt,## args)
#else
#define DBG_ADS7843(fmt, args...)
#endif
static void ads7843_ts_read_pos(struct ads7843_dev *, struct ts_event *);
static void ads7843_timer(unsigned long);
int ads7843_major = 0;
int ads7843_minor = 0;
struct ads7843_dev *ads7843_device;
static inline void ads7843_ts_evt_add(struct ads7843_dev *ts, unsigned short pressure, unsigned short x, unsigned short y)
{
int next_head;
next_head = (ts->evt_head + 1) & (NR_EVENTS - 1);
if (next_head != ts->evt_tail) {
ts->events[ts->evt_head].pressure = pressure;
ts->events[ts->evt_head].x = x;
ts->events[ts->evt_head].y = y;
do_gettimeofday(&ts->events[ts->evt_head].stamp);
ts->evt_head = next_head;
}
}
static inline void ads7843_ts_event_release(struct ads7843_dev *ts)
{
ads7843_ts_evt_add(ts, 0, 0, 0);
}
static irqreturn_t ads7843_pen_irq(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned long j=jiffies;
printk("ads7843_pen_irq\n");
disable_irq(AT91_PIN_PD0);
mod_timer(&ads7843_device->timer,j+HZ/100);
return 0;
}
static int ads7843_startup(struct ads7843_dev *ts)
{
int ret;
unsigned long j=jiffies;
if(down_interruptible(&ts->sem))
return -EINTR;
/* check if open first time */
if(ts->use_count!=0)
return -EBUSY;
/* init PIO */
DBG_ADS7843("ADS7843 init PIO.\n");
at91_set_gpio_output(TS_CS,0);
at91_set_gpio_output(TS_CLK,0);
at91_set_gpio_output(TS_DIN,0);
at91_set_gpio_input(TS_DOUT,0);
at91_set_gpio_input(AT91_PIN_PD0,0);
at91_set_gpio_input(AT91_PIN_PD4,0);
/* reg irq */
ret=request_irq(AT91_PIN_PD0, ads7843_pen_irq,
SA_SAMPLE_RANDOM | SA_TRIGGER_FALLING,
"ads7843", ts);
enable_irq(AT91_PIN_PD0);
if(ret)
{
DBG_ADS7843("request irq failed.\n");
return ret;
}
/* init timer */
init_timer(&ts->timer);
ts->timer.expires=j+HZ/100;
ts->timer.data = (unsigned long) ts;
ts->timer.function = ads7843_timer;
add_timer(&ts->timer);
/* clear event queue */
ads7843_ts_evt_clear(ts);
/* module counter add */
ts->use_count++;
up(&ts->sem);
return 0;
}
static void ads7843_timer(unsigned long handle)
{
struct ts_event event;
int val;
struct ads7843_dev *ts = (void *)handle;
/* read position */
ads7843_ts_read_pos(ts, &event);
DBG_ADS7843("read x:0X%x,\ty:0X%x\n",event.x,event.y);
/* get PENIRQ status */
val=at91_get_gpio_value(AT91_PIN_PD0);
if(val!=0)
{
//ads7843_ts_event_release(ts);
enable_irq(AT91_PIN_PD0);
}
else
{
ads7843_ts_evt_add(ts, event.pad, event.x, event.y);
ts->timer.expires+=HZ/100;
mod_timer(&ts->timer,ts->timer.expires);
}
}
static unsigned short ads7843_transfer(unsigned char cmd)
{
int i;
unsigned char bit;
unsigned short read_data = 0;
/* CS# Low */
at91_set_gpio_value(TS_CS, 0);
udelay(40);
/* Transform CMD */
for(i=1; i<=8; i++){
/* CMD Output */
bit = ( cmd >> (8-i) ) & 0x01 ;
at91_set_gpio_value(TS_DIN, bit);
udelay(20);
/* CLK High */
at91_set_gpio_value(TS_CLK, 1);
udelay(40);
/* CLK Low */
at91_set_gpio_value(TS_CLK, 0);
udelay(20);
}
/* Waiting for busy */
udelay(50);
/* Read DATA */
for(i=7; i>=0; i--){
/* CLK High */
at91_set_gpio_value(TS_CLK, 1);
udelay(40);
/* CLK Low */
at91_set_gpio_value(TS_CLK, 0);
/* Data in */
if( at91_get_gpio_value(TS_DOUT) != 0 )
read_data |= 1 << i;
udelay(40);
}
/* CS# High */
at91_set_gpio_value(TS_CS, 1);
udelay(50);
return read_data;
}
static void ads7843_ts_read_pos(struct ads7843_dev *ts, struct ts_event *event)
{
int i;
unsigned char cmd[2];
unsigned short data[2];
cmd[0] = MEASURE_8BIT_X;
cmd[1] = MEASURE_8BIT_Y;
for(i=0; i<2; i++){
data[i] = ads7843_transfer(cmd[i]);
}
event->x = data[0];
event->y = data[1];
event->pressure = 100;
//printk("ts read pos: x = %d, y = %d\n", event->x, event->y);
}
ssize_t ads7843_read(struct file *filp, char __user *buffer, size_t count,
loff_t *f_pos)
{
struct ads7843_dev *ts = filp->private_data;
char *ptr = buffer;
int err = 0;
while (count >= sizeof(struct ts_event)) {
err = -ERESTARTSYS;
if (ads7843_ts_evt_pending(ts)) {
struct ts_event *evt = ads7843_ts_evt_get(ts);
err = copy_to_user(ptr, evt, sizeof(struct ts_event));
ads7843_ts_evt_pull(ts);
if (err)
break;
ptr += sizeof(struct ts_event);
count -= sizeof(struct ts_event);
continue;
}
}
return ptr == buffer ? err : ptr - buffer;
}
int ads7843_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
return 0;
}
int ads7843_release(struct inode *inode, struct file *filp)
{
struct ads7843_dev *ts = filp->private_data;
down(&ts->sem);
if(--ts->use_count==0)
{
free_irq(AT91_PIN_PD0,ts);
kfree(ads7843_device);
}
up(&ts->sem);
return 0;
}
int ads7843_open(struct inode *inode, struct file *filp)
{
int ret = 0;
ret = ads7843_startup(ads7843_device);
if (ret == 0)
filp->private_data = ads7843_device;
DBG_ADS7843("open return %d",ret);
return ret;
}
ssize_t ads7843_write(struct file *filp, const char __user *buf, size_t count,
loff_t *f_pos)
{
return 0;
}
struct file_operations ads7843_fops = {
.owner = THIS_MODULE,
.read = ads7843_read,
.write = ads7843_write,
.ioctl = ads7843_ioctl,
.open = ads7843_open,
.release = ads7843_release,
};
void ads7843_cleanup_module(void)
{
dev_t devno = MKDEV(ads7843_major, ads7843_minor);
printk("ads7843 module cleanup.\n");
cdev_del(&ads7843_device->cdev);
kfree(ads7843_device);
unregister_chrdev_region(devno, 1);
free_irq(AT91_PIN_PD0,0);
}
int ads7843_init_module(void)
{
int result,dev_no;
dev_t dev = 0;
/* ask for a dynamic major */
result = alloc_chrdev_region(&dev, ads7843_minor, 0,
"ads7843");
ads7843_major = MAJOR(dev);
if (result < 0) {
printk(KERN_WARNING "ADS7843: can't get major %d\n", ads7843_major);
return result;
}
/* mem alloc for device */
ads7843_device = kmalloc(sizeof(struct ads7843_dev), GFP_KERNEL);
if (!ads7843_device) {
result = -ENOMEM;
printk(KERN_WARNING "ads 7843 memory alloc failed.\n");
goto fail; /* Make this more graceful */
}
memset(ads7843_device, 0, sizeof(struct ads7843_dev));
/* init mutex */
init_MUTEX(&(ads7843_device->sem));
/* reg device */
dev_no = MKDEV(ads7843_major, ads7843_minor);
cdev_init(&ads7843_device->cdev, &ads7843_fops);
ads7843_device->cdev.owner = THIS_MODULE;
ads7843_device->cdev.ops = &ads7843_fops;
result = cdev_add (&ads7843_device->cdev, dev_no, 1);
/* Fail gracefully if need be */
if (result){
printk(KERN_NOTICE "Error %d adding ads7843.\n", result);
goto fail;
}
printk("ads7843 module init major:%d.\n",ads7843_major);
return 0;
fail:
ads7843_cleanup_module();
return result;
}
module_init(ads7843_init_module);
module_exit(ads7843_cleanup_module);
MODULE_AUTHOR("Zhenhui wang, wangzhenhui2002@hotmail.com");
MODULE_LICENSE("GPL");