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 #include <sys/svrctl.h> 55 56 #include "readclock.h" 57 58 #define MACH_ID_ADDR 0xFFFFE /* BIOS Machine ID at FFFF:000E */ 59 60 #define PC_AT 0xFC /* Machine ID byte for PC/AT, 61 * PC/XT286, and PS/2 Models 50, 60 */ 62 #define PS_386 0xF8 /* Machine ID byte for PS/2 Model 80 */ 63 64 /* Manufacturers usually use the ID value of the IBM model they emulate. 65 * However some manufacturers, notably HP and COMPAQ, have had different 66 * ideas in the past. 67 * 68 * Machine ID byte information source: 69 * _The Programmer's PC Sourcebook_ by Thom Hogan, 70 * published by Microsoft Press 71 */ 72 73 /* used for logging */ 74 static struct log log = { 75 .name = "cmos_clock", 76 .log_level = LEVEL_INFO, 77 .log_func = default_log 78 }; 79 80 static int read_register(int reg_addr); 81 static int write_register(int reg_addr, int value); 82 83 static int arch_init(void); 84 static int arch_get_time(struct tm *t, int flags); 85 static int arch_set_time(struct tm *t, int flags); 86 static int arch_pwr_off(void); 87 static void arch_exit(void); 88 89 int 90 arch_setup(struct rtc *r) 91 { 92 r->init = arch_init; 93 r->get_time = arch_get_time; 94 r->set_time = arch_set_time; 95 r->pwr_off = arch_pwr_off; 96 r->exit = arch_exit; 97 98 return OK; 99 } 100 101 static int 102 arch_init(void) 103 { 104 int s; 105 unsigned char mach_id, cmos_state; 106 107 if ((s = sys_readbios(MACH_ID_ADDR, &mach_id, sizeof(mach_id))) != OK) { 108 log_warn(&log, "sys_readbios failed: %d.\n", s); 109 110 return -1; 111 } 112 113 if (mach_id != PS_386 && mach_id != PC_AT) { 114 log_warn(&log, "Machine ID unknown."); 115 log_warn(&log, "Machine ID byte = %02x\n", mach_id); 116 117 return -1; 118 } 119 120 cmos_state = read_register(CMOS_STATUS); 121 122 if (cmos_state & (CS_LOST_POWER | CS_BAD_CHKSUM | CS_BAD_TIME)) { 123 log_warn(&log, "CMOS RAM error(s) found..."); 124 log_warn(&log, "CMOS state = 0x%02x\n", cmos_state); 125 126 if (cmos_state & CS_LOST_POWER) 127 log_warn(&log, 128 "RTC lost power. Reset CMOS RAM with SETUP."); 129 if (cmos_state & CS_BAD_CHKSUM) 130 log_warn(&log, "CMOS RAM checksum is bad. Run SETUP."); 131 if (cmos_state & CS_BAD_TIME) 132 log_warn(&log, 133 "Time invalid in CMOS RAM. Reset clock."); 134 return -1; 135 } 136 137 return OK; 138 } 139 140 /***********************************************************************/ 141 /* */ 142 /* arch_get_time( time ) */ 143 /* */ 144 /* Update the structure pointed to by time with the current time */ 145 /* as read from CMOS RAM of the RTC. */ 146 /* If necessary, the time is converted into a binary format before */ 147 /* being stored in the structure. */ 148 /* */ 149 /***********************************************************************/ 150 151 static int 152 arch_get_time(struct tm *t, int flags) 153 { 154 int osec, n; 155 156 do { 157 osec = -1; 158 n = 0; 159 do { 160 /* Clock update in progress? */ 161 if (read_register(RTC_REG_A) & RTC_A_UIP) 162 continue; 163 164 t->tm_sec = read_register(RTC_SEC); 165 if (t->tm_sec != osec) { 166 /* Seconds changed. First from -1, then because the 167 * clock ticked, which is what we're waiting for to 168 * get a precise reading. 169 */ 170 osec = t->tm_sec; 171 n++; 172 } 173 } while (n < 2); 174 175 /* Read the other registers. */ 176 t->tm_min = read_register(RTC_MIN); 177 t->tm_hour = read_register(RTC_HOUR); 178 t->tm_mday = read_register(RTC_MDAY); 179 t->tm_mon = read_register(RTC_MONTH); 180 t->tm_year = read_register(RTC_YEAR); 181 182 /* Time stable? */ 183 } while (read_register(RTC_SEC) != t->tm_sec 184 || read_register(RTC_MIN) != t->tm_min 185 || read_register(RTC_HOUR) != t->tm_hour 186 || read_register(RTC_MDAY) != t->tm_mday 187 || read_register(RTC_MONTH) != t->tm_mon 188 || read_register(RTC_YEAR) != t->tm_year); 189 190 if ((read_register(RTC_REG_B) & RTC_B_DM_BCD) == 0) { 191 /* Convert BCD to binary (default RTC mode). */ 192 t->tm_year = bcd_to_dec(t->tm_year); 193 t->tm_mon = bcd_to_dec(t->tm_mon); 194 t->tm_mday = bcd_to_dec(t->tm_mday); 195 t->tm_hour = bcd_to_dec(t->tm_hour); 196 t->tm_min = bcd_to_dec(t->tm_min); 197 t->tm_sec = bcd_to_dec(t->tm_sec); 198 } 199 t->tm_mon--; /* Counts from 0. */ 200 201 /* Correct the year, good until 2080. */ 202 if (t->tm_year < 80) 203 t->tm_year += 100; 204 205 if ((flags & RTCDEV_Y2KBUG) == RTCDEV_Y2KBUG) { 206 /* Clock with Y2K bug, interpret 1980 as 2000, good until 2020. */ 207 if (t->tm_year < 100) 208 t->tm_year += 20; 209 } 210 211 return OK; 212 } 213 214 static int 215 read_register(int reg_addr) 216 { 217 u32_t r; 218 219 if (sys_outb(RTC_INDEX, reg_addr) != OK) { 220 log_warn(&log, "outb failed of %x\n", RTC_INDEX); 221 return -1; 222 } 223 if (sys_inb(RTC_IO, &r) != OK) { 224 log_warn(&log, "inb failed of %x (index %x) failed\n", RTC_IO, 225 reg_addr); 226 return -1; 227 } 228 return r; 229 } 230 231 /***********************************************************************/ 232 /* */ 233 /* arch_set_time( time ) */ 234 /* */ 235 /* Set the CMOS RTC to the time found in the structure. */ 236 /* */ 237 /***********************************************************************/ 238 239 static int 240 arch_set_time(struct tm *t, int flags) 241 { 242 int regA, regB; 243 244 if ((flags & RTCDEV_CMOSREG) == RTCDEV_CMOSREG) { 245 /* Set A and B registers to their proper values according to the AT 246 * reference manual. (For if it gets messed up, but the BIOS doesn't 247 * repair it.) 248 */ 249 write_register(RTC_REG_A, RTC_A_DV_OK | RTC_A_RS_DEF); 250 write_register(RTC_REG_B, RTC_B_24); 251 } 252 253 /* Inhibit updates. */ 254 regB = read_register(RTC_REG_B); 255 write_register(RTC_REG_B, regB | RTC_B_SET); 256 257 t->tm_mon++; /* Counts from 1. */ 258 259 if ((flags & RTCDEV_Y2KBUG) == RTCDEV_Y2KBUG) { 260 /* Set the clock back 20 years to avoid Y2K bug, good until 2020. */ 261 if (t->tm_year >= 100) 262 t->tm_year -= 20; 263 } 264 265 if ((regB & 0x04) == 0) { 266 /* Convert binary to BCD (default RTC mode) */ 267 t->tm_year = dec_to_bcd(t->tm_year % 100); 268 t->tm_mon = dec_to_bcd(t->tm_mon); 269 t->tm_mday = dec_to_bcd(t->tm_mday); 270 t->tm_hour = dec_to_bcd(t->tm_hour); 271 t->tm_min = dec_to_bcd(t->tm_min); 272 t->tm_sec = dec_to_bcd(t->tm_sec); 273 } 274 write_register(RTC_YEAR, t->tm_year); 275 write_register(RTC_MONTH, t->tm_mon); 276 write_register(RTC_MDAY, t->tm_mday); 277 write_register(RTC_HOUR, t->tm_hour); 278 write_register(RTC_MIN, t->tm_min); 279 write_register(RTC_SEC, t->tm_sec); 280 281 /* Stop the clock. */ 282 regA = read_register(RTC_REG_A); 283 write_register(RTC_REG_A, regA | RTC_A_DV_STOP); 284 285 /* Allow updates and restart the clock. */ 286 write_register(RTC_REG_B, regB); 287 write_register(RTC_REG_A, regA); 288 289 return OK; 290 } 291 292 static int 293 write_register(int reg_addr, int value) 294 { 295 if (sys_outb(RTC_INDEX, reg_addr) != OK) { 296 log_warn(&log, "outb failed of %x\n", RTC_INDEX); 297 return -1; 298 } 299 if (sys_outb(RTC_IO, value) != OK) { 300 log_warn(&log, "outb failed of %x (index %x)\n", RTC_IO, 301 reg_addr); 302 return -1; 303 } 304 305 return OK; 306 } 307 308 static int 309 arch_pwr_off(void) 310 { 311 /* Not Implemented */ 312 return ENOSYS; 313 } 314 315 static void 316 arch_exit(void) 317 { 318 /* Nothing to clean up here */ 319 log_debug(&log, "Exiting..."); 320 } 321 322