In-Depth Analysis of the Linux RTC Subsystem (Part 1)

1. RTC Subsystem Framework

The RTC (Real Time Clock) subsystem is divided into the following three parts:

  • RTC Core is responsible for the registration and deregistration of RTC devices, providing character device files and sysfs interfaces to user space.

  • RTC Driver registers the RTC device into the RTC subsystem and provides a set of low-level hardware operation functions.

  • User Space Interface includes the <span>/dev/rtcX</span> character device file, as well as debugging interfaces such as <span>/sys/class/rtc/rtcX/</span> and <span>/proc/driver/rtc</span>.

In-Depth Analysis of the Linux RTC Subsystem (Part 1)

The initialization of the RTC subsystem mainly includes allocating the <span>rtc_class</span> class and assigning device numbers for the RTC devices:

rtc_init  → class_create          // Create rtc_class class  → rtc_dev_init    → alloc_chrdev_region // Allocate sub-device number range (0~15) for RTC devices, with the major device number assigned randomly, and the result stored in rtc_devt

During system startup, the RTC time is set to the system time:

rtc_hctosys  → rtc_read_time  → rtc_tm_to_time64  → do_settimeofday64

Interrupt Type Description:

  • AIE: Alarm Interrupt Enable

  • UIE: Update Interrupt Enable

  • PIE: Periodic Interrupt Enable

  • WIE: Watchdog Interrupt Enable

2. RTC Configuration

Kernel Configuration Options:

Device Drivers  → Real Time Clock    → [*] Set system time from RTC on startup and resume    → (rtc0) RTC used to set the system time    → [*] Set the RTC time based on NTP synchronization    → (rtc0) RTC used to synchronize NTP adjustment    → [*] RTC debug support    → [*] RTC non-volatile storage support    → [*] /sys/class/rtc/rtcN (sysfs)    → [*] /proc/driver/rtc (procfs for rtcN)    → [*] /dev/rtcN (character devices)    → [*] RTC

Related Source Code File Structure:

drivers/rtc/├── class.c      // rtc_class creation, device registration/deregistration, power management├── dev.c        // RTC subsystem initialization, character device operation function set├── hctosys.c    // Set system time from RTC at startup (Hardware Clock to System)├── interface.c  // General timer interface and rtc_class_ops calls├── lib.c        // General functions for time and date├── nvmem.c      // NVRAM access for RTC information├── proc.c       // Implementation of /proc/driver/rtc├── rtc.c        // RTC driver├── sysfs.c      // Implementation of sysfs attribute interface└── systohc.c    // Write NTP time to RTC (System to Hardware Clock)

3. RTC Data Structures and APIs

1. Core Data Structure

struct rtc_device:

struct rtc_device {    struct device dev;    struct module *owner;    int id;                         // RTC device number    const struct rtc_class_ops *ops;// Low-level hardware operation function set    struct mutex ops_lock;    struct cdev char_dev;           // Character device    unsigned long flags;    unsigned long irq_data;    spinlock_t irq_lock;    wait_queue_head_t irq_queue;    // For poll wait queue    struct fasync_struct *async_queue; // For fasync asynchronous notification    // ...};

struct rtc_class_ops:

struct rtc_class_ops {    int (*ioctl)(struct device *, unsigned int, unsigned long);    int (*read_time)(struct device *, struct rtc_time *);    int (*set_time)(struct device *, struct rtc_time *);    int (*read_alarm)(struct device *, struct rtc_wkalrm *);    int (*set_alarm)(struct device *, struct rtc_wkalrm *);    int (*proc)(struct device *, struct seq_file *);    int (*alarm_irq_enable)(struct device *, unsigned int enabled);    int (*read_offset)(struct device *, long *offset);    int (*set_offset)(struct device *, long offset);};

2. RTC Device Registration Functions

struct rtc_device *devm_rtc_device_register(    struct device *dev,    const char *name,    const struct rtc_class_ops *ops,    struct module *owner);struct rtc_device *devm_rtc_allocate_device(struct device *dev);int __rtc_register_device(struct module *owner, struct rtc_device *rtc);

Overview of the Registration Process:

devm_rtc_device_register  → devm_rtc_allocate_device    → rtc_device_get_id        // Get device number    → devres_alloc             // Allocate resource management structure    → rtc_allocate_device      → kzalloc                // Allocate rtc_device      → device_initialize      // Initialize device structure      → rtc_timer_init         // Initialize timers (aie_timer, uie_rtctimer, pie_timer)  → __rtc_register_device    → rtc_dev_prepare          // Initialize cdev    → cdev_device_add          // Register character device    → rtc_proc_add_device      // Create /proc/driver/rtc interface

Each RTC device has the following attributes under <span>/sys/class/rtc/rtcX/</span>:

static struct attribute *rtc_attrs[] = {    &amp;dev_attr_name.attr,    &amp;dev_attr_date.attr,    &amp;dev_attr_time.attr,    &amp;dev_attr_since_epoch.attr,    &amp;dev_attr_max_user_freq.attr,    &amp;dev_attr_hctosys.attr,    &amp;dev_attr_wakealarm.attr,    &amp;dev_attr_offset.attr,    &amp;dev_attr_range.attr,    NULL,};

3. Device File Operation Function Set

static const struct file_operations rtc_dev_fops = {    .owner = THIS_MODULE,    .llseek = no_llseek,    .read = rtc_dev_read,    .poll = rtc_dev_poll,    .unlocked_ioctl = rtc_dev_ioctl,    .open = rtc_dev_open,    .release = rtc_dev_release,    .fasync = rtc_dev_fasync,};

Example of ioctl Operation:

static long rtc_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {    switch (cmd) {    case RTC_ALM_READ:        err = rtc_read_alarm(rtc, &amp;alarm);        // ...    case RTC_ALM_SET:        err = rtc_set_alarm(rtc, &amp;alarm);        // ...    case RTC_RD_TIME:        err = rtc_read_time(rtc, &amp;tm);        // ...    case RTC_SET_TIME:        err = rtc_set_time(rtc, &amp;tm);        // ...    default:        if (ops-&gt;ioctl)             err = ops-&gt;ioctl(rtc-&gt;dev.parent, cmd, arg);        // ...    }}

Poll and Fasync Mechanism:

// Poll waiting for interruptstatic __poll_t rtc_dev_poll(struct file *file, poll_table *wait) {    poll_wait(file, &amp;rtc-&gt;irq_queue, wait);    return (rtc-&gt;irq_data != 0) ? (EPOLLIN | EPOLLRDNORM) : 0;}// Fasync asynchronous notificationstatic int rtc_dev_fasync(int fd, struct file *file, int on) {    return fasync_helper(fd, file, on, &amp;rtc-&gt;async_queue);}

4. Common RTC Operation APIs

extern int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm);extern int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm);extern int rtc_set_ntp_time(struct timespec64 now, unsigned long *target_nsec);int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm);extern int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alrm);extern int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alrm);extern int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alrm);extern void rtc_update_irq(struct rtc_device *rtc, unsigned long num, unsigned long events);extern int rtc_irq_set_state(struct rtc_device *rtc, int enabled);extern int rtc_irq_set_freq(struct rtc_device *rtc, int freq);extern int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled);extern int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled);

Leave a Comment