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