[Skiboot] [RFC] hw/fake-rtc: Add support for emulated rtc clock

Vaibhav Jain vaibhav at linux.vnet.ibm.com
Wed Jun 3 15:03:38 AEST 2015


This patch adds support for an emulated rtc clock for generic
platform (e.g BML) which doesn't have support for rtc. Current
implementation provides a fake rtc clock for such platforms which is
driven by device tree provided pointer(ibm,fake-rtc) to bcd coded
date-time values. In absence of this rtc support is not provided.

The patch overcomes this limitation by emulating an rtc clock that's
driven by a periodically scheduled timer. If the device tree is missing
node "ibm,fake-rtc" it creates the periodic timer and on each tick the
rtc counter is incremented.

Signed-off-by: Vaibhav Jain <vaibhav at linux.vnet.ibm.com>
---
 hw/fake-rtc.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 96 insertions(+), 12 deletions(-)

diff --git a/hw/fake-rtc.c b/hw/fake-rtc.c
index 1b7c473..c2ac48d 100644
--- a/hw/fake-rtc.c
+++ b/hw/fake-rtc.c
@@ -17,10 +17,27 @@
 #include <skiboot.h>
 #include <opal.h>
 #include <mem_region.h>
+#include <device.h>
+#include <timebase.h>
+#include <timer.h>
+#include <time-utils.h>
+#include <lock.h>
 
 static uint32_t *fake_ymd;
 static uint64_t *fake_hmsm;
 
+/* incase fake rtc clock not configured use these to store and hold rtc value */
+static struct timer emulation_timer;
+
+/* timebase when synchronization happened */
+static unsigned long tb_synctime;
+
+/* current rtc value */
+struct tm tm_offset;
+
+/* protects tm_offset & tb_synctime */
+struct lock emulation_lock;
+
 static int64_t fake_rtc_write(uint32_t ymd, uint64_t hmsm)
 {
 	*fake_ymd = ymd;
@@ -40,28 +57,95 @@ static int64_t fake_rtc_read(uint32_t *ymd, uint64_t *hmsm)
 	return OPAL_SUCCESS;
 }
 
+static int64_t emulated_rtc_write(uint32_t ymd, uint64_t hmsm)
+{
+	lock(&emulation_lock);
+	datetime_to_tm(ymd, hmsm, &tm_offset);
+	tb_synctime = mftb();
+	unlock(&emulation_lock);
+
+	return OPAL_SUCCESS;
+}
+
+static int64_t emulated_rtc_read(uint32_t *ymd, uint64_t *hmsm)
+{
+	if (!ymd || !hmsm)
+		return OPAL_PARAMETER;
+
+	lock(&emulation_lock);
+	tm_to_datetime(&tm_offset, ymd, hmsm);
+	unlock(&emulation_lock);
+
+	return OPAL_SUCCESS;
+}
+
+/* update the emulated rtc. In case more than 60 seconds have passed
+ * since last sync then recompute the tm_offset.
+ */
+static void __emulated_rtc_update(struct timer *tm __unused,
+			       void *data __unused)
+{
+	time_t sec;
+
+	if (try_lock(&emulation_lock)) {
+
+		sec = tb_to_secs(mftb() - tb_synctime);
+		tb_synctime = mftb();
+
+		if ((sec + tm_offset.tm_sec) >= 60) {
+			sec += mktime(&tm_offset);
+			gmtime_r(&sec, &tm_offset);
+		} else {
+			tm_offset.tm_sec += sec;
+		}
+
+		unlock(&emulation_lock);
+	}
+	/* reschedule the timer */
+	schedule_timer(&emulation_timer, secs_to_tb(1));
+}
+
 void fake_rtc_init(void)
 {
 	struct mem_region *rtc_region = NULL;
 	uint32_t *rtc = NULL;
+	struct dt_node *np;
 
 	/* Read initial values from reserved memory */
 	rtc_region = find_mem_region("ibm,fake-rtc");
 
-	/* Should we register anyway? */
-	if (!rtc_region) {
-		prlog(PR_TRACE, "No initial RTC value found\n");
-		return;
-	}
+	/* Check if we need to provide emulation */
+	if (rtc_region) {
+		rtc = (uint32_t *) rtc_region->start;
+
+		fake_ymd = rtc;
+		fake_hmsm = ((uint64_t *) &rtc[1]);
+
+		opal_register(OPAL_RTC_READ, fake_rtc_read, 2);
+		opal_register(OPAL_RTC_WRITE, fake_rtc_write, 2);
+
+		prlog(PR_TRACE, "Init fake RTC to 0x%x 0x%llx\n",
+		      *fake_ymd, *fake_hmsm);
 
-	rtc = (uint32_t *) rtc_region->start;
+	} else {
+		const time_t sec = 0;
 
-	fake_ymd = rtc;
-	fake_hmsm = ((uint64_t *) &rtc[1]);
+		/* use a timer to emulate fake rtc */
+		gmtime_r(&sec, &tm_offset);
+		tb_synctime = mftb();
 
-	prlog(PR_TRACE, "Init fake RTC to 0x%x 0x%llx\n",
-	      *fake_ymd, *fake_hmsm);
+		init_lock(&emulation_lock);
+
+		init_timer(&emulation_timer, __emulated_rtc_update, NULL);
+		schedule_timer(&emulation_timer, secs_to_tb(1));
+
+		opal_register(OPAL_RTC_READ, emulated_rtc_read, 2);
+		opal_register(OPAL_RTC_WRITE, emulated_rtc_write, 2);
+
+		prlog(PR_TRACE, "Using emulated mode\n");
+	}
 
-	opal_register(OPAL_RTC_READ, fake_rtc_read, 2);
-	opal_register(OPAL_RTC_WRITE, fake_rtc_write, 2);
+	/* add the fake rtc dt node */
+	np = dt_new(opal_node, "rtc");
+	dt_add_property_strings(np, "compatible", "ibm,opal-rtc");
 }
-- 
2.2.1



More information about the Skiboot mailing list