aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Gunthorpe <jgunthorpe@obsidianresearch.com>2012-12-17 16:30:53 -0500
committerJohn Stultz <john.stultz@linaro.org>2013-01-15 21:16:06 -0500
commit023f333a99cee9b5cd3268ff87298eb01a31f78e (patch)
treebf1d316ac4f4ad6ccd9f98206debece6f24a7a4f
parent503637375269e33f368fd3484a199beace01f36e (diff)
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>
-rw-r--r--arch/powerpc/kernel/time.c2
-rw-r--r--drivers/rtc/Kconfig10
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/systohc.c44
-rw-r--r--include/linux/rtc.h1
-rw-r--r--kernel/time/ntp.c16
6 files changed, 68 insertions, 6 deletions
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index ce4cb772dc78..bc844a857e05 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -667,7 +667,7 @@ int update_persistent_clock(struct timespec now)
667 struct rtc_time tm; 667 struct rtc_time tm;
668 668
669 if (!ppc_md.set_rtc_time) 669 if (!ppc_md.set_rtc_time)
670 return 0; 670 return -ENODEV;
671 671
672 to_tm(now.tv_sec + 1 + timezone_offset, &tm); 672 to_tm(now.tv_sec + 1 + timezone_offset, &tm);
673 tm.tm_year -= 1900; 673 tm.tm_year -= 1900;
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 19c03ab2bdcb..b377e9672497 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -25,9 +25,17 @@ config RTC_HCTOSYS
25 the value read from a specified RTC device. This is useful to avoid 25 the value read from a specified RTC device. This is useful to avoid
26 unnecessary fsck runs at boot time, and to network better. 26 unnecessary fsck runs at boot time, and to network better.
27 27
28config 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
28config RTC_HCTOSYS_DEVICE 36config RTC_HCTOSYS_DEVICE
29 string "RTC used to set the system time" 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 default "rtc0" 39 default "rtc0"
32 help 40 help
33 The RTC device that will be used to (re)initialize the system 41 The RTC device that will be used to (re)initialize the system
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 56297f0fd388..69d11f1d76e4 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -6,6 +6,7 @@ ccflags-$(CONFIG_RTC_DEBUG) := -DDEBUG
6 6
7obj-$(CONFIG_RTC_LIB) += rtc-lib.o 7obj-$(CONFIG_RTC_LIB) += rtc-lib.o
8obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o 8obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o
9obj-$(CONFIG_RTC_SYSTOHC) += systohc.o
9obj-$(CONFIG_RTC_CLASS) += rtc-core.o 10obj-$(CONFIG_RTC_CLASS) += rtc-core.o
10rtc-core-y := class.o interface.o 11rtc-core-y := class.o interface.o
11 12
diff --git a/drivers/rtc/systohc.c b/drivers/rtc/systohc.c
new file mode 100644
index 000000000000..bf3e242ccc5c
--- /dev/null
+++ b/drivers/rtc/systohc.c
@@ -0,0 +1,44 @@
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 */
23int 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}
diff --git a/include/linux/rtc.h b/include/linux/rtc.h
index 9531845c419f..11d05f9fe8b6 100644
--- a/include/linux/rtc.h
+++ b/include/linux/rtc.h
@@ -138,6 +138,7 @@ extern void rtc_device_unregister(struct rtc_device *rtc);
138extern int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm); 138extern int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm);
139extern int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm); 139extern int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm);
140extern int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs); 140extern int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs);
141extern int rtc_set_ntp_time(struct timespec now);
141int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm); 142int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm);
142extern int rtc_read_alarm(struct rtc_device *rtc, 143extern int rtc_read_alarm(struct rtc_device *rtc,
143 struct rtc_wkalrm *alrm); 144 struct rtc_wkalrm *alrm);
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c
index 24174b4d669b..313b161504b7 100644
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -15,6 +15,7 @@
15#include <linux/time.h> 15#include <linux/time.h>
16#include <linux/mm.h> 16#include <linux/mm.h>
17#include <linux/module.h> 17#include <linux/module.h>
18#include <linux/rtc.h>
18 19
19#include "tick-internal.h" 20#include "tick-internal.h"
20 21
@@ -483,8 +484,7 @@ out:
483 return leap; 484 return leap;
484} 485}
485 486
486#ifdef CONFIG_GENERIC_CMOS_UPDATE 487#if defined(CONFIG_GENERIC_CMOS_UPDATE) || defined(CONFIG_RTC_SYSTOHC)
487
488static void sync_cmos_clock(struct work_struct *work); 488static void sync_cmos_clock(struct work_struct *work);
489 489
490static DECLARE_DELAYED_WORK(sync_cmos_work, sync_cmos_clock); 490static DECLARE_DELAYED_WORK(sync_cmos_work, sync_cmos_clock);
@@ -510,14 +510,22 @@ static void sync_cmos_clock(struct work_struct *work)
510 } 510 }
511 511
512 getnstimeofday(&now); 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 fail = update_persistent_clock(now); 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 next.tv_nsec = (NSEC_PER_SEC / 2) - now.tv_nsec - (TICK_NSEC / 2); 524 next.tv_nsec = (NSEC_PER_SEC / 2) - now.tv_nsec - (TICK_NSEC / 2);
517 if (next.tv_nsec <= 0) 525 if (next.tv_nsec <= 0)
518 next.tv_nsec += NSEC_PER_SEC; 526 next.tv_nsec += NSEC_PER_SEC;
519 527
520 if (!fail) 528 if (!fail || fail == -ENODEV)
521 next.tv_sec = 659; 529 next.tv_sec = 659;
522 else 530 else
523 next.tv_sec = 0; 531 next.tv_sec = 0;