Remove building with NOCRYPTO option
[minix.git] / minix / drivers / clock / readclock / arch / i386 / arch_readclock.c
blob07efbca69fc59f8342f0f18f1ac6f59d10811554
1 /* readclock - read the real time clock Authors: T. Holm & E. Froese
3 * Changed to be user-space driver.
4 */
6 /************************************************************************/
7 /* */
8 /* readclock.c */
9 /* */
10 /* Read the clock value from the 64 byte CMOS RAM */
11 /* area, then set system time. */
12 /* */
13 /* If the machine ID byte is 0xFC or 0xF8, the device */
14 /* /dev/mem exists and can be opened for reading, */
15 /* and no errors in the CMOS RAM are reported by the */
16 /* RTC, then the time is read from the clock RAM */
17 /* area maintained by the RTC. */
18 /* */
19 /* The clock RAM values are decoded and fed to mktime */
20 /* to make a time_t value, then stime(2) is called. */
21 /* */
22 /* This fails if: */
23 /* */
24 /* If the machine ID does not match 0xFC or 0xF8 (no */
25 /* error message.) */
26 /* */
27 /* If the machine ID is 0xFC or 0xF8 and /dev/mem */
28 /* is missing, or cannot be accessed. */
29 /* */
30 /* If the RTC reports errors in the CMOS RAM. */
31 /* */
32 /************************************************************************/
33 /* origination 1987-Dec-29 efth */
34 /* robustness 1990-Oct-06 C. Sylvain */
35 /* incorp. B. Evans ideas 1991-Jul-06 C. Sylvain */
36 /* set time & calibrate 1992-Dec-17 Kees J. Bot */
37 /* clock timezone 1993-Oct-10 Kees J. Bot */
38 /* set CMOS clock 1994-Jun-12 Kees J. Bot */
39 /************************************************************************/
41 #include <sys/types.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <stdio.h>
45 #include <time.h>
46 #include <errno.h>
47 #include <minix/type.h>
48 #include <minix/const.h>
49 #include <minix/syslib.h>
50 #include <minix/sysutil.h>
51 #include <minix/com.h>
52 #include <minix/log.h>
53 #include <machine/cmos.h>
55 #include "readclock.h"
57 #define MACH_ID_ADDR 0xFFFFE /* BIOS Machine ID at FFFF:000E */
59 #define PC_AT 0xFC /* Machine ID byte for PC/AT,
60 * PC/XT286, and PS/2 Models 50, 60 */
61 #define PS_386 0xF8 /* Machine ID byte for PS/2 Model 80 */
63 /* Manufacturers usually use the ID value of the IBM model they emulate.
64 * However some manufacturers, notably HP and COMPAQ, have had different
65 * ideas in the past.
67 * Machine ID byte information source:
68 * _The Programmer's PC Sourcebook_ by Thom Hogan,
69 * published by Microsoft Press
72 /* used for logging */
73 static struct log log = {
74 .name = "cmos_clock",
75 .log_level = LEVEL_INFO,
76 .log_func = default_log
79 static int read_register(int reg_addr);
80 static int write_register(int reg_addr, int value);
82 static int arch_init(void);
83 static int arch_get_time(struct tm *t, int flags);
84 static int arch_set_time(struct tm *t, int flags);
85 static int arch_pwr_off(void);
86 static void arch_exit(void);
88 int
89 arch_setup(struct rtc *r)
91 r->init = arch_init;
92 r->get_time = arch_get_time;
93 r->set_time = arch_set_time;
94 r->pwr_off = arch_pwr_off;
95 r->exit = arch_exit;
97 return OK;
100 static int
101 arch_init(void)
103 int s;
104 unsigned char mach_id, cmos_state;
106 if ((s = sys_readbios(MACH_ID_ADDR, &mach_id, sizeof(mach_id))) != OK) {
107 log_warn(&log, "sys_readbios failed: %d.\n", s);
109 return -1;
112 if (mach_id != PS_386 && mach_id != PC_AT) {
113 log_warn(&log, "Machine ID unknown.");
114 log_warn(&log, "Machine ID byte = %02x\n", mach_id);
116 return -1;
119 cmos_state = read_register(CMOS_STATUS);
121 if (cmos_state & (CS_LOST_POWER | CS_BAD_CHKSUM | CS_BAD_TIME)) {
122 log_warn(&log, "CMOS RAM error(s) found...");
123 log_warn(&log, "CMOS state = 0x%02x\n", cmos_state);
125 if (cmos_state & CS_LOST_POWER)
126 log_warn(&log,
127 "RTC lost power. Reset CMOS RAM with SETUP.");
128 if (cmos_state & CS_BAD_CHKSUM)
129 log_warn(&log, "CMOS RAM checksum is bad. Run SETUP.");
130 if (cmos_state & CS_BAD_TIME)
131 log_warn(&log,
132 "Time invalid in CMOS RAM. Reset clock.");
133 return -1;
136 return OK;
139 /***********************************************************************/
140 /* */
141 /* arch_get_time( time ) */
142 /* */
143 /* Update the structure pointed to by time with the current time */
144 /* as read from CMOS RAM of the RTC. */
145 /* If necessary, the time is converted into a binary format before */
146 /* being stored in the structure. */
147 /* */
148 /***********************************************************************/
150 static int
151 arch_get_time(struct tm *t, int flags)
153 int osec, n;
155 do {
156 osec = -1;
157 n = 0;
158 do {
159 /* Clock update in progress? */
160 if (read_register(RTC_REG_A) & RTC_A_UIP)
161 continue;
163 t->tm_sec = read_register(RTC_SEC);
164 if (t->tm_sec != osec) {
165 /* Seconds changed. First from -1, then because the
166 * clock ticked, which is what we're waiting for to
167 * get a precise reading.
169 osec = t->tm_sec;
170 n++;
172 } while (n < 2);
174 /* Read the other registers. */
175 t->tm_min = read_register(RTC_MIN);
176 t->tm_hour = read_register(RTC_HOUR);
177 t->tm_mday = read_register(RTC_MDAY);
178 t->tm_mon = read_register(RTC_MONTH);
179 t->tm_year = read_register(RTC_YEAR);
181 /* Time stable? */
182 } while (read_register(RTC_SEC) != t->tm_sec
183 || read_register(RTC_MIN) != t->tm_min
184 || read_register(RTC_HOUR) != t->tm_hour
185 || read_register(RTC_MDAY) != t->tm_mday
186 || read_register(RTC_MONTH) != t->tm_mon
187 || read_register(RTC_YEAR) != t->tm_year);
189 if ((read_register(RTC_REG_B) & RTC_B_DM_BCD) == 0) {
190 /* Convert BCD to binary (default RTC mode). */
191 t->tm_year = bcd_to_dec(t->tm_year);
192 t->tm_mon = bcd_to_dec(t->tm_mon);
193 t->tm_mday = bcd_to_dec(t->tm_mday);
194 t->tm_hour = bcd_to_dec(t->tm_hour);
195 t->tm_min = bcd_to_dec(t->tm_min);
196 t->tm_sec = bcd_to_dec(t->tm_sec);
198 t->tm_mon--; /* Counts from 0. */
200 /* Correct the year, good until 2080. */
201 if (t->tm_year < 80)
202 t->tm_year += 100;
204 if ((flags & RTCDEV_Y2KBUG) == RTCDEV_Y2KBUG) {
205 /* Clock with Y2K bug, interpret 1980 as 2000, good until 2020. */
206 if (t->tm_year < 100)
207 t->tm_year += 20;
210 return OK;
213 static int
214 read_register(int reg_addr)
216 u32_t r;
218 if (sys_outb(RTC_INDEX, reg_addr) != OK) {
219 log_warn(&log, "outb failed of %x\n", RTC_INDEX);
220 return -1;
222 if (sys_inb(RTC_IO, &r) != OK) {
223 log_warn(&log, "inb failed of %x (index %x) failed\n", RTC_IO,
224 reg_addr);
225 return -1;
227 return r;
230 /***********************************************************************/
231 /* */
232 /* arch_set_time( time ) */
233 /* */
234 /* Set the CMOS RTC to the time found in the structure. */
235 /* */
236 /***********************************************************************/
238 static int
239 arch_set_time(struct tm *t, int flags)
241 int regA, regB;
243 if ((flags & RTCDEV_CMOSREG) == RTCDEV_CMOSREG) {
244 /* Set A and B registers to their proper values according to the AT
245 * reference manual. (For if it gets messed up, but the BIOS doesn't
246 * repair it.)
248 write_register(RTC_REG_A, RTC_A_DV_OK | RTC_A_RS_DEF);
249 write_register(RTC_REG_B, RTC_B_24);
252 /* Inhibit updates. */
253 regB = read_register(RTC_REG_B);
254 write_register(RTC_REG_B, regB | RTC_B_SET);
256 t->tm_mon++; /* Counts from 1. */
258 if ((flags & RTCDEV_Y2KBUG) == RTCDEV_Y2KBUG) {
259 /* Set the clock back 20 years to avoid Y2K bug, good until 2020. */
260 if (t->tm_year >= 100)
261 t->tm_year -= 20;
264 if ((regB & 0x04) == 0) {
265 /* Convert binary to BCD (default RTC mode) */
266 t->tm_year = dec_to_bcd(t->tm_year % 100);
267 t->tm_mon = dec_to_bcd(t->tm_mon);
268 t->tm_mday = dec_to_bcd(t->tm_mday);
269 t->tm_hour = dec_to_bcd(t->tm_hour);
270 t->tm_min = dec_to_bcd(t->tm_min);
271 t->tm_sec = dec_to_bcd(t->tm_sec);
273 write_register(RTC_YEAR, t->tm_year);
274 write_register(RTC_MONTH, t->tm_mon);
275 write_register(RTC_MDAY, t->tm_mday);
276 write_register(RTC_HOUR, t->tm_hour);
277 write_register(RTC_MIN, t->tm_min);
278 write_register(RTC_SEC, t->tm_sec);
280 /* Stop the clock. */
281 regA = read_register(RTC_REG_A);
282 write_register(RTC_REG_A, regA | RTC_A_DV_STOP);
284 /* Allow updates and restart the clock. */
285 write_register(RTC_REG_B, regB);
286 write_register(RTC_REG_A, regA);
288 return OK;
291 static int
292 write_register(int reg_addr, int value)
294 if (sys_outb(RTC_INDEX, reg_addr) != OK) {
295 log_warn(&log, "outb failed of %x\n", RTC_INDEX);
296 return -1;
298 if (sys_outb(RTC_IO, value) != OK) {
299 log_warn(&log, "outb failed of %x (index %x)\n", RTC_IO,
300 reg_addr);
301 return -1;
304 return OK;
307 static int
308 arch_pwr_off(void)
310 /* Not Implemented */
311 return ENOSYS;
314 static void
315 arch_exit(void)
317 /* Nothing to clean up here */
318 log_debug(&log, "Exiting...");