linux設(shè)備驅(qū)動(dòng)歸納總結(jié)(十三):1.觸摸屏與ADC時(shí)鐘
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
這節(jié)的內(nèi)容說(shuō)不上是驅(qū)動(dòng),只是寫個(gè)代碼讓觸摸屏能夠工作,隨便介紹一下時(shí)鐘子系統(tǒng)(我不知道這樣叫合不合適),僅次而已。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
一、程序不能工作
程序的源代碼在13th_ts_input/13th_ts_input/1st。大致的操作就是配置寄存器,設(shè)置觸摸屏為自動(dòng)坐標(biāo)轉(zhuǎn)換模式,具體請(qǐng)根據(jù)程序?qū)φ?/font>S3C2440文檔。
但是寫完的程序不能工作,檢查原因。
1、中斷注冊(cè)失。
cat
/prov/interrupt 就知道,系統(tǒng)已經(jīng)注冊(cè)了adc和tc中斷,為了能讓我的模塊加載成功,內(nèi)核編譯時(shí)不能加入adc和觸摸屏驅(qū)動(dòng)。
make
menuconfig
1、Device
Drivers ---> Input device support --->[ ] Touchscreens
2、Device
Drivers ---> Character devices --->[ ] ADC driver for
FriendlyARM Mini2440/QQ2440 development boards
重新編譯后啟動(dòng),模塊加載成功,但觸摸屏還是不能工作。
2、使能adc時(shí)鐘
如果有編寫過(guò)裸機(jī)程序的應(yīng)該知道,ad轉(zhuǎn)換和觸摸屏的正常工作還依賴于時(shí)鐘控制寄存器的配置,必須使能adc時(shí)鐘(CLKCON[15])。
但在linux下,有一套專門的規(guī)矩來(lái)使能時(shí)鐘,接下來(lái)先從內(nèi)核啟動(dòng)時(shí)的代碼開(kāi)始介紹: 該網(wǎng)友的博客上有更詳細(xì)的介紹,可以看看,雖然是2410的。
2410下clock源碼分析
1、arch/arm/mach-s3c2440/mach-mini2440.c
265
static void __init mini2440_map_io(void)
266
{
267
s3c24xx_init_io(mini2440_iodesc, ARRAY_SIZE(mini2440_iodesc));
268
s3c24xx_init_clocks(12000000);
//初始化系統(tǒng)時(shí)鐘
269
s3c24xx_init_uarts(mini2440_uartcfgs,
ARRAY_SIZE(mini2440_uartcfgs));
270
}
內(nèi)核啟動(dòng)時(shí)會(huì)通過(guò)mini2440_map_io函數(shù)調(diào)用s3c24xx_init_clocks來(lái)初始化系統(tǒng)時(shí)鐘,接下來(lái)看一下函數(shù)原型。
2、arch/arm/plat-s3c/init.c
75
void __init s3c24xx_init_clocks(int xtal)
76
{
77
if (xtal == 0)
78
xtal = 12*1000*1000;
79
80
if (cpu == NULL)
81
panic("s3c24xx_init_clocks: no cpu setup?\n");
82
83
if (cpu->init_clocks == NULL)
84
panic("s3c24xx_init_clocks: cpu has no clock init\n");
85
else
86
(cpu->init_clocks)(xtal);
//查找struct
cpu_table
87
}
函數(shù)的原型很簡(jiǎn)單,就是調(diào)用了cpu結(jié)構(gòu)里面的成員init_clocks。接下來(lái)看一下2440的cpu結(jié)構(gòu)體在里面的成員。
3、arch/arm/plat-s3c24xx/cpu.c
68
static struct cpu_table cpu_ids[] __initdata = {
69
{
70
.idcode = 0x32410000,
71
.idmask = 0xffffffff,
72
.map_io = s3c2410_map_io,
73
.init_clocks = s3c2410_init_clocks,
74
.init_uarts = s3c2410_init_uarts,
75
.init = s3c2410_init,
76
.name = name_s3c2410
77
},
。。。。。
87
{
88
.idcode = 0x32440000,
89
.idmask = 0xffffffff,
90
.map_io = s3c244x_map_io,
91
.init_clocks = s3c244x_init_clocks,
//2440的
init_clocks函數(shù)原型在這里
92
.init_uarts = s3c244x_init_uarts,
93
.init = s3c2440_init,
94
.name = name_s3c2440
95
},
可以看,cpu->init_clocks的原型就是s4c244x_init_clocks,繼續(xù)看該函數(shù)里面做了什么操作
4、/arch/arm/plat-s3c24xx/s3c244x.c
125
void __init s3c244x_init_clocks(int xtal)
126
{
127
/* initialise the clocks here, to allow other things like the
128
* console to use them, and to add new ones after the
initialisation
129
*/
130
131
s3c24xx_register_baseclocks(xtal);
//三個(gè)步驟,接下來(lái)看一下
132
s3c244x_setup_clocks();
133
s3c2410_baseclk_add();
134
}
函數(shù)里面有三個(gè)操作,接下來(lái)逐個(gè)逐個(gè)看。
5、arch/arm/plat-s3c/clock.c
340
int __init s3c24xx_register_baseclocks(unsigned
long xtal)
341
{
342
printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec
Electronics\n");
343
344
clk_xtal.rate = xtal;
345
346
/* register our clocks */
347
348
if (s3c24xx_register_clock(&clk_xtal)
< 0)
//注冊(cè)時(shí)鐘
349
printk(KERN_ERR "failed to register master xtal\n");
350
351
if (s3c24xx_register_clock(&clk_mpll) < 0)
352
printk(KERN_ERR "failed to register mpll
clock\n");
353
354
if (s3c24xx_register_clock(&clk_upll) < 0)
355
printk(KERN_ERR "failed to register upll
clock\n");
356
357
if (s3c24xx_register_clock(&clk_f) < 0)
358
printk(KERN_ERR "failed to register cpu fclk\n");
359
360
if (s3c24xx_register_clock(&clk_h) < 0)
361
printk(KERN_ERR "failed to register cpu hclk\n");
362
363
if (s3c24xx_register_clock(&clk_p) < 0)
364
printk(KERN_ERR "failed to register cpu
pclk\n");
365
366
return 0;
367
}
跟名字一樣,
s3c24xx_register_baseclocks注冊(cè)了系統(tǒng)中基本所需的時(shí)鐘,如pclk等
6、arch/arm/plat-s3c24xx/s3c244x.c
76
void __init_or_cpufreq s3c244x_setup_clocks(void)
77
{
78
struct clk *xtal_clk;
79
unsigned long clkdiv;
80
unsigned long camdiv;
81
unsigned long xtal;
82
unsigned long hclk, fclk, pclk;
83
int hdiv = 1;
84
85
xtal_clk = clk_get(NULL, "xtal");
86
xtal = clk_get_rate(xtal_clk);
87
clk_put(xtal_clk);
88
89
fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2;
90
91
clkdiv = __raw_readl(S3C2410_CLKDIVN);
92
camdiv = __raw_readl(S3C2440_CAMDIVN);
93
94
/* work out clock scalings */
95
96
switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) {
97
case S3C2440_CLKDIVN_HDIVN_1:
98
hdiv = 1;
99
break;
100
101
case S3C2440_CLKDIVN_HDIVN_2:
102
hdiv = 2;
103
break;
104
105
case S3C2440_CLKDIVN_HDIVN_4_8:
106
hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4;
107
break;
108
109
case S3C2440_CLKDIVN_HDIVN_3_6:
110
hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3;
111
break;
112
}
113
114
hclk = fclk / hdiv;
115
pclk = hclk / ((clkdiv & S3C2440_CLKDIVN_PDIVN) ? 2 : 1);
116
117
/* print brief summary of clocks, etc */
118
119
printk("S3C244X: core %ld.%03ld MHz, memory %ld.%03ld MHz,
peripheral %ld.%03ld MH z\n",
120
print_mhz(fclk), print_mhz(hclk), print_mhz(pclk));
121
122
s3c24xx_setup_clocks(fclk, hclk, pclk);
123
}
上面的函數(shù)通過(guò)從寄存器讀取信息并給fclk、hclk和pclk賦值,然后通過(guò)
s3c24xx_setup_clocks函數(shù)添加到指定結(jié)構(gòu)體。
7、arch/arm/plat-s3c24xx/s3c2410-clock.c
211 int __init
s3c2410_baseclk_add(void)
212 {
213
unsigned long clkslow =
__raw_readl(S3C2410_CLKSLOW);
214
unsigned long clkcon =
__raw_readl(S3C2410_CLKCON);
215
struct clk *clkp;
216
struct clk *xtal;
217
int ret;
218
int ptr;
219
220
clk_upll.enable =
s3c2410_upll_enable;
221
222
if
(s3c24xx_register_clock(&clk_usb_bus) < 0)
223
printk(KERN_ERR "failed
to register usb bus clock\n");
224
225 /* register clocks from
clock array */
226
227
clkp =
init_clocks;
//該結(jié)構(gòu)體中存放著需要enable的時(shí)鐘成員,對(duì)應(yīng)寄存器CLKCON
228
for
(ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
229 /* ensure that we note
the clock state */
230
231
clkp->usage = clkcon
& clkp->ctrlbit ? 1 : 0;
232
233
ret = s3c24xx_register_clock(clkp);
//注冊(cè)并使能
234
if (ret < 0) {
235
printk(KERN_ERR
"Failed to register clock %s (%d)\n",
236
clkp->name,
ret);
237
}
238
}
239
240 /* We must be careful
disabling the clocks we are not intending to
241 * be using at boot time, as
subsystems such as the LCD which do
242 * their own DMA requests to
the bus can cause the system to lockup
243 * if they where in the
middle of requesting bus access.
244 *
245 * Disabling the LCD clock
if the LCD is active is very dangerous,
246 * and therefore the
bootloader should be careful to not enable
247 * the LCD clock if it is
not needed.
248 */
249
250 /* install (and disable) the
clocks we do not need immediately */
251
252
clkp =
init_clocks_disable;
//該結(jié)構(gòu)體中存放著需要disable的時(shí)鐘成員,對(duì)應(yīng)寄存器CLKCON
253
for
(ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++,
clkp++) {
254
255
ret = s3c24xx_register_clock(clkp);
//注冊(cè),默認(rèn)使能
256
if (ret < 0) {
257
printk(KERN_ERR
"Failed to register clock %s (%d)\n",
258
clkp->name,
ret);
259
}
260
261
s3c2410_clkcon_enable(clkp, 0);
//使能后又將成員disable
262
}
263
264 /* show the clock-slow value
*/
265
266
xtal = clk_get(NULL,
"xtal");
267
268
printk("CLOCK: Slow
mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",
269
print_mhz(clk_get_rate(xtal) /
270
( 2 *
S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),
271
(clkslow &
S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",
272
(clkslow &
S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
273
(clkslow &
S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
274
275
s3c_pwmclk_init();
276
return 0;
277 }
48 int s3c2410_clkcon_enable(struct
clk *clk, int enable)
49 {
50
unsigned int clocks =
clk->ctrlbit;
51
unsigned long clkcon;
52
53
clkcon =
__raw_readl(S3C2410_CLKCON);
54
55
if
(enable)
56
clkcon |= clocks;
57
else
58
clkcon &= ~clocks;
//傳入?yún)?shù)為0,所以是disable
59
60 /* ensure none of the
special function bits set */
61
clkcon &=
~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);
62
63
__raw_writel(clkcon,
S3C2410_CLKCON);
64
65
return 0;
66 }
可以看到,s3c2410_baseclk_add分了兩部分的操作。
第一部分:獲取init_clock的數(shù)據(jù)結(jié)構(gòu),并且調(diào)用s3c24xx_register_clock
來(lái)配置寄存器CLKCON并默認(rèn)使能init_clock結(jié)構(gòu)里面指定時(shí)鐘。
第二部分:獲取init_clock_disable數(shù)據(jù)結(jié)構(gòu),除了調(diào)用s3c24xx_register_clock注冊(cè)后,還調(diào)用3c2410_clkcon_enable來(lái)配置寄存器CLKCON來(lái)disable該init_clock_disable結(jié)構(gòu)體里面的時(shí)鐘。
init_clock和init_clock_disable里面的成員與寄存器CLKCON的成員對(duì)應(yīng).
接下來(lái)看看clkp的數(shù)據(jù)結(jié)構(gòu)init_clock在哪里定義:
/arch/arm/plat-s3c24xx/s3c2410-clock.c
130 static struct clk init_clocks[]
= {
131 {
132
.name = "lcd",
133
.id = -1,
134
.parent = &clk_h,
135
.enable =
s3c2410_clkcon_enable,
136
.ctrlbit =
S3C2410_CLKCON_LCDC,
137 }, {
138
.name = "gpio",
139
.id = -1,
140
.parent = &clk_p,
141
.enable =
s3c2410_clkcon_enable,
142
.ctrlbit =
S3C2410_CLKCON_GPIO,
143 }, {
144
.name =
"usb-host",
145
.id = -1,
146
.parent = &clk_h,
。。。。。。
可以看到,lcd、gpio等這類的時(shí)鐘是系統(tǒng)啟動(dòng)時(shí)就已經(jīng)使能了,所以之前我的lcd驅(qū)動(dòng)才能正常工作。
90 static struct clk
init_clocks_disable[] = {
91 {
92
.name = "nand",
93
.id = -1,
94
.parent = &clk_h,
95
.enable =
s3c2410_clkcon_enable,
96
.ctrlbit =
S3C2410_CLKCON_NAND,
97 }, {
98
.name = "sdi",
99
.id = -1,
100
.parent = &clk_p,
101
.enable =
s3c2410_clkcon_enable,
102
.ctrlbit =
S3C2410_CLKCON_SDI,
103 }, {
104
.name = "adc",
//觸摸屏的時(shí)鐘放在
init_clocks_disable中,所以觸摸屏不能工作!
105
.id = -1,
106
.parent = &clk_p,
107
.enable =
s3c2410_clkcon_enable,
108
.ctrlbit =
S3C2410_CLKCON_ADC,
109 }, {
。。。。。
現(xiàn)在就可以知道,為什么觸摸屏不能工作了,因?yàn)樵谙到y(tǒng)啟動(dòng)時(shí)被禁止了。所以需要在函數(shù)中啟動(dòng)它。
既然知道了原因,解決方法就好辦了——將adc成員從init)clocks_disable中刪除并添加到init_clocks中。
同時(shí)還有一個(gè)不用修改內(nèi)核代碼的方法。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
二、clk_enable:
第二個(gè)方法很簡(jiǎn)單,從獲取內(nèi)核中的adc時(shí)鐘,然后將它使能。
先看獲取函數(shù):
struct clk *clk_get(struct device
*dev, const char *con_id)
第一個(gè)參數(shù)中,因?yàn)?font face="DejaVu Serif, serif">clk->id一般為-1,所以直接傳入NULL就可以了。如果clk->id不為-1,函數(shù)會(huì)通過(guò)第一個(gè)參數(shù)傳入的dev獲取dev->bus_id。
第二個(gè)參數(shù)是一個(gè)字符串,用來(lái)指定你要獲取的時(shí)鐘的名字。
參數(shù)傳入后,內(nèi)核查找到一個(gè)dev->id(或者-1)是否與clk->id一致,并且con_id與clk->name一致的時(shí)鐘,如果不一致就會(huì)獲取失敗。
所以,我這里的函數(shù)應(yīng)該是:clk_get(NULL,
“adc”)。
使能時(shí)鐘使用函數(shù):
int clk_enable(struct clk *clk)
禁止時(shí)鐘使用函數(shù):
void clk_disable(struct clk *clk)
在原來(lái)的函數(shù)中使用這三條代碼后(13th_ts_input/13th_ts_input/2nd),觸摸屏就能工作了?纯葱Ч
[root: 2nd]# insmod ts_driver.ko
hello ts
[root: 2nd]# tc down
//當(dāng)接觸觸摸屏是打印
x:0, y:947
tc up
tc down
x:382, y:608
tc up
tc down
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
三、防抖(13th_ts_input/13th_ts_input/3rd)
同樣的,最后在代碼中添加個(gè)定時(shí)器,實(shí)現(xiàn)防抖功能。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
源代碼: 13th_ts_input.rar
|