ARM-Linux driver--RTC (real-time clock) driver analysis

Publisher:快乐舞蹈Latest update time:2016-05-05 Source: eefocusKeywords:ARM Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere
Hardware platform: FL2440 (S3C2440)

Kernel version: Linux 2.6.28

Host platform: Ubuntu 11.04

Kernel version: Linux 2.6.39

Cross compiler version: arm-linux-gcc 3.4.1

Original work, please indicate the source when reprinting http://blog.csdn.net/yming0221/article/details/6584285

1. Real-time clock overview

The real-time clock (RTC) unit can continue to keep time using a button battery when the power is off. The RTC uses the STRB/LDRB ARM operation to transmit 8 bits of binary decimal data to the CPU. The data includes time information such as seconds, minutes, hours, date, day, month, and year. The alarm function can be executed.

2. Real-time clock operation

Below is the circuit diagram of the RTC module

3. Introduction to RTC registers

Real Time Clock Control Register (RTCCON) - REAL TIME CLOCK CONTROL REGISTER

Tick ​​Time Count Register (TICNT) - TICK TIME COUNT REGISTER

RTC Alarm Control Register (RTCALM) - RTC ALARM CONTROL REGISTER

Alarm Seconds Register (ALMSEC) - ALARM SECOND DATA REGISTER

Alarm Minute Count Register (ALMMIN) - ALARM MIN DATA REGISTER

ALARM HOUR DATA REGISTER

ALARM DATE DATA REGISTER

Alarm Month Data Register (ALMMON) - ALARM MON DATA REGISTER 

ALARM YEAR DATA REGISTER

The format of the BCD data register is the same as the alarm register structure, except that the corresponding addresses are different.

BCD Second Register (BCDSEC) - BCD SECOND REGISTER Address: 0x57000070 (L) 0x57000073 (B)

BCD MINUTE REGISTER Address: 0x57000074 (L) 0x57000077 (B)

BCD Hour Register (BCDHOUR) - BCD HOUR REGISTER Address: 0x57000078 (L) 0x5700007B (B)

BCD Date Register (BCDDATE) - BCD DATE REGISTER Address: 0x5700007C (L) 0x5700007F (B)

BCD Day Register (BCDDAY) - BCD DAY REGISTER Address: 0x57000080 (L) 0x57000083 (B)

BCD Month Register (BCDMON) - BCD MONTH REGISTER Address: 0x57000084 (L) 0x57000087 (B)

BCD Year Register (BCDYEAR) - BCD YEAR REGISTER Address: 0x57000088 (L) 0x5700008B (B)

4. Driver Example Analysis

In order to make the driver easier to understand, the current RTC driver only completes the timing function, without adding the corresponding alarm function or power management function. The missing functions will be improved in the future.

Let's first have an overall understanding of the driver:

First is the structure of the RTC driver, in /include/linux/platform_device.h, as follows

 

[cpp] view plain copy
 
  1. 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. };  
Define the corresponding structure in the driver

 

 

[cpp] view plain copy
 
  1. static struct platform_driver s3c2410_rtc_driver = {  
  2.     .probe = s3c_rtc_probe, //RTC detection function  
  3.     .remove = __devexit_p(s3c_rtc_remove), //RTC removal function  
  4.     .driver     = {  
  5.         .name   = "s3c2410-rtc",  
  6.         .owner  = THIS_MODULE,  
  7.     },  
  8. };  
The following are the initialization and exit functions of the driver in the driver

 

 

[cpp] view plain copy
 
  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. }  

The platform_driver_register() and platform_driver_unregister() functions are implemented in /drivers/base/platform.c.

It can be seen that the role of the platform_driver_register() function is to provide interface functions for probe, remove, etc. in the driver in platform_driver

 

 

[cpp] view plain copy
 
  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);//Register the old driver  
  17. }  
[cpp] view plain copy
 
  1. void platform_driver_unregister(struct platform_driver *drv)  
  2. {  
  3.     driver_unregister(&drv->driver);  
  4. }  

Next is the RTC platform driver detection function s3c_rtc_probe. The function definition below uses __devinit to enable the compiler to optimize the code and place it in the correct memory location to reduce memory usage and improve kernel efficiency.

After the probe function receives the parameter plarform_device, it needs to extract the required information from it. It generally obtains relevant information by calling functions such as platform_get_resource and platform_get_irq provided by the kernel. For example, after obtaining the starting address of the device through platform_get_resource, operations such as request_mem_region and ioremap can be performed on it so that the application can operate it. After obtaining the interrupt number of the device through platform_get_irq, the request_irq function can be called to request an interrupt from the system. These operations are generally completed in the device driver.

 

[cpp] view plain copy
 
  1. static int __devinit s3c_rtc_probe(struct platform_device *pdev)  
  2. {  
  3.     struct rtc_device *rtc; //define rtc_device structure, defined in /include/linux/rtc.h  
  4.     struct resource *res; //define resource structure, defined in /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); //Get the interrupt number in the platform device defined by the system  
  12.     if (s3c_rtc_tickno < 0) {//Exception handling  
  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); //Get the IO resources used by the RTC platform  
  20.     if (res == NULL) {  
  21.         dev_err(&pdev->dev, "failed to get memory region resource\n");  
  22.         return -ENOENT;  
  23.     }  
  24.        //Apply for memory area, res is struct resource type, see the back of this function  
  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) {//Error in applying for memory  
  30.         dev_err(&pdev->dev, "failed to reserve memory region\n");  
  31.         right = -ENOENT;  
  32.         goto err_nores;  
  33.     }  
  34.         //Map register addresses into virtual addresses for easy access  
  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 = -ONE;  
  39.         goto err_nomap;  
  40.     }  
  41.   
  42.     /* check to see if everything is setup correctly */  
  43.   
  44.     s3c_rtc_enable(pdev, 1); //Set the RTCCON register, see the function implementation below for details  
  45.   
  46.     pr_debug("s3c2410_rtc: RTCCON=%02x\n",  
  47.          readb(s3c_rtc_base + S3C2410_RTCCON));  
  48.   
  49.     s3c_rtc_setfreq(&pdev->dev, 1);//For details, see the function implementation below  
  50.   
  51.     /* register RTC and exit */  
  52.   
  53.     rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,  
  54.                   THIS_MODULE); //Register RTC as RTC device, where s3c_rtcops is defined as follows  
  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; //Set the user's maximum relative value of the beat time count value of the RTC beat time count register TICNT  
  63.         //Pass the RTC class device data to the system device in /include/linux/platform_device.h  
[cpp] view plain copy
 
  1. //#define platform_set_drvdata(_dev,data) dev_set_drvdata(&(_dev)->dev, (data)), this function is defined in /include/linux/device.h, see below this function  
[cpp] view plain copy
 
  1. platform_set_drvdata(pdev, rtc);  
[cpp] view plain copy
 
  1. return 0;  
[cpp] view plain copy
 
  1. //Exception handling  
  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 right;  
  10. }  
The following is the definition of struct resource structure in /include/linux/ioport.h
[cpp] view plain copy
 
  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. };  
This is the function definition of dev_set_drvdata():

 

 

[cpp] view plain copy
 
  1. static inline void dev_set_drvdata(struct device *dev, void *data)  
  2. {  
  3.     dev->driver_data = data;  
  4. }  
Next are the two functions s3c_rtc_enable() and s3c_rtc_setfreq() used in the s3c_rtc_probe() function.

 

  1. static void s3c_rtc_enable(struct platform_device *pdev, int en)  
  2. {  
 
  1. void __iomem *base = s3c_rtc_base; //The function of __iomem is to enable the compiler to better optimize compilation  
  2. unsigned int tmp;  
  3.   
  4. if (s3c_rtc_base == NULL)  
  5.     return;  
  6.        //en is passed as a parameter. If en==0, the situation before turning off the power  
  7. if (!en) {  
  8.     tmp = readb(base + S3C2410_RTCCON);  
 
  1.         writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON); //Set the RTCCON register to disable RTC. Please refer to the definition of registers in the data sheet.  
  2.   
  3.         tmp = readb(base + S3C2410_TICNT);  
  4.         writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT); //Set TICNT register to enable the beat time interrupt  
  5.     } else {  
  6.         /* re-enable the device, and check it is ok */  
  7.                 //en!=0 means the system is reset and the RTC driver is enabled again  
  8.         if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){//RTCCON bit 0 is 0, set it to 1 and re-enable  
  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); //Set the second bit of RTCCON to 0, and set the BCD count to mixed BCD count  
  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 clock counter reset  
  27.         }  
  28.     }  
  29. }  
[cpp] view plain copy
 
  1. static int s3c_rtc_setfreq(struct device *dev, int freq) //Set the beat time count value  
  2. {  
  3.     unsigned int tmp;  
  4.   
  5.     spin_lock_irq(&s3c_rtc_pie_lock); //Get spin lock and mutually exclusive access to resources  
  6.   
  7.     tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE; //Beat time enable is valid  
  8.     tmp |= (128 / freq)-1;  
  9.   
  10.     writeb(tmp, s3c_rtc_base + S3C2410_TICNT);  
  11.     spin_unlock_irq(&s3c_rtc_pie_lock);//Unlock  
  12.   
  13.     return 0;  
  14. }  
Next is the operation of the RTC device class.

 

The following is rtc_class_ops, which is a structure defined in the core part of the RTC driver to operate the RTC device class, similar to the meaning of file_operations in the character device driver to operate the character device. This structure is defined in rtc.h. The operations on the RTC mainly include opening, closing, setting or getting time, setting or getting alarms, setting the beat time count value, etc. The implementation of the interface functions in this structure are all below

 

  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 opens the device function s3c_rtc_open()
  1. static int s3c_rtc_open(struct device *dev)  
  2. {  
  3.     struct platform_device *pdev = to_platform_device(dev); //Get the RTC device class data from the platform device  
  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); // Request interrupt  
  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 right;  
  17. }  
RTC TICK beat time interrupt service routine
 
  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 close device function s3c_rtc_release()

 

  1. static void s3c_rtc_release(struct device *dev)  
  2. {  
  3.     struct platform_device *pdev = to_platform_device(dev); //Get the RTC device class data from the platform device  
  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); //Function definition see below  
  10.     free_irq(s3c_rtc_tickno, rtc_dev);  
  11. }  
The s3c_rtc_setpie() function is mainly used to set the highest bit of the TICNT register according to the parameter. If the parameter is 0, it is disabled, and if the parameter is 1, it is enabled.
 
  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; //Read the value of TICNT and clear the highest bit to 0  
  9.   
  10.     if (enabled)  
  11.         tmp |= S3C2410_TICNT_ENABLE;  
  12.   
  13.     writeb(tmp, s3c_rtc_base + S3C2410_TICNT); //Write the new value after calculation  
  14.     spin_unlock_irq(&s3c_rtc_pie_lock);  
  15.   
  16.     return 0;  
  17. }  
The following two functions are used to set and read the time of the BCD register. The logic is very simple, just reading and setting the value of the corresponding register

 

  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 -SINGLE SELECT;  
  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. }  
At this point, the timing function of the RTC driver has been implemented, but the alarm function has not yet been completed. Below is the source code of this driver

 

  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. #include   
  7. #include   
  8. #include   
  9. #include   
  10. #include   
  11.   
  12. #include   
  13. #include   
  14. #include   
  15. #include   
  16. #include   
  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_GENERAL;  
  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 -SINGLE SELECT;  
  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 right;  
  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.         right = -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 = -ONE;  
  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 right;  
  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

  1. obj-m := rtc.o  
  2. KERNEL ?= /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  

After make, the rtc.ko driver is generated in the directory. Use NFS to mount it to the target board, and then use insmod rtc.ko to load the driver. Execute the hwclock command to check whether the hardware RTC can be read.
Keywords:ARM Reference address:ARM-Linux driver--RTC (real-time clock) driver analysis

Previous article:An example of RTC clock driver development on S3C2440
Next article:ARM-Linux driver transplantation--RTC (real-time clock) transplantation

Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号