summaryrefslogtreecommitdiffstats
path: root/drivers/rtc/class.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc/class.c')
-rw-r--r--drivers/rtc/class.c77
1 files changed, 73 insertions, 4 deletions
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 722d683e0b0f..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);
@@ -293,8 +362,6 @@ EXPORT_SYMBOL_GPL(rtc_device_register);
*/
void rtc_device_unregister(struct rtc_device *rtc)
{
- rtc_nvmem_unregister(rtc);
-
mutex_lock(&rtc->ops_lock);
/*
* Remove innards of this RTC, then disable it, before
@@ -312,6 +379,7 @@ static void devm_rtc_device_release(struct device *dev, void *res)
{
struct rtc_device *rtc = *(struct rtc_device **)res;
+ rtc_nvmem_unregister(rtc);
rtc_device_unregister(rtc);
}
@@ -382,6 +450,8 @@ static void devm_rtc_release_device(struct device *dev, void *res)
{
struct rtc_device *rtc = *(struct rtc_device **)res;
+ rtc_nvmem_unregister(rtc);
+
if (rtc->registered)
rtc_device_unregister(rtc);
else
@@ -435,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);
@@ -453,8 +524,6 @@ int __rtc_register_device(struct module *owner, struct rtc_device *rtc)
rtc_proc_add_device(rtc);
- rtc_nvmem_register(rtc);
-
rtc->registered = true;
dev_info(rtc->dev.parent, "registered as %s\n",
dev_name(&rtc->dev));