diff options
Diffstat (limited to 'drivers/rtc/class.c')
| -rw-r--r-- | drivers/rtc/class.c | 77 | 
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)); | 
