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