| ARM Cortex-A8/A9开发板 | ARM11开发板ARM11开发套件 | ARM9开发板ARM9开发套件 | 飞思卡尔I.MX28I.MX6开发板 |
| tiny210/mini210开发板 | mini6410开发板/套餐 | Tiny6410开发板无线套件 | mini2440开发板/套餐 | micro2440开发板带核心板 | 友善开发板配件 模块 | QQ2440 V3 SBC-2410X |
| FL2440 OK2440-IV V4 | OK2440-III OK2440V3 | TE2440-II TE2440V2 | OK6410 S3C6410 ARM11 | FL6410/OK6410B ARM11 | TE6410 S3C6410 ARM11 | TE-9263 AT91SAM9263 | 开发板配件 模块和其他 |
| Xilinx开发板/fpga开发套件 | altera开发板/fpga开发套件 | 基于FMC FPGA/DSP子卡 |
| BeagleBoard原厂开发板 | PandaBoard原厂开发板 |
| TI DM3730 DevKit8500D | TI系列 omap3530开发板 | ATMEL系列 开发板 | Samsung系列 开发板 | 开发板配件-仿真器 模块 | NXP LPC3250 mini3250 |
| Tiny210 三星S5PV210 | Mini210/Mini210s开发板 | Tiny4412开发板Cortex-A9 | Tiny6410开发板 ARM11 | mini2440开发板 ARM9 | micro2440开发板 ARM9 | mini6410开发板 ARM11 | 友善ARM开发板配件 模块 |
| keil ARM仿真器/开发工具 | STM32开发板 Cortex-M3 | NXP LPC开发板Cortex-M3 | ATMEL系列 ARM开发板 | Luminary EKK8962开发板 | Freescale i.MX31开发板 | 三星s3c2440 s3c6410开发板 |
| FL2440 OK2440-IV V4 | OK6410 S3C6410 ARM11 | FL6410/OK6410B ARM11 | OK210/-A Cortex-A8 | TE2440-II TE2440V2 | TE6410 S3C6410 ARM11 | TE-9263 AT91SAM9263 | ARM开发板配件 模块等 |
| Real6410开发板/套餐 | Real210开发板/Real2410 | WIFI GPS GPRS模块摄像头 |
| Xilinx开发板/开发套件KIT | Altera开发板/开发套件KIT | 配套模块 |
| NXP LPC开发板 ARM7 | Cortex-M3 STM32开发板 | 最小系统模块类(初学者勿选) | 开发工具 仿真器 调试器 | TFT液晶屏LCD 液晶模块 |
| DM642开发板/DM642实验箱 | DM6446开发板/DSP实验箱 | DSP仿真器 液晶屏 摄像头等 |
| DM3730/AM3715/AM3359 | OMAP3530AM1808AM3517 | 飞思卡尔Cortex-A9开发板 | ATMEL系列ARM开发板 | NXP LPC3250 LPC1788 | Samsung系列ARM开发板 | STM32系列F207/F407 | ARM开发板配件-仿真器 模块 |
| Realv210 S5PV210开发板 | Real6410开发板 ARM11 | WIFI GPS GPRS模块摄像头 |
| NXP LPC开发板 ARM7 | Cortex-M3 STM32开发板 | ARM小系统模块(初学勿选) | 开发工具 ARM仿真器 调试器 | TFT液晶屏LCD 液晶模块 |
| keil ARM仿真器/开发工具 | STM32开发板 Cortex-M3 | NXP LPC开发板Cortex-M3 | ATMEL系列 ARM开发板 | Luminary EKK8962开发板 | Freescale i.MX31开发板 | 三星s3c2440 s3c6410开发板 |
| Exynos4412开发板A9四核 | UT-S5PV210 S5PC100 | UT-S3C6410开发板 ARM11 | idea 6410开发板 ARM11 | UT-S3C2450开发板 ARM9 | UT-S3C2416开发板 ARM9 | UT-S3C6410核心板 工控 |
| Xilinx开发板 FPGA开发套件 | altera开发板 FPGA开发套件 | FMC模块 配件 下载线 |
| DM642开发板/DM642实验箱 | DM6446开发板/DSP实验箱 | DSP仿真器 液晶屏 摄像头等 |
| s3c2410 ARM9 FS2410 | s3c2440开发板 ARM9 | s3c44B0开发板 ARM7 | s3c6410 2450 2443/13 | PXA270 PXA320 PXA255 | Atmel 9200 9261 9G20 | Philips NXP开发板YL-LPC | FS-EP9315开发板ARM9 | LCD液晶屏TFT真彩/触摸屏 | ARM仿真器/仿真调试工具 | ARM开发板配套ARM核心板 |
| 三星s3c2410 s3c44B0 | 三星s3c6410 s3c2443 | Intel PXA270 PXA320 | UP-CUP OMAP5910平台 | FPGA Xilinx altera平台 | 配件-仿真器 模块 液晶屏LCD | 龙芯开发板UP-DRAGON-I | 物联网教学科研设备 | EDA/FPGA/SOPC教学科研 |
| utu2440-F-V4.5 含核心板 | utu2440-S-V4.1 单板结构 | YC2440-F-V5.1开发板 豪华型 | YC-ePC-A系列人机界面 | Mars-EDA套件CPLD FPGA | 触摸屏LCD 摄像头 WI-FI模块 |
| ARM开发板/开发平台 | FPGA开发板/开发平台 | 编程器/烧写器下载器烧录 | ZLG 仿真器ARM FPGA DSP | ZLG周立功逻辑分析仪 |
| DSP初学者开发套件(DSK) | DSP仿真器/仿真系统 | DSP开发板/DSP试验箱 | DaVinci系列多媒体平台 | Xilinx FPGA大学计划产品 | SEED教学实验系统 | FPGA/DSP FPGA开发板 | TI原厂开发板套件/CCS软件 |
| ARM开发板 | XSCALE开发板 | ARM仿真器 | SOPC开发板 | DSP开发板 | 达芬奇系列DSP开发平台 | DSP仿真器 | DSP实验箱 | DSP专业研究平台 | ARM实验箱系列 |
| 广嵌实验室项目产品 | GEC2440/GEC6410开发板 |
| 泰克Tektronix示波器 | 普源RIGOL示波器 | USB虚拟示波器 |
| UT-S5PC100 UT-S5PV210 | UT-S3C6410开发板 ARM11 | idea 6410开发板 ARM11 | UT-S3C2450开发板 ARM9 | UT-S3C2416开发板 ARM9 | UT-S3C6410核心板 工控 | 开发板模块wifi gps等 |
| EMA OMAP3530开发板 | EMA OMAP 3530核心板 |
| 三星s3c2410 s3c44B0 | 三星s3c6410 s3c2443 | Intel PXA270 PXA320 | UP-CUP OMAP5910平台 | FPGA Xilinx Altera平台 | 配件-仿真器 模块 液晶屏LCD | 龙芯系列 UP-DRAGON-I |
| utu2440-F-V4.5 含核心板 | utu2440-S-V4.1 单板结构 | YC2440-F-V5.1开发板 豪华型 | 触摸屏LCD 摄像头 WI-FI模块 | Mars-EDA套件CPLD FPGA | ePC系列工控一体机ARM9 A8 |
| ARM开发板/开发平台 | FPGA开发板/开发平台 | 编程器/烧写器下载器烧录 | ZLG 仿真器ARM FPGA DSP | ZLG周立功逻辑分析仪 |
| DSP初学者开发套件(DSK) | DSP仿真器 | DSP开发板 | DSP实验箱 | Xilinx FPGA大学计划产品 |
| ARM开发板 | XSCALE开发板 | ARM仿真器 | SOPC开发板 | DSP开发板 | 达芬奇系列DSP开发平台 | DSP仿真器 | DSP实验箱 | DSP专业研究平台 | ARM实验箱系列 |
| ARM开发板S3C6410等 | Cortex-A8/ARM11仿真器 | ADSP Blackfin开发板 | FPGA Spartan-3E开发板 |
| GEC2410开发套件ARM9 | GEC2440开发套件ARM9 |
| 泰克 示波器 |
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
084DEFINE_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
093static 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//==
099static 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
111static 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 */
119struct 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/*
127struct 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"
138static DECLARE_WAIT_QUEUE_HEAD(ts_waitq); //定义并初始化一个等待队列
139
140typedef unsigned TS_EVENT;
141#define NR_EVENTS 64 //触摸屏fifo大小
142
143static TS_EVENT events[NR_EVENTS];
144static 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进程
155static 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
178static 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//读 系统调用==
192static 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
234static 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等函数,最终会调用这个结构体中对应的函数
242static 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
251static 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
267static void __iomem *ts_base;
268static struct resource *ts_mem;
269static struct resource *ts_irq;
270static struct clk *ts_clock;
271static 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 */
280static 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
301static 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
363static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0);
364
365//触摸屏按下松开中断服务==
366static 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转换结束中断服务程序==
411static 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
474static unsigned int _adccon, _adctsc, _adcdly;
475
476//其它模块要用ADC时,需要调用这个函数,来确定ADC是否可用,如果可用,则将它锁住,不让别的驱动用
477int 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}
489EXPORT_SYMBOL(mini6410_adc_acquire_io); //声明为外部可用
490
491//其它模块不要用ADC了,需要调用这个函数,来解锁ADC让别的驱动用
492void 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
502EXPORT_SYMBOL(mini6410_adc_release_io);
503#endif
504
505//获得触摸屏的配置信息==
506static 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 */
518static 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
639fail:
640 free_irq(ts_irq->start, ts->dev);
641 free_irq(ts_irq->end, ts->dev);
642
643err_irq:
644 kfree(ts);
645
646 clk_disable(ts_clock);
647 clk_put(ts_clock);
648
649err_clk:
650 iounmap(ts_base);
651
652err_map:
653 release_resource(ts_mem);
654 kfree(ts_mem);
655
656err_req:
657 return ret;
658}
659
660static 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
683static unsigned int adccon, adctsc, adcdly;
684
685static 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
699static 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
717static 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
728static char banner[] __initdata = KERN_INFO "S3C Touchscreen driver, (c) 2010 FriendlyARM,\n";
729
730static int __init s3c_ts_init(void)
731{
732 printk(banner);
733 return platform_driver_register(&s3c_ts_driver);
734}
735
736static void __exit s3c_ts_exit(void)
737{
738 platform_driver_unregister(&s3c_ts_driver);
739}
740
741module_init(s3c_ts_init);
742module_exit(s3c_ts_exit);
743
744MODULE_AUTHOR("FriendlyARM Inc.");
745MODULE_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 * 匹配内核中所以已注册的设备名。
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
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 */