Linux内核---36.RTC驱动分析

一. 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中
  1. static struct resource s3c_rtc_resource[] = {
  2.     [0] = {
  3.         .start    = S3C_PA_RTC,
  4.         .end    = S3C_PA_RTC + 0xff,
  5.         .flags    = IORESOURCE_MEM,    //控制寄存器
  6.     },
  7.     [1] = {
  8.         .start    = IRQ_RTC_ALARM,
  9.         .end    = IRQ_RTC_ALARM,
  10.         .flags    = IORESOURCE_IRQ,    //alarm中断
  11.     },
  12.     [2] = {
  13.         .start    = IRQ_RTC_TIC,
  14.         .end    = IRQ_RTC_TIC,
  15.         .flags    = IORESOURCE_IRQ    //TIC中断
  16.     }
  17. };

  18. struct platform_device s3c_device_rtc = {
  19.     .name        = "s3c64xx-rtc",
  20.     .id        = -1,
  21.     .num_resources    = ARRAY_SIZE(s3c_rtc_resource),
  22.     .resource    = s3c_rtc_resource,
  23. };
在arch/arm/mach-s3c64xx/mach-smdk6410.c
  1. static struct platform_device *smdk6410_devices[] __initdata = {
  2.     &s3c_device_rtc,            //添加到设备列表中
  3. };

  4. arch/arm/mach-s3c64xx/mach-smdk6410.c
  5. static void __init smdk6410_machine_init(void)
  6. {
  7.     platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));    //设备在此处注册
  8. }

2. 系统控制rtc的开关
  1. arch/arm/plat-samsung/clock.c
  2. struct clk clk_p = {
  3.     .name        = "pclk",
  4.     .id        = -1,
  5.     .rate        = 0,
  6.     .parent        = NULL,
  7.     .ctrlbit    = 0,
  8.     .ops        = &clk_ops_def_setrate,
  9. };
  10. arch/arm/mach-s3c64xx/clock.c
  11. static struct clk init_clocks_disable[] = {
  12.  {
  13.         .name        = "rtc",
  14.         .id        = -1,
  15.         .parent        = &clk_p,
  16.         .enable        = s3c64xx_pclk_ctrl,    //控制是否启用rtc
  17.         .ctrlbit    = S3C_CLKCON_PCLK_RTC,     //#define S3C_CLKCON_PCLK_RTC (1<<6)
  18.     }, 
  19. };

  20. static int s3c64xx_pclk_ctrl(struct clk *clk, int enable)  //控制是否启用rtc
  21. {
  22.     return s3c64xx_gate(S3C_PCLK_GATE, clk, enable);     //PCLK_GATE相应位置0--disable,反之enbale
  23. }

  24. void __init s3c64xx_register_clocks(unsigned long xtal, unsigned armclk_divlimit)
  25. {
  26.     struct clk *clkp;
  27.     int ret;
  28.     int ptr;

  29.     armclk_mask = armclk_divlimit;

  30.     s3c24xx_register_baseclocks(xtal);
  31.     s3c24xx_register_clocks(clks, ARRAY_SIZE(clks));

  32.     s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks));

  33.     clkp = init_clocks_disable;
  34.     for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
  35.         ret = s3c24xx_register_clock(clkp);
  36.         (clkp->enable)(clkp, 0);                //调用每个设备的enable函数
  37.     }

  38.     s3c24xx_register_clocks(clks1, ARRAY_SIZE(clks1));
  39.     s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs));
  40.     s3c_pwmclk_init();
  41. }


二. RTC驱动
1. 在driver/rtc/rtc-s3c.c中,匹配设备与设备的驱动
  1. static struct platform_device_id s3c_rtc_driver_ids[] = {
  2.     {
  3.         .name        = "s3c2410-rtc",
  4.         .driver_data    = TYPE_S3C2410,
  5.     }, {
  6.         .name        = "s3c64xx-rtc",          //name="s3c64xx-rtc"与设备一致完成匹配
  7.         .driver_data    = TYPE_S3C64XX,
  8.     },
  9.     { }
  10. };

  11. MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids);

  12. static struct platform_driver s3c_rtc_driver = {
  13.     .probe        = s3c_rtc_probe,
  14.     .remove        = __devexit_p(s3c_rtc_remove),
  15.     .suspend    = s3c_rtc_suspend,
  16.     .resume        = s3c_rtc_resume,
  17.     .id_table    = s3c_rtc_driver_ids,            //该驱动支持的设备列表
  18.     .driver        = {
  19.         .name    = "s3c-rtc",
  20.         .owner    = THIS_MODULE,
  21.     },
  22. };
  23. static int __init s3c_rtc_init(void)
  24. {
  25.     return platform_driver_register(&s3c_rtc_driver);
  26. }
  27. module_init(s3c_rtc_init);
2. s3c_rtc_probe
  1. static int __devinit s3c_rtc_probe(struct platform_device *pdev)
  2. {
  3.     struct rtc_time rtc_tm;    
  4.     int ret;

  5.     s3c_rtc_tickno = platform_get_irq(pdev, 1);        //tick irq 34
  6.     s3c_rtc_alarmno = platform_get_irq(pdev, 0);       //alarm irq 92
  7.     
  8.     //申请io内存,三步曲
  9.     struct resource * res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 
  10.     s3c_rtc_mem = request_mem_region(res->start, res->end-res->start+1,pdev->name);
  11.     s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);   
  12.     
  13.     //设置PCLK_GATErtc位,使能rtc,因为在初始化时disable了 
  14.     rtc_clk = clk_get(&pdev->dev, "rtc");       
  15.     clk_enable(rtc_clk);
  16.    
  17.     s3c_rtc_enable(pdev, 1);    //检查rtc是否己启用,若没有启用则启用

  18.     device_init_wakeup(&pdev->dev, 1);
  19.     struct rtc_device * rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops, THIS_MODULE);
  20.     s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data;         //将系统类型保存在全局变量中

  21.     s3c_rtc_gettime(NULL, &rtc_tm);

  22.     if (rtc_valid_tm(&rtc_tm)) {
  23.         rtc_tm.tm_year    = 100;
  24.         rtc_tm.tm_mon    = 0;
  25.         rtc_tm.tm_mday    = 1;
  26.         rtc_tm.tm_hour    = 0;
  27.         rtc_tm.tm_min    = 0;
  28.         rtc_tm.tm_sec    = 0;
  29.         s3c_rtc_settime(NULL, &rtc_tm);     
  30.     }

  31.     if (s3c_rtc_cpu_type == TYPE_S3C64XX)                    //根据系统类型设定rtc时钟频率
  32.         rtc->max_user_freq = 32768;
  33.     else
  34.         rtc->max_user_freq = 128;
  35.     platform_set_drvdata(pdev, rtc);
  36.     s3c_rtc_setfreq(&pdev->dev, 1);
  37.     return 0;
  38. }
s3c_rtc_probe
    --> s3c_rtc_enable
  1. static void s3c_rtc_enable(struct platform_device *pdev, int en)
  2. {
  3.     void __iomem *base = s3c_rtc_base;
  4.     unsigned int tmp;

  5.     if (s3c_rtc_base == NULL)
  6.         return;
  7.     //检查RTCEN是否在运行
  8.     if ((readw(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0) {    //读取RTCEN位是否为1
  9.         tmp = readw(base + S3C2410_RTCCON);                            //不为1,设为1,使能rtc 
  10.         writew(tmp | S3C2410_RTCCON_RTCEN,   base + S3C2410_RTCCON);    
  11.     }
  12.     //检查BCD count select是否己运行
  13.     if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)) {
  14.         tmp = readw(base + S3C2410_RTCCON);
  15.         writew(tmp & ~S3C2410_RTCCON_CNTSEL,   base + S3C2410_RTCCON);
  16.     }
  17.     //检查RTC clock count reset是否在运行
  18.     if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)) {
  19.         tmp = readw(base + S3C2410_RTCCON);
  20.         writew(tmp & ~S3C2410_RTCCON_CLKRST,            base + S3C2410_RTCCON);
  21.     }    
  22. }
s3c_rtc_probe
    --> rtc_device_register
  1. struct rtc_device *rtc_device_register(const char *name, struct device *dev,
  2.                     const struct rtc_class_ops *ops, struct module *owner)
  3. {
  4.     struct rtc_wkalrm alrm;
  5.     
  6.     idr_pre_get(&rtc_idr, GFP_KERNEL) ; 

  7.     mutex_lock(&idr_lock);
  8.     idr_get_new(&rtc_idr, NULL, &id);
  9.     mutex_unlock(&idr_lock);

  10.     int id = id & MAX_ID_MASK;
  11.     
  12.     //申请并初始化rtc_device
  13.     struct rtc_device * rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
  14.     rtc->id = id;
  15.     rtc->ops = ops;
  16.     rtc->owner = owner;
  17.     rtc->irq_freq = 1;
  18.     rtc->max_user_freq = 64;
  19.     rtc->dev.parent = dev;
  20.     rtc->dev.class = rtc_class;
  21.     rtc->dev.release = rtc_device_release;


  22.     mutex_init(&rtc->ops_lock);
  23.     spin_lock_init(&rtc->irq_lock);
  24.     spin_lock_init(&rtc->irq_task_lock);
  25.     init_waitqueue_head(&rtc->irq_queue);

  26.     timerqueue_init_head(&rtc->timerqueue);
  27.     INIT_WORK(&rtc->irqwork, rtc_timer_do_work);

  28.     rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc);
  29.     rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc);
  30.     hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
  31.     rtc->pie_timer.function = rtc_pie_update_irq;
  32.     rtc->pie_enabled = 0;
  33.    
  34.     err = __rtc_read_alarm(rtc, &alrm);

  35.     if (!err && !rtc_valid_tm(&alrm.time))
  36.         rtc_initialize_alarm(rtc, &alrm);

  37.     strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
  38.     dev_set_name(&rtc->dev, "rtc%d", id);

  39.     rtc_dev_prepare(rtc);            //rtc->char_dev: cdev_init

  40.     err = device_register(&rtc->dev);
  41.    
  42.     rtc_dev_add_device(rtc);         //rtc->char_dev: cdev_add
  43.     rtc_sysfs_add_device(rtc);       //rtc->dev: device_create_file
  44.     rtc_proc_add_device(rtc);        //proc_create_data -->ops: rtc_proc_fops

  45.     return rtc;
  46. }
  47. EXPORT_SYMBOL_GPL(rtc_device_register);

s3c_rtc_probe
    -->  s3c_rtc_setfreq
  1. static int s3c_rtc_setfreq(struct device *dev, int freq)
  2. {
  3.     struct platform_device *pdev = to_platform_device(dev);
  4.     struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
  5.     unsigned int tmp = 0;

  6.     if (!is_power_of_2(freq))
  7.         return -EINVAL;

  8.     spin_lock_irq(&s3c_rtc_pie_lock);

  9.     if (s3c_rtc_cpu_type == TYPE_S3C2410) {        //s3c_rtc_cpu_type是一个全局变量
  10.         tmp = readb(s3c_rtc_base + S3C2410_TICNT);
  11.         tmp &= S3C2410_TICNT_ENABLE;
  12.     }

  13.     tmp |= (rtc_dev->max_user_freq / freq)-1;      //将TICNT设为32768-1

  14.     writel(tmp, s3c_rtc_base + S3C2410_TICNT);
  15.     spin_unlock_irq(&s3c_rtc_pie_lock);

  16.     return 0;
  17. }
注:为什么这儿  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

  1. static int s3c_rtc_open(struct device *dev)
  2. {
  3.     struct platform_device *pdev = to_platform_device(dev);
  4.     struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
  5.     int ret;
  6.     ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq, IRQF_DISABLED, "s3c2410-rtc alarm", rtc_dev);

  7.     ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,   IRQF_DISABLED, "s3c2410-rtc tick", rtc_dev);

  8.     return ret;
  9. }


driver/rtc/rtc-sysfs.c
  1. rtc_sysfs_show_time(struct device *dev, struct device_attribute *attrchar *buf)
  2. {
  3.     ssize_t retval;
  4.     struct rtc_time tm;

  5.     retval = rtc_read_time(to_rtc_device(dev), &tm);         //将数据读取到结构体tm中
  6.     if (retval == 0) {
  7.         retval = sprintf(buf, "%02d:%02d:%02d\n", tm.tm_hour, tm.tm_min, tm.tm_sec); //输出到buf
  8.     }

  9.     return retval;
  10. }

rtc_sysfs_show_time
    --> rtc_read_time
在driver/rtc/interface.c中
  1. int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)  //时间读取过程
  2. {
  3.     err = mutex_lock_interruptible(&rtc->ops_lock);
  4.     if (err)
  5.         return err;
  6.     err = __rtc_read_time(rtc, tm);   //时间读取
  7.     mutex_unlock(&rtc->ops_lock);
  8.     return err;
  9. }
  10. EXPORT_SYMBOL_GPL(rtc_read_time);
  11. rtc_sysfs_show_time
        --> rtc_read_time
  12.     --> __rtc_read_time
  13. static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
  14. {
  15.     int err;
  16.     if (!rtc->ops)
  17.         err = -ENODEV;
  18.     else if (!rtc->ops->read_time)
  19.         err = -EINVAL;
  20.     else {
  21.         memset(tm, 0, sizeof(struct rtc_time));
  22.         err = rtc->ops->read_time(rtc->dev.parent, tm);   //调用rtc->ops->read_time
  23.     }
  24.     return err;
  25. }

rtc_device_register 中有 rtc->ops = ops; ops=s3c_rtops
  1. static const struct rtc_class_ops s3c_rtcops = {
  2.     .open        = s3c_rtc_open,
  3.     .release    = s3c_rtc_release,
  4.     .read_time    = s3c_rtc_gettime,
  5.     .set_time    = s3c_rtc_settime,
  6.     .read_alarm    = s3c_rtc_getalarm,
  7.     .set_alarm    = s3c_rtc_setalarm,
  8.     .proc        = s3c_rtc_proc,
  9.     .alarm_irq_enable = s3c_rtc_setaie,
  10. };
rtc_sysfs_show_time
    --> rtc_read_time
    --> __rtc_read_time
        --> s3c_rtc_gettime 
  1. static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
  2. {
  3.     unsigned int have_retried = 0;
  4.     void __iomem *base = s3c_rtc_base;

  5.  retry_get_time:
  6.     rtc_tm->tm_min = readb(base + S3C2410_RTCMIN);
  7.     rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
  8.     rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);
  9.     rtc_tm->tm_mon = readb(base + S3C2410_RTCMON);
  10.     rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);
  11.     rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC);


  12.     if (rtc_tm->tm_sec == 0 && !have_retried) {
  13.         have_retried = 1;
  14.         goto retry_get_time;
  15.     }
  16.     rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
  17.     rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
  18.     rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
  19.     rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
  20.     rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
  21.     rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);

  22.     rtc_tm->tm_year += 100;
  23.     rtc_tm->tm_mon -= 1;

  24.     return rtc_valid_tm(rtc_tm);
  25. }


四. 测试及附录
1. 测试一下
  1. root@OK6410:~# date 080516492013       //设定当前时间为2013年08月05号16点49分
  2. Mon Aug 5 16:49:00 CST 2013
  3. root@OK6410:~# hwclock -w              //写入到硬件时钟,即cmos中去 
  4. root@OK6410:~# hwclock                 //查看一下硬件时钟 
  5. Mon Aug 5 16:49:04 2013 0.000000 seconds
2. bcd2bin函数
  1. unsigned bcd2bin(unsigned char val)
  2. {
  3.     return (val & 0x0f) + (val >> 4) * 10;
  4. }
  5. EXPORT_SYMBOL(bcd2bin);

  6. unsigned char bin2bcd(unsigned val)
  7. {
  8.     return ((val / 10) << 4) + val % 10;
  9. }
  10. EXPORT_SYMBOL(bin2bcd);
bcd码是用4位二进制来表示10进制的, 
所以要把char(8位)的bcd码转成10进制, 
假设bcd码 0010 0101 --> 10进制是25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值