如果对RTC硬件不熟悉的话,可以看这篇《S3C6410硬件RTC(实时时钟)》见 ,讲述了硬件有关的内容。
1、先从整体上做些分析,大致看了下linux2.6.28\drivers\rtc文件中的kconfig和Makefile文件,在Makefile文件中有如下内容:
ifeq ($(CONFIG_RTC_DEBUG),y)
EXTRA_CFLAGS+= -DDEBUG
endif
obj-$(CONFIG_RTC_LIB)+= rtc-lib.o
obj-$(CONFIG_RTC_HCTOSYS)+= hctosys.o
obj-$(CONFIG_RTC_CLASS)+= rtc-core.o
rtc-core-y:= class.o interface.o
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_RX8581)+= rtc-rx8581.o
obj-$(CONFIG_RTC_DRV_S35390A)+= rtc-s35390a.o
obj-$(CONFIG_RTC_DRV_S3C)+= rtc-s3c.o
obj-$(CONFIG_RTC_DRV_SA1100)+= rtc-sa1100.o
应该能从中看出,rtc的驱动大致和那些文件有关,再看对应的kconfig文件,可以看出对应文件的用途,我们就不列出对应的kconfig文件了,因为内容太多了。
2、现在的RTC驱动架构,当然看一下Rtc.txt (linux2.6.28\documentation)文件,对我们理解此架构是很有帮助的,列出部分:
New portable "RTC Class" drivers: /dev/rtcN
--------------------------------------------
Because Linux supports many non-ACPI and non-PC platforms, some of which have more than one RTC style clock, it needed a more portable solution than expecting a single battery-backed MC146818 clone on every system. Accordingly, a new "RTC Class" framework has been defined.讲述了新架构建立的原因。
It offers three different userspace interfaces:讲述了与用户空间的三种接口
*/dev/rtcN ... much the same as the older /dev/rtc interface
*/sys/class/rtc/rtcN ... sysfs attributes support readonly access to some RTC attributes.
*/proc/driver/rtc ... the first RTC (rtc0) may expose itself using a procfs interface. More information is (currently) shown here than through sysfs.
The RTC Class framework supports a wide variety of RTCs, ranging from those integrated into embeddable system-on-chip (SOC) processors to discrete chips using I2C, SPI, or some other bus to communicate with the host CPU. There's even support for PC-style RTCs ... including the features exposed on newer PCs through ACPI.
这种分类的架构,支持更广阔的RTC设备。
The new framework also removes the "one RTC per system" restriction. For example, maybe the low-power battery-backed RTC is a discrete I2C chip, but a high functionality RTC is integrated into the SOC. That system might read the system clock from the discrete RTC, but use the integrated one for all other tasks, because of its greater functionality.
The ioctl() calls supported by /dev/rtc are also supported by the RTC class framework. However, because the chips and systems are not standardized, some PC/AT functionality might not be provided. And in the same way, some newer features -- including those enabled by ACPI -- are exposed by the RTC class framework, but can't be supported by the older driver.
3、有关文件的作用
与RTC核心有关的文件有:
(1)、/drivers/rtc/class.c 这个文件向linux设备驱动模型核心注册了一个RTC类,然后向具体的驱动程序提供了rtc_device_register用于注册和rtc_device_unregister用于注销设备。还实现的rtc类设备的注册。
列出部分源码:
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);
}
subsys_initcall(rtc_init);
module_exit(rtc_exit);
(2)、/drivers/rtc/rtc-dev.c 这个文件定义了顶层基本的字符设备文件操作函数,如:open,read等,有个结构体,如下所示:
static const struct file_operations rtc_dev_fops = {
.owner= THIS_MODULE,
.llseek= no_llseek,
.read= rtc_dev_read,
.poll= rtc_dev_poll,
.unlocked_ioctl= rtc_dev_ioctl,
.open= rtc_dev_open,
.release= rtc_dev_release,
.fasync= rtc_dev_fasync,
};此结构体用于字符设备注册是使用。
(3)、/drivers/rtc/interface.c 这个文件主要提供了用户程序与RTC驱动的接口函数,用户程序一般通过ioctl与RTC驱动交互,这里定义了每个ioctl命令需要调用的函数。看函数名,也能猜出大概:
EXPORT_SYMBOL_GPL(rtc_set_time);
EXPORT_SYMBOL_GPL(rtc_read_alarm);
(4)、/drivers/rtc/rtc-sysfs.c 看名字就很清楚,与sysfs有关,看注释:
/*
* RTC subsystem, sysfs interface
*/
(5)、/drivers/rtc/rtc-proc.c 与proc文件系统有关,同样:
/*
* RTC subsystem, proc interface
*/
(6)、/drivers/rtc/rtc-lib.c 主要和时间转化有关,列出一个函数,看函数名就能看出大概,
int rtc_year_days(unsigned int day, unsigned int month, unsigned int year)
{
return rtc_ydays[LEAP_YEAR(year)][month] + day-1;
}
(7)、/drivers/rtc/hctosys.c 和系统时钟有关,就一个函数,
/*
* RTC subsystem, initialize system time on startup
*/
static int __init rtc_hctosys(void)
(8)、/include/linux/rtc.h 定义了与RTC有关的数据结构,如下所示:
structrtc_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);
};
struct rtc_device
{
struct device dev;
struct module *owner;
int id;
char name[RTC_DEVICE_NAME_SIZE];
const struct rtc_class_ops *ops;
struct mutex ops_lock;
struct cdev char_dev;
unsigned long flags;
unsigned long irq_data;
spinlock_t irq_lock;
wait_queue_head_t irq_queue;
struct fasync_struct *async_queue;
struct rtc_task *irq_task;
spinlock_t irq_task_lock;
int irq_freq;
int max_user_freq;
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
struct work_struct uie_task;
struct timer_list uie_timer;
/* Those fields are protected by rtc->irq_lock */
unsigned int oldsecs;
unsigned int irq_active:1;
unsigned int stop_uie_polling:1;
unsigned int uie_task_active:1;
unsigned int uie_timer_active:1;
#endif
};
4、因为主要关心的是三星架构的s3c6410的RTC,与之有关的文件是Rtc-s3c.c (linux2.6.28\drivers\rtc)。这个文件实现了具体rtc设备的驱动。同样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,
};
平台相关:
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 char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics\n";
static int __init s3c_rtc_init(void)
{
printk(banner);
return platform_driver_register(&s3c2410_rtc_driver);
}
static void __exit s3c_rtc_exit(void)
{
platform_driver_unregister(&s3c2410_rtc_driver);
}
module_init(s3c_rtc_init);
module_exit(s3c_rtc_exit);