- 论坛徽章:
- 0
|
在s3c2410板上移植一个触摸屏的驱动程序。在加载驱动,建立相关的设备节点后,cat /dev/tsc 进行简单测试时,点击触摸屏没有输出,驱动停在 read中。经过简单调试,发现用writel来设置arm的寄存器,readl读出寄存器值,没有发生变化,寄存器的值一直是初始值。(上面部分以解决)。不能响应INT_TC和INT_ADC中断。请各位帮忙看看。
cat /dev/tsc部分信息:
touchscreen s3c2410_ts_read
ADCCON = 0x4c40
ADCTSC = 0xc7
ADCDLY = 0xff
ADCDAT0 = 0xf000
ADCDAT1 = 0xf000
CLKCON = 0x8fff0
SUBSRCPND = 0x92
INTSUBMSK = 0x1fe
INTMSK = 0x6b4e3dcf
附上驱动源代码。
#define PEN_UP 0
#define PEN_DOWN 1
#define PEN_FLEETING 2
#define MAX_TS_BUF 8 /* how many do we want to buffer */
#define DRIVER_NAME "touchscreen"
#define TSRAW_MAJOR 224
#define TSRAW_MINOR 1
#define PDEBUG
#ifdef PDEBUG
#define pr_debug(fmt,arg...) \
printk(KERN_INFO fmt,##arg)
#else
#define pr_debug(fmt,arg...)
#endif
typedef struct {
unsigned short pressure;
unsigned short x;
unsigned short y;
unsigned short pad;
} tsret;
typedef struct {
struct cdev cdev;
unsigned int penStatus; /* PEN_UP, PEN_DOWN, PEN_SAMPLE */
tsret buf[MAX_TS_BUF]; /* protect against overrun */
unsigned int head, tail; /* head and tail for queued events */
wait_queue_head_t wq;
spinlock_t lock;
int xp;
int yp;
}tsdev;
static tsdev *ts;
static dev_t devno;
static int adc_state = 0;
static void __iomem *base_addr;
static struct clk *adc_clock;
#define BUF_HEAD (ts->buf[ts->head])
#define BUF_TAIL (ts->buf[ts->tail])
#define INCBUF(x,mod) ((++(x)) & ((mod) - 1))
static void (*tsEvent)(void);
#define WAIT4INT (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | \
S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(3) )
#define MODE_X (S3C2410_ADCTSC_XM_SEN | S3C2410_ADCTSC_YP_SEN | \
S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_PULL_UP_DISABLE | \
S3C2410_ADCTSC_XY_PST(1))
#define MODE_Y (S3C2410_ADCTSC_XP_SEN | S3C2410_ADCTSC_YM_SEN | \
S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_PULL_UP_DISABLE | \
S3C2410_ADCTSC_XY_PST(2))
#define ADC_X (S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(49) | \
S3C2410_ADCCON_SELMUX(7)| S3C2410_ADCCON_ENABLE_START)
#define ADC_Y (S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(49) | \
S3C2410_ADCCON_SELMUX(5)| S3C2410_ADCCON_ENABLE_START)
static inline void user_print(void)
{
pr_debug("ADCCON = 0x%x\n",readl(base_addr + S3C2410_ADCCON));
pr_debug("ADCTSC = 0x%x\n",readl(base_addr + S3C2410_ADCTSC));
pr_debug("ADCDLY = 0x%x\n",readl(base_addr + S3C2410_ADCDLY));
pr_debug("ADCDAT0 = 0x%x\n",readl(base_addr + S3C2410_ADCDAT0));
pr_debug("ADCDAT1 = 0x%x\n",readl(base_addr + S3C2410_ADCDAT1));
pr_debug("CLKCON = 0x%x\n",readl(S3C2410_CLKCON));
pr_debug("SUBSRCPND = 0x%x\n",readl(S3C2410_SUBSRCPND));
pr_debug("INTSUBMSK = 0x%x\n",readl(S3C2410_INTSUBMSK));
pr_debug("INTMSK = 0x%x\n",readl(S3C2410_INTMSK));
}
static inline void mode_x_axis(void)
{
writel(MODE_X, base_addr+S3C2410_ADCTSC);
}
static inline void mode_y_axis(void)
{
writel(MODE_Y, base_addr+S3C2410_ADCTSC);
}
static inline void start_adc_x(void)
{
writel(ADC_X, base_addr+S3C2410_ADCCON);
}
static inline void start_adc_y(void)
{
writel(ADC_Y, base_addr+S3C2410_ADCCON);
}
static inline void disable_ts_adc(void)
{
writel(readl(base_addr+S3C2410_ADCCON) & (~(S3C2410_ADCCON_READ_START)),
base_addr+S3C2410_ADCCON);
}
static void tsEvent_raw(void)
{
pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);
if (ts->penStatus == PEN_DOWN) {
BUF_HEAD.x = ts->xp;
BUF_HEAD.y = ts->yp;
BUF_HEAD.pressure = PEN_DOWN;
} else {
BUF_HEAD.x = 0;
BUF_HEAD.y = 0;
BUF_HEAD.pressure = PEN_UP;
}
ts->head = INCBUF(ts->head, MAX_TS_BUF);
wake_up_interruptible(&(ts->wq));
}
static int tsRead(tsret * ts_ret)
{
pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);
spin_lock_irq(&(ts->lock));
ts_ret->x = BUF_TAIL.x;
ts_ret->y = BUF_TAIL.y;
ts_ret->pressure = BUF_TAIL.pressure;
ts->tail = INCBUF(ts->tail, MAX_TS_BUF);
spin_unlock_irq(&(ts->lock));
return sizeof(tsret);
}
static ssize_t s3c2410_ts_read(struct file *filp, __user char *buffer, size_t count, loff_t *ppos)
{
tsret ts_ret;
pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);
user_print();
retry:
if (ts->head != ts->tail) {
int count;
count = tsRead(&ts_ret);
if(count){
copy_to_user(buffer, (char *)&ts_ret, count);
}
pr_debug( "copy to user\n" );
return count;
} else {
if (filp->f_flags & O_NONBLOCK){
pr_debug( "error: nonblock\n" );
return -EAGAIN;
}
pr_debug( "sleep\n" );
interruptible_sleep_on(&(ts->wq));
if (signal_pending(current)){
pr_debug( "error pending\n" );
return -ERESTARTSYS;
}
pr_debug( "%s %s retry.\n", DRIVER_NAME, __FUNCTION__);
goto retry;
}
return sizeof(tsret);
}
static unsigned int s3c2410_ts_poll(struct file *filp, struct poll_table_struct *wait)
{
pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);
poll_wait(filp, &(ts->wq), wait);
return (ts->head == ts->tail) ? 0 : (POLLIN | POLLRDNORM);
}
static inline void start_ts_adc(void)
{
pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);
adc_state = 0;
mode_x_axis();
start_adc_x();
}
static void s3c2410_get_XY(void)
{
pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);
if (adc_state == 0) {
adc_state = 1;
disable_ts_adc();
ts->yp = readl(base_addr + S3C2410_ADCDAT0) & 0x3ff;
mode_y_axis();
start_adc_y();
} else if (adc_state == 1) {
adc_state = 0;
disable_ts_adc();
ts->xp = readl(base_addr + S3C2410_ADCDAT1) & 0x3ff;
ts->penStatus = PEN_DOWN;
pr_debug( "PEN DOWN: x: %08d, y: %08d\n", ts->xp, ts->yp);
// wait_up_int();
writel(WAIT4INT, base_addr + S3C2410_ADCTSC);
tsEvent();
}
}
static irqreturn_t s3c2410_isr_adc(int irq, void *dev_id)
{
pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);
spin_lock_irq(&(ts->lock));
if (ts->penStatus == PEN_UP)
s3c2410_get_XY();
spin_unlock_irq(&(ts->lock));
return IRQ_HANDLED;
}
static irqreturn_t s3c2410_isr_tc(int irq, void *dev_id)
{
pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);
spin_lock_irq(&(ts->lock));
if (ts->penStatus == PEN_UP) {
start_ts_adc();
}else{
ts->penStatus = PEN_UP;
pr_debug( "PEN UP: x: %08d, y: %08d\n", ts->xp, ts->yp);
writel(WAIT4INT, base_addr + S3C2410_ADCTSC);
tsEvent();
}
spin_unlock_irq(&(ts->lock));
return IRQ_HANDLED;
}
static int s3c2410_ts_open(struct inode *inode, struct file *filp)
{
pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);
filp->private_data = ts;
ts->head = ts->tail = 0;
ts->penStatus = PEN_UP;
tsEvent = tsEvent_raw;
init_waitqueue_head(&(ts->wq));
return 0;
}
static int s3c2410_ts_release(struct inode *inode, struct file *filp)
{
pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);
return 0;
}
static struct file_operations s3c2410ts_fops = {
.owner = THIS_MODULE,
.open = s3c2410_ts_open,
.read = s3c2410_ts_read,
.release = s3c2410_ts_release,
.poll = s3c2410_ts_poll,
};
void tsEvent_dummy(void) {}
static int __init s3c2410_ts_init(void)
{
int err;
pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);
tsEvent = tsEvent_dummy;
adc_clock = clk_get(NULL, "adc");
if (!adc_clock) {
printk(KERN_WARNING "failed to get adc clock source\n");
return -ENOENT;
}
clk_enable(adc_clock);
base_addr = ioremap(S3C2410_PA_ADC,0x20);
if(base_addr == NULL){
printk(KERN_WARNING "%s : Can't remap register block!\n",DRIVER_NAME);
return -ENOMEM;
}
pr_debug( "ioremap base_addr = %x\n",(unsigned int)base_addr);
/* set gpio to XP, YM, YP and YM */
s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);
s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);
s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);
s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);
writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(49), base_addr + S3C2410_ADCCON);
writel(WAIT4INT, base_addr + S3C2410_ADCTSC);
writel(readl(S3C2410_INTMSK)&0x7fffffff, S3C2410_INTMSK);
pr_debug( "%s : initial pin connection.\n",DRIVER_NAME);
if(TSRAW_MAJOR){
devno = MKDEV(TSRAW_MAJOR,TSRAW_MINOR);
err = register_chrdev_region(devno,1,DRIVER_NAME);
}else{
err = alloc_chrdev_region(&devno,0,1,DRIVER_NAME);
}
if(err < 0){
printk(KERN_WARNING "%s :Can't get major!\n",DRIVER_NAME);
goto major_failed;
}else{
pr_debug( "%s : get major.\n",DRIVER_NAME);
}
ts = kmalloc(sizeof(tsdev),GFP_KERNEL);
if(!ts){
printk(KERN_WARNING "%s : Can't malloc memory!\n",DRIVER_NAME);
err = -ENOMEM;
goto allc_err;
}
memset(ts, 0, sizeof(tsdev));
cdev_init(&(ts->cdev), &s3c2410ts_fops);
ts->cdev.owner = THIS_MODULE;
ts->cdev.ops = &s3c2410ts_fops;
pr_debug( "%s : get memory.\n",DRIVER_NAME);
/* Enable touch interrupt */
err = request_irq(IRQ_ADC, s3c2410_isr_adc, IRQF_SHARED, DRIVER_NAME, &(ts->cdev));
if(err){
printk(KERN_WARNING "Could not allocate ts IRQ_ADC!\n");
err = -EIO;
goto adc_failed;
}
pr_debug( "%s : register IRQ_ADC.\n",DRIVER_NAME);
err = request_irq(IRQ_TC, s3c2410_isr_tc, IRQF_SHARED, DRIVER_NAME, &(ts->cdev));
if(err){
printk(KERN_WARNING "Could not allocate ts IRQ_TC!\n");
err = -EIO;
goto tc_failed;
}
pr_debug( "%s : register IRQ_TC.\n",DRIVER_NAME);
err = cdev_add(&(ts->cdev),devno,1);
if(err < 0){
printk(KERN_WARNING "%s : add cdev error!\n",DRIVER_NAME);
goto failed;
}
pr_debug( "char device %s loaded successfully!\n",DRIVER_NAME);
return 0;
failed:
free_irq(IRQ_TC, &(ts->cdev));
tc_failed:
free_irq(IRQ_ADC, &(ts->cdev));
adc_failed:
kfree(ts);
allc_err:
unregister_chrdev_region(devno,1);
major_failed:
iounmap(base_addr);
clk_disable(adc_clock);
clk_put(adc_clock);
adc_clock = NULL;
return err;
}
static void __exit s3c2410_ts_exit(void)
{
pr_debug( "%s %s\n", DRIVER_NAME, __FUNCTION__);
cdev_del(&(ts->cdev));
free_irq(IRQ_TC, &(ts->cdev));
free_irq(IRQ_ADC, &(ts->cdev));
kfree(ts);
unregister_chrdev_region(devno,1);
iounmap(base_addr);
if(adc_clock){
clk_disable(adc_clock);
clk_put(adc_clock);
adc_clock = NULL;
}
}
module_init(s3c2410_ts_init);
module_exit(s3c2410_ts_exit);
MODULE_LICENSE("GPL");
[ 本帖最后由 yawsh 于 2009-4-3 13:48 编辑 ] |
|