您的位置:首页 > 运维架构 > Linux

【Linux开发】linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟

2016-05-17 23:35 537 查看
linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

这节的内容说不上是驱动,只是写个代码让触摸屏能够工作,随便介绍一下时钟子系统(我不知道这样叫合不合适),仅次而已。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

一、程序不能工作

程序的源代码在13th_ts_input/13th_ts_input/1st。大致的操作就是配置寄存器,设置触摸屏为自动坐标转换模式,具体请根据程序对照S3C2440文档。

但是写完的程序不能工作,检查原因。

1、中断注册失败:

cat /prov/interrupt 就知道,系统已经注册了adc和tc中断,为了能让我的模块加载成功,内核编译时不能加入adc和触摸屏驱动。

make menuconfig

1、Device Drivers ---> Input device
support --->[ ] Touchscreens

2、Device Drivers ---> Character devices
--->[ ] ADC driver for FriendlyARM Mini2440/QQ2440 development boards

重新编译后启动,模块加载成功,但触摸屏还是不能工作。

2、使能adc时钟

如果有编写过裸机程序的应该知道,ad转换和触摸屏的正常工作还依赖于时钟控制寄存器的配置,必须使能adc时钟(CLKCON[15])。

但在linux下,有一套专门的规矩来使能时钟,接下来先从内核启动时的代码开始介绍:

该网友的博客上有更详细的介绍,可以看看,虽然是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);
//初始化系统时钟

269 s3c24xx_init_uarts(mini2440_uartcfgs,
ARRAY_SIZE(mini2440_uartcfgs));

270 }

内核启动时会通过mini2440_map_io函数调用s3c24xx_init_clocks来初始化系统时钟,接下来看一下函数原型。

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 }

函数的原型很简单,就是调用了cpu结构里面的成员init_clocks。接下来看一下2440的cpu结构体在里面的成员。

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函数原型在这里

92 .init_uarts
= s3c244x_init_uarts,

93 .init
= s3c2440_init,

94 .name
= name_s3c2440

95 },

可以看,cpu->init_clocks的原型就是s4c244x_init_clocks,继续看该函数里面做了什么操作

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);
//三个步骤,接下来看一下

132 s3c244x_setup_clocks();

133 s3c2410_baseclk_add();

134 }

函数里面有三个操作,接下来逐个逐个看。

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) //注册时钟

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注册了系统中基本所需的时钟,如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 }

上面的函数通过从寄存器读取信息并给fclk、hclk和pclk赋值,然后通过 s3c24xx_setup_clocks函数添加到指定结构体。

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; //该结构体中存放着需要enable的时钟成员,对应寄存器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); //注册并使能

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; //该结构体中存放着需要disable的时钟成员,对应寄存器CLKCON

253 for (ptr
= 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {

254

255 ret
= s3c24xx_register_clock(clkp); //注册,默认使能

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; //传入参数为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的数据结构,并且调用s3c24xx_register_clock 来配置寄存器CLKCON并默认使能init_clock结构里面指定时钟。

第二部分:获取init_clock_disable数据结构,除了调用s3c24xx_register_clock注册后,还调用3c2410_clkcon_enable来配置寄存器CLKCON来disable该init_clock_disable结构体里面的时钟。

init_clock和init_clock_disable里面的成员与寄存器CLKCON的成员对应.

接下来看看clkp的数据结构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等这类的时钟是系统启动时就已经使能了,所以之前我的lcd驱动才能正常工作。

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", //触摸屏的时钟放在 init_clocks_disable中,所以触摸屏不能工作!

105 .id
= -1,

106 .parent
= &clk_p,

107 .enable
= s3c2410_clkcon_enable,

108 .ctrlbit
= S3C2410_CLKCON_ADC,

109 }, {

。。。。。

现在就可以知道,为什么触摸屏不能工作了,因为在系统启动时被禁止了。所以需要在函数中启动它。

既然知道了原因,解决方法就好办了——将adc成员从init)clocks_disable中删除并添加到init_clocks中。

同时还有一个不用修改内核代码的方法。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

二、clk_enable:

第二个方法很简单,从获取内核中的adc时钟,然后将它使能。

先看获取函数:

struct clk *clk_get(struct device *dev, const char *con_id)

第一个参数中,因为clk->id一般为-1,所以直接传入NULL就可以了。如果clk->id不为-1,函数会通过第一个参数传入的dev获取dev->bus_id。

第二个参数是一个字符串,用来指定你要获取的时钟的名字。

参数传入后,内核查找到一个dev->id(或者-1)是否与clk->id一致,并且con_id与clk->name一致的时钟,如果不一致就会获取失败。

所以,我这里的函数应该是:clk_get(NULL, “adc”)。

使能时钟使用函数:

int clk_enable(struct clk *clk)

禁止时钟使用函数:

void clk_disable(struct clk *clk)

在原来的函数中使用这三条代码后(13th_ts_input/13th_ts_input/2nd),触摸屏就能工作了。看看效果:

[root: 2nd]# insmod ts_driver.ko

hello ts

[root: 2nd]# tc down //当接触触摸屏是打印

x:0, y:947

tc up

tc down

x:382, y:608

tc up

tc down

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

三、防抖(13th_ts_input/13th_ts_input/3rd)

同样的,最后在代码中添加个定时器,实现防抖功能。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

源代码:

13th_ts_input.rar
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐