基于Linux3.4的RTC驱动分析(rtc-pl031): 一.device注册

来源:互联网 发布:mac最新系统怎么升级 编辑:程序博客网 时间:2024/05/01 07:01

RTC驱动有很多, LZ将要讲的是kernel/drivers/rtc/rtc-pl031.c驱动, 这应该算是一个AMBA的一个比较标准的驱动,而且LZ的代码是Linux3.4的哦。

说实话,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,嘿嘿。如果有地方不对,也请大神们指出啊。。。小弟只是个菜鸟。。。-_-||||

原创粉丝点击