一. RTC定义
sun@ubuntu:/work/6410/kernel/linux-3.0.1/drivers/rtc$ tree
.
├── class.c //rtc_class类
├── hctosys.c //系统时钟相关的操作
├── interface.c //很多接口
├── rtc-core.h
├── rtc-dev.c //file_ops
├── rtc-lib.c //时间相关的操作
├── rtc-proc.c //rtc 中 proc相关的操作
├── rtc-s3c.c //s3c-rtc platform初始化 + class_ops
└── rtc-sysfs.c //rtc中sysfs相关的操作
1. s3c-rtc的定义
arch/arm/plat-samsung/dev-rtc.c中
在arch/arm/mach-s3c64xx/mach-smdk6410.c
中
2. 系统控制rtc的开关
二. RTC驱动
1. 在driver/rtc/rtc-s3c.c中,匹配设备与设备的驱动
2. s3c_rtc_probe
s3c_rtc_probe
--> s3c_rtc_enable
s3c_rtc_probe
--> rtc_device_register
s3c_rtc_probe
--> s3c_rtc_setfreq
注:为什么这儿 tmp
|
=
(
rtc_dev
-
>
max_user_freq
/
freq
)
-
1
;
有个减1呢?
Period = (n+1)/32768 second (n= tick counter value)
三. class的处理方法
root@OK6410:~# cat /sys/class/rtc/rtc0/date
2001-11-05
driver/rtc/rtc-sysfs.c
rtc_sysfs_show_time
--> rtc_read_time
在driver/rtc/interface.c中
rtc_device_register 中有 rtc->ops = ops; ops=s3c_rtops
rtc_sysfs_show_time
--> rtc_read_time
--> __rtc_read_time
--> s3c_rtc_gettime
四. 测试及附录
1. 测试一下
2. bcd2bin函数
bcd码是用4位二进制来表示10进制的,
所以要把char(8位)的bcd码转成10进制,
假设bcd码 0010 0101 --> 10进制是25
sun@ubuntu:/work/6410/kernel/linux-3.0.1/drivers/rtc$ tree
.
├── class.c //rtc_class类
├── hctosys.c //系统时钟相关的操作
├── interface.c //很多接口
├── rtc-core.h
├── rtc-dev.c //file_ops
├── rtc-lib.c //时间相关的操作
├── rtc-proc.c //rtc 中 proc相关的操作
├── rtc-s3c.c //s3c-rtc platform初始化 + class_ops
└── rtc-sysfs.c //rtc中sysfs相关的操作
1. s3c-rtc的定义
arch/arm/plat-samsung/dev-rtc.c中
- static struct resource s3c_rtc_resource[] = {
- [0] = {
- .start = S3C_PA_RTC,
- .end = S3C_PA_RTC + 0xff,
- .flags = IORESOURCE_MEM, //控制寄存器
- },
- [1] = {
- .start = IRQ_RTC_ALARM,
- .end = IRQ_RTC_ALARM,
- .flags = IORESOURCE_IRQ, //alarm中断
- },
- [2] = {
- .start = IRQ_RTC_TIC,
- .end = IRQ_RTC_TIC,
- .flags = IORESOURCE_IRQ //TIC中断
- }
- };
-
- struct platform_device s3c_device_rtc = {
- .name = "s3c64xx-rtc",
- .id = -1,
- .num_resources = ARRAY_SIZE(s3c_rtc_resource),
- .resource = s3c_rtc_resource,
- };
- static struct platform_device *smdk6410_devices[] __initdata = {
- &s3c_device_rtc, //添加到设备列表中
- };
-
- arch/arm/mach-s3c64xx/mach-smdk6410.c
- static void __init smdk6410_machine_init(void)
- {
- platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices)); //设备在此处注册
- }
2. 系统控制rtc的开关
- arch/arm/plat-samsung/clock.c
- struct clk clk_p = {
- .name = "pclk",
- .id = -1,
- .rate = 0,
- .parent = NULL,
- .ctrlbit = 0,
- .ops = &clk_ops_def_setrate,
- };
- arch/arm/mach-s3c64xx/clock.c
- static struct clk init_clocks_disable[] = {
- {
- .name = "rtc",
- .id = -1,
- .parent = &clk_p,
- .enable = s3c64xx_pclk_ctrl, //控制是否启用rtc
- .ctrlbit = S3C_CLKCON_PCLK_RTC, //#define S3C_CLKCON_PCLK_RTC (1<<6)
- },
- };
-
- static int s3c64xx_pclk_ctrl(struct clk *clk, int enable) //控制是否启用rtc
- {
- return s3c64xx_gate(S3C_PCLK_GATE, clk, enable); //PCLK_GATE相应位置0--disable,反之enbale
- }
-
- void __init s3c64xx_register_clocks(unsigned long xtal, unsigned armclk_divlimit)
- {
- struct clk *clkp;
- int ret;
- int ptr;
-
- armclk_mask = armclk_divlimit;
-
- s3c24xx_register_baseclocks(xtal);
- s3c24xx_register_clocks(clks, ARRAY_SIZE(clks));
-
- s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks));
-
- clkp = init_clocks_disable;
- for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
- ret = s3c24xx_register_clock(clkp);
- (clkp->enable)(clkp, 0); //调用每个设备的enable函数
- }
-
- s3c24xx_register_clocks(clks1, ARRAY_SIZE(clks1));
- s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs));
- s3c_pwmclk_init();
- }
二. RTC驱动
1. 在driver/rtc/rtc-s3c.c中,匹配设备与设备的驱动
- static struct platform_device_id s3c_rtc_driver_ids[] = {
- {
- .name = "s3c2410-rtc",
- .driver_data = TYPE_S3C2410,
- }, {
- .name = "s3c64xx-rtc", //name="s3c64xx-rtc"与设备一致完成匹配
- .driver_data = TYPE_S3C64XX,
- },
- { }
- };
-
- MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids);
-
- static struct platform_driver s3c_rtc_driver = {
- .probe = s3c_rtc_probe,
- .remove = __devexit_p(s3c_rtc_remove),
- .suspend = s3c_rtc_suspend,
- .resume = s3c_rtc_resume,
- .id_table = s3c_rtc_driver_ids, //该驱动支持的设备列表
- .driver = {
- .name = "s3c-rtc",
- .owner = THIS_MODULE,
- },
- };
- static int __init s3c_rtc_init(void)
- {
- return platform_driver_register(&s3c_rtc_driver);
- }
- module_init(s3c_rtc_init);
- static int __devinit s3c_rtc_probe(struct platform_device *pdev)
- {
- struct rtc_time rtc_tm;
- int ret;
-
- s3c_rtc_tickno = platform_get_irq(pdev, 1); //tick irq 34
- s3c_rtc_alarmno = platform_get_irq(pdev, 0); //alarm irq 92
-
- //申请io内存,三步曲
- struct resource * res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- s3c_rtc_mem = request_mem_region(res->start, res->end-res->start+1,pdev->name);
- s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);
-
- //设置PCLK_GATE中rtc位,使能rtc,因为在初始化时disable了
- rtc_clk = clk_get(&pdev->dev, "rtc");
- clk_enable(rtc_clk);
-
- s3c_rtc_enable(pdev, 1); //检查rtc是否己启用,若没有启用则启用
-
- device_init_wakeup(&pdev->dev, 1);
- struct rtc_device * rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops, THIS_MODULE);
- s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data; //将系统类型保存在全局变量中
-
- s3c_rtc_gettime(NULL, &rtc_tm);
-
- if (rtc_valid_tm(&rtc_tm)) {
- rtc_tm.tm_year = 100;
- rtc_tm.tm_mon = 0;
- rtc_tm.tm_mday = 1;
- rtc_tm.tm_hour = 0;
- rtc_tm.tm_min = 0;
- rtc_tm.tm_sec = 0;
- s3c_rtc_settime(NULL, &rtc_tm);
- }
-
- if (s3c_rtc_cpu_type == TYPE_S3C64XX) //根据系统类型设定rtc时钟频率
- rtc->max_user_freq = 32768;
- else
- rtc->max_user_freq = 128;
- platform_set_drvdata(pdev, rtc);
- s3c_rtc_setfreq(&pdev->dev, 1);
- return 0;
- }
--> s3c_rtc_enable
- static void s3c_rtc_enable(struct platform_device *pdev, int en)
- {
- void __iomem *base = s3c_rtc_base;
- unsigned int tmp;
-
- if (s3c_rtc_base == NULL)
- return;
- //检查RTCEN是否在运行
- if ((readw(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0) { //读取RTCEN位是否为1
- tmp = readw(base + S3C2410_RTCCON); //不为1,设为1,使能rtc
- writew(tmp | S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON);
- }
- //检查BCD count select是否己运行
- if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)) {
- tmp = readw(base + S3C2410_RTCCON);
- writew(tmp & ~S3C2410_RTCCON_CNTSEL, base + S3C2410_RTCCON);
- }
- //检查RTC clock count reset是否在运行
- if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)) {
- tmp = readw(base + S3C2410_RTCCON);
- writew(tmp & ~S3C2410_RTCCON_CLKRST, base + S3C2410_RTCCON);
- }
- }
--> rtc_device_register
- struct rtc_device *rtc_device_register(const char *name, struct device *dev,
- const struct rtc_class_ops *ops, struct module *owner)
- {
- struct rtc_wkalrm alrm;
-
- idr_pre_get(&rtc_idr, GFP_KERNEL) ;
-
- mutex_lock(&idr_lock);
- idr_get_new(&rtc_idr, NULL, &id);
- mutex_unlock(&idr_lock);
-
- int id = id & MAX_ID_MASK;
-
- //申请并初始化rtc_device
- struct rtc_device * rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
- rtc->id = id;
- rtc->ops = ops;
- rtc->owner = owner;
- rtc->irq_freq = 1;
- rtc->max_user_freq = 64;
- rtc->dev.parent = dev;
- rtc->dev.class = rtc_class;
- rtc->dev.release = rtc_device_release;
-
-
- mutex_init(&rtc->ops_lock);
- spin_lock_init(&rtc->irq_lock);
- spin_lock_init(&rtc->irq_task_lock);
- init_waitqueue_head(&rtc->irq_queue);
-
- timerqueue_init_head(&rtc->timerqueue);
- INIT_WORK(&rtc->irqwork, rtc_timer_do_work);
-
- rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc);
- rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc);
- hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- rtc->pie_timer.function = rtc_pie_update_irq;
- rtc->pie_enabled = 0;
-
- err = __rtc_read_alarm(rtc, &alrm);
-
- if (!err && !rtc_valid_tm(&alrm.time))
- rtc_initialize_alarm(rtc, &alrm);
-
- strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
- dev_set_name(&rtc->dev, "rtc%d", id);
-
- rtc_dev_prepare(rtc); //rtc->char_dev: cdev_init
-
- err = device_register(&rtc->dev);
-
- rtc_dev_add_device(rtc); //rtc->char_dev: cdev_add
- rtc_sysfs_add_device(rtc); //rtc->dev: device_create_file
- rtc_proc_add_device(rtc); //proc_create_data -->ops: rtc_proc_fops
-
- return rtc;
- }
- EXPORT_SYMBOL_GPL(rtc_device_register);
s3c_rtc_probe
--> s3c_rtc_setfreq
- static int s3c_rtc_setfreq(struct device *dev, int freq)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
- unsigned int tmp = 0;
-
- if (!is_power_of_2(freq))
- return -EINVAL;
-
- spin_lock_irq(&s3c_rtc_pie_lock);
-
- if (s3c_rtc_cpu_type == TYPE_S3C2410) { //s3c_rtc_cpu_type是一个全局变量
- tmp = readb(s3c_rtc_base + S3C2410_TICNT);
- tmp &= S3C2410_TICNT_ENABLE;
- }
-
- tmp |= (rtc_dev->max_user_freq / freq)-1; //将TICNT设为32768-1
-
- writel(tmp, s3c_rtc_base + S3C2410_TICNT);
- spin_unlock_irq(&s3c_rtc_pie_lock);
-
- return 0;
- }
Period = (n+1)/32768 second (n= tick counter value)
三. class的处理方法
root@OK6410:~# cat /sys/class/rtc/rtc0/date
2001-11-05
- static int s3c_rtc_open(struct device *dev)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
- int ret;
- ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq, IRQF_DISABLED, "s3c2410-rtc alarm", rtc_dev);
-
- ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq, IRQF_DISABLED, "s3c2410-rtc tick", rtc_dev);
-
- return ret;
- }
driver/rtc/rtc-sysfs.c
- 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); //将数据读取到结构体tm中
- if (retval == 0) {
- retval = sprintf(buf, "%02d:%02d:%02d\n", tm.tm_hour, tm.tm_min, tm.tm_sec); //输出到buf
- }
-
- return retval;
- }
rtc_sysfs_show_time
--> rtc_read_time
在driver/rtc/interface.c中
- int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) //时间读取过程
- {
- err = mutex_lock_interruptible(&rtc->ops_lock);
- if (err)
- return err;
- err = __rtc_read_time(rtc, tm); //时间读取
- mutex_unlock(&rtc->ops_lock);
- return err;
- }
- EXPORT_SYMBOL_GPL(rtc_read_time);
- rtc_sysfs_show_time
--> rtc_read_time - --> __rtc_read_time
- static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
- {
- int 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); //调用rtc->ops->read_time
- }
- return err;
- }
rtc_device_register 中有 rtc->ops = ops; ops=s3c_rtops
- static const struct rtc_class_ops s3c_rtcops = {
- .open = s3c_rtc_open,
- .release = s3c_rtc_release,
- .read_time = s3c_rtc_gettime,
- .set_time = s3c_rtc_settime,
- .read_alarm = s3c_rtc_getalarm,
- .set_alarm = s3c_rtc_setalarm,
- .proc = s3c_rtc_proc,
- .alarm_irq_enable = s3c_rtc_setaie,
- };
--> rtc_read_time
--> __rtc_read_time
--> s3c_rtc_gettime
- static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
- {
- unsigned int have_retried = 0;
- void __iomem *base = s3c_rtc_base;
-
- retry_get_time:
- rtc_tm->tm_min = readb(base + S3C2410_RTCMIN);
- rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
- rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);
- rtc_tm->tm_mon = readb(base + S3C2410_RTCMON);
- rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);
- rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC);
-
-
- if (rtc_tm->tm_sec == 0 && !have_retried) {
- have_retried = 1;
- goto retry_get_time;
- }
- rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
- rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
- rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
- rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
- rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
- rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
-
- rtc_tm->tm_year += 100;
- rtc_tm->tm_mon -= 1;
-
- return rtc_valid_tm(rtc_tm);
- }
四. 测试及附录
1. 测试一下
- root@OK6410:~# date 080516492013 //设定当前时间为2013年08月05号16点49分
- Mon Aug 5 16:49:00 CST 2013
- root@OK6410:~# hwclock -w //写入到硬件时钟,即cmos中去
- root@OK6410:~# hwclock //查看一下硬件时钟
- Mon Aug 5 16:49:04 2013 0.000000 seconds
- unsigned bcd2bin(unsigned char val)
- {
- return (val & 0x0f) + (val >> 4) * 10;
- }
- EXPORT_SYMBOL(bcd2bin);
-
- unsigned char bin2bcd(unsigned val)
- {
- return ((val / 10) << 4) + val % 10;
- }
- EXPORT_SYMBOL(bin2bcd);
所以要把char(8位)的bcd码转成10进制,
假设bcd码 0010 0101 --> 10进制是25