1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ 28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ 29 /* All Rights Reserved */ 30 31 /* Copyright (c) 1987, 1988 Microsoft Corporation */ 32 /* All Rights Reserved */ 33 34 #pragma ident "%Z%%M% %I% %E% SMI" 35 36 #include <sys/param.h> 37 #include <sys/time.h> 38 #include <sys/systm.h> 39 40 #include <sys/cpuvar.h> 41 #include <sys/clock.h> 42 #include <sys/debug.h> 43 #include <sys/rtc.h> 44 #include <sys/archsystm.h> 45 #include <sys/sysmacros.h> 46 #include <sys/lockstat.h> 47 #include <sys/stat.h> 48 #include <sys/sunddi.h> 49 50 static int todpc_rtcget(unsigned char *buf); 51 static void todpc_rtcput(unsigned char *buf); 52 53 /* 54 * Machine-dependent clock routines. 55 */ 56 57 /* 58 * Write the specified time into the clock chip. 59 * Must be called with tod_lock held. 60 */ 61 /*ARGSUSED*/ 62 static void 63 todpc_set(tod_ops_t *top, timestruc_t ts) 64 { 65 todinfo_t tod = utc_to_tod(ts.tv_sec - ggmtl()); 66 struct rtc_t rtc; 67 68 ASSERT(MUTEX_HELD(&tod_lock)); 69 70 if (todpc_rtcget((unsigned char *)&rtc)) 71 return; 72 73 /* 74 * rtc bytes are in binary-coded decimal, so we have to convert. 75 * We assume that we wrap the rtc year back to zero at 2000. 76 */ 77 /* LINTED: YRBASE = 0 for x86 */ 78 tod.tod_year -= YRBASE; 79 if (tod.tod_year >= 100) { 80 tod.tod_year -= 100; 81 rtc.rtc_century = BYTE_TO_BCD(20); /* 20xx year */ 82 } else 83 rtc.rtc_century = BYTE_TO_BCD(19); /* 19xx year */ 84 rtc.rtc_yr = BYTE_TO_BCD(tod.tod_year); 85 rtc.rtc_mon = BYTE_TO_BCD(tod.tod_month); 86 rtc.rtc_dom = BYTE_TO_BCD(tod.tod_day); 87 /* dow < 10, so no conversion */ 88 rtc.rtc_dow = (unsigned char)tod.tod_dow; 89 rtc.rtc_hr = BYTE_TO_BCD(tod.tod_hour); 90 rtc.rtc_min = BYTE_TO_BCD(tod.tod_min); 91 rtc.rtc_sec = BYTE_TO_BCD(tod.tod_sec); 92 93 todpc_rtcput((unsigned char *)&rtc); 94 } 95 96 /* 97 * Read the current time from the clock chip and convert to UNIX form. 98 * Assumes that the year in the clock chip is valid. 99 * Must be called with tod_lock held. 100 */ 101 /*ARGSUSED*/ 102 static timestruc_t 103 todpc_get(tod_ops_t *top) 104 { 105 timestruc_t ts; 106 todinfo_t tod; 107 struct rtc_t rtc; 108 int compute_century; 109 static int century_warn = 1; /* only warn once, not each time called */ 110 static int range_warn = 1; 111 112 ASSERT(MUTEX_HELD(&tod_lock)); 113 114 if (todpc_rtcget((unsigned char *)&rtc)) { 115 ts.tv_sec = 0; 116 ts.tv_nsec = 0; 117 tod_fault_reset(); 118 return (ts); 119 } 120 121 /* assume that we wrap the rtc year back to zero at 2000 */ 122 tod.tod_year = BCD_TO_BYTE(rtc.rtc_yr); 123 if (tod.tod_year < 69) { 124 if (range_warn && tod.tod_year > 38) { 125 cmn_err(CE_WARN, "hardware real-time clock is out " 126 "of range -- time needs to be reset"); 127 range_warn = 0; 128 } 129 tod.tod_year += 100 + YRBASE; /* 20xx year */ 130 compute_century = 20; 131 } else { 132 /* LINTED: YRBASE = 0 for x86 */ 133 tod.tod_year += YRBASE; /* 19xx year */ 134 compute_century = 19; 135 } 136 if (century_warn && BCD_TO_BYTE(rtc.rtc_century) != compute_century) { 137 cmn_err(CE_NOTE, 138 "The hardware real-time clock appears to have the " 139 "wrong century: %d.\nSolaris will still operate " 140 "correctly, but other OS's/firmware agents may " 141 "not.\nUse date(1) to set the date to the current " 142 "time to correct the RTC.", 143 BCD_TO_BYTE(rtc.rtc_century)); 144 century_warn = 0; 145 } 146 tod.tod_month = BCD_TO_BYTE(rtc.rtc_mon); 147 tod.tod_day = BCD_TO_BYTE(rtc.rtc_dom); 148 tod.tod_dow = rtc.rtc_dow; /* dow < 10, so no conversion needed */ 149 tod.tod_hour = BCD_TO_BYTE(rtc.rtc_hr); 150 tod.tod_min = BCD_TO_BYTE(rtc.rtc_min); 151 tod.tod_sec = BCD_TO_BYTE(rtc.rtc_sec); 152 153 ts.tv_sec = tod_to_utc(tod) + ggmtl(); 154 ts.tv_nsec = 0; 155 156 return (ts); 157 } 158 159 /* 160 * Routine to read contents of real time clock to the specified buffer. 161 * Returns ENXIO if clock not valid, or EAGAIN if clock data cannot be read 162 * else 0. 163 * The routine will busy wait for the Update-In-Progress flag to clear. 164 * On completion of the reads the Seconds register is re-read and the 165 * UIP flag is rechecked to confirm that an clock update did not occur 166 * during the accesses. Routine will error exit after 256 attempts. 167 * (See bugid 1158298.) 168 * Routine returns RTC_NREG (which is 15) bytes of data, as given in the 169 * technical reference. This data includes both time and status registers. 170 */ 171 172 static int 173 todpc_rtcget(unsigned char *buf) 174 { 175 unsigned char reg; 176 int i; 177 int retries = 256; 178 unsigned char *rawp; 179 180 ASSERT(MUTEX_HELD(&tod_lock)); 181 182 outb(RTC_ADDR, RTC_D); /* check if clock valid */ 183 reg = inb(RTC_DATA); 184 if ((reg & RTC_VRT) == 0) 185 return (ENXIO); 186 187 checkuip: 188 if (retries-- < 0) 189 return (EAGAIN); 190 outb(RTC_ADDR, RTC_A); /* check if update in progress */ 191 reg = inb(RTC_DATA); 192 if (reg & RTC_UIP) { 193 tenmicrosec(); 194 goto checkuip; 195 } 196 197 for (i = 0, rawp = buf; i < RTC_NREG; i++) { 198 outb(RTC_ADDR, i); 199 *rawp++ = inb(RTC_DATA); 200 } 201 outb(RTC_ADDR, RTC_CENTURY); /* do century */ 202 ((struct rtc_t *)buf)->rtc_century = inb(RTC_DATA); 203 outb(RTC_ADDR, 0); /* re-read Seconds register */ 204 reg = inb(RTC_DATA); 205 if (reg != ((struct rtc_t *)buf)->rtc_sec || 206 (((struct rtc_t *)buf)->rtc_statusa & RTC_UIP)) 207 /* update occured during reads */ 208 goto checkuip; 209 210 return (0); 211 } 212 213 /* 214 * This routine writes the contents of the given buffer to the real time 215 * clock. It is given RTC_NREGP bytes of data, which are the 10 bytes used 216 * to write the time and set the alarm. It should be called with the priority 217 * raised to 5. 218 */ 219 static void 220 todpc_rtcput(unsigned char *buf) 221 { 222 unsigned char reg; 223 int i; 224 225 outb(RTC_ADDR, RTC_B); 226 reg = inb(RTC_DATA); 227 outb(RTC_ADDR, RTC_B); 228 outb(RTC_DATA, reg | RTC_SET); /* allow time set now */ 229 for (i = 0; i < RTC_NREGP; i++) { /* set the time */ 230 outb(RTC_ADDR, i); 231 outb(RTC_DATA, buf[i]); 232 } 233 outb(RTC_ADDR, RTC_CENTURY); /* do century */ 234 outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_century); 235 outb(RTC_ADDR, RTC_B); 236 outb(RTC_DATA, reg & ~RTC_SET); /* allow time update */ 237 } 238 239 static tod_ops_t todpc_ops = { 240 TOD_OPS_VERSION, 241 todpc_get, 242 todpc_set, 243 NULL 244 }; 245 246 /* 247 * Initialize for the default TOD ops vector for use on hardware. 248 */ 249 250 tod_ops_t *tod_ops = &todpc_ops; 251