本文转载自:http://blog.chinaunix.net/uid-25014876-id-119723.html

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源码分析

、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来初始化系统时钟,接下来看一下函数原型。

、arch/arm/plat-s3c/init.c

75 void __init s3c24xx_init_clocks(int xtal)

76 {

77         if (xtal == 0)

78                 xtal = 12*1000*1000;

80         if (cpu == NULL)

81                 panic("s3c24xx_init_clocks: no cpu setup?\n");

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结构体在里面的成员。

、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,继续看该函数里面做了什么操作

、/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 */

131         s3c24xx_register_baseclocks(xtal); //三个步骤,接下来看一下

132         s3c244x_setup_clocks();

133         s3c2410_baseclk_add();

134 }

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

、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");

344         clk_xtal.rate = xtal;

346 /* register our clocks */

348         if (s3c24xx_register_clock(&clk_xtal) < 0) //注册时钟

349                printk(KERN_ERR "failed to register master xtal\n");

351         if (s3c24xx_register_clock(&clk_mpll) < 0)

352                 printk(KERN_ERR "failed to register mpll clock\n");

354         if (s3c24xx_register_clock(&clk_upll) < 0)

355                 printk(KERN_ERR "failed to register upll clock\n");

357         if (s3c24xx_register_clock(&clk_f) < 0)

358                 printk(KERN_ERR "failed to register cpu fclk\n");

360         if (s3c24xx_register_clock(&clk_h) < 0)

361                 printk(KERN_ERR "failed to register cpu hclk\n");

363         if (s3c24xx_register_clock(&clk_p) < 0)

364                 printk(KERN_ERR "failed to register cpu pclk\n");

366         return 0;

367 }

跟名字一样, s3c24xx_register_baseclocks注册了系统中基本所需的时钟,如pclk等

、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;

85         xtal_clk = clk_get(NULL, "xtal");

86         xtal = clk_get_rate(xtal_clk);

87         clk_put(xtal_clk);

89         fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2;

91         clkdiv = __raw_readl(S3C2410_CLKDIVN);

92         camdiv = __raw_readl(S3C2440_CAMDIVN);

94 /* work out clock scalings */

96         switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) {

97         case S3C2440_CLKDIVN_HDIVN_1:

98                 hdiv = 1;

99                 break;

101         case S3C2440_CLKDIVN_HDIVN_2:

102                 hdiv = 2;

103                 break;

105         case S3C2440_CLKDIVN_HDIVN_4_8:

106                 hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4;

107                 break;

109         case S3C2440_CLKDIVN_HDIVN_3_6:

110                 hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3;

111                 break;

112         }

114         hclk = fclk / hdiv;

115         pclk = hclk / ((clkdiv & S3C2440_CLKDIVN_PDIVN) ? 2 : 1);

117 /* print brief summary of clocks, etc */

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));

122         s3c24xx_setup_clocks(fclk, hclk, pclk);

123 }

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

、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

,所以是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

linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟【转】的更多相关文章

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

    linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  2. linux设备驱动归纳总结

    前言: (总结已经基本写完,这段时间我会从新排版和修正.错误总会有的,望能指正!) 前段时间学习了嵌入式驱动,趁着没开始找工作,这段时间我会每天抽出时间来复习. 我的总结是根据学习时的笔记(李杨老师授 ...

  3. 【Linux】linux设备驱动归纳总结

    前言: (总结已经基本写完,这段时间我会从新排版和修正.错误总会有的,望能指正!) 前段时间学习了嵌入式驱动,趁着没开始找工作,这段时间我会每天抽出时间来复习. 我的总结是根据学习时的笔记(李杨老师授 ...

  4. linux设备驱动归纳总结(十二):简单的数码相框【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-116926.html linux设备驱动归纳总结(十二):简单的数码相框 xxxxxxxxxxxxxx ...

  5. linux设备驱动归纳总结(十一):写个简单的看门狗驱动【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-112879.html linux设备驱动归纳总结(十一):写个简单的看门狗驱动 xxxxxxxxxxx ...

  6. linux设备驱动归纳总结(十):1.udev&misc【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-111839.html linux设备驱动归纳总结(十):1.udev&misc xxxxxxx ...

  7. linux设备驱动归纳总结(九):1.platform总线的设备和驱动【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-111745.html linux设备驱动归纳总结(九):1.platform总线的设备和驱动 xxxx ...

  8. linux设备驱动归纳总结(八):4.总线热插拔【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-110774.html linux设备驱动归纳总结(八):4.总线热插拔 xxxxxxxxxxxxxxx ...

  9. linux设备驱动归纳总结(八):3.设备管理的分层与面向对象思想【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-110738.html linux设备驱动归纳总结(八):3.设备管理的分层与面向对象思想 xxxxxx ...

随机推荐

  1. 微信小程序:开发之前要知道的三件事

    前言 微信之父张小龙在年初的那次演讲中曾表示:"我自己是很多年的程序员,我觉得我们应该为开发的团队做一些事情".几个月后,微信正式推出微信应用号(即微信小程序),在互联网中掀起了又 ...

  2. Oracle deadlock SX/SSX caused by no index on foreign key.

    Example to show the dead lock caused by lack of index on foreign key of child table. Session 1: crea ...

  3. ext4.1Grid中的column多选

    ext4.1中默认单选可以使用checkboxmodel实现多选selModel:Ext.create('Ext.selection.CheckboxModel'),

  4. iOS:crash崩溃日志分析

    一.前言: 作为一个合格的iOS开发者,除了具有规范强悍的编码能力外,还应该具有过硬的查错纠错能力.在项目运行时,程序崩溃是不可避免的,遇到这个问题,有时会出现一大堆的crash日志,艹,貌似看不懂呀 ...

  5. eigen主页

    http://eigen.tuxfamily.org/index.php?title=Main_Page

  6. pptp记录用户登陆日志

    pptp vpn记录用户登录和流量信息(转) 这个问题困扰了我很久,终于在pppd的man文档里,发现了踪迹.在man中的SCRIPTS下有一系列的参数,其中PEERNAME就是登陆的用户名,并且在/ ...

  7. C# 生成二维码,彩色二维码,带有Logo的二维码及普通条形码

    每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客!当然,希望将来的一天,某位老板看到此博客,给你的程序员职工加点薪资吧!因为程序员的世界除了苦逼就是沉默.我眼中的程序员大多都不 ...

  8. duplicate database的时候,rman连接 auxiliary database的后状态不正确

    duplicate database的时候,rman连接 auxiliary database的后状态不正确 auxiliary database 已经被startup nomount,但是rman连 ...

  9. Socket 编程

  10. Nginx简单配置

    Nginx 配置文件结构如果你下载好啦,你的安装文件,不妨打开 conf 文件夹的 nginx.conf 文件,Nginx 服务器的基础配置,默认的配置也存放在此.在 nginx.conf 的注释符号 ...