[PATCH] rtc: add rtc_systohc for ntp use

From: Ed Swierk
Date: Mon Jan 26 2009 - 20:04:40 EST


Following up to Alessandro's RFC of 10 Nov 2008, this patch implements
rtc_systohc() to update the configured rtc device (cmos, i2c, etc.) from
the ntp-synchronized system clock. It changes the ntp update code to
call rtc_systohc() rather than update_persistent_clock() which (at least
on x86) always updates the cmos rtc.

No attempt is made to compensate for the 500-ms delay imposed when
setting a cmos rtc chip. If you actually have a cmos rtc you can stick
with the existing code, but at some point it would be nice to unify
these.

Signed-off-by: Ed Swierk <eswierk@xxxxxxxxxxxxxxxxxx>

---
Index: linux-2.6.28.2/drivers/rtc/Kconfig
===================================================================
--- linux-2.6.28.2.orig/drivers/rtc/Kconfig
+++ linux-2.6.28.2/drivers/rtc/Kconfig
@@ -30,7 +30,7 @@ config RTC_HCTOSYS
unnecessary fsck runs at boot time, and to network better.

config RTC_HCTOSYS_DEVICE
- string "RTC used to set the system time"
+ string "RTC used to set the system time on startup and resume"
depends on RTC_HCTOSYS = y
default "rtc0"
help
@@ -52,6 +52,23 @@ config RTC_HCTOSYS_DEVICE
sleep states. Do not specify an RTC here unless it stays powered
during all this system's supported sleep states.

+config RTC_SYSTOHC
+ bool "Set RTC from system time in NTP sync mode"
+ depends on RTC_CLASS = y
+ default y
+ help
+ If you say yes here, the system time (wall clock) will be written
+ to the hardware clock every 11 minutes, if the kernel is in NTP
+ mode and your platforms supports it.
+
+config RTC_SYSTOHC_DEVICE
+ string "RTC used to save the system time in NTP sync mode"
+ depends on RTC_SYSTOHC = y
+ default "rtc0"
+ help
+ The RTC device that will get written with the system time
+ in NTP mode.
+
config RTC_DEBUG
bool "RTC debug support"
depends on RTC_CLASS = y
Index: linux-2.6.28.2/drivers/rtc/Makefile
===================================================================
--- linux-2.6.28.2.orig/drivers/rtc/Makefile
+++ linux-2.6.28.2/drivers/rtc/Makefile
@@ -8,6 +8,7 @@ endif

obj-$(CONFIG_RTC_LIB) += rtc-lib.o
obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o
+obj-$(CONFIG_RTC_SYSTOHC) += systohc.o
obj-$(CONFIG_RTC_CLASS) += rtc-core.o
rtc-core-y := class.o interface.o

Index: linux-2.6.28.2/drivers/rtc/systohc.c
===================================================================
--- /dev/null
+++ linux-2.6.28.2/drivers/rtc/systohc.c
@@ -0,0 +1,36 @@
+/*
+ * RTC subsystem, systohc for ntp use
+ *
+ * Copyright (C) 2008 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@xxxxxxxxxxxx>
+ *
+ * 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>
+
+int rtc_systohc(void)
+{
+ int err;
+ struct rtc_time tm;
+ struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_SYSTOHC_DEVICE);
+
+ if (rtc == NULL) {
+ printk("%s: unable to open rtc device (%s)\n",
+ __FILE__, CONFIG_RTC_SYSTOHC_DEVICE);
+ return -ENODEV;
+ }
+
+ rtc_time_to_tm(get_seconds(), &tm);
+ err = rtc_set_time(rtc, &tm);
+ if (err != 0)
+ dev_err(rtc->dev.parent,
+ "systohc: unable to set the hardware clock\n");
+
+ rtc_class_close(rtc);
+
+ return err;
+}
+EXPORT_SYMBOL(rtc_systohc);
Index: linux-2.6.28.2/kernel/time/ntp.c
===================================================================
--- linux-2.6.28.2.orig/kernel/time/ntp.c
+++ linux-2.6.28.2/kernel/time/ntp.c
@@ -221,17 +221,20 @@ static void sync_cmos_clock(struct work_

static DECLARE_DELAYED_WORK(sync_cmos_work, sync_cmos_clock);

+#ifdef CONFIG_RTC_SYSTOHC
+extern int rtc_systohc(void);
+#endif
+
static void sync_cmos_clock(struct work_struct *work)
{
+#ifndef CONFIG_RTC_SYSTOHC
struct timespec now, next;
int fail = 1;
+#endif

/*
* If we have an externally synchronized Linux clock, then update
- * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
- * called as close as possible to 500 ms before the new second starts.
- * This code is run on a timer. If the clock is set, that timer
- * may not expire at the correct time. Thus, we adjust...
+ * CMOS clock accordingly every ~11 minutes.
*/
if (!ntp_synced())
/*
@@ -240,6 +243,19 @@ static void sync_cmos_clock(struct work_
*/
return;

+#ifdef CONFIG_RTC_SYSTOHC
+
+ rtc_systohc();
+ schedule_delayed_work(&sync_cmos_work, 660*HZ);
+
+#else
+
+ /*
+ * Set_rtc_mmss() has to be called as close as possible to 500 ms
+ * before the new second starts. This code is run on a timer.
+ * If the clock is set, that timer may not expire at the correct
+ * time. Thus, we adjust...
+ */
getnstimeofday(&now);
if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec / 2)
fail = update_persistent_clock(now);
@@ -258,6 +274,8 @@ static void sync_cmos_clock(struct work_
next.tv_nsec -= NSEC_PER_SEC;
}
schedule_delayed_work(&sync_cmos_work, timespec_to_jiffies(&next));
+
+#endif
}

static void notify_cmos_timer(void)


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/