[RFC v2 PATCH 2/7] purgatory/x86: Support CMOS RTC

Hidehiro Kawai hidehiro.kawai.ez at hitachi.com
Mon Feb 22 03:56:21 PST 2016


Support CMOS RTC based on MC148618A RTC by overriding get_unix_time().
This is needed for the timeout API previously added.

Signed-off-by: Hidehiro Kawai <hidehiro.kawai.ez at hitachi.com>
---
 purgatory/arch/i386/Makefile   |    1 
 purgatory/arch/i386/rtc_cmos.c |  107 ++++++++++++++++++++++++++++++++++++++++
 purgatory/arch/x86_64/Makefile |    1 
 3 files changed, 109 insertions(+)
 create mode 100644 purgatory/arch/i386/rtc_cmos.c

diff --git a/purgatory/arch/i386/Makefile b/purgatory/arch/i386/Makefile
index 1532219..cb77328 100644
--- a/purgatory/arch/i386/Makefile
+++ b/purgatory/arch/i386/Makefile
@@ -13,6 +13,7 @@ i386_PURGATORY_SRCS += purgatory/arch/i386/console-x86.c
 i386_PURGATORY_SRCS += purgatory/arch/i386/vga.c
 i386_PURGATORY_SRCS += purgatory/arch/i386/pic.c
 i386_PURGATORY_SRCS += purgatory/arch/i386/crashdump_backup.c
+i386_PURGATORY_SRCS += purgatory/arch/i386/rtc_cmos.c
 
 dist += purgatory/arch/i386/Makefile $(i386_PURGATORY_SRCS)	\
 	purgatory/arch/i386/purgatory-x86.h			\
diff --git a/purgatory/arch/i386/rtc_cmos.c b/purgatory/arch/i386/rtc_cmos.c
new file mode 100644
index 0000000..9bef1b3
--- /dev/null
+++ b/purgatory/arch/i386/rtc_cmos.c
@@ -0,0 +1,107 @@
+#include <sys/io.h>
+#include "time.h"
+
+#define CMOS_PORT0		0x70
+#define  CMOS_NMI_DISABLE	0x80
+#define CMOS_PORT1		0x71
+
+#define RTC_SECONDS		0x00
+#define RTC_MINUTES		0x02
+#define RTC_HOURS		0x04
+#define RTC_DAY			0x07
+#define RTC_MONTH		0x08
+#define RTC_YEAR		0x09
+
+#define RTC_REGA		0x0a
+#define  RTC_UIP		0x80
+#define RTC_REGB		0x0b
+#define  RTC_HOURFORM		0x02
+#define  RTC_DM			0x04
+
+static int is_bcd;	/* 1 if the Data Mode is BCD */
+static int is_12hr;	/* 1 if the Hour Format is 12-hr mode */
+
+unsigned char read_cmos(int idx)
+{
+	static unsigned char nmi_disable_bit = 0xff;
+
+	/* Preserve NMI Disable bit at the first time */
+	if (nmi_disable_bit == 0xff)
+		nmi_disable_bit = inb(CMOS_PORT0) & CMOS_NMI_DISABLE;
+
+	outb(nmi_disable_bit | (idx & 0x7f), CMOS_PORT0);
+
+	return inb(CMOS_PORT1);
+}
+
+/**
+ * Return UNIX-epoch time.
+ *
+ * If the RTC is about to be updated (UIP bit is set), this function wait
+ * for that.  It takes at most 2228us with MC146818A RTC according to
+ * the specification.  Modern MC146818A-compatible RTCs in chipset will work
+ * faster, but not so much.  So you shouldn't call this function too often.
+ */
+time64_t get_unix_time(void)
+{
+	static int is_first_time = 1;
+	unsigned char byte;
+	int ss, mm, hh;
+	int day, mon, year;
+	int is_pm;
+
+	/* Check the modes at the first time. */
+	if (is_first_time) {
+		byte = read_cmos(RTC_REGB);
+		if (!(byte & RTC_DM))
+			is_bcd = 1;
+
+		/*
+		 * NOTE: Is this needed? Linux kernel doesn't support
+		 * 12-hr mode.
+		 */
+		if (!(byte & RTC_HOURFORM))
+			is_12hr = 1;
+
+		is_first_time = 0;
+	}
+
+	/* Wait for the clock update to be done if it is in progress */
+	do {
+		byte = read_cmos(RTC_REGA);
+	} while (byte & RTC_UIP);
+
+	/*
+	 * Now, we have 244us at least until the clock update will start.
+	 */
+	ss = read_cmos(RTC_SECONDS);
+	mm = read_cmos(RTC_MINUTES);
+	hh = read_cmos(RTC_HOURS);
+	day = read_cmos(RTC_DAY);
+	mon = read_cmos(RTC_MONTH);
+	year = read_cmos(RTC_YEAR);
+
+	/* If the RTC is in 12-hr mode, bit-7 of HOUR byte indicates PM. */
+	is_pm = (hh & 0x80);
+	hh &= 0x7f; /* drop AM/PM bit */
+
+	if (is_bcd) {
+		ss = (ss >> 4) * 10 + (ss & 0xf);
+		mm = (mm >> 4) * 10 + (mm & 0xf);
+		hh = (hh >> 4) * 10 + (hh & 0xf);
+		day = (day >> 4) * 10 + (day & 0xf);
+		mon = (mon >> 4) * 10 + (mon & 0xf);
+		year = (year >> 4) * 10 + (year & 0xf) +
+			2000; /* FIXME? */
+	}
+
+	if (is_12hr) {
+		if (is_pm && hh != 12)
+			hh += 12;
+
+		if (!is_pm && hh == 12)
+			hh = 0;
+	}
+
+	return date2unix(year, mon, day, hh, mm, ss);
+}
diff --git a/purgatory/arch/x86_64/Makefile b/purgatory/arch/x86_64/Makefile
index 7300937..bca1f71 100644
--- a/purgatory/arch/x86_64/Makefile
+++ b/purgatory/arch/x86_64/Makefile
@@ -22,5 +22,6 @@ x86_64_PURGATORY_SRCS += purgatory/arch/i386/crashdump_backup.c
 x86_64_PURGATORY_SRCS += purgatory/arch/i386/console-x86.c
 x86_64_PURGATORY_SRCS += purgatory/arch/i386/vga.c
 x86_64_PURGATORY_SRCS += purgatory/arch/i386/pic.c
+x86_64_PURGATORY_SRCS += purgatory/arch/i386/rtc_cmos.c
 
 x86_64_PURGATORY_EXTRA_CFLAGS = -mcmodel=large





More information about the kexec mailing list