rtc: tegra: backup hctosys device
Krishna Yarlagadda [Mon, 14 Dec 2015 10:15:16 +0000 (15:15 +0530)]
RTC device used for system time needs to be battery backed.
Non-battery backed devices could lose time upon power reset.
However battery backed devices from external source will add delay
from interface and take more time.

Introduced a config option for slower battery backed rtc device that
will update faster system rtc device.

Bug 1698877

Change-Id: Ifd4884335fdd6c6e375f9bd52fb83fb5bb652adc
Signed-off-by: Krishna Yarlagadda <kyarlagadda@nvidia.com>
Reviewed-on: http://git-master/r/922552
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>

drivers/rtc/Kconfig
drivers/rtc/hctosys.c
drivers/rtc/rtc-dev.c
drivers/rtc/systohc.c
include/linux/rtc.h

index 5e90bfc..3f54a68 100644 (file)
@@ -59,11 +59,31 @@ config RTC_HCTOSYS_DEVICE
          time when the system boots from a power-off state. Otherwise, your
          system will need an external clock source (like an NTP server).
 
+         If this clock is not battery-backed, backup clock can be used
+         to set time for this clock. Set it under RTC_HCTOSYS_BACKUP_DEVICE.
+         Leave RTC_HCTOSYS_BACKUP_DEVICE black to avoid modifying this clock.
+
          If the clock you specify here is not battery backed, it may still
          be useful to reinitialize system time when resuming from system
          sleep states. Do not specify an RTC here unless it stays powered
          during all this system's supported sleep states.
 
+config RTC_BACKUP_HCTOSYS_DEVICE
+       string "Backup RTC used to feed RTC maintaining system time"
+       default ""
+       help
+         Battery-backed RTC device to set time for system RTC. Leave it
+         blank if system RTC should not be changed or if there is no
+         battery-backed RTC in system.
+
+         RTC device used for RTC_HCTOSYS_DEVICE may not be battery-backed.
+         In this case system time will be reset upon device reset. A backup
+         RTC device can be specified here to feed time to system RTC.
+
+         Maintaining multiple RTC might be required when battery-backed
+         device is slow in responding when connected through I2C or other
+         slow interface.
+
 config RTC_DEBUG
        bool "RTC debug support"
        help
index 6c9593c..6a9d195 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * RTC subsystem, initialize system time on startup
  *
+ * Copyright (C) 2015 NVIDIA CORPORATION. All rights reserved.
  * Copyright (C) 2005 Tower Technologies
  * Author: Alessandro Zummo <a.zummo@towertech.it>
  *
  * the best guess is to add 0.5s.
  */
 
+static int set_hctosys_rtc_time(struct rtc_device *rtc)
+{
+       int err = -ENODEV;
+       struct rtc_time tm;
+       struct rtc_device *backup_rtc;
+
+       backup_rtc = rtc_class_open(CONFIG_RTC_BACKUP_HCTOSYS_DEVICE);
+       if (backup_rtc == NULL)
+               return err;
+
+       err = rtc_read_time(backup_rtc, &tm);
+       if (err) {
+               rtc_class_close(backup_rtc);
+               return err;
+       }
+
+       err = rtc_set_time(rtc, &tm);
+       if (err) {
+               rtc_class_close(backup_rtc);
+               return err;
+       }
+
+       rtc_class_close(backup_rtc);
+       return 0;
+}
+
+void set_systohc_rtc_time(void)
+{
+       int err = -ENODEV;
+       struct rtc_time tm;
+       struct rtc_device *backup_rtc;
+       struct rtc_device *system_rtc;
+
+       system_rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+       if (system_rtc == NULL)
+               return;
+
+       err = rtc_read_time(system_rtc, &tm);
+       if (err) {
+               rtc_class_close(system_rtc);
+               return;
+       }
+
+       backup_rtc = rtc_class_open(CONFIG_RTC_BACKUP_HCTOSYS_DEVICE);
+       if (backup_rtc == NULL) {
+               rtc_class_close(system_rtc);
+               return;
+       }
+
+       rtc_set_time(backup_rtc, &tm);
+
+       rtc_class_close(system_rtc);
+       rtc_class_close(backup_rtc);
+       return;
+}
+EXPORT_SYMBOL(set_systohc_rtc_time);
+
 int rtc_hctosys(void)
 {
        int err = -ENODEV;
@@ -37,6 +95,13 @@ int rtc_hctosys(void)
                goto err_open;
        }
 
+       if (CONFIG_RTC_BACKUP_HCTOSYS_DEVICE[0] != '\0') {
+               err = set_hctosys_rtc_time(rtc);
+               if (err)
+                       pr_warn("%s: Ignoring backup rtc device (%s)\n",
+                               __FILE__, CONFIG_RTC_BACKUP_HCTOSYS_DEVICE);
+       }
+
        err = rtc_read_time(rtc, &tm);
        if (err) {
                dev_err(rtc->dev.parent,
index d049393..a88a7b8 100644 (file)
@@ -347,7 +347,10 @@ static long rtc_dev_ioctl(struct file *file,
                if (copy_from_user(&tm, uarg, sizeof(tm)))
                        return -EFAULT;
 
-               return rtc_set_time(rtc, &tm);
+               err = rtc_set_time(rtc, &tm);
+               if (!err)
+                       set_systohc_rtc_time();
+               return err;
 
        case RTC_PIE_ON:
                err = rtc_irq_set_state(rtc, NULL, 1);
index bf3e242..70b8420 100644 (file)
@@ -39,6 +39,14 @@ int rtc_set_ntp_time(struct timespec now)
                        err = rtc_set_time(rtc, &tm);
                rtc_class_close(rtc);
        }
+       rtc = rtc_class_open(CONFIG_RTC_BACKUP_HCTOSYS_DEVICE);
+       if (rtc) {
+               /* rtc_hctosys exclusively uses UTC, so we call set_time here,
+                * not set_mmss. */
+               if (rtc->ops && (rtc->ops->set_time || rtc->ops->set_mmss))
+                       err = rtc_set_time(rtc, &tm);
+               rtc_class_close(rtc);
+       }
 
        return err;
 }
index 4eb328c..5e8ac66 100644 (file)
@@ -196,8 +196,10 @@ static inline bool is_leap_year(unsigned int year)
 #ifdef CONFIG_RTC_HCTOSYS_DEVICE
 extern int rtc_hctosys_ret;
 int rtc_hctosys(void);
+void set_systohc_rtc_time(void);
 #else
 #define rtc_hctosys_ret -ENODEV
+void set_systohc_rtc_time(void) { return; };
 #endif
 
 #endif /* _LINUX_RTC_H_ */