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.
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.
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]
22 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
25 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
29 /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
30 /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
31 /* All Rights Reserved */
33 /* Copyright (c) 1987, 1988 Microsoft Corporation */
34 /* All Rights Reserved */
36 #include <sys/param.h>
38 #include <sys/systm.h>
40 #include <sys/cpuvar.h>
41 #include <sys/clock.h>
42 #include <sys/debug.h>
44 #include <sys/archsystm.h>
45 #include <sys/sysmacros.h>
46 #include <sys/lockstat.h>
48 #include <sys/sunddi.h>
50 #include <sys/acpi/acpi.h>
51 #include <sys/acpica.h>
53 static int todpc_rtcget(unsigned char *buf
);
54 static void todpc_rtcput(unsigned char *buf
);
56 #define CLOCK_RES 1000 /* 1 microsec in nanosecs */
58 int clock_res
= CLOCK_RES
;
61 * The minimum sleep time till an alarm can be fired.
62 * This can be tuned in /etc/system, but if the value is too small,
63 * there is a danger that it will be missed if it takes too long to
64 * get from the set point to sleep. Or that it can fire quickly, and
65 * generate a power spike on the hardware. And small values are
66 * probably only usefull for test setups.
68 int clock_min_alarm
= 4;
71 * Machine-dependent clock routines.
83 static struct rtc_offset pc_rtc_offset
= {0, 0, 0, 0};
87 * Entry point for ACPI to pass RTC or other clock values that
91 pc_tod_set_rtc_offsets(ACPI_TABLE_FADT
*fadt
) {
95 * ASSERT is for debugging, but we don't want the machine
96 * falling over because for some reason we didn't get a valid
104 if (fadt
->DayAlarm
) {
105 pc_rtc_offset
.day_alrm
= fadt
->DayAlarm
;
109 if (fadt
->MonthAlarm
) {
110 pc_rtc_offset
.mon_alrm
= fadt
->MonthAlarm
;
115 pc_rtc_offset
.century
= fadt
->Century
;
119 pc_rtc_offset
.loaded
= ok
;
124 * Write the specified time into the clock chip.
125 * Must be called with tod_lock held.
129 todpc_set(tod_ops_t
*top
, timestruc_t ts
)
131 todinfo_t tod
= utc_to_tod(ts
.tv_sec
- ggmtl());
134 ASSERT(MUTEX_HELD(&tod_lock
));
136 if (todpc_rtcget((unsigned char *)&rtc
))
140 * rtc bytes are in binary-coded decimal, so we have to convert.
141 * We assume that we wrap the rtc year back to zero at 2000.
143 /* LINTED: YRBASE = 0 for x86 */
144 tod
.tod_year
-= YRBASE
;
145 if (tod
.tod_year
>= 100) {
147 rtc
.rtc_century
= BYTE_TO_BCD(20); /* 20xx year */
149 rtc
.rtc_century
= BYTE_TO_BCD(19); /* 19xx year */
150 rtc
.rtc_yr
= BYTE_TO_BCD(tod
.tod_year
);
151 rtc
.rtc_mon
= BYTE_TO_BCD(tod
.tod_month
);
152 rtc
.rtc_dom
= BYTE_TO_BCD(tod
.tod_day
);
153 /* dow < 10, so no conversion */
154 rtc
.rtc_dow
= (unsigned char)tod
.tod_dow
;
155 rtc
.rtc_hr
= BYTE_TO_BCD(tod
.tod_hour
);
156 rtc
.rtc_min
= BYTE_TO_BCD(tod
.tod_min
);
157 rtc
.rtc_sec
= BYTE_TO_BCD(tod
.tod_sec
);
159 todpc_rtcput((unsigned char *)&rtc
);
163 * Read the current time from the clock chip and convert to UNIX form.
164 * Assumes that the year in the clock chip is valid.
165 * Must be called with tod_lock held.
169 todpc_get(tod_ops_t
*top
)
175 static int century_warn
= 1; /* only warn once, not each time called */
176 static int range_warn
= 1;
178 ASSERT(MUTEX_HELD(&tod_lock
));
180 if (todpc_rtcget((unsigned char *)&rtc
)) {
181 tod_status_set(TOD_GET_FAILED
);
185 /* assume that we wrap the rtc year back to zero at 2000 */
186 tod
.tod_year
= BCD_TO_BYTE(rtc
.rtc_yr
);
187 if (tod
.tod_year
< 69) {
188 if (range_warn
&& tod
.tod_year
> 38) {
189 cmn_err(CE_WARN
, "hardware real-time clock is out "
190 "of range -- time needs to be reset");
193 tod
.tod_year
+= 100 + YRBASE
; /* 20xx year */
194 compute_century
= 20;
196 /* LINTED: YRBASE = 0 for x86 */
197 tod
.tod_year
+= YRBASE
; /* 19xx year */
198 compute_century
= 19;
200 if (century_warn
&& BCD_TO_BYTE(rtc
.rtc_century
) != compute_century
) {
202 "The hardware real-time clock appears to have the "
203 "wrong century: %d.\nSolaris will still operate "
204 "correctly, but other OS's/firmware agents may "
205 "not.\nUse date(1) to set the date to the current "
206 "time to correct the RTC.",
207 BCD_TO_BYTE(rtc
.rtc_century
));
210 tod
.tod_month
= BCD_TO_BYTE(rtc
.rtc_mon
);
211 tod
.tod_day
= BCD_TO_BYTE(rtc
.rtc_dom
);
212 tod
.tod_dow
= rtc
.rtc_dow
; /* dow < 10, so no conversion needed */
213 tod
.tod_hour
= BCD_TO_BYTE(rtc
.rtc_hr
);
214 tod
.tod_min
= BCD_TO_BYTE(rtc
.rtc_min
);
215 tod
.tod_sec
= BCD_TO_BYTE(rtc
.rtc_sec
);
217 /* read was successful so ensure failure flag is clear */
218 tod_status_clear(TOD_GET_FAILED
);
220 ts
.tv_sec
= tod_to_utc(tod
) + ggmtl();
226 #include <sys/promif.h>
228 * Write the specified wakeup alarm into the clock chip.
229 * Must be called with tod_lock held.
233 todpc_setalarm(tod_ops_t
*top
, int nsecs
)
236 int delta
, asec
, amin
, ahr
, adom
, amon
;
237 int day_alrm
= pc_rtc_offset
.day_alrm
;
238 int mon_alrm
= pc_rtc_offset
.mon_alrm
;
240 ASSERT(MUTEX_HELD(&tod_lock
));
242 /* A delay of zero is not allowed */
246 /* Make sure that we delay no less than the minimum time */
247 if (nsecs
< clock_min_alarm
)
248 nsecs
= clock_min_alarm
;
250 if (todpc_rtcget((unsigned char *)&rtc
))
254 * Compute alarm secs, mins and hrs, and where appropriate, dom
255 * and mon. rtc bytes are in binary-coded decimal, so we have
258 delta
= nsecs
+ BCD_TO_BYTE(rtc
.rtc_sec
);
261 delta
= (delta
/ 60) + BCD_TO_BYTE(rtc
.rtc_min
);
264 delta
= (delta
/ 60) + BCD_TO_BYTE(rtc
.rtc_hr
);
267 if (day_alrm
== 0 && delta
>= 24) {
268 prom_printf("No day alarm - set to end of today!\n");
273 int mon
= BCD_TO_BYTE(rtc
.rtc_mon
);
275 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
277 adom
= (delta
/ 24) + BCD_TO_BYTE(rtc
.rtc_dom
);
280 if (adom
> dpm
[mon
]) {
281 prom_printf("No mon alarm - "
282 "set to end of current month!\n");
290 amon
<= 12 && adom
> dpm
[amon
]; amon
++) {
294 prom_printf("Alarm too far in future - "
295 "set to end of current year!\n");
302 rtc
.rtc_amon
= BYTE_TO_BCD(amon
);
305 rtc
.rtc_adom
= BYTE_TO_BCD(adom
);
308 rtc
.rtc_asec
= BYTE_TO_BCD(asec
);
309 rtc
.rtc_amin
= BYTE_TO_BCD(amin
);
310 rtc
.rtc_ahr
= BYTE_TO_BCD(ahr
);
312 rtc
.rtc_statusb
|= RTC_AIE
; /* Enable alarm interrupt */
314 todpc_rtcput((unsigned char *)&rtc
);
318 * Clear an alarm. This is effectively setting an alarm of 0.
322 todpc_clralarm(tod_ops_t
*top
)
324 mutex_enter(&tod_lock
);
325 todpc_setalarm(top
, 0);
326 mutex_exit(&tod_lock
);
330 * Routine to read contents of real time clock to the specified buffer.
331 * Returns ENXIO if clock not valid, or EAGAIN if clock data cannot be read
333 * The routine will busy wait for the Update-In-Progress flag to clear.
334 * On completion of the reads the Seconds register is re-read and the
335 * UIP flag is rechecked to confirm that an clock update did not occur
336 * during the accesses. Routine will error exit after 256 attempts.
337 * (See bugid 1158298.)
338 * Routine returns RTC_NREG (which is 15) bytes of data, as given in the
339 * technical reference. This data includes both time and status registers.
343 todpc_rtcget(unsigned char *buf
)
349 unsigned char century
= RTC_CENTURY
;
350 unsigned char day_alrm
;
351 unsigned char mon_alrm
;
353 ASSERT(MUTEX_HELD(&tod_lock
));
355 day_alrm
= pc_rtc_offset
.day_alrm
;
356 mon_alrm
= pc_rtc_offset
.mon_alrm
;
357 if (pc_rtc_offset
.century
!= 0) {
358 century
= pc_rtc_offset
.century
;
361 outb(RTC_ADDR
, RTC_D
); /* check if clock valid */
363 if ((reg
& RTC_VRT
) == 0)
369 outb(RTC_ADDR
, RTC_A
); /* check if update in progress */
376 for (i
= 0, rawp
= buf
; i
< RTC_NREG
; i
++) {
378 *rawp
++ = inb(RTC_DATA
);
380 outb(RTC_ADDR
, century
); /* do century */
381 ((struct rtc_t
*)buf
)->rtc_century
= inb(RTC_DATA
);
384 outb(RTC_ADDR
, day_alrm
);
385 ((struct rtc_t
*)buf
)->rtc_adom
= inb(RTC_DATA
) & 0x3f;
388 outb(RTC_ADDR
, mon_alrm
);
389 ((struct rtc_t
*)buf
)->rtc_amon
= inb(RTC_DATA
);
392 outb(RTC_ADDR
, 0); /* re-read Seconds register */
394 if (reg
!= ((struct rtc_t
*)buf
)->rtc_sec
||
395 (((struct rtc_t
*)buf
)->rtc_statusa
& RTC_UIP
))
396 /* update occured during reads */
403 * This routine writes the contents of the given buffer to the real time
404 * clock. It is given RTC_NREGP bytes of data, which are the 10 bytes used
405 * to write the time and set the alarm. It should be called with the priority
409 todpc_rtcput(unsigned char *buf
)
413 unsigned char century
= RTC_CENTURY
;
414 unsigned char day_alrm
= pc_rtc_offset
.day_alrm
;
415 unsigned char mon_alrm
= pc_rtc_offset
.mon_alrm
;
418 if (pc_rtc_offset
.century
!= 0) {
419 century
= pc_rtc_offset
.century
;
422 outb(RTC_ADDR
, RTC_B
);
424 outb(RTC_ADDR
, RTC_B
);
425 outb(RTC_DATA
, reg
| RTC_SET
); /* allow time set now */
426 for (i
= 0; i
< RTC_NREGP
; i
++) { /* set the time */
428 outb(RTC_DATA
, buf
[i
]);
430 outb(RTC_ADDR
, century
); /* do century */
431 outb(RTC_DATA
, ((struct rtc_t
*)buf
)->rtc_century
);
434 outb(RTC_ADDR
, day_alrm
);
435 /* preserve RTC_VRT bit; some virt envs accept writes there */
436 tmp
= inb(RTC_DATA
) & RTC_VRT
;
437 tmp
|= ((struct rtc_t
*)buf
)->rtc_adom
& ~RTC_VRT
;
441 outb(RTC_ADDR
, mon_alrm
);
442 outb(RTC_DATA
, ((struct rtc_t
*)buf
)->rtc_amon
);
445 outb(RTC_ADDR
, RTC_B
);
447 outb(RTC_ADDR
, RTC_B
);
448 outb(RTC_DATA
, reg
& ~RTC_SET
); /* allow time update */
451 static tod_ops_t todpc_ops
= {
463 * Initialize for the default TOD ops vector for use on hardware.
466 tod_ops_t
*tod_ops
= &todpc_ops
;