大多数的单片机内部都集成有RTC模块即Real_Time Clock。I.MX6U内部也有这样的模块。SNVS的运行模式可见SNVS_LP是由系统主电源和备用电源供电的。SNVS的结构SNVS_HP有一个不安全的实时计数器。它系统断电的情况是不工作的。要访问SNVS_LP的寄存器必须在SNVS_HP上电的情况下。SNVS_LP的功能单元包括非溢出单调计数器、通用寄存器和控制和状态寄存器。SNVS编程使用以下步骤来编程同步值:读RTC值软件需要执行2个连续的RTC值读非特权软件要访问特权寄存器需要先设置SNVS_HP Command register寄存器的非特权软件访问使能位即HPCOMR bit31 NPSWA_EN。参考一下源码文件drivers/rtc/rtc-snvs.c/* These register offsets are relative to LP (Low Power) range */ #define SNVS_LPCR 0x04 #define SNVS_LPSR 0x18 #define SNVS_LPSRTCMR 0x1c #define SNVS_LPSRTCLR 0x20 #define SNVS_LPTAR 0x24 #define SNVS_LPPGDR 0x30 #define SNVS_LPCR_SRTC_ENV (1 0) #define SNVS_LPCR_LPTA_EN (1 1) #define SNVS_LPCR_LPWUI_EN (1 3) #define SNVS_LPSR_LPTA (1 0) #define SNVS_LPPGDR_INIT 0x41736166 #define CNTR_TO_SECS_SH 15 struct snvs_rtc_data { struct rtc_device *rtc; struct regmap *regmap; int offset; int irq; struct clk *clk; }; static u32 rtc_read_lp_counter(struct snvs_rtc_data *data) { u64 read1, read2; u32 val; do { regmap_read(data-regmap,>struct rtc_device { struct device dev; struct module *owner; int id; char name[RTC_DEVICE_NAME_SIZE]; const struct rtc_class_ops *ops; struct mutex ops_lock; struct cdev char_dev; unsigned long flags; unsigned long irq_data; spinlock_t irq_lock; wait_queue_head_t irq_queue; struct fasync_struct *async_queue; struct rtc_task *irq_task; spinlock_t irq_task_lock; int irq_freq; int max_user_freq; struct timerqueue_head timerqueue; struct rtc_timer aie_timer; struct rtc_timer uie_rtctimer; struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */ int pie_enabled; struct work_struct irqwork; /* Some hardware cant support UIE mode */ int uie_unsupported; #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL struct work_struct uie_task; struct timer_list uie_timer; /* Those fields are protected by rtc-irq_lock */ unsigned int oldsecs; unsigned int uie_irq_active:1; unsigned int stop_uie_polling:1; unsigned int uie_task_active:1; unsigned int uie_timer_active:1; #endif }; #define to_rtc_device(d) container_of(d, struct rtc_device, dev)其中struct rtc_class_ops *ops 是RTC设备的最底层操作函数集合。该结构体定义在include/linux/rtc.h文件中。/* * For these RTC methods the device parameter is the physical device * on whatever bus holds the hardware (I2C, Platform, SPI, etc), which * was passed to rtc_device_register(). Its driver_data normally holds * device state, including the rtc_device pointer for the RTC. * * Most of these methods are called with rtc_device.ops_lock held, * through the rtc_*(struct rtc_device *, ...) calls. * * The (current) exceptions are mostly filesystem hooks: * - the proc() hook for procfs * - non-ioctl() chardev hooks: open(), release(), read_callback() * * REVISIT those periodic irq calls *do* have ops_lock when theyre * issued through ioctl() ... */ struct rtc_class_ops { int (*open)(struct device *); void (*release)(struct device *); 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 (*set_mmss64)(struct device *, time64_t secs); int (*set_mmss)(struct device *, unsigned long secs); int (*read_callback)(struct device *, int data); int (*alarm_irq_enable)(struct device *, unsigned int enabled); };RTC通用字符设备驱动文件drivers/rtc/rtc-dev.c提供所有RTC设备共用的file_operations函数操作集。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, };再看下设备树文件imx6ull.dtsi找到snvs_rtc节点。其中的“020cc000”正是SNVS_HP的基地址。snvs: snvs020cc000 { compatible fsl,sec-v4.0-mon, syscon, simple-mfd; reg 0x020cc000 0x4000; snvs_rtc: snvs-rtc-lp { compatible fsl,sec-v4.0-mon-rtc-lp; regmap snvs; offset 0x34; interrupts GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH, GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH; };RTC设备驱动文件drivers/rtc/rtc-snvs.c定义了所有要底层操作的函数由struct rtc_class_ops snvs_rtc_ops把它们进行了赋值。static const struct rtc_class_ops snvs_rtc_ops { .read_time snvs_rtc_read_time, .set_time snvs_rtc_set_time, .read_alarm snvs_rtc_read_alarm, .set_alarm snvs_rtc_set_alarm, .alarm_irq_enable snvs_rtc_alarm_irq_enable, };rtc-snvs.c就是下图的 RTC设备驱动rtc_class_ops赋值后就可以注册到Linux内核中使用rtc_device_register()函数完成注册。至此整个LInux RTC的驱动架构已经很清楚了I.MX6U内部RTC驱动的分析可参加正点原子的Linux驱动开发指南。参考资料1.I.MX6Uq嵌入式Linux驱动开发指南V2.0.1正点原子2. Linux设备驱动开发 详解基于最新的Linux 4.0内核3. i.MX 6UltraLite Applications Processor Reference Manual Rev.1, 04/2016