1 /* readclock - read the real time clock Authors: T. Holm & E. Froese 2 * 3 * Changed to be user-space driver. 4 */ 5 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 /************************************************************************/ 40 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> 54 55 #include "readclock.h" 56 57 #define MACH_ID_ADDR 0xFFFFE /* BIOS Machine ID at FFFF:000E */ 58 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 */ 62 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. 66 * 67 * Machine ID byte information source: 68 * _The Programmer's PC Sourcebook_ by Thom Hogan, 69 * published by Microsoft Press 70 */ 71 72 /* used for logging */ 73 static struct log log = { 74 .name = "cmos_clock", 75 .log_level = LEVEL_INFO, 76 .log_func = default_log 77 }; 78 79 static int read_register(int reg_addr); 80 static int write_register(int reg_addr, int value); 81 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); 87 88 int 89 arch_setup(struct rtc *r) 90 { 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; 96 97 return OK; 98 } 99 100 static int 101 arch_init(void) 102 { 103 int s; 104 unsigned char mach_id, cmos_state; 105 106 if ((s = sys_readbios(MACH_ID_ADDR, &mach_id, sizeof(mach_id))) != OK) { 107 log_warn(&log, "sys_readbios failed: %d.\n", s); 108 109 return -1; 110 } 111 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); 115 116 return -1; 117 } 118 119 cmos_state = read_register(CMOS_STATUS); 120 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); 124 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; 134 } 135 136 return OK; 137 } 138 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 /***********************************************************************/ 149 150 static int 151 arch_get_time(struct tm *t, int flags) 152 { 153 int osec, n; 154 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; 162 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. 168 */ 169 osec = t->tm_sec; 170 n++; 171 } 172 } while (n < 2); 173 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); 180 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); 188 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); 197 } 198 t->tm_mon--; /* Counts from 0. */ 199 200 /* Correct the year, good until 2080. */ 201 if (t->tm_year < 80) 202 t->tm_year += 100; 203 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; 208 } 209 210 return OK; 211 } 212 213 static int 214 read_register(int reg_addr) 215 { 216 u32_t r; 217 218 if (sys_outb(RTC_INDEX, reg_addr) != OK) { 219 log_warn(&log, "outb failed of %x\n", RTC_INDEX); 220 return -1; 221 } 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; 226 } 227 return r; 228 } 229 230 /***********************************************************************/ 231 /* */ 232 /* arch_set_time( time ) */ 233 /* */ 234 /* Set the CMOS RTC to the time found in the structure. */ 235 /* */ 236 /***********************************************************************/ 237 238 static int 239 arch_set_time(struct tm *t, int flags) 240 { 241 int regA, regB; 242 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.) 247 */ 248 write_register(RTC_REG_A, RTC_A_DV_OK | RTC_A_RS_DEF); 249 write_register(RTC_REG_B, RTC_B_24); 250 } 251 252 /* Inhibit updates. */ 253 regB = read_register(RTC_REG_B); 254 write_register(RTC_REG_B, regB | RTC_B_SET); 255 256 t->tm_mon++; /* Counts from 1. */ 257 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; 262 } 263 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); 272 } 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); 279 280 /* Stop the clock. */ 281 regA = read_register(RTC_REG_A); 282 write_register(RTC_REG_A, regA | RTC_A_DV_STOP); 283 284 /* Allow updates and restart the clock. */ 285 write_register(RTC_REG_B, regB); 286 write_register(RTC_REG_A, regA); 287 288 return OK; 289 } 290 291 static int 292 write_register(int reg_addr, int value) 293 { 294 if (sys_outb(RTC_INDEX, reg_addr) != OK) { 295 log_warn(&log, "outb failed of %x\n", RTC_INDEX); 296 return -1; 297 } 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; 302 } 303 304 return OK; 305 } 306 307 static int 308 arch_pwr_off(void) 309 { 310 /* Not Implemented */ 311 return ENOSYS; 312 } 313 314 static void 315 arch_exit(void) 316 { 317 /* Nothing to clean up here */ 318 log_debug(&log, "Exiting..."); 319 } 320 321