基于Linux3.4的RTC驱动分析(rtc-pl031): 一.device注册
2012-11-17 17:37
603 查看
RTC驱动有很多, LZ将要讲的是kernel/drivers/rtc/rtc-pl031.c驱动, 这应该算是一个AMBA的一个比较标准的驱动,而且LZ的代码是Linux3.4的哦。
说实话,LZ对RTC了解不多,估计RTC的功能也就是让系统启动时获得当前时间,还有就是定时上报中断(闹钟, 定时关机等功能)。因为LZ主要从事手机(Android)开发的, 所以以后说系统可能主要是用手机一词代替。。- _-
闲话少说,说到驱动,大家一定都知道Linux下的platform-device-driver体系。这个体系可以让驱动工程师偷懒,不用指定什么设备号啦什么的。只要根据体系构架的约定, 在板文件和驱动文件的地方添加点自己的东西,打开编译脚本,把原生的对应驱动编写进去,基本上不出什么大问题的话, 应该能让模块跑起来,当然要达到使用要求,估计免不了要解决些相关性问题。。 -_-
先看下该驱动的板文件需要写点什么东西吧。。
老鸟们应该会比较注意.init_name = "XXXX-rtc"这一段,因为他们知道这个名字一般要与device_driver里对应name要一致才能probe,我只能说你们naive了。这个名字在amba驱动里,是可以随便写的。。。-_-
当然为了照顾初学者,LZ还是把上面的代码稍微讲一遍:首先rtc_device里面指定了这个设备的名字叫"XXXX-rtc".start = REG_BASE_RTC,end = REG_BASE_RTC + SZ_4K - 1,这个指定了这个设备所在的寄存器物理地址,.irq = IRQ_RTC,指定了这个设备的中断号,最后,.periphid = 0x00041031应该是AMBA驱动的比较关键的ID号(这个才是用来匹配驱动与设备实体用的)。
接着我想我们该看看amba_device_register里搞了些什么吧,继续贴代码。
amba_device_register里的amba_device_initialize(dev, dev->dev.init_name);我并没有贴出代码, 因为那句代码会揪出很多类似kobject,kset什么的很多东西,当然,如果要透彻了解linux kernel,那些东西是逃不过的,但我们现在主要说的是rtc,一个小驱动, 深究那些毕竟还没必要。我们只需要知道那句代码会初始化一些dev参数,仅此而已。往下if (!dev->dev.coherent_dma_mask && dev->dma_mask)这句只是检查然后打个报警log,告诉我们dev->dma_mask被设置了但是dev->dev.coherent_dma_mask没被设置的话,这样不太好。这两个mask其实是关于DMA能够寻址的范围。幸运的是rtc一般用不到DMA,所以这两个mask我们都没填写,也不会有那个log。
言归正传, 我们来看amba_device_add(dev, parent);这里我们先看parent,这个parent就是在xxxx_amba_init(void)函数中的amba_device_register(d, &iomem_resource);,这个iomem_resource我们看下他的真面目:
这个iomem_resource像是很多IO资源的祖宗了,他规定了所有指定他为父资源的那些资源的其实地址和结束地址,也就是0~-1(0x00000000,到0xFFFFFFFF)。资源树的组成和是由ret = request_resource(parent, &dev->res);这句话实现的,想看的可以往深了看,反正我不看了。
amba_device_add开始我们一句一句看,WARN_ON(dev->irq[0] == (unsigned int)-1);WARN_ON(dev->irq[1] == (unsigned int)-1);这两句话就是个判断告警语句。这里有两点LZ想说,一个是WARN_ON是个很好的打debug方法,只要WARN_ON括号里的东西为真,就能够打出一堆调用栈。第二点LZ想说的是(unsigned int)-1这个挺蛋疼的,因为写代码的大牛们不知道各位跑的系统CPU是多少位宽的,所以就这么写了,意思是,中断号别给我设成全F,要不就打一堆调用栈给你看。。-_-
接下来就是这段代码了:
看到没有,如果我们有设periphid的话,我们就不用跑下面一大段话,而如果我们没设periphid,嘿嘿,那就有意思了,等会儿我们再说。
我们由于先设了periphid = 0x00041031,所以我们就走到 ret = device_add(&dev->dev);这里。走到这里那么我们就能够确定,我们的这个设备能被挂到设备树上了, device_add就是帮我们挂设备的接口,当然,到这里我就不往下深究了。下面的这些:
这些只是建立些中断的文件节点。完了后就完成了这个amba_device_add函数。当然,我们还没有完,能如此顺利地走完这个函数,全都是因为我们设了periphid,那如果没设呢? 那就往回看看。。-_-
嘿嘿,原来是这样,如果你没有设periphid那么这段代码会帮你找出periphid。 首先tmp = ioremap(dev->res.start, size);映射物理地址,使得这段代码可以读取寄存器。ret = amba_get_enable_pclk(dev);使能了这个设备的时钟,使得这个设备使能,接着,他通过两端for循环,读出了你之前所给的地址空间的最后一段区域。读出AMBA规定的CID和PID地址空间,看看,如果CID==AMBA_CID,那么没错了,PID就是你这个设备的periphid,如果CID!=AMBA_CID,呵呵,那说明你这设备不是人家AMBA弄得设备,别滥竽充数了,滚吧,你被拆穿了。。。-_-
至此,我们可以发现periphid是个对AMBA设备很关键的东西哦! 至于为什么,我们下一节再说吧。
以上内容为原创, 如有雷同, 肯定被抄了-_-,原作在http://blog.csdn.net/zhang_heaven,嘿嘿。如果有地方不对,也请大神们指出啊。。。小弟只是个菜鸟。。。-_-||||
说实话,LZ对RTC了解不多,估计RTC的功能也就是让系统启动时获得当前时间,还有就是定时上报中断(闹钟, 定时关机等功能)。因为LZ主要从事手机(Android)开发的, 所以以后说系统可能主要是用手机一词代替。。- _-
闲话少说,说到驱动,大家一定都知道Linux下的platform-device-driver体系。这个体系可以让驱动工程师偷懒,不用指定什么设备号啦什么的。只要根据体系构架的约定, 在板文件和驱动文件的地方添加点自己的东西,打开编译脚本,把原生的对应驱动编写进去,基本上不出什么大问题的话, 应该能让模块跑起来,当然要达到使用要求,估计免不了要解决些相关性问题。。 -_-
先看下该驱动的板文件需要写点什么东西吧。。
static struct amba_device rtc_device = { .dev = { .init_name = "XXXX-rtc", }, .res = { .start = REG_BASE_RTC, .end = REG_BASE_RTC + SZ_4K - 1, .flags = IORESOURCE_MEM, }, .irq = IRQ_RTC, .periphid = 0x00041031, }; static struct amba_device *amba_devs[] __initdata = { &rtc_device, } void __init xxxx_amba_init(void) { int i; for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { struct amba_device *d = amba_devs[i]; amba_device_register(d, &iomem_resource); } } subsys_initcall(xxxx_amba_init);
老鸟们应该会比较注意.init_name = "XXXX-rtc"这一段,因为他们知道这个名字一般要与device_driver里对应name要一致才能probe,我只能说你们naive了。这个名字在amba驱动里,是可以随便写的。。。-_-
当然为了照顾初学者,LZ还是把上面的代码稍微讲一遍:首先rtc_device里面指定了这个设备的名字叫"XXXX-rtc".start = REG_BASE_RTC,end = REG_BASE_RTC + SZ_4K - 1,这个指定了这个设备所在的寄存器物理地址,.irq = IRQ_RTC,指定了这个设备的中断号,最后,.periphid = 0x00041031应该是AMBA驱动的比较关键的ID号(这个才是用来匹配驱动与设备实体用的)。
接着我想我们该看看amba_device_register里搞了些什么吧,继续贴代码。
/** * amba_device_register - register an AMBA device * @dev: AMBA device to register * @parent: parent memory resource * * Setup the AMBA device, reading the cell ID if present. * Claim the resource, and register the AMBA device with * the Linux device manager. */ int amba_device_register(struct amba_device *dev, struct resource *parent) { amba_device_initialize(dev, dev->dev.init_name); dev->dev.init_name = NULL; if (!dev->dev.coherent_dma_mask && dev->dma_mask) dev_warn(&dev->dev, "coherent dma mask is unset\n"); return amba_device_add(dev, parent); } /** * amba_device_add - add a previously allocated AMBA device structure * @dev: AMBA device allocated by amba_device_alloc * @parent: resource parent for this devices resources * * Claim the resource, and read the device cell ID if not already * initialized. Register the AMBA device with the Linux device * manager. */ int amba_device_add(struct amba_device *dev, struct resource *parent) { u32 size; void __iomem *tmp; int i, ret; WARN_ON(dev->irq[0] == (unsigned int)-1); WARN_ON(dev->irq[1] == (unsigned int)-1); ret = request_resource(parent, &dev->res); if (ret) goto err_out; /* Hard-coded primecell ID instead of plug-n-play */ if (dev->periphid != 0) goto skip_probe; /* * Dynamically calculate the size of the resource * and use this for iomap */ size = resource_size(&dev->res); tmp = ioremap(dev->res.start, size); if (!tmp) { ret = -ENOMEM; goto err_release; } ret = amba_get_enable_pclk(dev); if (ret == 0) { u32 pid, cid; /* * Read pid and cid based on size of resource * they are located at end of region */ for (pid = 0, i = 0; i < 4; i++) pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) << (i * 8); for (cid = 0, i = 0; i < 4; i++) cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << (i * 8); amba_put_disable_pclk(dev); if (cid == AMBA_CID) dev->periphid = pid; if (!dev->periphid) ret = -ENODEV; } iounmap(tmp); if (ret) goto err_release; skip_probe: ret = device_add(&dev->dev); if (ret) goto err_release; if (dev->irq[0] && dev->irq[0] != NO_IRQ) ret = device_create_file(&dev->dev, &dev_attr_irq0); if (ret == 0 && dev->irq[1] && dev->irq[1] != NO_IRQ) ret = device_create_file(&dev->dev, &dev_attr_irq1); if (ret == 0) return ret; device_unregister(&dev->dev); err_release: release_resource(&dev->res); err_out: return ret; } //location: linux/arch/arm/common/amba.c
amba_device_register里的amba_device_initialize(dev, dev->dev.init_name);我并没有贴出代码, 因为那句代码会揪出很多类似kobject,kset什么的很多东西,当然,如果要透彻了解linux kernel,那些东西是逃不过的,但我们现在主要说的是rtc,一个小驱动, 深究那些毕竟还没必要。我们只需要知道那句代码会初始化一些dev参数,仅此而已。往下if (!dev->dev.coherent_dma_mask && dev->dma_mask)这句只是检查然后打个报警log,告诉我们dev->dma_mask被设置了但是dev->dev.coherent_dma_mask没被设置的话,这样不太好。这两个mask其实是关于DMA能够寻址的范围。幸运的是rtc一般用不到DMA,所以这两个mask我们都没填写,也不会有那个log。
言归正传, 我们来看amba_device_add(dev, parent);这里我们先看parent,这个parent就是在xxxx_amba_init(void)函数中的amba_device_register(d, &iomem_resource);,这个iomem_resource我们看下他的真面目:
struct resource iomem_resource = { .name = "PCI mem", .start = 0, .end = -1, .flags = IORESOURCE_MEM, }; //location:linux/kernel/resource.c
这个iomem_resource像是很多IO资源的祖宗了,他规定了所有指定他为父资源的那些资源的其实地址和结束地址,也就是0~-1(0x00000000,到0xFFFFFFFF)。资源树的组成和是由ret = request_resource(parent, &dev->res);这句话实现的,想看的可以往深了看,反正我不看了。
amba_device_add开始我们一句一句看,WARN_ON(dev->irq[0] == (unsigned int)-1);WARN_ON(dev->irq[1] == (unsigned int)-1);这两句话就是个判断告警语句。这里有两点LZ想说,一个是WARN_ON是个很好的打debug方法,只要WARN_ON括号里的东西为真,就能够打出一堆调用栈。第二点LZ想说的是(unsigned int)-1这个挺蛋疼的,因为写代码的大牛们不知道各位跑的系统CPU是多少位宽的,所以就这么写了,意思是,中断号别给我设成全F,要不就打一堆调用栈给你看。。-_-
接下来就是这段代码了:
/* Hard-coded primecell ID instead of plug-n-play */ if (dev->periphid != 0) goto skip_probe;
看到没有,如果我们有设periphid的话,我们就不用跑下面一大段话,而如果我们没设periphid,嘿嘿,那就有意思了,等会儿我们再说。
我们由于先设了periphid = 0x00041031,所以我们就走到 ret = device_add(&dev->dev);这里。走到这里那么我们就能够确定,我们的这个设备能被挂到设备树上了, device_add就是帮我们挂设备的接口,当然,到这里我就不往下深究了。下面的这些:
if (dev->irq[0] && dev->irq[0] != NO_IRQ) ret = device_create_file(&dev->dev, &dev_attr_irq0); if (ret == 0 && dev->irq[1] && dev->irq[1] != NO_IRQ) ret = device_create_file(&dev->dev, &dev_attr_irq1); if (ret == 0) return ret;
这些只是建立些中断的文件节点。完了后就完成了这个amba_device_add函数。当然,我们还没有完,能如此顺利地走完这个函数,全都是因为我们设了periphid,那如果没设呢? 那就往回看看。。-_-
/* * Dynamically calculate the size of the resource * and use this for iomap */ size = resource_size(&dev->res); tmp = ioremap(dev->res.start, size); if (!tmp) { ret = -ENOMEM; goto err_release; } ret = amba_get_enable_pclk(dev); if (ret == 0) { u32 pid, cid; /* * Read pid and cid based on size of resource * they are located at end of region */ for (pid = 0, i = 0; i < 4; i++) pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) << (i * 8); for (cid = 0, i = 0; i < 4; i++) cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << (i * 8); amba_put_disable_pclk(dev); if (cid == AMBA_CID) dev->periphid = pid; if (!dev->periphid) ret = -ENODEV; } iounmap(tmp); if (ret) goto err_release;
嘿嘿,原来是这样,如果你没有设periphid那么这段代码会帮你找出periphid。 首先tmp = ioremap(dev->res.start, size);映射物理地址,使得这段代码可以读取寄存器。ret = amba_get_enable_pclk(dev);使能了这个设备的时钟,使得这个设备使能,接着,他通过两端for循环,读出了你之前所给的地址空间的最后一段区域。读出AMBA规定的CID和PID地址空间,看看,如果CID==AMBA_CID,那么没错了,PID就是你这个设备的periphid,如果CID!=AMBA_CID,呵呵,那说明你这设备不是人家AMBA弄得设备,别滥竽充数了,滚吧,你被拆穿了。。。-_-
至此,我们可以发现periphid是个对AMBA设备很关键的东西哦! 至于为什么,我们下一节再说吧。
以上内容为原创, 如有雷同, 肯定被抄了-_-,原作在http://blog.csdn.net/zhang_heaven,嘿嘿。如果有地方不对,也请大神们指出啊。。。小弟只是个菜鸟。。。-_-||||
相关文章推荐
- 基于linux 3.10.49内核 从dts文件里注册platform_device流程分析
- 以RTC为例分析linux platform_bus platform_device和platform_driver注册过程
- Linux RTC驱动模型分析之rtc-proc.c
- 基于linux-2.6.38.8内核的SDIO/wifi驱动分析
- linux sd卡驱动分析,基于mini2440,sdio mmc sd卡驱动编写
- linux下设备device_register和驱动driver_register先后注册的影响和关系
- ARM-Linux驱动--RTC(实时时钟)驱动分析
- 基于linux-2.6.38.8内核的SDIO/wifi驱动分析
- 基于mini6410的linux按键驱动实例分析
- Linux设备模型分析之device(基于3.10.1内核)
- linux IDE驱动分析之Ide_driver的注册(三)
- 基于GPL329xx linux平台电容屏gsl1680的驱动调试分析
- linux触摸屏驱动分析,touchscreen, struct input_dev,基于TSC2007
- 基于linux-2.6.38.8内核的wifi驱动分析
- 基于linux-2.6.38.8内核的SDIO/wifi驱动分析(转)
- LINUX驱动分析之RTC(一)
- linux下的nandflash驱动分析(2)——基于s3c6410平台
- 基于mini6410的linux按键驱动实例分析
- linux IDE驱动分析之IDE总线、驱动注册(五)
- Linux RTC 驱动模型分析(2)