九鼎创展论坛中文版English
登录 | 立即注册 设为首页收藏本站 切换到宽版
查看: 3454|回复: 0
打印 上一主题 下一主题

s3c6410的RTC在linux中的驱动(4)

[复制链接]
跳转到指定楼层
楼主
发表于 2014-10-21 16:28:47 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

还记得这句话吗?

rtc_dev_prepare终于完了,现在回到rtc_device_register函数中。美好的时光总是短暂的,又到了一篇结束的时刻,就像灰太狼先生说的那样:“我还会回来的!”下篇见。

现在灰太狼先生回来了。

1、接着回到rtc_device_register函数中,

err = device_register(&rtc->dev);
if (err)
goto exit_kfree;

直接看注释,就是把设备注册到设备模型中,即系统中。

/**
* device_register - register a device with the system.
* @dev: pointer to the device structure
*
* This happens in two clean steps - initialize the device
* and add it to the system.

*/

rtc_dev_add_device(rtc);这个函数在上一篇中提到了一此,对应那两个问题中的一个,还记得吗?如果忘记了,可以会过去查看。列出其源码:

void rtc_dev_add_device(struct rtc_device *rtc)
{
if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1))看到这句,应该明白了吧?
printk(KERN_WARNING "%s: failed to add char device %d:%d\n",
rtc->name, MAJOR(rtc_devt), rtc->id);
else
pr_debug("%s: dev (%d:%d)\n", rtc->name,
MAJOR(rtc_devt), rtc->id);
}
rtc_sysfs_add_device(rtc);

在Rtc-sysfs.c (linux2.6.28\drivers\rtc)文件中,对sysfs文件系统,我也不太懂,就不说了。
rtc_proc_add_device(rtc);

在Rtc-proc.c (linux2.6.28\drivers\rtc)文件中。

dev_info(dev, "rtc core: registered %s as %s\n",
rtc->name, rtc->dev.bus_id);

return rtc;

下面这些是和错误处理有关的。
exit_kfree:
kfree(rtc);


exit_idr:
mutex_lock(&idr_lock);
idr_remove(&rtc_idr, id);
mutex_unlock(&idr_lock);


exit:
dev_err(dev, "rtc core: unable to register %s, err = %d\n",
name, err);
return ERR_PTR(err);
}rtc_device_register函数到这里也就完了。

回到s3c_rtc_probe函数中,接着看:

/* register RTC and exit */
rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,
THIS_MODULE);上面刚分析完。
if (IS_ERR(rtc)) {
dev_err(&pdev->dev, "cannot attach rtc\n");
ret = PTR_ERR(rtc);
goto err_nortc;
}


rtc->max_user_freq = S3C_MAX_CNT;定义用户可以设备的最大频率,和TICK TIME定时器有关。


/* check rtc time */检测RTC,基本原理就是,先读出寄存器 S3C2410_RTCSEC  和 寄存器S3C2410_RTCYEAR之间的每个寄存器,然后进行判断(判断条件相应的四位BCD码与9进行比较,因为四位BCD码不可能大于9,如果大于,肯定出错了,就清零。),在对相应的寄存器写0。看下面的图就更清楚了。
for (bcd_loop = S3C2410_RTCSEC ; bcd_loop <= S3C2410_RTCYEAR ; bcd_loop +=0x4)
{
bcd_tmp = readb(s3c_rtc_base + bcd_loop);
if(((bcd_tmp & 0xf) > 0x9) || ((bcd_tmp & 0xf0) > 0x90))
writeb(0, s3c_rtc_base + bcd_loop);
}


platform_set_drvdata(pdev, rtc);这个函数在平台设备中也很常有,以前也有一篇博客讲过,这个函数。看下面:

#define platform_set_drvdata(_dev,data)dev_set_drvdata(&(_dev)->dev, (data))

static inline void dev_set_drvdata(struct device *dev, void *data)
{
dev->driver_data = data;应该明白了吧。
}


return 0;

下面这些与错误处理有关。
err_nortc:
s3c_rtc_enable(pdev, 0);
iounmap(s3c_rtc_base);


err_nomap:
release_resource(s3c_rtc_mem);


err_nores:
return ret;
}

现在s3c_rtc_probe函数也说完了,但对于RTC设备驱动来说,只是迈出了很小的一步,但不积硅步,无以至千里。

2、这两篇说的都是RTC平台设备的注册和对应的probe函数,相应的平台设备的注销函数和remove函数还没说,那相比于RTC平台设备的注册和对应的probe函数,就简单许多了。直接上源码:

平台注销函数:

static void __exit s3c_rtc_exit(void)
{
platform_driver_unregister(&s3c2410_rtc_driver);
}

module_exit(s3c_rtc_exit);

/**
* platform_driver_unregister
* @drv: platform driver structure
*/
void platform_driver_unregister(struct platform_driver *drv)
{
driver_unregister(&drv->driver);
}

void driver_unregister(struct device_driver *drv)
{
driver_remove_groups(drv, drv->groups);
bus_remove_driver(drv);
}

相应的remove函数:

static struct platform_driver s3c2410_rtc_driver = {
.probe  = s3c_rtc_probe,
.remove= s3c_rtc_remove,
.suspend  = s3c_rtc_suspend,
.resume  = s3c_rtc_resume,
.driver  = {
.name = "s3c2410-rtc",
.owner  = THIS_MODULE,
},
};

static int s3c_rtc_remove(struct platform_device *dev)
{
struct rtc_device *rtc = platform_get_drvdata(dev);


platform_set_drvdata(dev, NULL);
rtc_device_unregister(rtc);


s3c_rtc_setpie(&dev->dev, 0);
s3c_rtc_setaie(0);


iounmap(s3c_rtc_base);
release_resource(s3c_rtc_mem);
kfree(s3c_rtc_mem);


return 0;
}

看到这些,是否觉得似曾相识?它们就像被分离的双胞胎一样,永远不能相见。各有各的职责,各有各的风采。

3、在讲顶层字符设备的注册时,说过RTC设备的设备好是在rtc_dev_init()函数中申请的,相应的释放也在对应的文件中。

Class.c (linux2.6.28\drivers\rtc)文件:

subsys_initcall(rtc_init);
module_exit(rtc_exit);

static int __init rtc_init(void)
{
rtc_class = class_create(THIS_MODULE, "rtc");
if (IS_ERR(rtc_class)) {
printk(KERN_ERR "%s: couldn't create class\n", __FILE__);
return PTR_ERR(rtc_class);
}
rtc_class->suspend = rtc_suspend;
rtc_class->resume = rtc_resume;
rtc_dev_init();
rtc_sysfs_init(rtc_class);
return 0;
}

static void __exit rtc_exit(void)
{
rtc_dev_exit();
class_destroy(rtc_class);
}

void __exit rtc_dev_exit(void)
{
if (rtc_devt)
unregister_chrdev_region(rtc_devt, RTC_DEV_MAX);
}

看到这应该明白了吧!

4、写到这里,注册和注销都已经讲完了,还是那句话,要走的路还有很长,看下面的结构体:

struct rtc_class_ops {
int (*open)(struct device *);
void (*release)(struct device *);
int (*ioctl)(struct device *, unsigned int, unsigned long);
int (*read_time)(struct device *, struct rtc_time *);
int (*set_time)(struct device *, struct rtc_time *);
int (*read_alarm)(struct device *, struct rtc_wkalrm *);
int (*set_alarm)(struct device *, struct rtc_wkalrm *);
int (*proc)(struct device *, struct seq_file *);
int (*set_mmss)(struct device *, unsigned long secs);
int (*irq_set_state)(struct device *, int enabled);
int (*irq_set_freq)(struct device *, int freq);
int (*read_callback)(struct device *, int data);
};

这个结构的作用在s3c6410的RTC在linux中的驱动(1)有说明,它是底层与RTC设备模型的接口,它对应的实例是在Rtc-s3c.c (linux2.6.28\drivers\rtc) 文件中

static const struct rtc_class_ops s3c_rtcops = {
.open = s3c_rtc_open,
.release  = s3c_rtc_release,
.ioctl  = s3c_rtc_ioctl,
.read_time  = s3c_rtc_gettime,
.set_time  = s3c_rtc_settime,
.read_alarm  = s3c_rtc_getalarm,
.set_alarm  = s3c_rtc_setalarm,
.irq_set_freq  = s3c_rtc_setfreq,
.irq_set_state= s3c_rtc_setpie,
.proc        = s3c_rtc_proc,
};

看到这么多函数,你应该明白我说的,我们的路还有很长要走。下篇再说。


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳市九鼎创展科技官方论坛 ( 粤ICP备11028681号-2  

GMT+8, 2024-5-5 00:35 , Processed in 0.019329 second(s), 18 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表