时钟RTC驱动分析

原文出自http://blog.csdn.net/yming0221/article/details/6584527

 

硬件平台:FL2440(S3C2440)

内核版本:Linux 2.6.28

主机平台:Ubuntu 11.04

内核版本:Linux 2.6.39

交叉编译器版本:arm-linux-gcc 3.4.1

原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/6584285

1、实时时钟概述

实时时钟(RTC)单元可以在断电的情况下使用纽扣电池继续计时工作。RTC使用STRB/LDRB ARM操作传输二进制码十进制数的8位数据给CPU。其中的数据包括秒、分、时、日期、天、月、年的时间信息。可以执行报警功能。

2、实时时钟操作

下面是RTC模块的电路图

时钟RTC驱动分析_第1张图片

3、RTC寄存器介绍

实时时钟控制寄存器(RTCCON)-REAL TIME CLOCK CONTROL REGISTER


时钟RTC驱动分析_第2张图片

节拍时间计数寄存器(TICNT)-TICK TIME COUNT REGISTER

时钟RTC驱动分析_第3张图片

RTC报警控制寄存器(RTCALM)-RTC ALARM CONTROL REGISTER

时钟RTC驱动分析_第4张图片

报警秒数寄存器(ALMSEC)-ALARM SECOND DATA REGISTER

时钟RTC驱动分析_第5张图片

报警分钟计数寄存器(ALMMIN)-ALARM MIN DATA REGISTER

时钟RTC驱动分析_第6张图片

报警小时数据寄存器(ALMHOUR)-ALARM HOUR DATA REGISTER

时钟RTC驱动分析_第7张图片

报警日期数据寄存器(ALMDATE)-ALARM DATE DATA REGISTER

时钟RTC驱动分析_第8张图片

报警月数数据寄存器(ALMMON)-ALARM MON DATA REGISTER 

时钟RTC驱动分析_第9张图片

报警年数数据寄存器(ALMYEAR)-ALARM YEAR DATA REGISTER

时钟RTC驱动分析_第10张图片

BCD数据寄存器的格式和报警寄存器结构相同,只是对应的地址不同。

BCD秒寄存器(BCDSEC)-BCD SECOND REGISTER 地址:0x57000070(L) 0x57000073(B)

BCD分寄存器(BCDMIN)-BCD MINUTE REGISTER 地址:0x57000074(L) 0x57000077(B)

BCD小时寄存器(BCDHOUR)-BCD HOUR REGISTER  地址:0x57000078(L) 0x5700007B(B)

BCD日期寄存器(BCDDATE)-BCD DATE REGISTER  地址:0x5700007C(L) 0x5700007F(B)

BCD日寄存器(BCDDAY)-BCD DAY REGISTER 地址:0x57000080(L) 0x57000083(B)

BCD月寄存器(BCDMON)-BCD MONTH REGISTER 地址:0x57000084(L) 0x57000087(B)

BCD年寄存器(BCDYEAR)-BCD YEAR REGISTER 地址:0x57000088(L) 0x5700008B(B)

4、驱动实例分析

为了使驱动更容易理解,现在这个RTC驱动只完成了计时功能,没有添加相应的报警功能,也没有添加电源管理的功能,缺少的功能今后完善。

下面先总体了解驱动:

首先是RTC驱动的结构体,在/include/linux/platform_device.h中,如下

[cpp] view plain copy print ?
  1. <SPAN style="FONT-SIZE: 16px">struct platform_driver {  
  2.     int (*probe)(struct platform_device *);  
  3.     int (*remove)(struct platform_device *);  
  4.     void (*shutdown)(struct platform_device *);  
  5.     int (*suspend)(struct platform_device *, pm_message_t state);  
  6.     int (*suspend_late)(struct platform_device *, pm_message_t state);  
  7.     int (*resume_early)(struct platform_device *);  
  8.     int (*resume)(struct platform_device *);  
  9.     struct pm_ext_ops *pm;  
  10.     struct device_driver driver;  
  11. };</SPAN>  
驱动中定义对应的结构体

[cpp] view plain copy print ?
  1. static struct platform_driver s3c2410_rtc_driver = {  
  2.     .probe      = s3c_rtc_probe,//RTC探测函数  
  3.     .remove     = __devexit_p(s3c_rtc_remove),//RTC移除函数  
  4.     .driver     = {  
  5.         .name   = "s3c2410-rtc",  
  6.         .owner  = THIS_MODULE,  
  7.     },  
  8. };  
下面是驱动中驱动的初始化和退出函数

[cpp] view plain copy print ?
  1. static int __init s3c_rtc_init(void)  
  2. {  
  3.     printk(banner);  
  4.     return platform_driver_register(&s3c2410_rtc_driver);  
  5. }  
  6.   
  7. static void __exit s3c_rtc_exit(void)  
  8. {  
  9.     platform_driver_unregister(&s3c2410_rtc_driver);  
  10. }  

platform_driver_register()和platform_driver_unregister()函数在/drivers/base/platform.c中实现的。

可以看出,platform_driver_register()函数的作用就是为platform_driver中的driver中的probe、remove等提供接口函数

[cpp] view plain copy print ?
  1. int platform_driver_register(struct platform_driver *drv)  
  2. {  
  3.     drv->driver.bus = &platform_bus_type;  
  4.     if (drv->probe)  
  5.         drv->driver.probe = platform_drv_probe;  
  6.     if (drv->remove)  
  7.         drv->driver.remove = platform_drv_remove;  
  8.     if (drv->shutdown)  
  9.         drv->driver.shutdown = platform_drv_shutdown;  
  10.     if (drv->suspend)  
  11.         drv->driver.suspend = platform_drv_suspend;  
  12.     if (drv->resume)  
  13.         drv->driver.resume = platform_drv_resume;  
  14.     if (drv->pm)  
  15.         drv->driver.pm = &drv->pm->base;  
  16.     return driver_register(&drv->driver);//注册老的驱动  
  17. }  
[cpp] view plain copy print ?
  1. void platform_driver_unregister(struct platform_driver *drv)  
  2. {  
  3.     driver_unregister(&drv->driver);  
  4. }  

接下来是RTC平台驱动探测函数s3c_rtc_probe,下面函数定义的时候使用了__devinit的作用是使编译器优化代码,将其放在和是的内存位置,减少内存占用和提高内核效率。

probe函数接收到plarform_device这个参数后,就需要从中提取出需要的信息。它一般会通过调用内核提供的platform_get_resource和platform_get_irq等函数来获得相关信息。如通过platform_get_resource获得设备的起始地址后,可以对其进行request_mem_region和ioremap等操作,以便应用程序对其进行操作。通过platform_get_irq得到设备的中断号以后,就可以调用request_irq函数来向系统申请中断。这些操作在设备驱动程序中一般都要完成。

[cpp] view plain copy print ?
  1. static int __devinit s3c_rtc_probe(struct platform_device *pdev)  
  2. {  
  3.     struct rtc_device *rtc;//定义rtc_device结构体,定义在/include/linux/rtc.h  
  4.     struct resource *res;//定义资源结构体,定义在/include/linux/ioport.h  
  5.     int ret;  
  6.   
  7.     pr_debug("%s: probe=%p\n", __func__, pdev);  
  8.   
  9.     /* find the IRQs */  
  10.   
  11.     s3c_rtc_tickno = platform_get_irq(pdev, 1);//在系统定义的平台设备中获取中断号  
  12.     if (s3c_rtc_tickno < 0) {//异常处理  
  13.         dev_err(&pdev->dev, "no irq for rtc tick\n");  
  14.         return -ENOENT;  
  15.     }  
  16.   
  17.     /* get the memory region */  
  18.   
  19.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获取RTC平台使用的IO资源  
  20.     if (res == NULL) {  
  21.         dev_err(&pdev->dev, "failed to get memory region resource\n");  
  22.         return -ENOENT;  
  23.     }  
  24.        //申请内存区域,res是struct resource类型,见本函数后面  
  25.     s3c_rtc_mem = request_mem_region(res->start,  
  26.                      res->end-res->start+1,  
  27.                      pdev->name);  
  28.   
  29.     if (s3c_rtc_mem == NULL) {//申请内存出错  
  30.         dev_err(&pdev->dev, "failed to reserve memory region\n");  
  31.         ret = -ENOENT;  
  32.         goto err_nores;  
  33.     }  
  34.         //将寄存器地址映射成虚拟地址,以便访问  
  35.     s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);  
  36.     if (s3c_rtc_base == NULL) {  
  37.         dev_err(&pdev->dev, "failed ioremap()\n");  
  38.         ret = -EINVAL;  
  39.         goto err_nomap;  
  40.     }  
  41.   
  42.     /* check to see if everything is setup correctly */  
  43.   
  44.     s3c_rtc_enable(pdev, 1);//对RTCCON寄存器设置,详情见下面的函数实现  
  45.   
  46.     pr_debug("s3c2410_rtc: RTCCON=%02x\n",  
  47.          readb(s3c_rtc_base + S3C2410_RTCCON));  
  48.   
  49.     s3c_rtc_setfreq(&pdev->dev, 1);//详情见下面的函数实现  
  50.   
  51.     /* register RTC and exit */  
  52.   
  53.     rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,  
  54.                   THIS_MODULE);//注册RTC为RTC设备,其中s3c_rtcops定义见下  
  55.   
  56.     if (IS_ERR(rtc)) {  
  57.         dev_err(&pdev->dev, "cannot attach rtc\n");  
  58.         ret = PTR_ERR(rtc);  
  59.         goto err_nortc;  
  60.     }  
  61.   
  62.     rtc->max_user_freq = 128;//设置RTC节拍时间计数寄存器TICNT的节拍时间计数值的用户最大相对值  
  63.         //将RTC类的设备数据传递给系统设备,在/include/linux/platform_device.h中  
[cpp] view plain copy print ?
  1. //#define platform_set_drvdata(_dev,data)<SPAN style="WHITE-SPACE: pre">    </SPAN>dev_set_drvdata(&(_dev)->dev, (data)),该函数在/include/linux/device.h中定义,见本函数下面  
[cpp] view plain copy print ?
  1. platform_set_drvdata(pdev, rtc);  
[cpp] view plain copy print ?
  1. return 0;  
[cpp] view plain copy print ?
  1. //异常处理   
  2.  err_nortc:  
  3.     s3c_rtc_enable(pdev, 0);  
  4.     iounmap(s3c_rtc_base);  
  5.   
  6.  err_nomap:  
  7.     release_resource(s3c_rtc_mem);  
  8.  err_nores:  
  9.     return ret;  
  10. }  
下面是/include/linux/ioport.h中struct resource结构体定义
[cpp] view plain copy print ?
  1. struct resource {  
  2.     resource_size_t start;  
  3.     resource_size_t end;  
  4.     const char *name;  
  5.     unsigned long flags;  
  6.     struct resource *parent, *sibling, *child;  
  7. };  
这是dev_set_drvdata()的函数定义:

[cpp] view plain copy print ?
  1. static inline void dev_set_drvdata(struct device *dev, void *data)  
  2. {  
  3.     dev->driver_data = data;  
  4. }  
接下来是在s3c_rtc_probe()函数用到的两个函数s3c_rtc_enable()和s3c_rtc_setfreq()

[cpp] view plain copy print ?
  1. static void s3c_rtc_enable(struct platform_device *pdev, int en)  
  2. {  
[cpp] view plain copy print ?
  1. void __iomem *base = s3c_rtc_base;//__iomem的作用就是为了使编译器更好的优化编译  
  2. unsigned int tmp;  
  3.   
  4. if (s3c_rtc_base == NULL)  
  5.     return;  
  6.        //en作为参数传递过来如果en==0,关闭电源前的情况  
  7. if (!en) {  
  8.     tmp = readb(base + S3C2410_RTCCON);  
[cpp] view plain copy print ?
  1.         writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON);//设置RTCCON寄存器,屏蔽RTC使能,可以参考数据手册中寄存器的相关定义  
  2.   
  3.         tmp = readb(base + S3C2410_TICNT);  
  4.         writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT);//设置TICNT寄存器,屏蔽节拍时间中断使能  
  5.     } else {  
  6.         /* re-enable the device, and check it is ok */  
  7.                 //en!=0的情况,表示系统复位,重新使能RTC驱动  
  8.         if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){//RTCCON第0位为0,将其设置为1,重新使能  
  9.             dev_info(&pdev->dev, "rtc disabled, re-enabling\n");  
  10.   
  11.             tmp = readb(base + S3C2410_RTCCON);  
  12.             writeb(tmp|S3C2410_RTCCON_RTCEN, base+S3C2410_RTCCON);  
  13.         }  
  14.   
  15.         if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){  
  16.             dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n");  
  17.   
  18.             tmp = readb(base + S3C2410_RTCCON);  
  19.             writeb(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON);//设置RTCCON第2位为0,设置BCD计数为混合BCD计数  
  20.         }  
  21.   
  22.         if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){  
  23.             dev_info(&pdev->dev, "removing RTCCON_CLKRST\n");  
  24.   
  25.             tmp = readb(base + S3C2410_RTCCON);  
  26.             writeb(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON);//RTC时钟计数器复位  
  27.         }  
  28.     }  
  29. }  
[cpp] view plain copy print ?
  1. static int s3c_rtc_setfreq(struct device *dev, int freq)//设定节拍时间计数值  
  2. {  
  3.     unsigned int tmp;  
  4.   
  5.     spin_lock_irq(&s3c_rtc_pie_lock);//获取自旋锁,对资源互斥访问  
  6.   
  7.     tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE;//节拍时间使能有效  
  8.     tmp |= (128 / freq)-1;  
  9.   
  10.     writeb(tmp, s3c_rtc_base + S3C2410_TICNT);  
  11.     spin_unlock_irq(&s3c_rtc_pie_lock);//解锁  
  12.   
  13.     return 0;  
  14. }  
接下来是RTC设备类的操作。

下面是rtc_class_ops是RTC设备类在RTC驱动核心部分中定义的对RTC设备类进行操作的结构体,类似字符设备在驱动中的file_operations对字符设备进行操作的意思。该结构体被定义 在rtc.h中,对RTC的操作主要有打开、关闭、设置或获取时间、设置或获取报警、设置节拍时间计数值等等,该结构体内接口函数的实现都在下面

[cpp] view plain copy print ?
  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.     .irq_set_freq   = s3c_rtc_setfreq,  
  7.     .irq_set_state  = s3c_rtc_setpie,  
  8. };  
RTC打开设备函数s3c_rtc_open()

[cpp] view plain copy print ?
  1. static int s3c_rtc_open(struct device *dev)  
  2. {  
  3.     struct platform_device *pdev = to_platform_device(dev);//从平台设备中获取RTC设备类的数据  
  4.     struct rtc_device *rtc_dev = platform_get_drvdata(pdev);  
  5.     int ret;  
  6.   
  7.     ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,  
  8.               IRQF_DISABLED,  "s3c2410-rtc tick", rtc_dev);//申请中断  
  9.   
  10.     if (ret) {  
  11.         dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);  
  12.         goto tick_err;  
  13.     }  
  14.   
  15.  tick_err:  
  16.     return ret;  
  17. }  
RTC TICK节拍时间中断服务程序

[cpp] view plain copy print ?
  1. static irqreturn_t s3c_rtc_tickirq(int irq, void *id)  
  2. {  
  3.     struct rtc_device *rdev = id;  
  4.   
  5.     rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);  
  6.     return IRQ_HANDLED;  
  7. }  
RTC关闭设备函数s3c_rtc_release()

[cpp] view plain copy print ?
  1. static void s3c_rtc_release(struct device *dev)  
  2. {  
  3.     struct platform_device *pdev = to_platform_device(dev);//从平台设备中获取RTC设备类的数据  
  4.   
  5.     struct rtc_device *rtc_dev = platform_get_drvdata(pdev);  
  6.   
  7.     /* do not clear AIE here, it may be needed for wake */  
  8.   
  9.     s3c_rtc_setpie(dev, 0);//函数定义见下面  
  10.     free_irq(s3c_rtc_tickno, rtc_dev);  
  11. }  
s3c_rtc_setpie()函数,该函数主要作用就是根据参数设置TICNT寄存器的最高位,参数为0,禁止使能,参数为1,使能
[cpp] view plain copy print ?
  1. static int s3c_rtc_setpie(struct device *dev, int enabled)  
  2. {  
  3.     unsigned int tmp;  
  4.   
  5.     pr_debug("%s: pie=%d\n", __func__, enabled);  
  6.   
  7.     spin_lock_irq(&s3c_rtc_pie_lock);  
  8.     tmp = readb(s3c_rtc_base + S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE;//读取TICNT的值并将最高位清0  
  9.   
  10.     if (enabled)  
  11.         tmp |= S3C2410_TICNT_ENABLE;  
  12.   
  13.     writeb(tmp, s3c_rtc_base + S3C2410_TICNT);//写入计算后新的值  
  14.     spin_unlock_irq(&s3c_rtc_pie_lock);  
  15.   
  16.     return 0;  
  17. }  
下面两个函数是设置和读取BCD寄存器的时间,逻辑很简单,只是读取和设置相应寄存器的值

[cpp] view plain copy print ?
  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.   
  6.  retry_get_time:  
  7.     rtc_tm->tm_min  = readb(base + S3C2410_RTCMIN);  
  8.     rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);  
  9.     rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);  
  10.     rtc_tm->tm_mon  = readb(base + S3C2410_RTCMON);  
  11.     rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);  
  12.     rtc_tm->tm_sec  = readb(base + S3C2410_RTCSEC);  
  13.   
  14.     /* the only way to work out wether the system was mid-update 
  15.      * when we read it is to check the second counter, and if it 
  16.      * is zero, then we re-try the entire read 
  17.      */  
  18.   
  19.     if (rtc_tm->tm_sec == 0 && !have_retried) {  
  20.         have_retried = 1;  
  21.         goto retry_get_time;  
  22.     }  
  23.   
  24.     pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n",  
  25.          rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,  
  26.          rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);  
  27.   
  28.     rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);  
  29.     rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);  
  30.     rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);  
  31.     rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);  
  32.     rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);  
  33.     rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);  
  34.   
  35.     rtc_tm->tm_year += 100;  
  36.     rtc_tm->tm_mon -= 1;  
  37.   
  38.     return 0;  
  39. }  
  40.   
  41. static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)  
  42. {  
  43.     void __iomem *base = s3c_rtc_base;  
  44.     int year = tm->tm_year - 100;  
  45.   
  46.     pr_debug("set time %02d.%02d.%02d %02d/%02d/%02d\n",  
  47.          tm->tm_year, tm->tm_mon, tm->tm_mday,  
  48.          tm->tm_hour, tm->tm_min, tm->tm_sec);  
  49.   
  50.     /* we get around y2k by simply not supporting it */  
  51.   
  52.     if (year < 0 || year >= 100) {  
  53.         dev_err(dev, "rtc only supports 100 years\n");  
  54.         return -EINVAL;  
  55.     }  
  56.   
  57.     writeb(bin2bcd(tm->tm_sec),  base + S3C2410_RTCSEC);  
  58.     writeb(bin2bcd(tm->tm_min),  base + S3C2410_RTCMIN);  
  59.     writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR);  
  60.     writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);  
  61.     writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);  
  62.     writeb(bin2bcd(year), base + S3C2410_RTCYEAR);  
  63.   
  64.     return 0;  
  65. }  
到这里RTC驱动的计时功能实现,报警功能还没有完成。下面是这个驱动源代码

[cpp] view plain copy print ?
  1. #include <linux/module.h>  
  2. #include <linux/fs.h>  
  3. #include <linux/string.h>  
  4. #include <linux/init.h>  
  5. #include <linux/platform_device.h>  
  6. #include <linux/interrupt.h>  
  7. #include <linux/rtc.h>  
  8. #include <linux/bcd.h>  
  9. #include <linux/clk.h>  
  10. #include <linux/log2.h>  
  11.   
  12. #include <mach/hardware.h>  
  13. #include <asm/uaccess.h>  
  14. #include <asm/io.h>  
  15. #include <asm/irq.h>  
  16. #include <asm/plat-s3c/regs-rtc.h>  
  17.   
  18.   
  19. static struct resource *s3c_rtc_mem;  
  20.   
  21. static void __iomem *s3c_rtc_base;  
  22.   
  23. static int s3c_rtc_tickno  = NO_IRQ;  
  24.   
  25. static DEFINE_SPINLOCK(s3c_rtc_pie_lock);  
  26.   
  27.   
  28. static irqreturn_t s3c_rtc_tickirq(int irq, void *id)  
  29. {  
  30.     struct rtc_device *rdev = id;  
  31.   
  32.     rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);  
  33.     return IRQ_HANDLED;  
  34. }  
  35.   
  36. /* Update control registers */  
  37. static void s3c_rtc_setaie(int to)  
  38. {  
  39.     unsigned int tmp;  
  40.   
  41.     pr_debug("%s: aie=%d\n", __func__, to);  
  42.   
  43.     tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;  
  44.   
  45.     if (to)  
  46.         tmp |= S3C2410_RTCALM_ALMEN;  
  47.   
  48.     writeb(tmp, s3c_rtc_base + S3C2410_RTCALM);  
  49. }  
  50.   
  51. static int s3c_rtc_setpie(struct device *dev, int enabled)  
  52. {  
  53.     unsigned int tmp;  
  54.   
  55.     pr_debug("%s: pie=%d\n", __func__, enabled);  
  56.   
  57.     spin_lock_irq(&s3c_rtc_pie_lock);  
  58.     tmp = readb(s3c_rtc_base + S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE;  
  59.   
  60.     if (enabled)  
  61.         tmp |= S3C2410_TICNT_ENABLE;  
  62.   
  63.     writeb(tmp, s3c_rtc_base + S3C2410_TICNT);  
  64.     spin_unlock_irq(&s3c_rtc_pie_lock);  
  65.   
  66.     return 0;  
  67. }  
  68.   
  69. static int s3c_rtc_setfreq(struct device *dev, int freq)  
  70. {  
  71.     unsigned int tmp;  
  72.   
  73.     spin_lock_irq(&s3c_rtc_pie_lock);  
  74.   
  75.     tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE;  
  76.     tmp |= (128 / freq)-1;  
  77.   
  78.     writeb(tmp, s3c_rtc_base + S3C2410_TICNT);  
  79.     spin_unlock_irq(&s3c_rtc_pie_lock);  
  80.   
  81.     return 0;  
  82. }  
  83.   
  84. /* Time read/write */  
  85.   
  86. static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)  
  87. {  
  88.     unsigned int have_retried = 0;  
  89.     void __iomem *base = s3c_rtc_base;  
  90.   
  91.  retry_get_time:  
  92.     rtc_tm->tm_min  = readb(base + S3C2410_RTCMIN);  
  93.     rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);  
  94.     rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);  
  95.     rtc_tm->tm_mon  = readb(base + S3C2410_RTCMON);  
  96.     rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);  
  97.     rtc_tm->tm_sec  = readb(base + S3C2410_RTCSEC);  
  98.   
  99.     /* the only way to work out wether the system was mid-update 
  100.      * when we read it is to check the second counter, and if it 
  101.      * is zero, then we re-try the entire read 
  102.      */  
  103.   
  104.     if (rtc_tm->tm_sec == 0 && !have_retried) {  
  105.         have_retried = 1;  
  106.         goto retry_get_time;  
  107.     }  
  108.   
  109.     pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n",  
  110.          rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,  
  111.          rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);  
  112.   
  113.     rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);  
  114.     rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);  
  115.     rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);  
  116.     rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);  
  117.     rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);  
  118.     rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);  
  119.   
  120.     rtc_tm->tm_year += 100;  
  121.     rtc_tm->tm_mon -= 1;  
  122.   
  123.     return 0;  
  124. }  
  125.   
  126. static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)  
  127. {  
  128.     void __iomem *base = s3c_rtc_base;  
  129.     int year = tm->tm_year - 100;  
  130.   
  131.     pr_debug("set time %02d.%02d.%02d %02d/%02d/%02d\n",  
  132.          tm->tm_year, tm->tm_mon, tm->tm_mday,  
  133.          tm->tm_hour, tm->tm_min, tm->tm_sec);  
  134.   
  135.     /* we get around y2k by simply not supporting it */  
  136.   
  137.     if (year < 0 || year >= 100) {  
  138.         dev_err(dev, "rtc only supports 100 years\n");  
  139.         return -EINVAL;  
  140.     }  
  141.   
  142.     writeb(bin2bcd(tm->tm_sec),  base + S3C2410_RTCSEC);  
  143.     writeb(bin2bcd(tm->tm_min),  base + S3C2410_RTCMIN);  
  144.     writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR);  
  145.     writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);  
  146.     writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);  
  147.     writeb(bin2bcd(year), base + S3C2410_RTCYEAR);  
  148.   
  149.     return 0;  
  150. }  
  151.   
  152. static int s3c_rtc_open(struct device *dev)  
  153. {  
  154.     struct platform_device *pdev = to_platform_device(dev);  
  155.     struct rtc_device *rtc_dev = platform_get_drvdata(pdev);  
  156.     int ret;  
  157.   
  158.     ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,  
  159.               IRQF_DISABLED,  "s3c2410-rtc tick", rtc_dev);  
  160.   
  161.     if (ret) {  
  162.         dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);  
  163.         goto tick_err;  
  164.     }  
  165.   
  166.  tick_err:  
  167.     return ret;  
  168. }  
  169.   
  170. static void s3c_rtc_release(struct device *dev)  
  171. {  
  172.     struct platform_device *pdev = to_platform_device(dev);  
  173.     struct rtc_device *rtc_dev = platform_get_drvdata(pdev);  
  174.   
  175.     /* do not clear AIE here, it may be needed for wake */  
  176.   
  177.     s3c_rtc_setpie(dev, 0);  
  178.     free_irq(s3c_rtc_tickno, rtc_dev);  
  179. }  
  180.   
  181. static const struct rtc_class_ops s3c_rtcops = {  
  182.     .open       = s3c_rtc_open,  
  183.     .release    = s3c_rtc_release,  
  184.     .read_time  = s3c_rtc_gettime,  
  185.     .set_time   = s3c_rtc_settime,  
  186.     .irq_set_freq   = s3c_rtc_setfreq,  
  187.     .irq_set_state  = s3c_rtc_setpie,  
  188. };  
  189.   
  190. static void s3c_rtc_enable(struct platform_device *pdev, int en)  
  191. {  
  192.     void __iomem *base = s3c_rtc_base;  
  193.     unsigned int tmp;  
  194.   
  195.     if (s3c_rtc_base == NULL)  
  196.         return;  
  197.   
  198.     if (!en) {  
  199.         tmp = readb(base + S3C2410_RTCCON);  
  200.         writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON);  
  201.   
  202.         tmp = readb(base + S3C2410_TICNT);  
  203.         writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT);  
  204.     } else {  
  205.         /* re-enable the device, and check it is ok */  
  206.   
  207.         if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){  
  208.             dev_info(&pdev->dev, "rtc disabled, re-enabling\n");  
  209.   
  210.             tmp = readb(base + S3C2410_RTCCON);  
  211.             writeb(tmp|S3C2410_RTCCON_RTCEN, base+S3C2410_RTCCON);  
  212.         }  
  213.   
  214.         if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){  
  215.             dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n");  
  216.   
  217.             tmp = readb(base + S3C2410_RTCCON);  
  218.             writeb(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON);  
  219.         }  
  220.   
  221.         if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){  
  222.             dev_info(&pdev->dev, "removing RTCCON_CLKRST\n");  
  223.   
  224.             tmp = readb(base + S3C2410_RTCCON);  
  225.             writeb(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON);  
  226.         }  
  227.     }  
  228. }  
  229.   
  230. static int __devexit s3c_rtc_remove(struct platform_device *dev)  
  231. {  
  232.     struct rtc_device *rtc = platform_get_drvdata(dev);  
  233.   
  234.     platform_set_drvdata(dev, NULL);  
  235.     rtc_device_unregister(rtc);  
  236.   
  237.     s3c_rtc_setpie(&dev->dev, 0);  
  238.     s3c_rtc_setaie(0);  
  239.   
  240.     iounmap(s3c_rtc_base);  
  241.     release_resource(s3c_rtc_mem);  
  242.     kfree(s3c_rtc_mem);  
  243.   
  244.     return 0;  
  245. }  
  246.   
  247. static int __devinit s3c_rtc_probe(struct platform_device *pdev)  
  248. {  
  249.     struct rtc_device *rtc;  
  250.     struct resource *res;  
  251.     int ret;  
  252.   
  253.     pr_debug("%s: probe=%p\n", __func__, pdev);  
  254.   
  255.     /* find the IRQs */  
  256.   
  257.     s3c_rtc_tickno = platform_get_irq(pdev, 1);  
  258.     if (s3c_rtc_tickno < 0) {  
  259.         dev_err(&pdev->dev, "no irq for rtc tick\n");  
  260.         return -ENOENT;  
  261.     }  
  262.   
  263.     /* get the memory region */  
  264.   
  265.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  266.     if (res == NULL) {  
  267.         dev_err(&pdev->dev, "failed to get memory region resource\n");  
  268.         return -ENOENT;  
  269.     }  
  270.   
  271.     s3c_rtc_mem = request_mem_region(res->start,  
  272.                      res->end-res->start+1,  
  273.                      pdev->name);  
  274.   
  275.     if (s3c_rtc_mem == NULL) {  
  276.         dev_err(&pdev->dev, "failed to reserve memory region\n");  
  277.         ret = -ENOENT;  
  278.         goto err_nores;  
  279.     }  
  280.   
  281.     s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);  
  282.     if (s3c_rtc_base == NULL) {  
  283.         dev_err(&pdev->dev, "failed ioremap()\n");  
  284.         ret = -EINVAL;  
  285.         goto err_nomap;  
  286.     }  
  287.   
  288.     /* check to see if everything is setup correctly */  
  289.   
  290.     s3c_rtc_enable(pdev, 1);  
  291.   
  292.     pr_debug("s3c2410_rtc: RTCCON=%02x\n",  
  293.          readb(s3c_rtc_base + S3C2410_RTCCON));  
  294.   
  295.     s3c_rtc_setfreq(&pdev->dev, 1);  
  296.   
  297.     /* register RTC and exit */  
  298.   
  299.     rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,  
  300.                   THIS_MODULE);  
  301.   
  302.     if (IS_ERR(rtc)) {  
  303.         dev_err(&pdev->dev, "cannot attach rtc\n");  
  304.         ret = PTR_ERR(rtc);  
  305.         goto err_nortc;  
  306.     }  
  307.   
  308.     rtc->max_user_freq = 128;  
  309.   
  310.     platform_set_drvdata(pdev, rtc);  
  311.     return 0;  
  312.   
  313.  err_nortc:  
  314.     s3c_rtc_enable(pdev, 0);  
  315.     iounmap(s3c_rtc_base);  
  316.   
  317.  err_nomap:  
  318.     release_resource(s3c_rtc_mem);  
  319.  err_nores:  
  320.     return ret;  
  321. }  
  322.   
  323. static struct platform_driver s3c2410_rtc_driver = {  
  324.     .probe      = s3c_rtc_probe,  
  325.     .remove     = __devexit_p(s3c_rtc_remove),  
  326.     .driver     = {  
  327.         .name   = "s3c2410-rtc",  
  328.         .owner  = THIS_MODULE,  
  329.     },  
  330. };  
  331.   
  332. static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics\n";  
  333.   
  334. static int __init s3c_rtc_init(void)  
  335. {  
  336.     printk(banner);  
  337.     return platform_driver_register(&s3c2410_rtc_driver);  
  338. }  
  339.   
  340. static void __exit s3c_rtc_exit(void)  
  341. {  
  342.     platform_driver_unregister(&s3c2410_rtc_driver);  
  343. }  
  344.   
  345. module_init(s3c_rtc_init);  
  346. module_exit(s3c_rtc_exit);  
  347.   
  348. MODULE_DESCRIPTION("My s3c2440 RTC Driver");  
  349. MODULE_AUTHOR("YanMing - [email protected]");  
  350. MODULE_LICENSE("GPL");  
  351. MODULE_ALIAS("platform:s3c2410-rtc");  

Makefile文件

[plain] view plain copy print ?
  1. obj-m := rtc.o  
  2. KERNELDIR ?= /arm/linux-2.6.28.7-2440  
  3. PWD := $(shell pwd)  
  4. default:  
  5.     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules  
  6. clean:  
  7.     rm -f *.o *.ko *.order *.symvers  

make后在目录下生成rtc.ko驱动,利用NFS挂在到目标板,insmod rtc.ko驱动就可以加载,执行hwclock命令,查看是否可以读取硬件的RTC。

你可能感兴趣的:(分析,时钟,驱动,RTC)