linux Medium rtc Drive in drivers/rtc Next , It contains many development platforms RTC drive , We are here with S3C24xx Mainly , So it's RTC Drive as rtc-s3c.c

1. Get into ./drivers/rtc/rtc-s3c.c

Let's go to the entry function first , As shown in the figure below :
 Insert picture description here
Here's a sign up “s3c2410-rtc” Name of the platform device driver

and “s3c2410-rtc” Platform equipment for , stay ./arch/arm/plat-s3c24xx/dev.c It's defined in , It's just that there's no registration , As shown in the figure below :
 Insert picture description here
When the kernel matches a platform device with the same name , Will call .probe function , And then we go to s3c2410_rtcdrv->probe Function , What did you do :

static int s3c_rtc_probe(struct platform_device *pdev){struct rtc_device *rtc;           //rtc Equipment structure struct resource *res;int ret;s3c_rtc_tickno = platform_get_irq(pdev, 1);      // obtain IRQ_TICK Beat interrupt resources s3c_rtc_alarmno = platform_get_irq(pdev, 0);   // obtain IRQ_RTC Alarm interrupt resource res = platform_get_resource(pdev, IORESOURCE_MEM, 0);   // Get memory resources s3c_rtc_mem = request_mem_region(res->start,res->end-res->start+1,pdev->name);// Apply for memory resources s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);     // Remapping memory s3c_rtc_enable(pdev, 1);       // Set hardware related settings , Can make RTC register s3c_rtc_setfreq(s3c_rtc_freq);      // Set up TICONT register , Enable beat interrupt , Set the beat count value /*1. register RTC equipment */rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,THIS_MODULE);  rtc->max_user_freq = 128;platform_set_drvdata(pdev, rtc);  return 0;}

Obviously, in the end rtc_device_register() Function to register with the kernel rtc_device equipment , Successful registration will return a registered rtc_device,

and s3c_rtcops It's a rtc_class_ops Structure , It's how to operate it rtc Function of device , Like reading and writing RTC Time , Read and write alarm clock time, etc , After registration , Are saved in rtc_device->ops in

The function is in drivers/rtc/Class.c It's defined in the file .Class.c The document mainly defines RTC Subsystem , And kernel initialization , Will enter Class.c, Get into rtc_init()->rtc_dev_init(), To register the character device :

 err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");    // RTC_DEV_MAX=16, It means to register only 0~15 Individual device number , The equipment number is saved in rtc_devt in 

2. It is associated with rtc_device_register() Function registration RTC equipment , What does it matter ?

Next, let's look at rtc_device_register(), The code is as follows :

struct rtc_device *rtc_device_register(const char *name, struct device *dev,const struct rtc_class_ops *ops,struct module *owner){   struct rtc_device *rtc;    // Define a rtc_device Structure    ... ...   rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);  // Distribute rtc_device The structure is a global variable
 
       /* Set up rtc_device*/ rtc->id = id;   rtc->ops = ops;            // take s3c_rtcops Save in rtc_device->ops in    rtc->owner = owner;   rtc->max_user_freq = 64;   rtc->dev.parent = dev;   rtc->dev.class = rtc_class;   rtc->dev.release = rtc_device_release;   ... ...   rtc_dev_prepare(rtc);                   //1. Prepare ahead of time , initialization cdev Structure    ... ...   rtc_dev_add_device(rtc);               //2. stay /dev Create rtc Related documents , take cdev Add to the system    rtc_sysfs_add_device(rtc);             // stay /sysfs Create rtc Related documents    rtc_proc_add_device(rtc);             // stay /proc Create rtc Related documents    ... ... return rtc;}

above rtc_dev_prepare(rtc) and rtc_dev_add_device(rtc) We mainly do the following two ( be located ./drivers/rtc/rtc-dev.c):

cdev_init(&rtc->char_dev, &rtc_dev_fops);          // binding file_operations  cdev_add(&rtc->char_dev, rtc->dev.devt, 1); // register rtc->char_dev Character device , Add a slave device to the system 

Obviously, the registered character device here , It's exactly the same process as we talked about in the last section .
therefore “s3c2410-rtc” Platform device driven .probe I mainly did the following things :

1. Set up RTC Related registers
2. Distribute rtc_device Structure
3. Set up rtc_device Structure
-> 3.1 take struct rtc_class_ops s3c_rtcops Put in rtc_device->ops, Realize to RTC Read and write time and other operations
4. register rtc->char_dev Character device , And the operation structure of the character device is : struct file_operations rtc_dev_fops

3. above file_operations Operating structure rtc_dev_fops Members of , As shown in the figure below :

 Insert picture description here

3.1 When we use the application layer open(”/dev/rtcXX”) when , Will call rtc_dev_fops-> rtc_dev_open(), Let's see how open Of :

static int rtc_dev_open(struct inode *inode, struct file *file){
   struct rtc_device *rtc = container_of(inode->i_cdev,struct rtc_device, char_dev);// Get the corresponding rtc_device
   const struct rtc_class_ops *ops = rtc->ops;               // In the end, it's equal to s3c_rtcops
   file->private_data = rtc;                     // Set up file The private members of a structure are equal to rtc_device, Re execution ioctl Wait for the function , You can extract it directly file->private_data that will do
   err = ops->open ? ops->open(rtc->dev.parent) : 0;  // call s3c_rtcops->open
   mutex_unlock(&rtc->char_lock);
   return err;}

Obviously, in the end, it's still calling rtc_device Under the s3c_rtcops->open:
 Insert picture description here
and s3c_rtc_open() Function is mainly applied for two interrupts , An alarm clock interrupts , A timing interrupt :

static int s3c_rtc_open(struct device *dev){     
 struct platform_device *pdev = to_platform_device(dev);  struct rtc_device *rtc_dev = platform_get_drvdata(pdev);      
 int ret;
 ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,IRQF_DISABLED,  "s3c2410-rtc alarm", rtc_dev);        // Apply for alarm clock interrupt                         if (ret) {  dev_err(dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);  return ret;   }
 
 ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,IRQF_DISABLED,  "s3c2410-rtc tick", rtc_dev);// Apply for time interruption       if (ret) {  dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);  goto tick_err;   }   return ret;
 tick_err:   free_irq(s3c_rtc_alarmno, rtc_dev);   return ret;}

3.2 When we use the application layer open after , Use ioctl(int fd, unsigned long cmd, …) when , Will call rtc_dev_fops-> rtc_dev_ioctl ():

static int rtc_dev_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg){struct rtc_device *rtc = file->private_data;  // extract rtc_device
 void __user *uarg = (void __user *) arg;
  ... ...
 switch (cmd) {   case RTC_EPOCH_SET:   case RTC_SET_TIME:      // Setup time   if (!capable(CAP_SYS_TIME)) return -EACCES;  break;   case RTC_IRQP_SET:   // Change the interrupt trigger speed    ... ...}   ... ...   switch (cmd) {   case RTC_ALM_READ:    // Read the alarm time   err = rtc_read_alarm(rtc, &alarm);              // call s3c_rtcops-> read_alarm  if (err < 0) return err;  if (copy_to_user(uarg, &alarm.time, sizeof(tm)))  // Long time data  return -EFAULT; break;   case RTC_ALM_SET:              // Set the alarm time  ,  call s3c_rtcops-> set_alarm  ... ...   case RTC_RD_TIME:              // read RTC Time ,  call s3c_rtcops-> read_alarm  ... ...   case RTC_SET_TIME:      // Write RTC Time , call s3c_rtcops-> set_time  ... ...   case RTC_IRQP_SET:      // Change the interrupt trigger frequency , call s3c_rtcops-> irq_set_freq  ... ...}

Finally, call s3c_rtcops Next member function , We use s3c_rtcops-> read_alarm() Function as an example , See how to read the time :

static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm){   unsigned int have_retried = 0;   void __iomem *base = s3c_rtc_base;    // obtain RTC Related register base address retry_get_time:   /* Year of acquisition , month , Japan , when , branch , Second register */   rtc_tm->tm_min  = readb(base + S3C2410_RTCMIN);     
       rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);   rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);   rtc_tm->tm_mon  = readb(base + S3C2410_RTCMON);   rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);   rtc_tm->tm_sec  = readb(base + S3C2410_RTCSEC);  
       /*   Judge if the second register is 0, A minute has passed , So many hours , God , month , The value in the register may have changed , You need to reread the values of these registers */if (rtc_tm->tm_sec == 0 && !have_retried) {  have_retried = 1;  goto retry_get_time;   }   /* The register value that will be obtained , Convert to real time data */   BCD_TO_BIN(rtc_tm->tm_sec);   BCD_TO_BIN(rtc_tm->tm_min);   BCD_TO_BIN(rtc_tm->tm_hour);   BCD_TO_BIN(rtc_tm->tm_mday);   BCD_TO_BIN(rtc_tm->tm_mon);   BCD_TO_BIN(rtc_tm->tm_year);rtc_tm->tm_year += 100;    // What's in the memory is from 1900 When the year began , So add 100 rtc_tm->tm_mon -= 1;return 0;}

Again , stay s3c_rtcops-> set_time() In the function , Also write to the related register RTC Time

therefore , The summary is as follows :

rtc_device->char_dev: Character device , And application layer 、 And lower level functions
rtc_device->ops: Lower level operation functions , Operate hardware related registers directly , By rtc_device->char_dev call

4. Modify the kernel

We use it on the board ls /dev/rtc*, The character device could not be found , Because the kernel only defines s3c_device_rtc This RTC Platform equipment , Not registered , So the platform driver is not matched , Next, let's modify the register array in the kernel

4.1 Get into arch/arm/plat-s3c24xx/Common-smdk.c

As shown in the figure below , stay smdk_devs[] in , add to RTC It's just a platform device , When the kernel starts , The array is called , The inside platform_device Register it all
 Insert picture description here
And then Common-smdk.c Instead of the kernel directory of the virtual machine Common-smdk.c, again make uImage Just compile the kernel

5. test run

After starting , As shown in the figure below , Use ls /dev/rtc*, I found it rtc0 This character device
 Insert picture description here

5.1 Next , It's set up RTC Time

stay linux There are two clocks in it :

Hardware clock (2440 The clock in the register )、 The system clock ( The clock in the kernel )

So there are two different orders : date command 、hwclock command

5.2 date Command to use :

Input date Look at the system clock :
 Insert picture description here
If you find it inconvenient, you can also specify the format to display the date , You need to precede the string with ”+”

As shown in the figure below , Input date “+ %Y/%m/%d %H:%M:%S”
 Insert picture description here
%M: For seconds
%m: Represents the month
%Y: Indicate year , When only the last two digits are needed , Input %y that will do
date The format of the command setting time is as follows :

date Month day hour year . second

As shown in the figure below , Input date 111515292017.20, You can set the system clock
 Insert picture description here

5.3 hwclock Command to use :

Common parameters are as follows

-r, --show Read and print the hardware clock (read hardware clock and print result )
-s, --hctosys Synchronize the hardware clock to the system clock (set the system time from the hardware clock )
-w, --systohc Synchronize the system clock to the hardware clock (set the hardware clock to the current system time )

As shown in the figure below , Use hwclock -w, To synchronize the hardware clock

 Insert picture description here
And then after restarting , Use date command , See the time is normal