Linux下的RTC子系统
版權(quán)所有,轉(zhuǎn)載必須說明轉(zhuǎn)自?http://my.csdn.net/weiqing1981127?
?
實時時鐘的作用主要是為操作系統(tǒng)提供一個可靠的時間,并在斷電下,RTC時鐘也可以通過電池供電一直運行下去。實時時鐘驅(qū)動也有一個子系統(tǒng),叫做RTC子系統(tǒng),其源代碼目錄是/driver/rtc/,在這個目錄下有一個rtc核心代碼區(qū),主要是Rtc-dev.c、Rtc-sysfs.c和Rtc-proc.c三個文件,其中Rtc-dev.c主要是增加一個字符設(shè)備的作用,例如用戶層的ioctl命令就是通過訪問該文件;Rtc-sysfs.c主要是創(chuàng)建device_attribute機制;Rtc-proc.c文件主要創(chuàng)建/proc屬性文件。另外對于RTC設(shè)備。內(nèi)核中的說明文檔在/Document/Rtc.txt中
我們這里講的是基于mini2440的RTC驅(qū)動,其對應(yīng)驅(qū)動是/driver/rtc/Rtc-s3c.c
?
RTC驅(qū)動源碼路徑在/driver/rtc/Rtc-s3c.c
查看/driver/rtc/Makefile
rtc-core-$(CONFIG_RTC_INTF_DEV)?????? += rtc-dev.o
rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
obj-$(CONFIG_RTC_DRV_S3C)? += rtc-s3c.o
查看/driver/rtc//Konfig
config RTC_INTF_DEV
?????? boolean "/dev/rtcN (character devices)"
?????? default RTC_CLASS
config RTC_INTF_PROC
?????? boolean "/proc/driver/rtc (procfs for rtc0)"
?????? depends on PROC_FS
config RTC_INTF_SYSFS
?????? boolean "/sys/class/rtc/rtcN (sysfs)"
?????? depends on SYSFS
?????? default RTC_CLASS
config RTC_DRV_S3C
?????? tristate "Samsung S3C series SoC RTC"
?????? depends on ARCH_S3C2410
所以配置內(nèi)核make menuconfig?時,需要選中這幾項。
?
現(xiàn)在先來看如何移植,下面就看移植代碼了,因為通過查看"s3c2410-rtc"名知道,在內(nèi)核Devs.c文件中已經(jīng)定義如下代碼
struct platform_device s3c_device_rtc = {
?????? .name???????????? ? = "s3c2410-rtc",
?????? .id?????????? ? = -1,
?????? .num_resources???? ? = ARRAY_SIZE(s3c_rtc_resource),
?????? .resource ? = s3c_rtc_resource,
};
所以只要在mach-mini2440.c這個mini2440開發(fā)板的BSP中把這個s3c_device_rtc加入到mini2440_devices數(shù)組
static struct platform_device *mini2440_devices[] __initdata = {
?????? ……
?????? & s3c_device_rtc, //添加
};
這樣配置完后,進(jìn)行make zImage生成zImage內(nèi)核鏡像。
?
下面大致說說/driver/rtc/Rtc-s3c.c
static struct platform_driver s3c2410_rtc_driver = {
?????? .probe??????????? = s3c_rtc_probe,
?????? .remove????????? = __devexit_p(s3c_rtc_remove),
?????? .suspend? = s3c_rtc_suspend,
?????? .resume????????? = s3c_rtc_resume,
?????? .driver??????????? = {
????????????? .name????? = "s3c2410-rtc",? //驅(qū)動名
????????????? .owner??? = THIS_MODULE,
?????? },
};
跟蹤下探測函數(shù)probe
static int __devinit s3c_rtc_probe(struct platform_device *pdev)
{
?????? struct rtc_device *rtc;
?????? struct resource *res;
?????? int ret;
?????? pr_debug("%s: probe=%p\n", __func__, pdev);
?????? s3c_rtc_tickno = platform_get_irq(pdev, 1);? //獲取滴答中斷號
?????? if (s3c_rtc_tickno < 0) {
????????????? dev_err(&pdev->dev, "no irq for rtc tick\n");
????????????? return -ENOENT;
?????? }
?????? s3c_rtc_alarmno = platform_get_irq(pdev, 0);? //獲取鬧鐘中斷號
?????? if (s3c_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",
????????????? ?s3c_rtc_tickno, s3c_rtc_alarmno);
?????? res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //獲取資源
?????? if (res == NULL) {
????????????? dev_err(&pdev->dev, "failed to get memory region resource\n");
????????????? return -ENOENT;
?????? }
?????? s3c_rtc_mem = request_mem_region(res->start,
?????????????????????????????????? ?res->end-res->start+1,
?????????????????????????????????? ?pdev->name);? //申請資源
?????? if (s3c_rtc_mem == NULL) {
????????????? dev_err(&pdev->dev, "failed to reserve memory region\n");
????????????? ret = -ENOENT;
????????????? goto err_nores;
?????? }
?????? s3c_rtc_base = ioremap(res->start, res->end - res->start + 1); //物理地址到虛擬地址的映射
?????? if (s3c_rtc_base == NULL) {
????????????? dev_err(&pdev->dev, "failed ioremap()\n");
????????????? ret = -EINVAL;
????????????? goto err_nomap;
?????? }
?????? s3c_rtc_enable(pdev, 1);
????? pr_debug("s3c2410_rtc: RTCCON=%02x\n",
????????????? ?readb(s3c_rtc_base + S3C2410_RTCCON));
?????? s3c_rtc_setfreq(&pdev->dev, 1);???? //設(shè)置頻率
?????? device_init_wakeup(&pdev->dev, 1);?
?????? rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,
??????????????????????????? ? THIS_MODULE);? //注冊rtc設(shè)備
?????? if (IS_ERR(rtc)) {
????????????? dev_err(&pdev->dev, "cannot attach rtc\n");
????????????? ret = PTR_ERR(rtc);
????????????? goto err_nortc;
?????? }
?????? rtc->max_user_freq = 128;
?????? platform_set_drvdata(pdev, rtc);
?????? return 0;
?err_nortc:
?????? s3c_rtc_enable(pdev, 0);
?????? iounmap(s3c_rtc_base);
?err_nomap:
?????? release_resource(s3c_rtc_mem);
?err_nores:
?????? return ret;
}
我們主要關(guān)注注冊rtc設(shè)備的時傳入?yún)?shù)s3c_rtcops
static const struct rtc_class_ops s3c_rtcops = {
?????? .open???????????? = s3c_rtc_open, ?//打開
?????? .release??? = s3c_rtc_release,? //關(guān)閉
?????? .read_time????? = s3c_rtc_gettime,? //獲取當(dāng)前時間
?????? .set_time = s3c_rtc_settime,? //設(shè)置當(dāng)前時間
?????? .read_alarm???? = s3c_rtc_getalarm,? //獲取鬧鐘時間
?????? .set_alarm?????? = s3c_rtc_setalarm,? //設(shè)置鬧鐘時間
?????? .irq_set_freq?? = s3c_rtc_setfreq,? //設(shè)置頻率
?????? .irq_set_state? = s3c_rtc_setpie,
?????? .proc?????? ??????? = s3c_rtc_proc,
};
對于struct rtc_class_ops結(jié)構(gòu)體中的成員,其每個函數(shù)的具體實現(xiàn),都是跟自己使用的設(shè)備相關(guān)的,比如我們這樣使用的是S3C2410,那么在struct rtc_class_ops里定義的函數(shù)使用的就是三星平臺下的資源。如果要在其他平臺下使用,那么就是修改這里的struct rtc_class_ops操作函數(shù)。
在rtc_device_register中,會調(diào)用rtc_dev_prepare函數(shù),而rtc_dev_prepare函數(shù)會把rtc_dev_fops注冊進(jìn)內(nèi)核,而rtc_dev_fops就是我們在增加字符設(shè)備的文件Rtc-dev.c中定義的file_operations操作函數(shù),這樣注冊rtc設(shè)備其實就表示用戶可以訪問通過訪問file_operations函數(shù)來達(dá)到訪問rtc設(shè)備的目的。
?
在我們的/driver/rtc/Class.c中的模塊加載函數(shù)中調(diào)用rtc_sysfs_init來完成注冊sys文件系統(tǒng),而rtc_sysfs_init就是我們在Rtc-sysyfs.c中定義的,跟蹤下rtc_sysfs_ini
void __init rtc_sysfs_init(struct class *rtc_class)
{
?????? rtc_class->dev_attrs = rtc_attrs;
}
然后看看rtc_attrs屬性
static struct device_attribute rtc_attrs[] = {
?????? __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL),
?????? __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL),
?????? __ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL),
?????? __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL),
?????? __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq,
???????????????????? rtc_sysfs_set_max_user_freq),
?????? __ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL),
?????? { },
};
好了,這就是給用戶的第二個操作接口,我們來看看這些屬性的show和store屬性是不是真的能調(diào)用在Rtc-s3c.c中的RTC操作函數(shù)s3c_rtcops。我們把注意力放在time的show屬性函數(shù)rtc_sysfs_show_time上
static ssize_t rtc_sysfs_show_time(struct device *dev, struct device_attribute *attr,
????????????? char *buf)
{
?????? ssize_t retval;
?????? struct rtc_time tm;
?????? retval = rtc_read_time(to_rtc_device(dev), &tm);? //調(diào)用封裝的讀時間函數(shù)
?????? if (retval == 0) {
????????????? retval = sprintf(buf, "%02d:%02d:%02d\n",
???????????????????? tm.tm_hour, tm.tm_min, tm.tm_sec);
?????? }
?????? return retval;
}
跟蹤rtc_read_time函數(shù)
int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
{
?????? int err;
?????? err = mutex_lock_interruptible(&rtc->ops_lock);
?????? if (err)
????????????? return err;
?????? if (!rtc->ops)
????????????? err = -ENODEV;
?????? else if (!rtc->ops->read_time)
????????????? err = -EINVAL;
?????? else {
????????????? memset(tm, 0, sizeof(struct rtc_time));
????????????? err = rtc->ops->read_time(rtc->dev.parent, tm);? //調(diào)用s3c_rtcops操作中讀時間函數(shù)
?????? }
?????? mutex_unlock(&rtc->ops_lock);
?????? return err;
}
好了,我已經(jīng)跟蹤到我們需要找的信息了,這樣我們就能證實在Rtc-sysyfs.c中定義的設(shè)備的show和store屬性是真的能調(diào)用在Rtc-s3c.c中的RTC操作函數(shù)s3c_rtcops的。
?
RTC驅(qū)動測試
Linux?中更改時間的方法一般使用date?命令,為了把S3C2440?內(nèi)部帶的時鐘與linux?系統(tǒng)時鐘同步,
一般使用hwclock?命令,下面是它們的使用方法:
(1) date -s 042916352007 #設(shè)置時間為?2007-04-29 16:34
(2) hwclock -w #把剛剛設(shè)置的時間存入S3C2440?內(nèi)部的RTC
(3).開機時使用hwclock -s?命令可以恢復(fù)?linux?系統(tǒng)時鐘為RTC,?一般把該語句放入
/etc/init.d/rcS?文件自動執(zhí)行。
?
另外需要注意的是:有時候你會發(fā)現(xiàn)自己的實時時鐘會在走時一段時間后不準(zhǔn),這注意是設(shè)計時鐘電路時匹配電容的取值不對,電容公式是C1*C2/(C1+C2)+C3,其中C1和C2是兩個并聯(lián)電容,C3是寄生電容,C3一般取3-5PF。
總結(jié)
以上是生活随笔為你收集整理的Linux下的RTC子系统的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux下的Backlight子系统
- 下一篇: Linux下的Keyboard子系统