ARM-Linux驱动移植--RTC(实时时钟)移植

来源:互联网 发布:适合女生喝的红酒知乎 编辑:程序博客网 时间:2024/04/28 06:50

硬件平台: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模块的电路图

3、RTC寄存器介绍

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

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

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

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

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

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

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

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

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

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 plaincopy

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 plaincopy

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 plaincopy

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 plaincopy

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 plaincopy

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 plaincopy

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.         //申请内存区域,resstruct 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);//注册RTCRTC设备,其中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 plaincopy

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 plaincopy

1.    platform_set_drvdata(pdev, rtc);  

[cpp] view plaincopy

1.    return 0;  

[cpp] view plaincopy

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中structresource结构体定义

[cpp] view plaincopy

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 plaincopy

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 plaincopy

1.    static void s3c_rtc_enable(struct platform_device *pdev, int en)  

2.    {  

[cpp] view plaincopy

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 plaincopy

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){//RTCCON0位为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);//设置RTCCON2位为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 plaincopy

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 plaincopy

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 plaincopy

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 plaincopy

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 plaincopy

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 plaincopy

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 plaincopy

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 plaincopy

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 - yming0221@gmail.com");  

350.  MODULE_LICENSE("GPL");  

351.  MODULE_ALIAS("platform:s3c2410-rtc");  



Makefile文件

[plain] view plaincopy

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。

0 0
原创粉丝点击