一. device的註冊1.0 兩個註冊//在smdk6410_machine_init中既註冊了touchscreen的私有信息也註冊了ts資源 在arch/arm/mach-s3c64xx/mach-smdk6410.c中 static void __init smdk6410_machine_
一. device的註冊
1.0 兩個註冊
//在smdk6410_machine_init中既註冊了touchscreen的私有信息也註冊了ts資源
- 在arch/arm/mach-s3c64xx/mach-smdk6410.c中
- static void __init smdk6410_machine_init(void)
- {
- //在arch/arm/mach-s3c64xx/dev-ts.c中
- s3c_ts_set_platdata(&s3c_ts_platform); //1.設備私有信息的註冊
- //在driver/base/platform.c中
- platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices)); //2.設備資源的註冊
- }
1.1 ts私有信息的註冊
在arch/arm/mach-s3c64xx/mach-smdk6410.c中
- static struct s3c_ts_mach_info s3c_ts_platform __initdata = {
- .delay = 10000, //延時
- .presc = 49, //分頻
- .oversampling_shift = 2, //分頻
- .resol_bit = 12, //精度
- .s3c_adc_con = ADC_TYPE_2, //分頻
- };
- smdk6410_machine_init
- {
- //下麵這個函數在arch/arm/mach-s3c64xx/dev-ts.c中
- s3c_ts_set_platdata(&s3c_ts_platform);
- }
1.2 ts設備資源的註冊
- //在arch/arm/mach-s3c64xx/dev-ts.c中
- static struct resource s3c_ts_resource[] = {
- [0] = {
- .start = SAMSUNG_PA_ADC, //0x7E00B000
- .end = SAMSUNG_PA_ADC + SZ_256 - 1, //0x7E00B100
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_PENDN, //0x5e=94
- .end = IRQ_PENDN,
- .flags = IORESOURCE_IRQ,
- },
- [2] = {
- .start = IRQ_ADC, //0x5f=95
- .end = IRQ_ADC,
- .flags = IORESOURCE_IRQ,
- }
- };
- struct platform_device s3c_device_ts = {
- .name = "s3c-ts",
- .id = -1,
- .num_resources = ARRAY_SIZE(s3c_ts_resource),
- .resource = s3c_ts_resource,
- };
- //在arch/arm/mach-s3c64xx/mach-smdk6410.c中
- static struct platform_device *smdk6410_devices[] __initdata = {
- &s3c_device_ts, //把ts放入到總的ts列表中
- }
- //在arch/arm/mach-s3c64xx/mach-smdk6410.c中
static void __init smdk6410_machine_init(void)
{
//在driver/base/platform.c中一起註冊
platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));
}
二. device_driver
2.0 兩個巨集
- #define WAIT4INT(x) //只是針對於S3C_ADCTSC寄存器
- (((x)<<8) | //<bit8> 0->down 1->up interrupt signal
- S3C_ADCTSC_YM_SEN | //<bit7> 1 = Switch enable (YM = VSSA_ADC)
- S3C_ADCTSC_YP_SEN | //<bit6> 1 = Switch disable (YP=AIN5, Hi-z)
- //XM_SEN //<bit5> 0 = Switch disable (XM = AIN6, Hi-z)
- S3C_ADCTSC_XP_SEN | //<bit4> 1 = Switch disable (XP=AIN7, Hi-z)
- //PULL_UP //<bit3> 0 = XP Pull-up Enable.
- //AUTO_PST //<bit2> 0 = Normal ADC conversion.
- S3C_ADCTSC_XY_PST(3)) //<bit1-0> 3: Waiting for Interrupt Mode
- #define AUTOPST
- (S3C_ADCTSC_YM_SEN | //1 = Switch enable (YM = VSSA_ADC)
- S3C_ADCTSC_YP_SEN | //1 = Switch disable (YP=AIN5, Hi-z)
- S3C_ADCTSC_XP_SEN | //1 = Switch disable (XP=AIN7, Hi-z)
- S3C_ADCTSC_AUTO_PST | //1 = Auto Sequential measurement of X-position, Y-position
- S3C_ADCTSC_XY_PST(0)) //0 = No operation mode
WAIT4INT(x) :
當x=0時,設為等侍down中斷
當x=1時,設為等侍up中斷
2.1 初始化
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);
2.2 probe函數
- static int __init s3c_ts_probe(struct platform_device *pdev)
- {
- struct resource *res;
- struct device *dev;
- struct input_dev *input_dev;
- struct s3c_ts_mach_info * s3c_ts_cfg;
- int ret, size;
- dev = &pdev->dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //獲取ts寄存器地址
- size = (res->end - res->start) + 1;
- ts_mem = request_mem_region(res->start, size, pdev->name); //申請I/O記憶體
- ts_base = ioremap(res->start, size); //request_mem_region申請的記憶體在使用前要調用ioremap
- ts_clock = clk_get(&pdev->dev, "adc"); //獲取clock
- clk_enable(ts_clock); //在初始化時disable了ts_clock,這個地方要enable
- //下麵這幾行是要把ts的配置信息寫到寄存器中去
- s3c_ts_cfg = s3c_ts_get_platdata(&pdev->dev); //獲取ts的配置信息,
- //ts的私有信息:在arch/arm/mach-s3c64xx/mach-smdk6410.c中
- //enable prescaler && 設置prescaler_value=s3c_ts_cfg->presc
- writel(S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(s3c_ts_cfg->presc&0xff), ts_base+S3C_ADCCON);
- //s3c_ts_cfg->delay=0x10000 --> External input clock
- writel(s3c_ts_cfg->delay & 0xffff, ts_base+S3C_ADCDLY);
- //A/D converter resolution selection--> 12-bit A/D conversion
- writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT, ts_base+S3C_ADCCON);
- //設為等侍down中斷模式
- writel(WAIT4INT(0), ts_base+S3C_ADCTSC);
- ts = kzalloc(sizeof(struct s3c_ts_info), GFP_KERNEL); //下麵這幾行是要初始化s3c_ts_info結構體
- input_dev = input_allocate_device();
- ts->dev = input_dev;
- ts->dev->evbit[0] = ts->dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- ts->dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
- if (s3c_ts_cfg->resol_bit==12) {
- input_set_abs_params(ts->dev, ABS_X, 0, 0xFFF, 0, 0); //設置x軸的最大最小值
- input_set_abs_params(ts->dev, ABS_Y, 0, 0xFFF, 0, 0); //設置y軸的最大最小值
- }
- input_set_abs_params(ts->dev, ABS_PRESSURE, 0, 1, 0, 0); //設置Press狀態的最大最小值(按下或空閑)
- sprintf(ts->phys, "input(ts)");
- ts->dev->name = s3c_ts_name;
- ts->dev->phys = ts->phys;
- ts->dev->id.bustype = BUS_RS232;
- ts->dev->id.vendor = 0xDEAD;
- ts->dev->id.product = 0xBEEF;
- ts->dev->id.version = S3C_TSVERSION;
- ts->shift = s3c_ts_cfg->oversampling_shift;
- ts->resol_bit = s3c_ts_cfg->resol_bit;
- ts->s3c_adc_con = s3c_ts_cfg->s3c_adc_con;
- /* For IRQ_PENDUP */
- ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); //獲取中斷號
- //申請中斷,RANDOM表示設備可以看作隨機的發生源
- ret = request_irq(ts_irq->start, stylus_updown, IRQF_SAMPLE_RANDOM, "s3c_updown", ts); //申請中斷
- /* For IRQ_ADC */
- ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1); //獲取中斷號
- ret = request_irq(ts_irq->start, stylus_action, IRQF_SAMPLE_RANDOM | IRQF_SHARED, "s3c_action", ts); //申請共用中斷
- /* All went ok, so register to the input system */
- ret = input_register_device(ts->dev); //把這個input_dev添加到input系統中
- }
ts底板圖:
ts連到核心板圖:
TSXP --> AIN7
TSYP --> AIN5
2.3 IRQ_PENDN
- static irqreturn_t stylus_updown(int irqno, void *param)
- {
- unsigned long data0;
- unsigned long data1;
- if (!ADC_locked4TS()) //進入中斷函數,如果沒有加鎖,則加上鎖
- if (s3c_ts_adc_lock(LOCK_TS)) //如果加鎖失敗,則直接返回
- return IRQ_HANDLED;
- data0 = readl(ts_base+S3C_ADCDAT0);
- data1 = readl(ts_base+S3C_ADCDAT1);
- touch_timer_fire(0);
- if(ts->s3c_adc_con==ADC_TYPE_2) {
- //ADCCLRINTPNDNUP: INT_PNDNUP interrupt clear
- __raw_writel(0x0, ts_base+S3C_ADCCLRWK);
- //ADCCLRINT: Clear ADC Interrupt
- __raw_writel(0x0, ts_base+S3C_ADCCLRINT);
- }
- return IRQ_HANDLED;
- }
2.3.1 fire
- static void touch_timer_fire(unsigned long data)
- {
- unsigned long data0;
- unsigned long data1;
- int pendown;
- if (!ADC_locked4TS()) //如果當前狀態是free,說明加鎖失敗,直接返回
- return;
- //這兒的數據讀取,是為了判斷是down還是up狀態
- data0 = readl(ts_base+S3C_ADCDAT0); //讀
- data1 = readl(ts_base+S3C_ADCDAT1); //讀
- //data0的bit15: 0->按下狀態; 1->鬆開狀態
- //如果data0與data1都不為鬆開狀態,就是按下狀態
- pendown = (!(data0 & S3C_ADCDAT0_UPDOWN)) && (!(data1 & S3C_ADCDAT1_UPDOWN));
- if (pendown) { //在按下狀態,如果有數據則提交數據,
- if (ts->count) { //這個ts->count是在IRQ_ADC中改變的
- input_report_abs(ts->dev, ABS_X, ts->xp); //提交
- input_report_abs(ts->dev, ABS_Y, ts->yp); //提交
- input_report_key(ts->dev, BTN_TOUCH, 1); //提交
- input_report_abs(ts->dev, ABS_PRESSURE, 1); //提交
- input_sync(ts->dev); //提交
- } //ts->count>0,說明ADC己經轉化過數據了,就提交完數據,
- ts->xp = 0; //然後把所有數據歸零
- ts->yp = 0;
- ts->count = 0;
- //設ADC的模式為自動轉換
- writel(S3C_ADCTSC_PULL_UP_DISABLE | AUTOPST, ts_base+S3C_ADCTSC);
- //ADCCON bit0 --> A/D conversion starts: 啟動adc轉換,產生一個IRQ_ADC中斷
- //註意:這兒是要啟動ADC中斷,但具體是down還是up中斷
- writel(readl(ts_base+S3C_ADCCON) | S3C_ADCCON_ENABLE_START, ts_base+S3C_ADCCON);
- }
- else { //如果是鬆開
- ts->count = 0;
- input_report_key(ts->dev, BTN_TOUCH, 0); //提交
- input_report_abs(ts->dev, ABS_PRESSURE, 0); //提交
- input_sync(ts->dev);
- writel(WAIT4INT(0), ts_base+S3C_ADCTSC); //等侍按下中斷
- if (ADC_locked4TS()) //如果還處於鎖定狀態
- s3c_ts_adc_unlock(); //釋放鎖,表示一次按鍵結束
- }
- }
註意:
在按下狀態,先提交數據,產生ADC中斷,
在鬆開狀態,先提交數據,切換為按下中斷
2.4 IRQ_ADC
在進入IRQ_ADC中斷之前,己經定義了一個時間定時器,它的處理函數是 touch_timer_fire
- static struct timer_list touch_timer =
- TIMER_INITIALIZER(touch_timer_fire, 0, 0);
IRQ_ADC中斷處理函數:
- static irqreturn_t stylus_action(int irqno, void *param)
- {
- unsigned long data0;
- unsigned long data1;
- if (!ADC_locked4TS()) { //如果處於未鎖定狀態,說明出錯
- if (ADC_free()) //鎖是在IRQ_TS中加上的
- __raw_writel(0x0, ts_base + S3C_ADCCLRINT);
- return IRQ_HANDLED;
- }
- data0 = readl(ts_base+S3C_ADCDAT0); //讀
- data1 = readl(ts_base+S3C_ADCDAT1); //讀
- if(ts->resol_bit==12) {
- ts->xp += data0 & S3C_ADCDAT0_XPDATA_MASK_12BIT; //怎麼能讓我相信這是在求平均值呢?
- ts->yp += data1 & S3C_ADCDAT1_YPDATA_MASK_12BIT;
- }
- ts->count++;
- if (ts->count < (1<<ts->shift)) { //小於4次,ts->shift=2
- writel(S3C_ADCTSC_PULL_UP_DISABLE | AUTOPST, ts_base+S3C_ADCTSC);
- writel(readl(ts_base+S3C_ADCCON) | S3C_ADCCON_ENABLE_START, ts_base+S3C_ADCCON);
- } else { //超過4次,則
- //啟動定時器,把超時時間設為jiffies+1,調用touch_timer_fire
- mod_timer(&touch_timer, jiffies+1);
- //等侍鬆開
- writel(WAIT4INT(1), ts_base+S3C_ADCTSC);
- }
- if(ts->s3c_adc_con==ADC_TYPE_2) {
- //ADCCLRINTPNDNUP: INT_PNDNUP interrupt clear
- __raw_writel(0x0, ts_base+S3C_ADCCLRWK);
- //ADCCLRINT: Clear ADC Interrupt
- __raw_writel(0x0, ts_base+S3C_ADCCLRINT);
- }
- return IRQ_HANDLED;
- }
註意:
在按下狀態,先提交數據,產生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中斷,彙報坐標信息,切換到等侍按下中斷