转自:http://www.cnblogs.com/liu_xf/archive/2011/06/22/2086750.html
摘要:
分析内核s3c-ts.c源码,看它是如何采集坐标信息及防抖动处理的。
介绍:
直接上源码吧,完全注释:
- 001 /* linux/drivers/input/touchscreen/s3c-ts.c
- 002 *
- 003 * This program is free software; you can redistribute it and/or modify
- 004 * it under the terms of the GNU General Public License as published by
- 005 * the Free Software Foundation; either version 2 of the License, or
- 006 * (at your option) any later version.
- 007 *
- 008 * This program is distributed in the hope that it will be useful,
- 009 * but WITHOUT ANY WARRANTY; without even the implied warranty of
- 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- 011 * GNU General Public License for more details.
- 012 *
- 013 * You should have received a copy of the GNU General Public License
- 014 * along with this program; if not, write to the Free Software
- 015 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- 016 *
- 017 * a misc driver for mini6410 touch screen
- 018 * by FriendlyARM 2010
- 019 *
- 020 * Based on following software:
- 021 *
- 022 ** Copyright (c) 2004 Arnaud Patard <arnaud.patard@rtp-net.org>
- 023 ** iPAQ H1940 touchscreen support
- 024 **
- 025 ** ChangeLog
- 026 **
- 027 ** 2004-09-05: Herbert Potzl <herbert@13thfloor.at>
- 028 ** - added clock (de-)allocation code
- 029 **
- 030 ** 2005-03-06: Arnaud Patard <arnaud.patard@rtp-net.org>
- 031 ** - h1940_ -> s3c24xx (this driver is now also used on the n30
- 032 ** machines :P)
- 033 ** - Debug messages are now enabled with the config option
- 034 ** TOUCHSCREEN_S3C_DEBUG
- 035 ** - Changed the way the value are read
- 036 ** - Input subsystem should now work
- 037 ** - Use ioremap and readl/writel
- 038 **
- 039 ** 2005-03-23: Arnaud Patard <arnaud.patard@rtp-net.org>
- 040 ** - Make use of some undocumented features of the touchscreen
- 041 ** controller
- 042 **
- 043 ** 2006-09-05: Ryu Euiyoul <ryu.real@gmail.com>
- 044 ** - added power management suspend and resume code
- 045 *
- 046 */
- 047
- 048 #include <linux/errno.h>
- 049 #include <linux/kernel.h>
- 050 #include <linux/module.h>
- 051 #include <linux/slab.h>
- 052 #include <linux/input.h>
- 053 #include <linux/init.h>
- 054 #include <linux/serio.h>
- 055 #include <linux/delay.h>
- 056 #include <linux/platform_device.h>
- 057 #include <linux/clk.h>
- 058 #include <linux/fs.h>
- 059 #include <linux/poll.h>
- 060 #include <linux/irq.h>
- 061 #include <linux/interrupt.h>
- 062 #include <linux/cdev.h>
- 063 #include <linux/miscdevice.h>
- 064
- 065 #include <asm/uaccess.h>
- 066 #include <asm/io.h>
- 067 #include <asm/irq.h>
- 068 #include <mach/hardware.h>
- 069
- 070 #include <plat/regs-adc.h>
- 071 #include <mach/irqs.h>
- 072 #include <mach/map.h>
- 073 #include <mach/regs-clock.h>
- 074 #include <mach/regs-gpio.h>
- 075 #include <mach/gpio-bank-a.h>
- 076 #include <mach/ts.h>
- 077
- 078 #define CONFIG_TOUCHSCREEN_S3C_DEBUG
- 079 #undef CONFIG_TOUCHSCREEN_S3C_DEBUG
- 080 #define DEBUG_LVL KERN_DEBUG
- 081
- 082
- 083 #ifdef CONFIG_MINI6410_ADC
- 084 DEFINE_SEMAPHORE(ADC_LOCK); //定义并初始化了一个信号量
- 085 //37内核就没有DECLARE_MUTEX了吧,功能应该是一样的
- 086
- 087
- 088 /* Indicate who is using the ADC controller */
- 089 //ADC的状态,防止触摸屏转换时,ADC正在被使用
- 090 #define LOCK_FREE 0
- 091 #define LOCK_TS 1
- 092 #define LOCK_ADC 2
- 093 static int adc_lock_id = LOCK_FREE;
- 094
- 095 #define ADC_free() (adc_lock_id == LOCK_FREE)
- 096 #define ADC_locked4TS() (adc_lock_id == LOCK_TS)
- 097
- 098 //==
- 099 static inline int s3c_ts_adc_lock(int id) {
- 100 int ret;
- 101
- 102 ret = down_trylock(&ADC_LOCK); //获取自旋锁
- 103 if (!ret) {
- 104 adc_lock_id = id;
- 105 }
- 106
- 107 return ret; //返回状态 1:失败 0:成功
- 108 }
- 109 //--
- 110
- 111 static inline void s3c_ts_adc_unlock(void) {
- 112 adc_lock_id = 0;
- 113 up(&ADC_LOCK); //释放自旋锁
- 114 }
- 115 #endif
- 116
- 117
- 118 /* Touchscreen default configuration */
- 119 struct s3c_ts_mach_info s3c_ts_default_cfg __initdata = {
- 120 .delay = 10000, //转换延时
- 121 .presc = 49, //转换时钟分频
- 122 .oversampling_shift = 2, //转换次数 4次
- 123 .resol_bit = 12, //转换精度
- 124 .s3c_adc_con = ADC_TYPE_2 //6410是type2
- 125 };
- 126 /*
- 127 struct s3c_ts_mach_info s3c_ts_default_cfg __initdata = {
- 128 .delay = 10000,
- 129 .presc = 49,
- 130 .oversampling_shift = 2,
- 131 .resol_bit = 10
- 132 };
- 133 */
- 134 /*
- 135 * Definitions & global arrays.
- 136 */
- 137 #define DEVICE_NAME "touchscreen"
- 138 static DECLARE_WAIT_QUEUE_HEAD(ts_waitq); //定义并初始化一个等待队列
- 139
- 140 typedef unsigned TS_EVENT;
- 141 #define NR_EVENTS 64 //触摸屏fifo大小
- 142
- 143 static TS_EVENT events[NR_EVENTS];
- 144 static int evt_head, evt_tail; //fifo的头的尾
- 145 //驱动写fifo时evt_head++,应用读fifo时 evt_tail++
- 146
- 147 #define ts_evt_pending() ((volatile u8)(evt_head != evt_tail)) //相等就表示满了
- 148 #define ts_evt_get() (events + evt_tail)
- 149 #define ts_evt_pull() (evt_tail = (evt_tail + 1) & (NR_EVENTS - 1))
- 150 #define ts_evt_clear() (evt_head = evt_tail = 0)
- 151
- 152 //将AD转换的值放入FIFO
- 153 //这里是一个先进先出的fifo
- 154 //只要有数据被添加进来,就会唤醒ts_waitq进程
- 155 static void ts_evt_add(unsigned x, unsigned y, unsigned down) {
- 156 unsigned ts_event;
- 157 int next_head;
- 158
- 159 ts_event = ((x << 16) | (y)) | (down << 31);
- 160 next_head = (evt_head + 1) & (NR_EVENTS - 1);
- 161 //没满就装入
- 162 if (next_head != evt_tail) {
- 163 events[evt_head] = ts_event;
- 164 evt_head = next_head;
- 165 //printk("====>Add ... [ %4d, %4d ]%s\n", x, y, down ? "":" ~~~");
- 166
- 167 /* wake up any read call */
- 168 if (waitqueue_active(&ts_waitq)) { //判斷等待隊列是否有進程睡眠
- 169 wake_up_interruptible(&ts_waitq); //唤醒ts_waitq等待队列中所有interruptible类型的进程
- 170 }
- 171 } else {
- 172 /* drop the event and try to wakeup readers */
- 173 printk(KERN_WARNING "mini6410-ts: touch event buffer full");
- 174 wake_up_interruptible(&ts_waitq);
- 175 }
- 176 }
- 177
- 178 static unsigned int s3c_ts_poll( struct file *file, struct poll_table_struct *wait)
- 179 {
- 180 unsigned int mask = 0;
- 181
- 182 //将ts_waitq等待队列添加到poll_table里去
- 183 poll_wait(file, &ts_waitq, wait);
- 184 //返回掩码
- 185 if (ts_evt_pending())
- 186 mask |= POLLIN | POLLRDNORM; //返回设备可读
- 187
- 188 return mask;
- 189 }
- 190
- 191 //读 系统调用==
- 192 static int s3c_ts_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
- 193 {
- 194 DECLARE_WAITQUEUE(wait, current); //把当前进程加到定义的等待队列头wait中
- 195 char *ptr = buff;
- 196 int err = 0;
- 197
- 198 add_wait_queue(&ts_waitq, &wait); //把wait入到等待队列头中。该队列会在进程等待的条件满足时唤醒它。
- 199 //我们必须在其他地方写相关代码,在事件发生时,对等的队列执行wake_up()操作。
- 200 //这里是在ts_evt_add里wake_up
- 201 while (count >= sizeof(TS_EVENT)) {
- 202 err = -ERESTARTSYS;
- 203 if (signal_pending(current)) //如果是信号唤醒 参考http://www.360doc.com/content/10/1009/17/1317564_59632874.shtml
- 204 break;
- 205
- 206 if (ts_evt_pending()) {
- 207 TS_EVENT *evt = ts_evt_get();
- 208
- 209 err = copy_to_user(ptr, evt, sizeof(TS_EVENT));
- 210 ts_evt_pull();
- 211
- 212 if (err)
- 213 break;
- 214
- 215 ptr += sizeof(TS_EVENT);
- 216 count -= sizeof(TS_EVENT);
- 217 continue;
- 218 }
- 219
- 220 set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为可中断的睡眠
- 221 err = -EAGAIN;
- 222 if (filp->f_flags & O_NONBLOCK) //如果上层调用是非阻塞方式,则不阻塞该进程,直接返回EAGAIN
- 223 break;
- 224 schedule(); //本进程在此处交出CPU控制权,等待被唤醒
- 225 //进程调度的意思侧重于把当前任务从CPU拿掉,再从就绪队列中按照调度算法取一就绪进程占用CPU
- 226 }
- 227 current->state = TASK_RUNNING;
- 228 remove_wait_queue(&ts_waitq, &wait);
- 229
- 230 return ptr == buff ? err : ptr - buff;
- 231 }
- 232 //--
- 233
- 234 static int s3c_ts_open(struct inode *inode, struct file *filp) {
- 235 /* flush event queue */
- 236 ts_evt_clear();
- 237
- 238 return 0;
- 239 }
- 240
- 241 //当应用程序操作设备文件时调用的open read等函数,最终会调用这个结构体中对应的函数
- 242 static struct file_operations dev_fops = {
- 243 .owner = THIS_MODULE,
- 244 .read = s3c_ts_read,
- 245 .poll = s3c_ts_poll, //select系统调用
- 246 .open = s3c_ts_open,
- 247 };
- 248
- 249 //设备号,设备名,注册的时候用到这个数组
- 250 //混杂设备主设备号为10
- 251 static struct miscdevice misc = {
- 252 .minor = MISC_DYNAMIC_MINOR, //自动分配次设置号
- 253 //.minor = 180,
- 254 .name = DEVICE_NAME,
- 255 .fops = &dev_fops,
- 256 };
- 257
- 258 //x为0时为等待按下中断,x为1是为等待抬起中断
- 259 #define WAIT4INT(x) (((x) << 8) | \
- 260 S3C_ADCTSC_YM_SEN | S3C_ADCTSC_YP_SEN | S3C_ADCTSC_XP_SEN | \
- 261 S3C_ADCTSC_XY_PST(3))
- 262
- 263 //自动连续测量X坐标和Y坐标
- 264 #define AUTOPST (S3C_ADCTSC_YM_SEN | S3C_ADCTSC_YP_SEN | S3C_ADCTSC_XP_SEN | \
- 265 S3C_ADCTSC_AUTO_PST | S3C_ADCTSC_XY_PST(0))
- 266
- 267 static void __iomem *ts_base;
- 268 static struct resource *ts_mem;
- 269 static struct resource *ts_irq;
- 270 static struct clk *ts_clock;
- 271 static struct s3c_ts_info *ts;
- 272
- 273 /**
- 274 * get_down - return the down state of the pen
- 275 * @data0: The data read from ADCDAT0 register.
- 276 * @data1: The data read from ADCDAT1 register.
- 277 *
- 278 * Return non-zero if both readings show that the pen is down.
- 279 */
- 280 static inline bool get_down(unsigned long data0, unsigned long data1)
- 281 {
- 282 /* returns true if both data values show stylus down */
- 283 return (!(data0 & S3C_ADCDAT0_UPDOWN) && !(data1 & S3C_ADCDAT1_UPDOWN)); //判断data0,data1最高位是否仍为"0",为“0”表示触摸笔状态保持为down
- 284 }
- 285
- 286
- 287 /*===========================================================================================
- 288 touch_timer_fire这个函数主要实现以下功能:
- 289
- 290 1、 触摸笔开始点击的时候, 在中断函数stylus_updown里面被调用,
- 291 此时缓存区没有数据,ts.count为0, 并且开启AD转换,而后进入 AD 中断
- 292
- 293 2、 ADC中断函数stylus_action把缓冲区填满的时候,作为中断后半段函数稍后被调用(由内核定时器触发中断),
- 294 此时ts.count为4,算出其平均值后,交给事件处理层(Event Handler)处理,
- 295 主要是填写缓冲,然后唤醒等待输入数据的进程。
- 296
- 297 3、 stylus抬起,等到缓冲区填满后(可能会包含一些无用的数据)被调用,
- 298 这时候判断出stylus up,报告stylus up事件,重新等待stylus down。
- 299 ============================================================================================*/
- 300
- 301 static void touch_timer_fire(unsigned long data) {
- 302 unsigned long data0;
- 303 unsigned long data1;
- 304 int pendown;
- 305
- 306 #ifdef CONFIG_MINI6410_ADC
- 307 if (!ADC_locked4TS()) {
- 308 /* Note: pen UP interrupt detected and handled, the lock is released,
- 309 * so do nothing in the timer which started by ADC ISR. */
- 310 return;
- 311 }
- 312 #endif
- 313
- 314 data0 = readl(ts_base + S3C_ADCDAT0);
- 315 data1 = readl(ts_base + S3C_ADCDAT1);//读取AD转换数据的值
- 316
- 317 pendown = get_down(data0, data1);
- 318
- 319 if (pendown) {
- 320 if (ts->count == (1 << ts->shift)) { //定时器触发touch_timer_fire中断时执行这个括号里
- 321 #ifdef CONFIG_TOUCHSCREEN_S3C_DEBUG
- 322 {
- 323 struct timeval tv;
- 324 do_gettimeofday(&tv);
- 325 printk(KERN_INFO "T: %06d, X: %03ld, Y: %03ld\n",
- 326 (int)tv.tv_usec, ts->xp, ts->yp);
- 327 }
- 328 #endif
- 329
- 330 ts_evt_add((ts->xp >> ts->shift), (ts->yp >> ts->shift), 1);//求平均,并写入fifo
- 331
- 332 ts->xp = 0;
- 333 ts->yp = 0;
- 334 ts->count = 0;
- 335 }
- 336
- 337 /* start automatic sequencing A/D conversion */
- 338 //每次按下有四次AD转换,以下为在按下中断中触发的第一次AD转换,其余三次在AD转换中断处理函数中触发
- 339 //AUTOPST表示自动连续测量 以得到X位置,Y位置
- 340 writel(S3C_ADCTSC_PULL_UP_DISABLE | AUTOPST, ts_base + S3C_ADCTSC);
- 341 // 启动D转换,转换后会产生中断IRQ_ADC
- 342 writel(readl(ts_base + S3C_ADCCON) | S3C_ADCCON_ENABLE_START,
- 343 ts_base + S3C_ADCCON);
- 344
- 345 } else { //如果是松开,报告其触摸笔状态
- 346 ts->xp = 0;
- 347 ts->yp = 0;
- 348 ts->count = 0;
- 349
- 350 ts_evt_add(0, 0, 0);
- 351
- 352 /* PEN is UP, Let's wait the PEN DOWN interrupt */
- 353 writel(WAIT4INT(0), ts_base + S3C_ADCTSC); // 设置INT 位,等待 DOWN 中断
- 354
- 355 #ifdef CONFIG_MINI6410_ADC
- 356 if (ADC_locked4TS()) {
- 357 s3c_ts_adc_unlock();
- 358 }
- 359 #endif
- 360 }
- 361 }
- 362
- 363 static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0);
- 364
- 365 //触摸屏按下松开中断服务==
- 366 static irqreturn_t stylus_updown(int irqno, void *param)
- 367 {
- 368 #ifdef CONFIG_TOUCHSCREEN_S3C_DEBUG
- 369 unsigned long data0;
- 370 unsigned long data1;
- 371 int is_waiting_up;
- 372 int pendown;
- 373 #endif
- 374
- 375 #ifdef CONFIG_MINI6410_ADC
- 376 if (!ADC_locked4TS()) {
- 377 if (s3c_ts_adc_lock(LOCK_TS)) {
- 378 /* Locking ADC controller failed */
- 379 printk("Lock ADC failed, %d\n", adc_lock_id);
- 380 return IRQ_HANDLED;
- 381 }
- 382 }
- 383 #endif
- 384
- 385 #ifdef CONFIG_TOUCHSCREEN_S3C_DEBUG
- 386 data0 = readl(ts_base + S3C_ADCDAT0);
- 387 data1 = readl(ts_base + S3C_ADCDAT1);
- 388
- 389 is_waiting_up = readl(ts_base + S3C_ADCTSC) & (1 << 8);
- 390 pendown = get_down(data0, data1);
- 391
- 392 printk("P: %d <--> %c\n", pendown, is_waiting_up ? 'u':'d');
- 393 #endif
- 394 //执行如下语句否则不断产生中断从而把系统卡死
- 395 if (ts->s3c_adc_con == ADC_TYPE_2) {
- 396 /* Clear ADC and PEN Down/UP interrupt */
- 397 __raw_writel(0x0, ts_base + S3C_ADCCLRWK);
- 398 __raw_writel(0x0, ts_base + S3C_ADCCLRINT);
- 399 }
- 400
- 401 /* TODO we should never get an interrupt with pendown set while
- 402 * the timer is running, but maybe we ought to verify that the
- 403 * timer isn't running anyways. */
- 404
- 405 touch_timer_fire(1);
- 406
- 407 return IRQ_HANDLED;
- 408 }
- 409
- 410 //ad转换结束中断服务程序==
- 411 static irqreturn_t stylus_action(int irqno, void *param)
- 412 {
- 413 unsigned long data0;
- 414 unsigned long data1;
- 415
- 416 #ifdef CONFIG_MINI6410_ADC
- 417 if (!ADC_locked4TS()) {
- 418 if (ADC_free()) {
- 419 printk("Unexpected\n");
- 420
- 421 /* Clear ADC interrupt */
- 422 __raw_writel(0x0, ts_base + S3C_ADCCLRINT);
- 423 }
- 424
- 425 return IRQ_HANDLED;
- 426 }
- 427 #endif
- 428
- 429 data0 = readl(ts_base + S3C_ADCDAT0);
- 430 data1 = readl(ts_base + S3C_ADCDAT1);
- 431
- 432 if (ts->resol_bit == 12) {
- 433 #if defined(CONFIG_TOUCHSCREEN_NEW)
- 434 ts->yp += S3C_ADCDAT0_XPDATA_MASK_12BIT - (data0 & S3C_ADCDAT0_XPDATA_MASK_12BIT);
- 435 ts->xp += S3C_ADCDAT1_YPDATA_MASK_12BIT - (data1 & S3C_ADCDAT1_YPDATA_MASK_12BIT);
- 436 #else
- 437 ts->xp += data0 & S3C_ADCDAT0_XPDATA_MASK_12BIT;
- 438 ts->yp += data1 & S3C_ADCDAT1_YPDATA_MASK_12BIT;
- 439 #endif
- 440 } else {
- 441 #if defined(CONFIG_TOUCHSCREEN_NEW)
- 442 ts->yp += S3C_ADCDAT0_XPDATA_MASK - (data0 & S3C_ADCDAT0_XPDATA_MASK);
- 443 ts->xp += S3C_ADCDAT1_YPDATA_MASK - (data1 & S3C_ADCDAT1_YPDATA_MASK);
- 444 #else
- 445 ts->xp += data0 & S3C_ADCDAT0_XPDATA_MASK;
- 446 ts->yp += data1 & S3C_ADCDAT1_YPDATA_MASK;
- 447 #endif
- 448 } // 转换结果累加
- 449
- 450 ts->count++;
- 451
- 452 if (ts->count < (1 << ts->shift)) { // 采样未完成,继续下一次采样 ,通过 ENABLE_START 启动 AD 转换,一次一个数据
- 453 writel(S3C_ADCTSC_PULL_UP_DISABLE | AUTOPST, ts_base + S3C_ADCTSC);
- 454 writel(readl(ts_base + S3C_ADCCON) | S3C_ADCCON_ENABLE_START, ts_base + S3C_ADCCON);
- 455 } else { // 采样完毕,激活下半部处理程序touch_timer_fire,处理接收数据
- 456 mod_timer(&touch_timer, jiffies + 1); //设置定时器超时的时间,目的是为了延时触发 touch_timer_fire 中断,如果在这段时间有抬起中断发生,则表示是抖动
- 457 //jiffies变量记录了系统启动以来,系统定时器已经触发的次数。内核每秒钟将jiffies变量增加HZ次。
- 458 //因此,对于HZ值为100的系统,1个jiffy等于10ms,而对于HZ为1000的系统,1个jiffy仅为1ms
- 459
- 460 writel(WAIT4INT(1), ts_base + S3C_ADCTSC); //设置为等待抬起中断
- 461 }
- 462
- 463 if (ts->s3c_adc_con == ADC_TYPE_2) {
- 464 /* Clear ADC and PEN Down/UP interrupt */
- 465 __raw_writel(0x0, ts_base + S3C_ADCCLRWK);
- 466 __raw_writel(0x0, ts_base + S3C_ADCCLRINT);
- 467 }
- 468
- 469 return IRQ_HANDLED;
- 470 }
- 471
- 472
- 473 #ifdef CONFIG_MINI6410_ADC
- 474 static unsigned int _adccon, _adctsc, _adcdly;
- 475
- 476 //其它模块要用ADC时,需要调用这个函数,来确定ADC是否可用,如果可用,则将它锁住,不让别的驱动用
- 477 int mini6410_adc_acquire_io(void) {
- 478 int ret;
- 479
- 480 ret = s3c_ts_adc_lock(LOCK_ADC); //锁住ADC,不让其它模块使用
- 481 if (!ret) { //如果ADC没有被使用,则保存ADC寄存器的值
- 482 _adccon = readl(ts_base + S3C_ADCCON);
- 483 _adctsc = readl(ts_base + S3C_ADCTSC);
- 484 _adcdly = readl(ts_base + S3C_ADCDLY);
- 485 }
- 486
- 487 return ret;// 0:操作成功 1:操作失败
- 488 }
- 489 EXPORT_SYMBOL(mini6410_adc_acquire_io); //声明为外部可用
- 490
- 491 //其它模块不要用ADC了,需要调用这个函数,来解锁ADC让别的驱动用
- 492 void mini6410_adc_release_io(void) {
- 493 //还原ADC寄存器的设置
- 494 writel(_adccon, ts_base + S3C_ADCCON);
- 495 writel(_adctsc, ts_base + S3C_ADCTSC);
- 496 writel(_adcdly, ts_base + S3C_ADCDLY);
- 497 writel(WAIT4INT(0), ts_base + S3C_ADCTSC);
- 498
- 499 s3c_ts_adc_unlock(); //释放ADC,其它模块可以使用
- 500 }
- 501
- 502 EXPORT_SYMBOL(mini6410_adc_release_io);
- 503 #endif
- 504
- 505 //获得触摸屏的配置信息==
- 506 static struct s3c_ts_mach_info *s3c_ts_get_platdata(struct device *dev)
- 507 {
- 508 if (dev->platform_data != NULL)
- 509 return (struct s3c_ts_mach_info *)dev->platform_data; //优先使用 arch/arm/mach-s3c64xx中的定义
- 510
- 511 return &s3c_ts_default_cfg; //如果前面没定义,则使用本函数的default定义
- 512 }
- 513 //--
- 514
- 515 /*
- 516 * The functions for inserting/removing us as a module.
- 517 */
- 518 static int __init s3c_ts_probe(struct platform_device *pdev)
- 519 {
- 520 struct resource *res;
- 521 struct device *dev;
- 522 struct s3c_ts_mach_info * s3c_ts_cfg;
- 523 int ret, size;
- 524
- 525 dev = &pdev->dev;
- 526
- 527 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- 528 if (res == NULL) {
- 529 dev_err(dev,"no memory resource specified\n");
- 530 return -ENOENT;
- 531 }
- 532
- 533 size = (res->end - res->start) + 1;
- 534 ts_mem = request_mem_region(res->start, size, pdev->name);
- 535 if (ts_mem == NULL) {
- 536 dev_err(dev, "failed to get memory region\n");
- 537 ret = -ENOENT;
- 538 goto err_req;
- 539 }
- 540
- 541 ts_base = ioremap(res->start, size);
- 542 if (ts_base == NULL) {
- 543 dev_err(dev, "failed to ioremap() region\n");
- 544 ret = -EINVAL;
- 545 goto err_map;
- 546 }
- 547
- 548 ts_clock = clk_get(&pdev->dev, "adc");
- 549 if (IS_ERR(ts_clock)) {
- 550 dev_err(dev, "failed to find watchdog clock source\n");
- 551 ret = PTR_ERR(ts_clock);
- 552 goto err_clk;
- 553 }
- 554
- 555 clk_enable(ts_clock);
- 556
- 557 s3c_ts_cfg = s3c_ts_get_platdata(&pdev->dev); //获取配置参数
- 558
- 559 //设置ADC分频
- 560 if ((s3c_ts_cfg->presc & 0xff) > 0)
- 561 writel(S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(s3c_ts_cfg->presc & 0xff),
- 562 ts_base + S3C_ADCCON);
- 563 else
- 564 writel(0, ts_base + S3C_ADCCON);
- 565
- 566 /* Initialise registers */
- 567 //设置转换延时
- 568 if ((s3c_ts_cfg->delay & 0xffff) > 0)
- 569 writel(s3c_ts_cfg->delay & 0xffff, ts_base + S3C_ADCDLY);
- 570
- 571 if (s3c_ts_cfg->resol_bit == 12) {
- 572 switch(s3c_ts_cfg->s3c_adc_con) {
- 573 case ADC_TYPE_2:
- 574 writel(readl(ts_base + S3C_ADCCON) | S3C_ADCCON_RESSEL_12BIT,
- 575 ts_base + S3C_ADCCON);
- 576 break;
- 577
- 578 case ADC_TYPE_1:
- 579 writel(readl(ts_base + S3C_ADCCON) | S3C_ADCCON_RESSEL_12BIT_1,
- 580 ts_base + S3C_ADCCON);
- 581 break;
- 582
- 583 default:
- 584 dev_err(dev, "Touchscreen over this type of AP isn't supported !\n");
- 585 break;
- 586 }
- 587 }
- 588
- 589 writel(WAIT4INT(0), ts_base + S3C_ADCTSC);
- 590
- 591 ts = kzalloc(sizeof(struct s3c_ts_info), GFP_KERNEL);
- 592
- 593 ts->shift = s3c_ts_cfg->oversampling_shift;
- 594 ts->resol_bit = s3c_ts_cfg->resol_bit;
- 595 ts->s3c_adc_con = s3c_ts_cfg->s3c_adc_con;
- 596
- 597 /* For IRQ_PENDUP */
- 598 ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- 599 if (ts_irq == NULL) {
- 600 dev_err(dev, "no irq resource specified\n");
- 601 ret = -ENOENT;
- 602 goto err_irq;
- 603 }
- 604
- 605 ret = request_irq(ts_irq->start, stylus_updown, IRQF_SAMPLE_RANDOM, "s3c_updown", ts);
- 606 if (ret != 0) {
- 607 dev_err(dev,"s3c_ts.c: Could not allocate ts IRQ_PENDN !\n");
- 608 ret = -EIO;
- 609 goto err_irq;
- 610 }
- 611
- 612 /* For IRQ_ADC */
- 613 ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
- 614 if (ts_irq == NULL) {
- 615 dev_err(dev, "no irq resource specified\n");
- 616 ret = -ENOENT;
- 617 goto err_irq;
- 618 }
- 619
- 620 ret = request_irq(ts_irq->start, stylus_action, IRQF_SAMPLE_RANDOM | IRQF_SHARED,
- 621 "s3c_action", ts);
- 622 if (ret != 0) {
- 623 dev_err(dev, "s3c_ts.c: Could not allocate ts IRQ_ADC !\n");
- 624 ret = -EIO;
- 625 goto err_irq;
- 626 }
- 627
- 628 printk(KERN_INFO "%s got loaded successfully : %d bits\n", DEVICE_NAME, s3c_ts_cfg->resol_bit);
- 629
- 630 ret = misc_register(&misc); //注册这混杂字符设备
- 631 if (ret) {
- 632 dev_err(dev, "s3c_ts.c: Could not register device(mini6410 touchscreen)!\n");
- 633 ret = -EIO;
- 634 goto fail;
- 635 }
- 636
- 637 return 0;
- 638
- 639 fail:
- 640 free_irq(ts_irq->start, ts->dev);
- 641 free_irq(ts_irq->end, ts->dev);
- 642
- 643 err_irq:
- 644 kfree(ts);
- 645
- 646 clk_disable(ts_clock);
- 647 clk_put(ts_clock);
- 648
- 649 err_clk:
- 650 iounmap(ts_base);
- 651
- 652 err_map:
- 653 release_resource(ts_mem);
- 654 kfree(ts_mem);
- 655
- 656 err_req:
- 657 return ret;
- 658 }
- 659
- 660 static int s3c_ts_remove(struct platform_device *dev)
- 661 {
- 662 printk(KERN_INFO "s3c_ts_remove() of TS called !\n");
- 663
- 664 disable_irq(IRQ_ADC);
- 665 disable_irq(IRQ_PENDN);
- 666
- 667 free_irq(IRQ_PENDN, ts->dev);
- 668 free_irq(IRQ_ADC, ts->dev);
- 669
- 670 if (ts_clock) {
- 671 clk_disable(ts_clock);
- 672 clk_put(ts_clock);
- 673 ts_clock = NULL;
- 674 }
- 675
- 676 misc_deregister(&misc);
- 677 iounmap(ts_base);
- 678
- 679 return 0;
- 680 }
- 681
- 682 #ifdef CONFIG_PM
- 683 static unsigned int adccon, adctsc, adcdly;
- 684
- 685 static int s3c_ts_suspend(struct platform_device *dev, pm_message_t state)
- 686 {
- 687 adccon = readl(ts_base + S3C_ADCCON);
- 688 adctsc = readl(ts_base + S3C_ADCTSC);
- 689 adcdly = readl(ts_base + S3C_ADCDLY);
- 690
- 691 disable_irq(IRQ_ADC);
- 692 disable_irq(IRQ_PENDN);
- 693
- 694 clk_disable(ts_clock);
- 695
- 696 return 0;
- 697 }
- 698
- 699 static int s3c_ts_resume(struct platform_device *pdev)
- 700 {
- 701 clk_enable(ts_clock);
- 702
- 703 writel(adccon, ts_base + S3C_ADCCON);
- 704 writel(adctsc, ts_base + S3C_ADCTSC);
- 705 writel(adcdly, ts_base + S3C_ADCDLY);
- 706 writel(WAIT4INT(0), ts_base + S3C_ADCTSC);
- 707
- 708 enable_irq(IRQ_ADC);
- 709 enable_irq(IRQ_PENDN);
- 710 return 0;
- 711 }
- 712 #else
- 713 #define s3c_ts_suspend NULL
- 714 #define s3c_ts_resume NULL
- 715 #endif
- 716
- 717 static struct platform_driver s3c_ts_driver = {
- 718 .probe = s3c_ts_probe,
- 719 .remove = s3c_ts_remove,
- 720 .suspend = s3c_ts_suspend,
- 721 .resume = s3c_ts_resume,
- 722 .driver = {
- 723 .owner = THIS_MODULE,
- 724 .name = "s3c-ts",
- 725 },
- 726 };
- 727
- 728 static char banner[] __initdata = KERN_INFO "S3C Touchscreen driver, (c) 2010 FriendlyARM,\n";
- 729
- 730 static int __init s3c_ts_init(void)
- 731 {
- 732 printk(banner);
- 733 return platform_driver_register(&s3c_ts_driver);
- 734 }
- 735
- 736 static void __exit s3c_ts_exit(void)
- 737 {
- 738 platform_driver_unregister(&s3c_ts_driver);
- 739 }
- 740
- 741 module_init(s3c_ts_init);
- 742 module_exit(s3c_ts_exit);
- 743
- 744 MODULE_AUTHOR("FriendlyARM Inc.");
- 745 MODULE_LICENSE("GPL");
- 746
- 747
- 748 /*
- 749 * 驱动分析
- 750 * 1、内核是如何加载驱动的?
- 751 * 首先要提到两个结构体:设备用Platform_device表示,驱动用Platform_driver进行注册
- 752 * Platform机制开发发底层驱动的大致流程为: 定义 platform_device 注册 platform_device 定义 platform_driver 注册 platform_driver
- 753 * 首先要确认的就是设备的资源信息platform_device,例如设备的地址,中断号等 该结构体定义在kernel\include\linux\platform_device.h
- 754 * 该结构一个重要的元素是resource,该元素存入了最为重要的设备资源信息,定义在kernel\include\linux\ioport.h中
- 755 * 下面我们以本例来进行说明:
- 756 * arch/arm/mach-s3c64xx中dev-ts-mini6410.c中定义了platform_device s3c_device_ts
- 757 * 定义好了platform_device结构体后就可以调用函数platform_add_devices向系统中添加该设备了,之后可以调用platform_driver_register()进行设备注册。
- 758 * 要注意的是,这里的platform_device设备的注册过程必须在相应设备驱动加载之前被调用,即执行platform_driver_register之前,原因是因为驱动注册时需要
- 759 * 匹配内核中所以已注册的设备名。
- 760 * platform_devicerr的注册是在arch/arm/mach-s3c64xx中mach-mini6410.c中的mini6410_machine_init函数实现的。
- 761 * mini6410_machine_init是在启动后调用,它是在module_init之前;更具体的见MACHINE_START
- 762 * MACHINE_START(MINI6410, "MINI6410")
- 763 *
- 764 * .boot_params = S3C64XX_PA_SDRAM + 0x100, //.boot_params是bootloader向内核传递的参数的位置,这要和bootloader中参数的定义要一致。
- 765 *
- 766 * .init_irq = s3c6410_init_irq, //.init_irq在start_kernel() --> init_IRQ() --> init_arch_irq()中被调用
- 767 * .map_io = mini6410_map_io, //.map_io 在 setup_arch() --> paging_init() --> devicemaps_init()中被调用
- 768 * .init_machine = mini6410_machine_init, //init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,
- 769 * //放在 arch_initcall() 段里面,会自动按顺序被调用。
- 770 * .timer = &s3c24xx_timer, //.timer是定义系统时钟,定义TIMER4为系统时钟,在arch/arm/plat-s3c/time.c中体现。
- 771 * //在start_kernel() --> time_init()中被调用。
- 772 * MACHINE_END
- 773 * 再来看看platform_driver,这个定义在本文中,
- 774 * 在驱动初始化函数中调用函数platform_driver_register()注册platform_driver,需要注意的是s3c_device_ts结构中name元素和s3c_ts_driver结构中driver.name
- 775 * 必须是相同的,这样在platform_driver_register()注册时会对所有已注册的所有platform_device中的name和当前注册的platform_driver的driver.name进行比较,
- 776 * 只有找到相同的名称的platfomr_device才能注册成功,当注册成功时会调用platform_driver结构元素probe函数指针,这里就是s3c_ts_probe
- 777 * 参考资料:http://blogold.chinaunix.net/u2/60011/showart.php?id=1018502
- 778 *
- 779 * 2、timer在这里的作用
- 780 * timer是用来防抖的,我们知道,触摸屏处理分为两个时间段,一个是由按下中断触发的四次AD转换的时间A,一个是4次AD转换完成后将AD数据存入FIFO的时间B,在时间A,没有打开抬起中断,
- 781 * 也就是说如果在这段时间有抬起事件,也不会触发中断,不会影响AD的转换。在时间B,打开抬起中断,打开定时器延时触发touch_timer_fire,如果在延时这段时间,有抬起事件发生
- 782 * 则touch_timer_fire不会将前面的数据存入到FIFO中,否则写入FIFO,表示值有效。
- 783 *
- 784 *
- 785 */