转自: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 */