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. //申请内存区域,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 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){//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 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。
- ARM-Linux驱动移植--RTC(实时时钟)移植
- ARM-Linux驱动移植--RTC(实时时钟)移植
- 【原创】ARM LINUX 外部RTC实时时钟驱动移植(RX8025)
- s3c2440 RTC(实时时钟) 驱动移植
- Arm9+linux fl2440 驱动移植之RTC(实时时钟)和USB host
- ARM-Linux驱动--RTC(实时时钟)驱动分析
- ARM-Linux驱动--RTC(实时时钟)驱动分析 .
- linux RTC驱动移植
- Linux驱动之RTC移植
- linux 实时时钟(RTC)驱动
- linux 实时时钟(RTC)驱动
- linux 实时时钟(RTC)驱动
- Linux 实时时钟(RTC)驱动调试
- linux 实时时钟(RTC)驱动 .
- linux 实时时钟(RTC)驱动
- linux 实时时钟(RTC)驱动
- linux 实时时钟(RTC)驱动 .
- linux 实时时钟(RTC)驱动
- Python 利用urllib2 lxml 抓取网页信息
- splay
- postfix make出错
- 动作按钮【Action Button】-可没你想的那样简单
- 设计模式-创建型:单例模式(1)
- ARM-Linux驱动移植--RTC(实时时钟)移植
- 控件视图以及控件视图的事件处理
- JAVA String.format 方法使用介绍
- 瞬间的细节:触摸与点击的不同
- 用户界面设计中“状态”和“动作”的表达
- 语音特征参数MFCC提取过程详解
- 我的第一篇博客
- Oracle死锁查询及处理
- Python 如何根据关键词查询包名