Linux RTC Driver is relatively simple, it can be used as a normal character device, or a misc device, or a platform device, which is not related, mainly to rtc_ops this file operation structure of the member population, Here are mainly related to two aspects more important: 1. There are two kinds of clocks, such as hardware clock and system clock, in Linux. The hardware clock is the clock device on the motherboard, which is usually the clock that can be set on the BIOS screen. The system clock refers to the clock in the kernel. When Linux starts, the system clock reads the settings of the hardware clock, and then the system clock runs independently. All Linux-related instructions and functions are programmed to read the system clock. The setting of the system clock is our usual date command, and the RTC driver we write is for the hardware clock, it has its own command hwclock, so using the date command is not possible to call to our driver (at this point it began to depressed me, after writing the driver, Silly to test with the date command, and of course the result is nothing), we can update the RTC clock with some instructions from hwclock-that is, the interaction between the system clock and the hardware clock. HWCLOCK–R display hardware clock and date The hwclock–s adjusts the system clock to match the current hardware clock. The hwclock–w adjusts the hardware clock to coincide with the current system clock. 2. the 2nd is the interaction between the kernel space and user space, at the end of the system start, we are actually in the user state, so we use the command input content is also in the user state, and our drive is in the kernel state, the kernel state and the user state is not visible to each other, So we need a special function to achieve the interaction of these two forms, which is the following two functions: Copy_from_user (from user state to kernel state) Copy_to_user (from kernel state to user state) Of course, these two functions need to be implemented in the kernel driver. The most basic two commands for RTC are set time, read time. Set time-The set time will call the system default Rtc_set_time, it is clear that the user state of the user to set their own time information to pass to the kernel state, Case Rtc_set_time: { struct Rtc_time rtc_tm; if (Copy_from_user (&rtc_tm, (struct rtc_time*) arg, sizeof (struct rtc_time))) Return-efault; Sep4020_rtc_settime (&RTC_TM);//Pass the user-state information to the set time function return 0; } Read time-set time will call the system default Rtc_rd_time, it is clear that the kernel will need to drive the chip clock out, and pass to the user state Case Rtc_rd_time:/* Read the time/date from RTC */ { Sep4020_rtc_gettime (&septime);//Read the chip clock with a read function driven Copy_to_user (void *) arg, &septime, sizeof septime);//pass to User state } --------------------------------------------------------------------------------------------------------------- ----- start by figuring out the role RTC has in kernel:
The Linux system has two clocks: one is a motherboard battery-powered "Real time Clock" called RTC or a CMOS clock, Hardware clock. When the operating system shuts down, use this to record the time, but for the running system is not the time. The other time is "System clock" also called the kernel clock or software clock, is the software according to time interrupt to count, The kernel clock does not exist in the case of system shutdown, so when the operating system starts, the kernel clock is to read the RTC time To make time synchronization. And when the system shuts down, the system time is written back to the RTC for synchronization.
As mentioned earlier, there are only two times when the Linux kernel is interoperable with RTC: 1) The kernel reads the start time and date from RTC at startup; 2) The kernel writes the time and date back to the RTC when needed.
When the system starts, the kernel initializes the kernel clock by reading the RTC, also called the wall time, which is placed in the Xtime variable. The current time of day (the wall time) was defined in KERNEL/TIMER.C: struct Timespec xtime; The TIMESPEC data structure is defined in as: struct Timespec { time_t tv_sec; /* seconds */ Long tv_nsec; /* nanoseconds */ }; 1: At what point does the system start reading the RTC value and setting the kernel clock for time synchronization ? The most likely location to read the RTC settings kernel clock should be within the Time_init function in arch/arm/kernel/time.c. The time.c is the clock-driven part of the system. The Time_init function is called by the Start_kernel function in the INIT/MAIN.C when the system is initialized. The X86 architecture is where the RTC value is read and the system clock xtime initialized.
The Time_init code for the ARM architecture is as follows: /* ARCH/ARM/KERNEL/TIME.C */ void __init time_init (void) { if (System_timer->offset = = NULL) System_timer->offset = Dummy_gettimeoffset; System_timer->init (); #ifdef CONFIG_NO_IDLE_HZ if (System_timer->dyn_tick) System_timer->dyn_tick->lock = spin_lock_unlocked; #endif }
The above System_timer->init () actually executes the INIT function defined by the clock-driven architecture-related (platform-specific) section, and if the s3c2410 platform is executed, the arch/arm/mach-s3c2410/ The S3c2410_timer_init function defined in time.c. But S3c2410_timer_init () did not read the RTC code. The entire clock-driven initialization process roughly executes the code. Since the RTC value was not read and the kernel clock was set during the system clock driver initialization, where would it be set?
I searched and found that the kernel seemed to have RTC-related code only in Arch/cris/kernel/time.c, as follows: /* ARCH/CRIS/KERNEL/TIME.C */ /* Grab the time from the RTC chip */ Functions for reading RTC unsigned long get_cmos_time (void) { unsigned int year, Mon, day, Hour, Min, sec; SEC = Cmos_read (rtc_seconds); min = Cmos_read (rtc_minutes); hour = Cmos_read (rtc_hours); Day = Cmos_read (rtc_day_of_month); Mon = Cmos_read (rtc_month); ............ Return Mktime (Year, Mon, day, Hour, Min, sec); }
This function is called within the Update_xtime_from_cmos: void Update_xtime_from_cmos (void) { if (HAVE_RTC) { Xtime.tv_sec = Get_cmos_time (); xtime.tv_nsec = 0; } }
There are also functions to set up RTC int Set_rtc_mmss (unsigned long nowtime); /* Write time into RTC chip */
But I added the PRINTK test, as if arch/cris/kernel/time.c this file and these two functions are only applicable with X86? ARM platform does not go this way when it is started. Therefore, these functions are not performed. When the arm platform starts, where does the system read the value of RTC and initialize the kernel clock (walltime)?
Resolved: The embedded Linux kernel (ARM) executes the/etc/init.d/hwclock.sh script at system startup, which invokes the Hwclock applet to read the values of the RTC and set the system clock. (In other words, it depends on whether you have such a script in the file system you're making) /*/etc/init.d/hwclock.sh */ Daemon1=/sbin/hwclock Start () { Local RET error= [!-f/etc/adjtime] && echo "0.0 0 0.0" >/etc/adjtime Log_status_msg "Setting the System clock using the Hardware clock as reference ..."-N # Copies Hardware clock time to System clock using the correct # TimeZone for hardware clocks in local time, and sets kernel # timezone. Do not REMOVE. ["$HWCLOCKACCESS" = no] && $DAEMON 1--hctosys $GMT $BADYEAR # # now That/usr/share/zoneinfo should is available, # announce the local time. # Log_status_msg "System Clock set. Local time: ' Date ' Log_status_msg "" return 0 } Hwclock first read the device file is/DEV/RTC, BusyBox inside the Hwclock is implemented as follows: static int XOPEN_RTC (int flags) { int RTC; if (!rtcname) { RTC = Open ("/DEV/RTC", flags); if (RTC >= 0) return RTC; RTC = Open ("/DEV/RTC0", flags); if (RTC >= 0) return RTC; Rtcname = "/DEV/MISC/RTC"; } Return Xopen (Rtcname, flags); }
2. How does the kernel update the RTC clock? SET_RTC in arch/arm/kernel/time.c with a function pointed to by the SET_RTC function pointer /* ARCH/ARM/KERNEL/TIME.C */ /* * Hook for setting the RTC's idea of the current time. */ Int (*SET_RTC) (void); But where does the SET_RTC function pointer initialize? The SET_RTC should be a function associated with the RTC driver. After searching for kernel source code, it seems that the kernel is not initialized elsewhere. To be solved! SET_RTC calls within the DO_SET_RTC static inline void do_set_rtc (void) { ...... if (SET_RTC ()) /* * RTC update failed. Try again in 60s */ Next_rtc_update = xtime.tv_sec + 60; Else Next_rtc_update = xtime.tv_sec + 660; /* Update every ~ minutes by default*/ }
DO_SET_RTC called in Timer_tick. /* * Kernel System timer support. */ void Timer_tick (struct pt_regs *regs) { Profile_tick (cpu_profiling, regs); Do_leds (); DO_SET_RTC (); Do_timer (1); ...... } Timer_tick provides an architecture-independent clock interrupt handler for kernel, which is typically called within an architecture-related clock interrupt handler function. As s3c2410 is: In the ARCH/ARM/MACH-S3C2410/TIME.C * IRQ handler for the timer */ Static irqreturn_t S3c2410_timer_interrupt (int irq, void *dev_id, struct pt_regs *regs) { Write_seqlock (&xtime_lock); Timer_tick (regs); Write_sequnlock (&xtime_lock); return irq_handled; } *nix Timer mechanism standard implementation, generally with Sigalarm + setitimer () to achieve, but this and select/epoll and other logic conflict, I hope that all event notification logic from the Select/epoll trigger. (FreeBSD kqueue default is Filter_timer, how good)
PS./DEV/RTC can only be open () once, so the idea of merging with Epoll above is almost impossible ~
The following is a timer mechanism implemented through the/DEV/RTC (real-time clock) hardware clock. :-) The third parameter of the IOCTL (FD, Rtc_irqp_set, 4) can only be 2, 4, 8, 16, 32, which represents XX Hz.
------------------------------------------------- #include <linux/rtc.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/types.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <time.h> #include <err.h>
int main (void) { unsigned long i = 0; unsigned Long data = 0; int fd = open ("/DEV/RTC", o_rdonly);
if (FD < 0) Errx (1, "open () fail");
/* Set the Freq as 4Hz */ if (IOCTL (FD, Rtc_irqp_set, 4) < 0) Errx (1, "IOCTL (Rtc_irqp_set) fail");
/* Enable periodic interrupts */ if (IOCTL (FD, rtc_pie_on, 0) < 0) Errx (1, "IOCTL (rtc_pie_on)");
for (i = 0; i <; i++) { if (read (FD, &data, sizeof (data)) < 0) Errx (1, "read () error");
printf ("Timer%d\n", Time (NULL)); }
/* Enable periodic interrupts */ if (IOCTL (FD, Rtc_pie_off, 0) < 0) Errx (1, "IOCTL (Rtc_pie_off)");
Close (FD); return 0; } --------------------------------------------------------------------------------------------------------------- ----- User Mode test Code: #include <stdio.h> #include <stdlib.h> #include <linux/rtc.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> int main (void) { int I, FD, retval, irqcount = 0; unsigned long tmp, data; struct Rtc_time rtc_tm;
FD = open ("/DEV/RTC", o_rdonly); if (fd = =-1) { Perror ("/DEV/RTC"); Exit (1); } Alarm example,10 Mintues later Alarm
/* Read the RTC time/date */ retval = IOCTL (FD, Rtc_rd_time, &RTC_TM); if (retval = =-1) { Perror ("IOCTL"); Exit (1); } fprintf (stderr, "current RTC Date/time is%d-%d-%d,%02d:%02d:%02d.\n", Rtc_tm.tm_mday, Rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900, Rtc_tm.tm_hour, Rtc_tm.tm_min, rtc_tm.tm_sec);
Setting Alarm Time Rtc_tm.tm_min + = 10; if (rtc_tm.tm_sec >= 60) { Rtc_tm.tm_sec%= 60; rtc_tm.tm_min++; } if (rtc_tm.tm_min = = 60) { rtc_tm.tm_min = 0; rtc_tm.tm_hour++; } if (Rtc_tm.tm_hour = = 24) Rtc_tm.tm_hour = 0;
Setting retval = IOCTL (FD, Rtc_alm_set, &RTC_TM); if (retval = =-1) { Perror ("IOCTL"); Exit (1); } /* Read the current alarm settings */ retval = IOCTL (FD, Rtc_alm_read, &RTC_TM); if (retval = =-1) { Perror ("IOCTL"); Exit (1); } fprintf (stderr, "Alarm time now set to%02d:%02d:%02d.\n", Rtc_tm.tm_hour, Rtc_tm.tm_min, rtc_tm.tm_sec); /* Enable alarm interrupts after setting*/ retval = IOCTL (FD, rtc_aie_on, 0); if (retval = =-1) { Perror ("IOCTL"); Exit (1); } /* This blocks until the alarm ring causes an interrupt * * retval = Read (fd, &data, sizeof (unsigned long)); if (retval = =-1) { Perror ("read"); Exit (1); } irqcount++; fprintf (stderr, "okay. Alarm rang.\n "); } --------------------------------------------------------------------------------------------------------------- --------------------------------- s3c2410 RTC (Real time Clock) IntroductionThe real-time clock (RTC) unit can operate on a backup battery when the system is powered off. RTC can pass 8-bit data (BCD code) to the CPU by using the two arm instructions of the STRB/LDDRB. Data includes seconds, minutes, hours, dates, days, months, and years. The RTC unit relies on an external 32.768kHZ grown imitations and can also perform alarm functions. Characteristics
BCD code: Seconds, minutes, hours, date, day, month, and year
Run year generator
Alarm function: Alarm interrupt, or wake from Power-off state.
Removed 2000 years of problem
Independent power supply Angle: RTCVDD
A millisecond tick time interrupt is supported for the RTOs kernel tick.
Round reset function.
RTC can produce an alarm signal at a specified time in Power-off mode or normal operation mode. In normal operation mode, the alarm interrupt (almint) is activated and in Power-off mode, the power management wake-up signal (pmwkup) is activated together with the Almint. The RTC Alarm Register (Rtcalm) determines the enable/disable status of the alarm and the conditions for setting the alarm time. The RTC TICK time is used to interrupt the request. The TICNT register has a count value for interrupt enable bits and interrupts. Tick time is interrupted when the count value reaches 0 o'clock. So the period of interruption is as follows: Period = (n+1)/128 sec N:tick Time Count value (1~127) This RTC time tick can be used for the Realtime operating system (RTOS) kernel tick. If the time tick is generated by the RTC tick, then the RTOS-related functions need to always be synchronized with real time. ROUND RESET function The Rund reset function can be performed via the RTC Round Reset Register (rtcrst). The round boundary (+, +, or sec.) of the second carry generation can be selected, and the second value are rounded to Zero in the round reset. For example while the current time is 23:37:47 and the round boundary are selected to the SEC, the round reset changes the C Urrent time to 23:38:00. NOTE All RTC registers has the to is accessed for each byte unit using the STRB and LDRB instructions or char type pointer. --------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------- --- The items related to us in/drivers/rtc/makefile. obj-$ (config_rtc_lib) + = RTC-LIB.O obj-$ (config_rtc_hctosys) + = HCTOSYS.O obj-$ (config_rtc_class) + = RTC-CORE.O Rtc-core-y: = CLASS.O interface.o rtc-core-$ (config_rtc_intf_dev) + = RTC-DEV.O rtc-core-$ (config_rtc_intf_proc) + = RTC-PROC.O rtc-core-$ (CONFIG_RTC_INTF_SYSFS) + = RTC-SYSFS.O obj-$ (config_rtc_drv_s3c) + = RTC-S3C.O Where RTC-LIB.C: provides some functions for converting time formats to each other. HCTOSYS.C: Initializes the system time at startup. RTC Core file: class.c interface.c rtc-dev.c: Character device registration and user layer file Operation function interface. RTC-PROC.C RTC-SYSFS.C rtc-s3c.o:s3c2410 RTC's chip platform driver. /////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////4> in the root file system to do the action, the PC Linux on the/etc/localtime copy to the board/etc/below can be 5> MKNOD/DEV/RTC C 254 0 The following actions only need to be done once, and once the RTC chip is written, chip clocks itself, unless the battery is dead. The first time the board starts, If the set system time is October 2, 2007, 13:49 minutes, you can set this 1> Date 100213492007 2> hwclock–w If there is no error, the October 2, 2007, 13:49 is written to the RTC chip, Test: Repeat the Hwclock to see if the time is changing. 3> Restart the board, test, execute hwclock and see if the time is ticking. |