backported more fixes from 256boss
[bootcensus] / src / rtc.c
1 /*
2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018-2019  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <time.h>
20 #include <asmops.h>
21 #include "rtc.h"
22
23 /* CMOS I/O ports */
24 #define PORT_CTL        0x70
25 #define PORT_DATA       0x71
26
27 /* CMOS RTC registers */
28 #define REG_SEC                 0
29 #define REG_ALARM_SEC   1
30 #define REG_MIN                 2
31 #define REG_ALARM_MIN   3
32 #define REG_HOUR                4
33 #define REG_ALARM_HOUR  5
34 #define REG_WEEKDAY             6
35 #define REG_DAY                 7
36 #define REG_MONTH               8
37 #define REG_YEAR                9
38 #define REG_STATA               10
39 #define REG_STATB               11
40 #define REG_STATC               12
41 #define REG_STATD               13
42
43 #define STATA_BUSY      (1 << 7)
44 #define STATB_24HR      (1 << 1)
45 #define STATB_BIN       (1 << 2)
46
47 #define HOUR_PM_BIT             (1 << 7)
48
49 #define BCD_TO_BIN(x)   ((((x) >> 4) & 0xf) * 10 + ((x) & 0xf))
50
51 static void read_rtc(struct tm *tm);
52 static int read_reg(int reg);
53
54
55 void init_rtc(void)
56 {
57         struct tm tm;
58
59         read_rtc(&tm);
60         start_time = mktime(&tm);
61
62         printf("System real-time clock: %s", asctime(&tm));
63 }
64
65
66 static void read_rtc(struct tm *tm)
67 {
68         int statb, pm;
69
70         /* wait for any clock updates to finish */
71         while(read_reg(REG_STATA) & STATA_BUSY);
72
73         tm->tm_sec = read_reg(REG_SEC);
74         tm->tm_min = read_reg(REG_MIN);
75         tm->tm_hour = read_reg(REG_HOUR);
76         tm->tm_mday = read_reg(REG_DAY);
77         tm->tm_mon = read_reg(REG_MONTH);
78         tm->tm_year = read_reg(REG_YEAR);
79
80         /* in 12hour mode, bit 7 means post-meridiem */
81         pm = tm->tm_hour & HOUR_PM_BIT;
82         tm->tm_hour &= ~HOUR_PM_BIT;
83
84         /* convert to binary if needed */
85         statb = read_reg(REG_STATB);
86         if(!(statb & STATB_BIN)) {
87                 tm->tm_sec = BCD_TO_BIN(tm->tm_sec);
88                 tm->tm_min = BCD_TO_BIN(tm->tm_min);
89                 tm->tm_hour = BCD_TO_BIN(tm->tm_hour);
90                 tm->tm_mday = BCD_TO_BIN(tm->tm_mday);
91                 tm->tm_mon = BCD_TO_BIN(tm->tm_mon);
92                 tm->tm_year = BCD_TO_BIN(tm->tm_year);
93         }
94
95         /* make the year an offset from 1900 */
96         if(tm->tm_year < 100) {
97                 tm->tm_year += 100;
98         } else {
99                 tm->tm_year -= 1900;
100         }
101
102         /* if tm_hour is in 12h mode, convert to 24h */
103         if(!(statb & STATB_24HR)) {
104                 if(tm->tm_hour == 12) {
105                         tm->tm_hour = 0;
106                 }
107                 if(pm) {
108                         tm->tm_hour += 12;
109                 }
110         }
111
112         tm->tm_mon -= 1;        /* we want months to start from 0 */
113 }
114
115 static int read_reg(int reg)
116 {
117         unsigned char val;
118         outb(reg, PORT_CTL);
119         iodelay();
120         val = inb(PORT_DATA);
121         iodelay();
122         return val;
123 }