summaryrefslogtreecommitdiffstats
path: root/drivers/rtc/class.c
diff options
context:
space:
mode:
authorBaolin Wang <baolin.wang@linaro.org>2018-01-08 07:04:50 +0100
committerAlexandre Belloni <alexandre.belloni@bootlin.com>2018-03-17 14:20:55 +0100
commit989515647e783221f9737ed1cf519573d26ce99b (patch)
treed908376f5a2c6c0e4455dcb1ee26c3a99f4a342f /drivers/rtc/class.c
parentrtc: Factor out the RTC range validation into rtc_valid_range() (diff)
downloadlinux-989515647e783221f9737ed1cf519573d26ce99b.tar.xz
linux-989515647e783221f9737ed1cf519573d26ce99b.zip
rtc: Add one offset seconds to expand RTC range
From our investigation for all RTC drivers, 1 driver will be expired before year 2017, 7 drivers will be expired before year 2038, 23 drivers will be expired before year 2069, 72 drivers will be expired before 2100 and 104 drivers will be expired before 2106. Especially for these early expired drivers, we need to expand the RTC range to make the RTC can still work after the expired year. So we can expand the RTC range by adding one offset to the time when reading from hardware, and subtracting it when writing back. For example, if you have an RTC that can do 100 years, and currently is configured to be based in Jan 1 1970, so it can represents times from 1970 to 2069. Then if you change the start year from 1970 to 2000, which means it can represents times from 2000 to 2099. By adding or subtracting the offset produced by moving the wrap point, all times between 1970 and 1999 from RTC hardware could get interpreted as times from 2070 to 2099, but the interpretation of dates between 2000 and 2069 would not change. Signed-off-by: Baolin Wang <baolin.wang@linaro.org> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Diffstat (limited to 'drivers/rtc/class.c')
-rw-r--r--drivers/rtc/class.c70
1 files changed, 70 insertions, 0 deletions
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 5a5ab4fa14f9..d37588f08055 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -211,6 +211,73 @@ static int rtc_device_get_id(struct device *dev)
return id;
}
+static void rtc_device_get_offset(struct rtc_device *rtc)
+{
+ time64_t range_secs;
+ u32 start_year;
+ int ret;
+
+ /*
+ * If RTC driver did not implement the range of RTC hardware device,
+ * then we can not expand the RTC range by adding or subtracting one
+ * offset.
+ */
+ if (rtc->range_min == rtc->range_max)
+ return;
+
+ ret = device_property_read_u32(rtc->dev.parent, "start-year",
+ &start_year);
+ if (!ret) {
+ rtc->start_secs = mktime64(start_year, 1, 1, 0, 0, 0);
+ rtc->set_start_time = true;
+ }
+
+ /*
+ * If user did not implement the start time for RTC driver, then no
+ * need to expand the RTC range.
+ */
+ if (!rtc->set_start_time)
+ return;
+
+ range_secs = rtc->range_max - rtc->range_min + 1;
+
+ /*
+ * If the start_secs is larger than the maximum seconds (rtc->range_max)
+ * supported by RTC hardware or the maximum seconds of new expanded
+ * range (start_secs + rtc->range_max - rtc->range_min) is less than
+ * rtc->range_min, which means the minimum seconds (rtc->range_min) of
+ * RTC hardware will be mapped to start_secs by adding one offset, so
+ * the offset seconds calculation formula should be:
+ * rtc->offset_secs = rtc->start_secs - rtc->range_min;
+ *
+ * If the start_secs is larger than the minimum seconds (rtc->range_min)
+ * supported by RTC hardware, then there is one region is overlapped
+ * between the original RTC hardware range and the new expanded range,
+ * and this overlapped region do not need to be mapped into the new
+ * expanded range due to it is valid for RTC device. So the minimum
+ * seconds of RTC hardware (rtc->range_min) should be mapped to
+ * rtc->range_max + 1, then the offset seconds formula should be:
+ * rtc->offset_secs = rtc->range_max - rtc->range_min + 1;
+ *
+ * If the start_secs is less than the minimum seconds (rtc->range_min),
+ * which is similar to case 2. So the start_secs should be mapped to
+ * start_secs + rtc->range_max - rtc->range_min + 1, then the
+ * offset seconds formula should be:
+ * rtc->offset_secs = -(rtc->range_max - rtc->range_min + 1);
+ *
+ * Otherwise the offset seconds should be 0.
+ */
+ if (rtc->start_secs > rtc->range_max ||
+ rtc->start_secs + range_secs - 1 < rtc->range_min)
+ rtc->offset_secs = rtc->start_secs - rtc->range_min;
+ else if (rtc->start_secs > rtc->range_min)
+ rtc->offset_secs = range_secs;
+ else if (rtc->start_secs < rtc->range_min)
+ rtc->offset_secs = -range_secs;
+ else
+ rtc->offset_secs = 0;
+}
+
/**
* rtc_device_register - register w/ RTC class
* @dev: the device to register
@@ -247,6 +314,8 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
dev_set_name(&rtc->dev, "rtc%d", id);
+ rtc_device_get_offset(rtc);
+
/* Check to see if there is an ALARM already set in hw */
err = __rtc_read_alarm(rtc, &alrm);
@@ -436,6 +505,7 @@ int __rtc_register_device(struct module *owner, struct rtc_device *rtc)
return -EINVAL;
rtc->owner = owner;
+ rtc_device_get_offset(rtc);
/* Check to see if there is an ALARM already set in hw */
err = __rtc_read_alarm(rtc, &alrm);