Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

NTP: Add a CONFIG_RTC_SYSTOHC configuration

The purpose of this option is to allow ARM/etc systems that rely on the
class RTC subsystem to have the same kind of automatic NTP based
synchronization that we have on PC platforms. Today ARM does not
implement update_persistent_clock and makes extensive use of the class
RTC system.

When enabled CONFIG_RTC_SYSTOHC will provide a generic
rtc_update_persistent_clock that stores the current time in the RTC and
is intended complement the existing CONFIG_RTC_HCTOSYS option that loads
the RTC at boot.

Like with RTC_HCTOSYS the platform's update_persistent_clock is used
first, if it works. Platforms with mixed class RTC and non-RTC drivers
need to return ENODEV when class RTC should be used. Such an update for
PPC is included in this patch.

Long term, implementations of update_persistent_clock should migrate to
proper class RTC drivers and use CONFIG_RTC_SYSTOHC instead.

Tested on ARM kirkwood and PPC405

Signed-off-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
Signed-off-by: John Stultz <john.stultz@linaro.org>

authored by

Jason Gunthorpe and committed by
John Stultz
023f333a 50363737

+68 -6
+1 -1
arch/powerpc/kernel/time.c
··· 667 667 struct rtc_time tm; 668 668 669 669 if (!ppc_md.set_rtc_time) 670 - return 0; 670 + return -ENODEV; 671 671 672 672 to_tm(now.tv_sec + 1 + timezone_offset, &tm); 673 673 tm.tm_year -= 1900;
+9 -1
drivers/rtc/Kconfig
··· 25 25 the value read from a specified RTC device. This is useful to avoid 26 26 unnecessary fsck runs at boot time, and to network better. 27 27 28 + config RTC_SYSTOHC 29 + bool "Set the RTC time based on NTP synchronization" 30 + default y 31 + help 32 + If you say yes here, the system time (wall clock) will be stored 33 + in the RTC specified by RTC_HCTOSYS_DEVICE approximately every 11 34 + minutes if userspace reports synchronized NTP status. 35 + 28 36 config RTC_HCTOSYS_DEVICE 29 37 string "RTC used to set the system time" 30 - depends on RTC_HCTOSYS = y 38 + depends on RTC_HCTOSYS = y || RTC_SYSTOHC = y 31 39 default "rtc0" 32 40 help 33 41 The RTC device that will be used to (re)initialize the system
+1
drivers/rtc/Makefile
··· 6 6 7 7 obj-$(CONFIG_RTC_LIB) += rtc-lib.o 8 8 obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o 9 + obj-$(CONFIG_RTC_SYSTOHC) += systohc.o 9 10 obj-$(CONFIG_RTC_CLASS) += rtc-core.o 10 11 rtc-core-y := class.o interface.o 11 12
+44
drivers/rtc/systohc.c
··· 1 + /* 2 + * This program is free software; you can redistribute it and/or modify it 3 + * under the terms of the GNU General Public License version 2 as published by 4 + * the Free Software Foundation. 5 + * 6 + */ 7 + #include <linux/rtc.h> 8 + #include <linux/time.h> 9 + 10 + /** 11 + * rtc_set_ntp_time - Save NTP synchronized time to the RTC 12 + * @now: Current time of day 13 + * 14 + * Replacement for the NTP platform function update_persistent_clock 15 + * that stores time for later retrieval by rtc_hctosys. 16 + * 17 + * Returns 0 on successful RTC update, -ENODEV if a RTC update is not 18 + * possible at all, and various other -errno for specific temporary failure 19 + * cases. 20 + * 21 + * If temporary failure is indicated the caller should try again 'soon' 22 + */ 23 + int rtc_set_ntp_time(struct timespec now) 24 + { 25 + struct rtc_device *rtc; 26 + struct rtc_time tm; 27 + int err = -ENODEV; 28 + 29 + if (now.tv_nsec < (NSEC_PER_SEC >> 1)) 30 + rtc_time_to_tm(now.tv_sec, &tm); 31 + else 32 + rtc_time_to_tm(now.tv_sec + 1, &tm); 33 + 34 + rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); 35 + if (rtc) { 36 + /* rtc_hctosys exclusively uses UTC, so we call set_time here, 37 + * not set_mmss. */ 38 + if (rtc->ops && (rtc->ops->set_time || rtc->ops->set_mmss)) 39 + err = rtc_set_time(rtc, &tm); 40 + rtc_class_close(rtc); 41 + } 42 + 43 + return err; 44 + }
+1
include/linux/rtc.h
··· 138 138 extern int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm); 139 139 extern int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm); 140 140 extern int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs); 141 + extern int rtc_set_ntp_time(struct timespec now); 141 142 int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm); 142 143 extern int rtc_read_alarm(struct rtc_device *rtc, 143 144 struct rtc_wkalrm *alrm);
+12 -4
kernel/time/ntp.c
··· 15 15 #include <linux/time.h> 16 16 #include <linux/mm.h> 17 17 #include <linux/module.h> 18 + #include <linux/rtc.h> 18 19 19 20 #include "tick-internal.h" 20 21 ··· 484 483 return leap; 485 484 } 486 485 487 - #ifdef CONFIG_GENERIC_CMOS_UPDATE 488 - 486 + #if defined(CONFIG_GENERIC_CMOS_UPDATE) || defined(CONFIG_RTC_SYSTOHC) 489 487 static void sync_cmos_clock(struct work_struct *work); 490 488 491 489 static DECLARE_DELAYED_WORK(sync_cmos_work, sync_cmos_clock); ··· 510 510 } 511 511 512 512 getnstimeofday(&now); 513 - if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec / 2) 513 + if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec / 2) { 514 + fail = -ENODEV; 515 + #ifdef CONFIG_GENERIC_CMOS_UPDATE 514 516 fail = update_persistent_clock(now); 517 + #endif 518 + #ifdef CONFIG_RTC_SYSTOHC 519 + if (fail == -ENODEV) 520 + fail = rtc_set_ntp_time(now); 521 + #endif 522 + } 515 523 516 524 next.tv_nsec = (NSEC_PER_SEC / 2) - now.tv_nsec - (TICK_NSEC / 2); 517 525 if (next.tv_nsec <= 0) 518 526 next.tv_nsec += NSEC_PER_SEC; 519 527 520 - if (!fail) 528 + if (!fail || fail == -ENODEV) 521 529 next.tv_sec = 659; 522 530 else 523 531 next.tv_sec = 0;