Merge branch 'master' of github.com:jtsiomb/pcboot
[bootcensus] / src / rtc.c
diff --git a/src/rtc.c b/src/rtc.c
new file mode 100644 (file)
index 0000000..40eca9a
--- /dev/null
+++ b/src/rtc.c
@@ -0,0 +1,123 @@
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <time.h>
+#include <asmops.h>
+#include "rtc.h"
+
+/* CMOS I/O ports */
+#define PORT_CTL       0x70
+#define PORT_DATA      0x71
+
+/* CMOS RTC registers */
+#define REG_SEC                        0
+#define REG_ALARM_SEC  1
+#define REG_MIN                        2
+#define REG_ALARM_MIN  3
+#define REG_HOUR               4
+#define REG_ALARM_HOUR 5
+#define REG_WEEKDAY            6
+#define REG_DAY                        7
+#define REG_MONTH              8
+#define REG_YEAR               9
+#define REG_STATA              10
+#define REG_STATB              11
+#define REG_STATC              12
+#define REG_STATD              13
+
+#define STATA_BUSY     (1 << 7)
+#define STATB_24HR     (1 << 1)
+#define STATB_BIN      (1 << 2)
+
+#define HOUR_PM_BIT            (1 << 7)
+
+#define BCD_TO_BIN(x)  ((((x) >> 4) & 0xf) * 10 + ((x) & 0xf))
+
+static void read_rtc(struct tm *tm);
+static int read_reg(int reg);
+
+
+void init_rtc(void)
+{
+       struct tm tm;
+
+       read_rtc(&tm);
+       start_time = mktime(&tm);
+
+       printf("System real-time clock: %s", asctime(&tm));
+}
+
+
+static void read_rtc(struct tm *tm)
+{
+       int statb, pm;
+
+       /* wait for any clock updates to finish */
+       while(read_reg(REG_STATA) & STATA_BUSY);
+
+       tm->tm_sec = read_reg(REG_SEC);
+       tm->tm_min = read_reg(REG_MIN);
+       tm->tm_hour = read_reg(REG_HOUR);
+       tm->tm_mday = read_reg(REG_DAY);
+       tm->tm_mon = read_reg(REG_MONTH);
+       tm->tm_year = read_reg(REG_YEAR);
+
+       /* in 12hour mode, bit 7 means post-meridiem */
+       pm = tm->tm_hour & HOUR_PM_BIT;
+       tm->tm_hour &= ~HOUR_PM_BIT;
+
+       /* convert to binary if needed */
+       statb = read_reg(REG_STATB);
+       if(!(statb & STATB_BIN)) {
+               tm->tm_sec = BCD_TO_BIN(tm->tm_sec);
+               tm->tm_min = BCD_TO_BIN(tm->tm_min);
+               tm->tm_hour = BCD_TO_BIN(tm->tm_hour);
+               tm->tm_mday = BCD_TO_BIN(tm->tm_mday);
+               tm->tm_mon = BCD_TO_BIN(tm->tm_mon);
+               tm->tm_year = BCD_TO_BIN(tm->tm_year);
+       }
+
+       /* make the year an offset from 1900 */
+       if(tm->tm_year < 100) {
+               tm->tm_year += 100;
+       } else {
+               tm->tm_year -= 1900;
+       }
+
+       /* if tm_hour is in 12h mode, convert to 24h */
+       if(!(statb & STATB_24HR)) {
+               if(tm->tm_hour == 12) {
+                       tm->tm_hour = 0;
+               }
+               if(pm) {
+                       tm->tm_hour += 12;
+               }
+       }
+
+       tm->tm_mon -= 1;        /* we want months to start from 0 */
+}
+
+static int read_reg(int reg)
+{
+       unsigned char val;
+       outb(reg, PORT_CTL);
+       iodelay();
+       val = inb(PORT_DATA);
+       iodelay();
+       return val;
+}