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

基于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体系。这个体系可以让驱动工程师偷懒,不用指定什么设备号啦什么的。只要根据体系构架的约定, 在板文件和驱动文件的地方添加点自己的东西,打开编译脚本,把原生的对应驱动编写进去,基本上不出什么大问题的话, 应该能让模块跑起来,当然要达到使用要求,估计免不了要解决些相关性问题。。 -_-

 

先看下该驱动的板文件需要写点什么东西吧。。

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 文档