Print this page
8680 Time of Day clock error
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/i86pc/io/todpc_subr.c
+++ new/usr/src/uts/i86pc/io/todpc_subr.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
↓ open down ↓ |
11 lines elided |
↑ open up ↑ |
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 + * Copyright 2018 Gary Mills
22 23 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
23 24 */
24 25 /*
25 26 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
26 27 * Use is subject to license terms.
27 28 */
28 29
29 30 /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
30 31 /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
31 32 /* All Rights Reserved */
32 33
33 34 /* Copyright (c) 1987, 1988 Microsoft Corporation */
34 35 /* All Rights Reserved */
35 36
36 37 #include <sys/param.h>
37 38 #include <sys/time.h>
38 39 #include <sys/systm.h>
↓ open down ↓ |
7 lines elided |
↑ open up ↑ |
39 40
40 41 #include <sys/cpuvar.h>
41 42 #include <sys/clock.h>
42 43 #include <sys/debug.h>
43 44 #include <sys/rtc.h>
44 45 #include <sys/archsystm.h>
45 46 #include <sys/sysmacros.h>
46 47 #include <sys/lockstat.h>
47 48 #include <sys/stat.h>
48 49 #include <sys/sunddi.h>
50 +#include <sys/ddi.h>
49 51
50 52 #include <sys/acpi/acpi.h>
51 53 #include <sys/acpica.h>
52 54
53 55 static int todpc_rtcget(unsigned char *buf);
54 56 static void todpc_rtcput(unsigned char *buf);
55 57
56 58 #define CLOCK_RES 1000 /* 1 microsec in nanosecs */
57 59
58 60 int clock_res = CLOCK_RES;
59 61
60 62 /*
61 63 * The minimum sleep time till an alarm can be fired.
62 64 * This can be tuned in /etc/system, but if the value is too small,
63 65 * there is a danger that it will be missed if it takes too long to
64 66 * get from the set point to sleep. Or that it can fire quickly, and
65 67 * generate a power spike on the hardware. And small values are
66 68 * probably only usefull for test setups.
67 69 */
68 70 int clock_min_alarm = 4;
69 71
70 72 /*
71 73 * Machine-dependent clock routines.
72 74 */
73 75
74 76 extern long gmt_lag;
75 77
76 78 struct rtc_offset {
77 79 int8_t loaded;
78 80 uint8_t day_alrm;
79 81 uint8_t mon_alrm;
80 82 uint8_t century;
81 83 };
82 84
83 85 static struct rtc_offset pc_rtc_offset = {0, 0, 0, 0};
84 86
85 87
86 88 /*
87 89 * Entry point for ACPI to pass RTC or other clock values that
88 90 * are useful to TOD.
89 91 */
90 92 void
91 93 pc_tod_set_rtc_offsets(ACPI_TABLE_FADT *fadt) {
92 94 int ok = 0;
93 95
94 96 /*
95 97 * ASSERT is for debugging, but we don't want the machine
96 98 * falling over because for some reason we didn't get a valid
97 99 * pointer.
98 100 */
99 101 ASSERT(fadt);
100 102 if (fadt == NULL) {
101 103 return;
102 104 }
103 105
104 106 if (fadt->DayAlarm) {
105 107 pc_rtc_offset.day_alrm = fadt->DayAlarm;
106 108 ok = 1;
107 109 }
108 110
109 111 if (fadt->MonthAlarm) {
110 112 pc_rtc_offset.mon_alrm = fadt->MonthAlarm;
111 113 ok = 1;
112 114 }
113 115
114 116 if (fadt->Century) {
115 117 pc_rtc_offset.century = fadt->Century;
116 118 ok = 1;
117 119 }
118 120
119 121 pc_rtc_offset.loaded = ok;
120 122 }
121 123
122 124
123 125 /*
124 126 * Write the specified time into the clock chip.
125 127 * Must be called with tod_lock held.
126 128 */
127 129 /*ARGSUSED*/
128 130 static void
129 131 todpc_set(tod_ops_t *top, timestruc_t ts)
130 132 {
131 133 todinfo_t tod = utc_to_tod(ts.tv_sec - ggmtl());
132 134 struct rtc_t rtc;
133 135
134 136 ASSERT(MUTEX_HELD(&tod_lock));
135 137
136 138 if (todpc_rtcget((unsigned char *)&rtc))
137 139 return;
138 140
139 141 /*
140 142 * rtc bytes are in binary-coded decimal, so we have to convert.
141 143 * We assume that we wrap the rtc year back to zero at 2000.
142 144 */
143 145 /* LINTED: YRBASE = 0 for x86 */
144 146 tod.tod_year -= YRBASE;
145 147 if (tod.tod_year >= 100) {
146 148 tod.tod_year -= 100;
147 149 rtc.rtc_century = BYTE_TO_BCD(20); /* 20xx year */
148 150 } else
149 151 rtc.rtc_century = BYTE_TO_BCD(19); /* 19xx year */
150 152 rtc.rtc_yr = BYTE_TO_BCD(tod.tod_year);
151 153 rtc.rtc_mon = BYTE_TO_BCD(tod.tod_month);
152 154 rtc.rtc_dom = BYTE_TO_BCD(tod.tod_day);
153 155 /* dow < 10, so no conversion */
154 156 rtc.rtc_dow = (unsigned char)tod.tod_dow;
155 157 rtc.rtc_hr = BYTE_TO_BCD(tod.tod_hour);
156 158 rtc.rtc_min = BYTE_TO_BCD(tod.tod_min);
157 159 rtc.rtc_sec = BYTE_TO_BCD(tod.tod_sec);
158 160
159 161 todpc_rtcput((unsigned char *)&rtc);
160 162 }
161 163
162 164 /*
163 165 * Read the current time from the clock chip and convert to UNIX form.
164 166 * Assumes that the year in the clock chip is valid.
165 167 * Must be called with tod_lock held.
166 168 */
167 169 /*ARGSUSED*/
168 170 static timestruc_t
169 171 todpc_get(tod_ops_t *top)
170 172 {
171 173 timestruc_t ts;
172 174 todinfo_t tod;
173 175 struct rtc_t rtc;
174 176 int compute_century;
175 177 static int century_warn = 1; /* only warn once, not each time called */
176 178 static int range_warn = 1;
177 179
178 180 ASSERT(MUTEX_HELD(&tod_lock));
179 181
180 182 if (todpc_rtcget((unsigned char *)&rtc)) {
181 183 tod_status_set(TOD_GET_FAILED);
182 184 return (hrestime);
183 185 }
184 186
185 187 /* assume that we wrap the rtc year back to zero at 2000 */
186 188 tod.tod_year = BCD_TO_BYTE(rtc.rtc_yr);
187 189 if (tod.tod_year < 69) {
188 190 if (range_warn && tod.tod_year > 38) {
189 191 cmn_err(CE_WARN, "hardware real-time clock is out "
190 192 "of range -- time needs to be reset");
191 193 range_warn = 0;
192 194 }
193 195 tod.tod_year += 100 + YRBASE; /* 20xx year */
194 196 compute_century = 20;
195 197 } else {
196 198 /* LINTED: YRBASE = 0 for x86 */
197 199 tod.tod_year += YRBASE; /* 19xx year */
198 200 compute_century = 19;
199 201 }
200 202 if (century_warn && BCD_TO_BYTE(rtc.rtc_century) != compute_century) {
201 203 cmn_err(CE_NOTE,
202 204 "The hardware real-time clock appears to have the "
203 205 "wrong century: %d.\nSolaris will still operate "
204 206 "correctly, but other OS's/firmware agents may "
205 207 "not.\nUse date(1) to set the date to the current "
206 208 "time to correct the RTC.",
207 209 BCD_TO_BYTE(rtc.rtc_century));
208 210 century_warn = 0;
209 211 }
210 212 tod.tod_month = BCD_TO_BYTE(rtc.rtc_mon);
211 213 tod.tod_day = BCD_TO_BYTE(rtc.rtc_dom);
212 214 tod.tod_dow = rtc.rtc_dow; /* dow < 10, so no conversion needed */
213 215 tod.tod_hour = BCD_TO_BYTE(rtc.rtc_hr);
214 216 tod.tod_min = BCD_TO_BYTE(rtc.rtc_min);
215 217 tod.tod_sec = BCD_TO_BYTE(rtc.rtc_sec);
216 218
217 219 /* read was successful so ensure failure flag is clear */
218 220 tod_status_clear(TOD_GET_FAILED);
219 221
220 222 ts.tv_sec = tod_to_utc(tod) + ggmtl();
221 223 ts.tv_nsec = 0;
222 224
223 225 return (ts);
224 226 }
225 227
226 228 #include <sys/promif.h>
227 229 /*
228 230 * Write the specified wakeup alarm into the clock chip.
229 231 * Must be called with tod_lock held.
230 232 */
231 233 void
232 234 /*ARGSUSED*/
233 235 todpc_setalarm(tod_ops_t *top, int nsecs)
234 236 {
235 237 struct rtc_t rtc;
236 238 int delta, asec, amin, ahr, adom, amon;
237 239 int day_alrm = pc_rtc_offset.day_alrm;
238 240 int mon_alrm = pc_rtc_offset.mon_alrm;
239 241
240 242 ASSERT(MUTEX_HELD(&tod_lock));
241 243
242 244 /* A delay of zero is not allowed */
243 245 if (nsecs == 0)
244 246 return;
245 247
246 248 /* Make sure that we delay no less than the minimum time */
247 249 if (nsecs < clock_min_alarm)
248 250 nsecs = clock_min_alarm;
249 251
250 252 if (todpc_rtcget((unsigned char *)&rtc))
251 253 return;
252 254
253 255 /*
254 256 * Compute alarm secs, mins and hrs, and where appropriate, dom
255 257 * and mon. rtc bytes are in binary-coded decimal, so we have
256 258 * to convert.
257 259 */
258 260 delta = nsecs + BCD_TO_BYTE(rtc.rtc_sec);
259 261 asec = delta % 60;
260 262
261 263 delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_min);
262 264 amin = delta % 60;
263 265
264 266 delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_hr);
265 267 ahr = delta % 24;
266 268
267 269 if (day_alrm == 0 && delta >= 24) {
268 270 prom_printf("No day alarm - set to end of today!\n");
269 271 asec = 59;
270 272 amin = 59;
271 273 ahr = 23;
272 274 } else {
273 275 int mon = BCD_TO_BYTE(rtc.rtc_mon);
274 276 static int dpm[] =
275 277 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
276 278
277 279 adom = (delta / 24) + BCD_TO_BYTE(rtc.rtc_dom);
278 280
279 281 if (mon_alrm == 0) {
280 282 if (adom > dpm[mon]) {
281 283 prom_printf("No mon alarm - "
282 284 "set to end of current month!\n");
283 285 asec = 59;
284 286 amin = 59;
285 287 ahr = 23;
286 288 adom = dpm[mon];
287 289 }
288 290 } else {
289 291 for (amon = mon;
290 292 amon <= 12 && adom > dpm[amon]; amon++) {
291 293 adom -= dpm[amon];
292 294 }
293 295 if (amon > 12) {
294 296 prom_printf("Alarm too far in future - "
295 297 "set to end of current year!\n");
296 298 asec = 59;
297 299 amin = 59;
298 300 ahr = 23;
299 301 adom = dpm[12];
300 302 amon = 12;
301 303 }
302 304 rtc.rtc_amon = BYTE_TO_BCD(amon);
303 305 }
304 306
305 307 rtc.rtc_adom = BYTE_TO_BCD(adom);
306 308 }
307 309
308 310 rtc.rtc_asec = BYTE_TO_BCD(asec);
309 311 rtc.rtc_amin = BYTE_TO_BCD(amin);
310 312 rtc.rtc_ahr = BYTE_TO_BCD(ahr);
311 313
312 314 rtc.rtc_statusb |= RTC_AIE; /* Enable alarm interrupt */
313 315
314 316 todpc_rtcput((unsigned char *)&rtc);
315 317 }
316 318
317 319 /*
318 320 * Clear an alarm. This is effectively setting an alarm of 0.
319 321 */
320 322 void
321 323 /*ARGSUSED*/
322 324 todpc_clralarm(tod_ops_t *top)
↓ open down ↓ |
264 lines elided |
↑ open up ↑ |
323 325 {
324 326 mutex_enter(&tod_lock);
325 327 todpc_setalarm(top, 0);
326 328 mutex_exit(&tod_lock);
327 329 }
328 330
329 331 /*
330 332 * Routine to read contents of real time clock to the specified buffer.
331 333 * Returns ENXIO if clock not valid, or EAGAIN if clock data cannot be read
332 334 * else 0.
333 - * The routine will busy wait for the Update-In-Progress flag to clear.
335 + * Some RTC hardware is very slow at asserting the validity flag on
336 + * startup. The routine will busy wait for the RTC to become valid.
337 + * The routine will also busy wait for the Update-In-Progress flag to clear.
334 338 * On completion of the reads the Seconds register is re-read and the
335 339 * UIP flag is rechecked to confirm that an clock update did not occur
336 340 * during the accesses. Routine will error exit after 256 attempts.
337 341 * (See bugid 1158298.)
338 342 * Routine returns RTC_NREG (which is 15) bytes of data, as given in the
339 343 * technical reference. This data includes both time and status registers.
340 344 */
341 345
342 346 static int
343 347 todpc_rtcget(unsigned char *buf)
344 348 {
345 349 unsigned char reg;
346 350 int i;
347 - int retries = 256;
351 + int uip_try = 256;
352 + int vrt_try = 512;
348 353 unsigned char *rawp;
349 354 unsigned char century = RTC_CENTURY;
350 355 unsigned char day_alrm;
351 356 unsigned char mon_alrm;
352 357
353 358 ASSERT(MUTEX_HELD(&tod_lock));
354 359
355 360 day_alrm = pc_rtc_offset.day_alrm;
356 361 mon_alrm = pc_rtc_offset.mon_alrm;
357 362 if (pc_rtc_offset.century != 0) {
358 363 century = pc_rtc_offset.century;
359 364 }
360 365
361 - outb(RTC_ADDR, RTC_D); /* check if clock valid */
362 - reg = inb(RTC_DATA);
363 - if ((reg & RTC_VRT) == 0)
364 - return (ENXIO);
366 + for (;;) {
367 + if (vrt_try-- < 0)
368 + return (ENXIO);
369 + outb(RTC_ADDR, RTC_D); /* check if clock valid */
370 + reg = inb(RTC_DATA);
371 + if ((reg & RTC_VRT) != 0)
372 + break;
373 + drv_usecwait(5000); /* Delay for 5000 us */
374 + }
365 375
376 +
366 377 checkuip:
367 - if (retries-- < 0)
378 + if (uip_try-- < 0)
368 379 return (EAGAIN);
369 380 outb(RTC_ADDR, RTC_A); /* check if update in progress */
370 381 reg = inb(RTC_DATA);
371 382 if (reg & RTC_UIP) {
372 383 tenmicrosec();
373 384 goto checkuip;
374 385 }
375 386
376 387 for (i = 0, rawp = buf; i < RTC_NREG; i++) {
377 388 outb(RTC_ADDR, i);
378 389 *rawp++ = inb(RTC_DATA);
379 390 }
380 391 outb(RTC_ADDR, century); /* do century */
381 392 ((struct rtc_t *)buf)->rtc_century = inb(RTC_DATA);
382 393
383 394 if (day_alrm > 0) {
384 395 outb(RTC_ADDR, day_alrm);
385 396 ((struct rtc_t *)buf)->rtc_adom = inb(RTC_DATA) & 0x3f;
386 397 }
387 398 if (mon_alrm > 0) {
388 399 outb(RTC_ADDR, mon_alrm);
389 400 ((struct rtc_t *)buf)->rtc_amon = inb(RTC_DATA);
390 401 }
391 402
392 403 outb(RTC_ADDR, 0); /* re-read Seconds register */
393 404 reg = inb(RTC_DATA);
394 405 if (reg != ((struct rtc_t *)buf)->rtc_sec ||
395 406 (((struct rtc_t *)buf)->rtc_statusa & RTC_UIP))
396 407 /* update occured during reads */
397 408 goto checkuip;
398 409
399 410 return (0);
400 411 }
401 412
402 413 /*
403 414 * This routine writes the contents of the given buffer to the real time
404 415 * clock. It is given RTC_NREGP bytes of data, which are the 10 bytes used
405 416 * to write the time and set the alarm. It should be called with the priority
406 417 * raised to 5.
407 418 */
408 419 static void
409 420 todpc_rtcput(unsigned char *buf)
410 421 {
411 422 unsigned char reg;
412 423 int i;
413 424 unsigned char century = RTC_CENTURY;
414 425 unsigned char day_alrm = pc_rtc_offset.day_alrm;
415 426 unsigned char mon_alrm = pc_rtc_offset.mon_alrm;
416 427 unsigned char tmp;
417 428
418 429 if (pc_rtc_offset.century != 0) {
419 430 century = pc_rtc_offset.century;
420 431 }
421 432
422 433 outb(RTC_ADDR, RTC_B);
423 434 reg = inb(RTC_DATA);
424 435 outb(RTC_ADDR, RTC_B);
425 436 outb(RTC_DATA, reg | RTC_SET); /* allow time set now */
426 437 for (i = 0; i < RTC_NREGP; i++) { /* set the time */
427 438 outb(RTC_ADDR, i);
428 439 outb(RTC_DATA, buf[i]);
429 440 }
430 441 outb(RTC_ADDR, century); /* do century */
431 442 outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_century);
432 443
433 444 if (day_alrm > 0) {
434 445 outb(RTC_ADDR, day_alrm);
435 446 /* preserve RTC_VRT bit; some virt envs accept writes there */
436 447 tmp = inb(RTC_DATA) & RTC_VRT;
437 448 tmp |= ((struct rtc_t *)buf)->rtc_adom & ~RTC_VRT;
438 449 outb(RTC_DATA, tmp);
439 450 }
440 451 if (mon_alrm > 0) {
441 452 outb(RTC_ADDR, mon_alrm);
442 453 outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_amon);
443 454 }
444 455
445 456 outb(RTC_ADDR, RTC_B);
446 457 reg = inb(RTC_DATA);
447 458 outb(RTC_ADDR, RTC_B);
448 459 outb(RTC_DATA, reg & ~RTC_SET); /* allow time update */
449 460 }
450 461
451 462 static tod_ops_t todpc_ops = {
452 463 TOD_OPS_VERSION,
453 464 todpc_get,
454 465 todpc_set,
455 466 NULL,
456 467 NULL,
457 468 todpc_setalarm,
458 469 todpc_clralarm,
459 470 NULL
460 471 };
461 472
462 473 /*
463 474 * Initialize for the default TOD ops vector for use on hardware.
464 475 */
465 476
466 477 tod_ops_t *tod_ops = &todpc_ops;
↓ open down ↓ |
89 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX