一. device的注册
1.0 两个注册
//在smdk6410_machine_init中既注册了touchscreen的私有信息也注册了ts资源
1.1 ts私有信息的注册
在arch/arm/mach-s3c64xx/mach-smdk6410.c中
1.2 ts设备资源的注册
二. device_driver
2.0 两个宏
2.1 初始化
ok6410的touchscreen在内核源码的位置:driver/input/touchscreen/s3c-ts.c
device 与 device_driver按名字s3c-ts匹配之后,就进入s3c_ts_probe函数
2.2 probe函数
ts底板图:
ts连到核心板图:
TSXP --> AIN7
TSYP --> AIN5
2.3 IRQ_PENDN
2.3.1 fire
注意:
在按下状态,先提交数据,产生ADC中断,
在松开状态,先提交数据,切换为按下中断
2.4 IRQ_ADC
在进入IRQ_ADC中断之前,己经定义了一个时间定时器,它的处理函数是 touch_timer_fire
IRQ_ADC中断处理函数:
注意:
在按下状态,先提交数据,产生ADC中断,
在松开状态,先提交数据,切换为按下中断
三.总结
3.1 按下到松开时的流程如下:
3.2 文字说明
初始化时设为等侍down中断模式
当有触摸笔按下时:
a.触发中断,进入stylus_updown函数
stylus_updown:判断是down中断, 如果ts->count,触发adc中断
b.触发ADC中断,进入stylus_action函数
stylus_action: ts->count小于4次, 触发adc中断;
继续自动检测,直到満足4次
stylus_action: 时间定时器触发touch_timer_fire,并切换到等侍up中断模式
c. touch_timer_fire:
判断是down中断,汇报坐标信息,触发adc中断,与b进入循环
持继汇报触摸笔按下信息
当有触摸笔松开时:
a. touch_timer_fire:
判断是up中断,汇报坐标信息,切换到等侍按下中断
1.0 两个注册
//在smdk6410_machine_init中既注册了touchscreen的私有信息也注册了ts资源
-
1 在arch/arm/mach-s3c64xx/mach-smdk6410.c中 2 static void __init smdk6410_machine_init(void) 3 { 4 //在arch/arm/mach-s3c64xx/dev-ts.c中 5 s3c_ts_set_platdata(&s3c_ts_platform); //1.设备私有信息的注册 6 //在driver/base/platform.c中 7 platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices)); //2.设备资源的注册 8 }
在arch/arm/mach-s3c64xx/mach-smdk6410.c中
-
1 static struct s3c_ts_mach_info s3c_ts_platform __initdata = { 2 .delay = 10000, //延时 3 .presc = 49, //分频 4 .oversampling_shift = 2, //分频 5 .resol_bit = 12, //精度 6 .s3c_adc_con = ADC_TYPE_2, //分频 7 }; 8 smdk6410_machine_init 9 { 10 //下面这个函数在arch/arm/mach-s3c64xx/dev-ts.c中 11 s3c_ts_set_platdata(&s3c_ts_platform); 12 }
-
1 //在arch/arm/mach-s3c64xx/dev-ts.c中 2 static struct resource s3c_ts_resource[] = { 3 [0] = { 4 .start = SAMSUNG_PA_ADC, //0x7E00B000 5 .end = SAMSUNG_PA_ADC + SZ_256 - 1, //0x7E00B100 6 .flags = IORESOURCE_MEM, 7 }, 8 [1] = { 9 .start = IRQ_PENDN, //0x5e=94 10 .end = IRQ_PENDN, 11 .flags = IORESOURCE_IRQ, 12 }, 13 [2] = { 14 .start = IRQ_ADC, //0x5f=95 15 .end = IRQ_ADC, 16 .flags = IORESOURCE_IRQ, 17 } 18 }; 19 struct platform_device s3c_device_ts = { 20 .name = "s3c-ts", 21 .id = -1, 22 .num_resources = ARRAY_SIZE(s3c_ts_resource), 23 .resource = s3c_ts_resource, 24 }; 25 //在arch/arm/mach-s3c64xx/mach-smdk6410.c中 26 static struct platform_device *smdk6410_devices[] __initdata = { 27 &s3c_device_ts, //把ts放入到总的ts列表中 28 } 29 //在arch/arm/mach-s3c64xx/mach-smdk6410.c中 30 static void __init smdk6410_machine_init(void) 31 { 32 //在driver/base/platform.c中一起注册 33 platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices)); 34 }
二. device_driver
2.0 两个宏
-
1 #define WAIT4INT(x) //只是针对于S3C_ADCTSC寄存器 2 (((x)<<8) | //<bit8> 0->down 1->up interrupt signal 3 S3C_ADCTSC_YM_SEN | //<bit7> 1 = Switch enable (YM = VSSA_ADC) 4 S3C_ADCTSC_YP_SEN | //<bit6> 1 = Switch disable (YP=AIN5, Hi-z) 5 //XM_SEN //<bit5> 0 = Switch disable (XM = AIN6, Hi-z) 6 S3C_ADCTSC_XP_SEN | //<bit4> 1 = Switch disable (XP=AIN7, Hi-z) 7 //PULL_UP //<bit3> 0 = XP Pull-up Enable. 8 //AUTO_PST //<bit2> 0 = Normal ADC conversion. 9 S3C_ADCTSC_XY_PST(3)) //<bit1-0> 3: Waiting for Interrupt Mode 10 #define AUTOPST 11 (S3C_ADCTSC_YM_SEN | //1 = Switch enable (YM = VSSA_ADC) 12 S3C_ADCTSC_YP_SEN | //1 = Switch disable (YP=AIN5, Hi-z) 13 S3C_ADCTSC_XP_SEN | //1 = Switch disable (XP=AIN7, Hi-z) 14 S3C_ADCTSC_AUTO_PST | //1 = Auto Sequential measurement of X-position, Y-position 15 S3C_ADCTSC_XY_PST(0)) //0 = No operation mode 16 WAIT4INT(x) : 17 当x=0时,设为等侍down中断 18 当x=1时,设为等侍up中断
ok6410的touchscreen在内核源码的位置:driver/input/touchscreen/s3c-ts.c
device 与 device_driver按名字s3c-ts匹配之后,就进入s3c_ts_probe函数
-
static struct platform_driver s3c_ts_driver = { .probe = s3c_ts_probe, .remove = s3c_ts_remove, .suspend = s3c_ts_suspend, .resume = s3c_ts_resume, .driver = { .owner = THIS_MODULE, .name = "s3c-ts", }, }; static int __init s3c_ts_init(void) { return platform_driver_register(&s3c_ts_driver); } static void __exit s3c_ts_exit(void) { platform_driver_unregister(&s3c_ts_driver); } module_init(s3c_ts_init); module_exit(s3c_ts_exit);
-
1 static int __init s3c_ts_probe(struct platform_device *pdev) 2 { 3 struct resource *res; 4 struct device *dev; 5 struct input_dev *input_dev; 6 struct s3c_ts_mach_info * s3c_ts_cfg; 7 int ret, size; 8 dev = &pdev->dev; 9 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取ts寄存器地址 10 size = (res->end - res->start) + 1; 11 ts_mem = request_mem_region(res->start, size, pdev->name); //申请I/O内存 12 ts_base = ioremap(res->start, size); //request_mem_region申请的内存在使用前要调用ioremap 13 ts_clock = clk_get(&pdev->dev, "adc"); //获取clock 14 clk_enable(ts_clock); //在初始化时disable了ts_clock,这个地方要enable 15 16 //下面这几行是要把ts的配置信息写到寄存器中去 17 s3c_ts_cfg = s3c_ts_get_platdata(&pdev->dev); //获取ts的配置信息, 18 //ts的私有信息:在arch/arm/mach-s3c64xx/mach-smdk6410.c中 19 //enable prescaler && 设置prescaler_value=s3c_ts_cfg->presc 20 writel(S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(s3c_ts_cfg->presc&0xff), ts_base+S3C_ADCCON); 21 22 //s3c_ts_cfg->delay=0x10000 --> External input clock 23 writel(s3c_ts_cfg->delay & 0xffff, ts_base+S3C_ADCDLY); 24 25 //A/D converter resolution selection--> 12-bit A/D conversion 26 writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT, ts_base+S3C_ADCCON); 27 //设为等侍down中断模式 28 writel(WAIT4INT(0), ts_base+S3C_ADCTSC); 29 ts = kzalloc(sizeof(struct s3c_ts_info), GFP_KERNEL); //下面这几行是要初始化s3c_ts_info结构体 30 input_dev = input_allocate_device(); 31 32 ts->dev = input_dev; 33 ts->dev->evbit[0] = ts->dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 34 ts->dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 35 if (s3c_ts_cfg->resol_bit==12) { 36 input_set_abs_params(ts->dev, ABS_X, 0, 0xFFF, 0, 0); //设置x轴的最大最小值 37 input_set_abs_params(ts->dev, ABS_Y, 0, 0xFFF, 0, 0); //设置y轴的最大最小值 38 } 39 input_set_abs_params(ts->dev, ABS_PRESSURE, 0, 1, 0, 0); //设置Press状态的最大最小值(按下或空闲) 40 sprintf(ts->phys, "input(ts)"); 41 ts->dev->name = s3c_ts_name; 42 ts->dev->phys = ts->phys; 43 ts->dev->id.bustype = BUS_RS232; 44 ts->dev->id.vendor = 0xDEAD; 45 ts->dev->id.product = 0xBEEF; 46 ts->dev->id.version = S3C_TSVERSION; 47 ts->shift = s3c_ts_cfg->oversampling_shift; 48 ts->resol_bit = s3c_ts_cfg->resol_bit; 49 ts->s3c_adc_con = s3c_ts_cfg->s3c_adc_con; 50 /* For IRQ_PENDUP */ 51 ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); //获取中断号 52 //申请中断,RANDOM表示设备可以看作随机的发生源 53 ret = request_irq(ts_irq->start, stylus_updown, IRQF_SAMPLE_RANDOM, "s3c_updown", ts); //申请中断 54 55 /* For IRQ_ADC */ 56 ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1); //获取中断号 57 ret = request_irq(ts_irq->start, stylus_action, IRQF_SAMPLE_RANDOM | IRQF_SHARED, "s3c_action", ts); //申请共享中断 58 59 /* All went ok, so register to the input system */ 60 ret = input_register_device(ts->dev); //把这个input_dev添加到input系统中 61 }
ts连到核心板图:
TSXP --> AIN7
TSYP --> AIN5
2.3 IRQ_PENDN
-
1 static irqreturn_t stylus_updown(int irqno, void *param) 2 { 3 unsigned long data0; 4 unsigned long data1; 5 if (!ADC_locked4TS()) //进入中断函数,如果没有加锁,则加上锁 6 if (s3c_ts_adc_lock(LOCK_TS)) //如果加锁失败,则直接返回 7 return IRQ_HANDLED; 8 data0 = readl(ts_base+S3C_ADCDAT0); 9 data1 = readl(ts_base+S3C_ADCDAT1); 10 touch_timer_fire(0); 11 if(ts->s3c_adc_con==ADC_TYPE_2) { 12 //ADCCLRINTPNDNUP: INT_PNDNUP interrupt clear 13 __raw_writel(0x0, ts_base+S3C_ADCCLRWK); 14 //ADCCLRINT: Clear ADC Interrupt 15 __raw_writel(0x0, ts_base+S3C_ADCCLRINT); 16 } 17 return IRQ_HANDLED; 18 }
2.3.1 fire
-
1 static void touch_timer_fire(unsigned long data) 2 { 3 unsigned long data0; 4 unsigned long data1; 5 int pendown; 6 if (!ADC_locked4TS()) //如果当前状态是free,说明加锁失败,直接返回 7 return; 8 9 //这儿的数据读取,是为了判断是down还是up状态 10 data0 = readl(ts_base+S3C_ADCDAT0); //读 11 data1 = readl(ts_base+S3C_ADCDAT1); //读 12 //data0的bit15: 0->按下状态; 1->松开状态 13 //如果data0与data1都不为松开状态,就是按下状态 14 pendown = (!(data0 & S3C_ADCDAT0_UPDOWN)) && (!(data1 & S3C_ADCDAT1_UPDOWN)); 15 if (pendown) { //在按下状态,如果有数据则提交数据, 16 if (ts->count) { //这个ts->count是在IRQ_ADC中改变的 17 input_report_abs(ts->dev, ABS_X, ts->xp); //提交 18 input_report_abs(ts->dev, ABS_Y, ts->yp); //提交 19 input_report_key(ts->dev, BTN_TOUCH, 1); //提交 20 input_report_abs(ts->dev, ABS_PRESSURE, 1); //提交 21 input_sync(ts->dev); //提交 22 } //ts->count>0,说明ADC己经转化过数据了,就提交完数据, 23 ts->xp = 0; //然后把所有数据归零 24 ts->yp = 0; 25 ts->count = 0; 26 //设ADC的模式为自动转换 27 writel(S3C_ADCTSC_PULL_UP_DISABLE | AUTOPST, ts_base+S3C_ADCTSC); 28 //ADCCON bit0 --> A/D conversion starts: 启动adc转换,产生一个IRQ_ADC中断 29 //注意:这儿是要启动ADC中断,但具体是down还是up中断 30 writel(readl(ts_base+S3C_ADCCON) | S3C_ADCCON_ENABLE_START, ts_base+S3C_ADCCON); 31 } 32 else { //如果是松开 33 ts->count = 0; 34 input_report_key(ts->dev, BTN_TOUCH, 0); //提交 35 input_report_abs(ts->dev, ABS_PRESSURE, 0); //提交 36 input_sync(ts->dev); 37 writel(WAIT4INT(0), ts_base+S3C_ADCTSC); //等侍按下中断 38 if (ADC_locked4TS()) //如果还处于锁定状态 39 s3c_ts_adc_unlock(); //释放锁,表示一次按键结束 40 } 41 }
在按下状态,先提交数据,产生ADC中断,
在松开状态,先提交数据,切换为按下中断
2.4 IRQ_ADC
在进入IRQ_ADC中断之前,己经定义了一个时间定时器,它的处理函数是 touch_timer_fire
- static struct timer_list touch_timer =
- TIMER_INITIALIZER(touch_timer_fire, 0, 0);
-
1 static irqreturn_t stylus_action(int irqno, void *param) 2 { 3 unsigned long data0; 4 unsigned long data1; 5 if (!ADC_locked4TS()) { //如果处于未锁定状态,说明出错 6 if (ADC_free()) //锁是在IRQ_TS中加上的 7 __raw_writel(0x0, ts_base + S3C_ADCCLRINT); 8 return IRQ_HANDLED; 9 } 10 data0 = readl(ts_base+S3C_ADCDAT0); //读 11 data1 = readl(ts_base+S3C_ADCDAT1); //读 12 if(ts->resol_bit==12) { 13 ts->xp += data0 & S3C_ADCDAT0_XPDATA_MASK_12BIT; //怎么能让我相信这是在求平均值呢? 14 ts->yp += data1 & S3C_ADCDAT1_YPDATA_MASK_12BIT; 15 } 16 ts->count++; 17 if (ts->count < (1<<ts->shift)) { //小于4次,ts->shift=2 18 writel(S3C_ADCTSC_PULL_UP_DISABLE | AUTOPST, ts_base+S3C_ADCTSC); 19 writel(readl(ts_base+S3C_ADCCON) | S3C_ADCCON_ENABLE_START, ts_base+S3C_ADCCON); 20 } else { //超过4次,则 21 //启动定时器,把超时时间设为jiffies+1,调用touch_timer_fire 22 mod_timer(&touch_timer, jiffies+1); 23 //等侍松开 24 writel(WAIT4INT(1), ts_base+S3C_ADCTSC); 25 } 26 if(ts->s3c_adc_con==ADC_TYPE_2) { 27 //ADCCLRINTPNDNUP: INT_PNDNUP interrupt clear 28 __raw_writel(0x0, ts_base+S3C_ADCCLRWK); 29 //ADCCLRINT: Clear ADC Interrupt 30 __raw_writel(0x0, ts_base+S3C_ADCCLRINT); 31 } 32 return IRQ_HANDLED; 33 }
在按下状态,先提交数据,产生ADC中断,
在松开状态,先提交数据,切换为按下中断
三.总结
3.1 按下到松开时的流程如下:
3.2 文字说明
初始化时设为等侍down中断模式
当有触摸笔按下时:
a.触发中断,进入stylus_updown函数
stylus_updown:判断是down中断, 如果ts->count,触发adc中断
b.触发ADC中断,进入stylus_action函数
stylus_action: ts->count小于4次, 触发adc中断;
继续自动检测,直到満足4次
stylus_action: 时间定时器触发touch_timer_fire,并切换到等侍up中断模式
c. touch_timer_fire:
判断是down中断,汇报坐标信息,触发adc中断,与b进入循环
持继汇报触摸笔按下信息
当有触摸笔松开时:
a. touch_timer_fire:
判断是up中断,汇报坐标信息,切换到等侍按下中断