0f295b0650
ntp is currently hardwired to try and call the rtc set when wall clock tv_nsec is 0.5 seconds. This historical behaviour works well with certain PC RTCs, but is not universal to all rtc hardware. Change how this works by introducing the driver specific concept of set_offset_nsec, the delay between current wall clock time and the target time to set (with a 0 tv_nsecs). For x86-style CMOS set_offset_nsec should be -0.5 s which causes the last second to be written 0.5 s after it has started. For compat with the old rtc_set_ntp_time, the value is defaulted to + 0.5 s, which causes the next second to be written 0.5s before it starts, as things were before this patch. Testing shows many non-x86 RTCs would like set_offset_nsec ~= 0, so ultimately each RTC driver should set the set_offset_nsec according to its needs, and non x86 architectures should stop using update_persistent_clock64 in order to access this feature. Future patches will revise the drivers as needed. Since CMOS and RTC now have very different handling they are split into two dedicated code paths, sharing the support code, and ifdefs are replaced with IS_ENABLED. Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Ingo Molnar <mingo@kernel.org> Cc: Miroslav Lichvar <mlichvar@redhat.com> Cc: Richard Cochran <richardcochran@gmail.com> Cc: Prarit Bhargava <prarit@redhat.com> Cc: Stephen Boyd <stephen.boyd@linaro.org> Signed-off-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com> Signed-off-by: John Stultz <john.stultz@linaro.org>
70 lines
1.9 KiB
C
70 lines
1.9 KiB
C
/*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 as published by
|
|
* the Free Software Foundation.
|
|
*
|
|
*/
|
|
#include <linux/rtc.h>
|
|
#include <linux/time.h>
|
|
|
|
/**
|
|
* rtc_set_ntp_time - Save NTP synchronized time to the RTC
|
|
* @now: Current time of day
|
|
* @target_nsec: pointer for desired now->tv_nsec value
|
|
*
|
|
* Replacement for the NTP platform function update_persistent_clock64
|
|
* that stores time for later retrieval by rtc_hctosys.
|
|
*
|
|
* Returns 0 on successful RTC update, -ENODEV if a RTC update is not
|
|
* possible at all, and various other -errno for specific temporary failure
|
|
* cases.
|
|
*
|
|
* -EPROTO is returned if now.tv_nsec is not close enough to *target_nsec.
|
|
(
|
|
* If temporary failure is indicated the caller should try again 'soon'
|
|
*/
|
|
int rtc_set_ntp_time(struct timespec64 now, unsigned long *target_nsec)
|
|
{
|
|
struct rtc_device *rtc;
|
|
struct rtc_time tm;
|
|
struct timespec64 to_set;
|
|
int err = -ENODEV;
|
|
bool ok;
|
|
|
|
rtc = rtc_class_open(CONFIG_RTC_SYSTOHC_DEVICE);
|
|
if (!rtc)
|
|
goto out_err;
|
|
|
|
if (!rtc->ops || (!rtc->ops->set_time && !rtc->ops->set_mmss64 &&
|
|
!rtc->ops->set_mmss))
|
|
goto out_close;
|
|
|
|
/* Compute the value of tv_nsec we require the caller to supply in
|
|
* now.tv_nsec. This is the value such that (now +
|
|
* set_offset_nsec).tv_nsec == 0.
|
|
*/
|
|
set_normalized_timespec64(&to_set, 0, -rtc->set_offset_nsec);
|
|
*target_nsec = to_set.tv_nsec;
|
|
|
|
/* The ntp code must call this with the correct value in tv_nsec, if
|
|
* it does not we update target_nsec and return EPROTO to make the ntp
|
|
* code try again later.
|
|
*/
|
|
ok = rtc_tv_nsec_ok(rtc->set_offset_nsec, &to_set, &now);
|
|
if (!ok) {
|
|
err = -EPROTO;
|
|
goto out_close;
|
|
}
|
|
|
|
rtc_time64_to_tm(to_set.tv_sec, &tm);
|
|
|
|
/* rtc_hctosys exclusively uses UTC, so we call set_time here, not
|
|
* set_mmss.
|
|
*/
|
|
err = rtc_set_time(rtc, &tm);
|
|
|
|
out_close:
|
|
rtc_class_close(rtc);
|
|
out_err:
|
|
return err;
|
|
}
|