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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值