分类: LINUX
2010-01-27 10:22:27
首先RTC设备是一种片上设备, 用platform_device来表示 该设备(即platform_device的RTC对象)在设备的初始化过程中就已经注册进了系统(可以参考2410下soc上的设备的驱动流程(RTC, watchdog等)), 因此我们在RTC驱动的初始化里把RTC驱动注册到系统后,系统会probe到这个RTC设备,最后调用我们RTC驱动的probe函数.
下面就重点分析2410的RTC驱动.
首先是初始化函数和退出函数:
static void __init s3c2410_rtc_init(void)
{
printk(banner);
/*
* 这就把这个RTC驱动注册进了系统,同时系统会查找匹配的RTC设备,并调用
* s3c2410_rtcdrv的probe函数.
*/
return platform_driver_register(&s3c2410_rtcdrv);
}
Static void __exit s3c2410_rtc_exit(void)
{
/*卸载这个RTC驱动, 之后RTC设备就无法使用了*/
platform_driver_unregister(&s3c2410_rtcdrv);
}
static struct platform_driver s3c2410_rtcdrv = {
.probe = s3c2410_rtc_probe,
.remove = s3c2410_rtc_remove,
.suspend = s3c2410_rtc_suspend,
.resume = s3c2410_rtc_resume,
.driver = {
.name = “s3c2410-rtc”, //这个字符串必须和RTC设备的定义一样,系统才会匹配正确.
.owner = THIS_MODULE,
},
};
当系统把RTC设备注册到系统后,它就开始查找相应总线上的所有设备, 并与这个驱动比较, 如果系统找到匹配的RTC设备就会调用RTC驱动的s3c2410_rtc_probe函数, 注意RTC设备实在系统初始化时就注册进了系统.
static int s3c2410_rtc_probe(struct platform_device *pdev /*rtc设备*/)
{
struct resource *res;
int ret;
pr_debug("%s: probe=%p\n", __FUNCTION__, pdev);
/*
* 获取设备的tick中断资源, 通过查看platform_get_irq可知,实际上通过查找pdev的资源获得的, * 而pdev的资源是在构建platform_device对象的时候就指定好的, 可到Devs.c中查看
*/
s3c2410_rtc_tickno = platform_get_irq(pdev, 1);
if (s3c2410_rtc_tickno < 0) {
dev_err(&pdev->dev, "no irq for rtc tick\n");
return -ENOENT;
}
/*
* 获取设备的rtc中断资源, 通过查看platform_get_irq可知,实际上通过查找pdev的资源获得的, * 而pdev的资源是在构建platform_device对象的时候就指定好的, 可到Devs.c中查看
*/
s3c2410_rtc_alarmno = platform_get_irq(pdev, 0);
if (s3c2410_rtc_alarmno < 0) {
dev_err(&pdev->dev, "no irq for alarm\n");
return -ENOENT;
}
pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n",
s3c2410_rtc_tickno, s3c2410_rtc_alarmno);
/*
* 获取设备的memory资源, 通过查看platform_get_irq可知,实际上通过查找pdev的资源获得的, * 而pdev的资源是在构建platform_device对象的时候就指定好的, 可到Devs.c中查看
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get memory region resource\n");
return -ENOENT;
}
/*
* 查看指定的memory是否可以使用.
*/
s3c2410_rtc_mem = request_mem_region(res->start, res->end-res->start+1,
pdev->name);
if (s3c2410_rtc_mem == NULL) {
dev_err(&pdev->dev, "failed to reserve memory region\n");
ret = -ENOENT;
goto exit_err;
}
/*
* 重映射指定的内存区域,即把原来的物理地址映射到相应的虚拟地址, 这样以后就可以用这个* 虚拟地址直接访问原来的物理地址了, 这里是映射RTC的寄存器地址区间.
*/
s3c2410_rtc_base = ioremap(res->start, res->end - res->start + 1);
if (s3c2410_rtc_base == NULL) {
dev_err(&pdev->dev, "failed ioremap()\n");
ret = -EINVAL;
goto exit_err;
}
s3c2410_rtc_mem = res;
pr_debug("s3c2410_rtc_base=%p\n", s3c2410_rtc_base);
pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON));
/*正确初始化RTC设备 */
s3c2410_rtc_enable(pdev, 1);
pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON));
/*设置好设备的频率*/
s3c2410_rtc_setfreq(s3c2410_rtc_freq);
/* 注册RTC设备的操作函数, 以后对该设备的访问将调用这些操作函数*/
register_rtc(&s3c2410_rtcops);
return 0;
exit_err:
dev_err(&pdev->dev, "error %d during initialisation\n", ret);
return ret;
}