xref: /titanic_41/usr/src/uts/i86pc/io/todpc_subr.c (revision 8500cbf9b5297326cb6b4abec139e8784dcd5ee5)
1ae115bc7Smrj /*
2ae115bc7Smrj  * CDDL HEADER START
3ae115bc7Smrj  *
4ae115bc7Smrj  * The contents of this file are subject to the terms of the
5ae115bc7Smrj  * Common Development and Distribution License (the "License").
6ae115bc7Smrj  * You may not use this file except in compliance with the License.
7ae115bc7Smrj  *
8ae115bc7Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9ae115bc7Smrj  * or http://www.opensolaris.org/os/licensing.
10ae115bc7Smrj  * See the License for the specific language governing permissions
11ae115bc7Smrj  * and limitations under the License.
12ae115bc7Smrj  *
13ae115bc7Smrj  * When distributing Covered Code, include this CDDL HEADER in each
14ae115bc7Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15ae115bc7Smrj  * If applicable, add the following below this CDDL HEADER, with the
16ae115bc7Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
17ae115bc7Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
18ae115bc7Smrj  *
19ae115bc7Smrj  * CDDL HEADER END
20ae115bc7Smrj  */
21*8500cbf9SDan Kruchinin /*
22*8500cbf9SDan Kruchinin  * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
23*8500cbf9SDan Kruchinin  */
24ae115bc7Smrj /*
258fc99e42STrevor Thompson  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
26ae115bc7Smrj  * Use is subject to license terms.
27ae115bc7Smrj  */
28ae115bc7Smrj 
29ae115bc7Smrj /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
30ae115bc7Smrj /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
31ae115bc7Smrj /*	  All Rights Reserved  	*/
32ae115bc7Smrj 
33ae115bc7Smrj /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
34ae115bc7Smrj /*	  All Rights Reserved	*/
35ae115bc7Smrj 
36ae115bc7Smrj #include <sys/param.h>
37ae115bc7Smrj #include <sys/time.h>
38ae115bc7Smrj #include <sys/systm.h>
39ae115bc7Smrj 
40ae115bc7Smrj #include <sys/cpuvar.h>
41ae115bc7Smrj #include <sys/clock.h>
42ae115bc7Smrj #include <sys/debug.h>
43ae115bc7Smrj #include <sys/rtc.h>
44ae115bc7Smrj #include <sys/archsystm.h>
45ae115bc7Smrj #include <sys/sysmacros.h>
46ae115bc7Smrj #include <sys/lockstat.h>
47ae115bc7Smrj #include <sys/stat.h>
48ae115bc7Smrj #include <sys/sunddi.h>
49ae115bc7Smrj 
502df1fe9cSrandyf #include <sys/acpi/acpi.h>
512df1fe9cSrandyf #include <sys/acpica.h>
522df1fe9cSrandyf 
53ae115bc7Smrj static int todpc_rtcget(unsigned char *buf);
54ae115bc7Smrj static void todpc_rtcput(unsigned char *buf);
55ae115bc7Smrj 
562df1fe9cSrandyf #define	CLOCK_RES	1000		/* 1 microsec in nanosecs */
572df1fe9cSrandyf 
582df1fe9cSrandyf int clock_res = CLOCK_RES;
592df1fe9cSrandyf 
602df1fe9cSrandyf /*
612df1fe9cSrandyf  * The minimum sleep time till an alarm can be fired.
622df1fe9cSrandyf  * This can be tuned in /etc/system, but if the value is too small,
632df1fe9cSrandyf  * there is a danger that it will be missed if it takes too long to
642df1fe9cSrandyf  * get from the set point to sleep.  Or that it can fire quickly, and
652df1fe9cSrandyf  * generate a power spike on the hardware.  And small values are
662df1fe9cSrandyf  * probably only usefull for test setups.
672df1fe9cSrandyf  */
682df1fe9cSrandyf int clock_min_alarm = 4;
692df1fe9cSrandyf 
70ae115bc7Smrj /*
71ae115bc7Smrj  * Machine-dependent clock routines.
72ae115bc7Smrj  */
73ae115bc7Smrj 
742df1fe9cSrandyf extern long gmt_lag;
752df1fe9cSrandyf 
762df1fe9cSrandyf struct rtc_offset {
772df1fe9cSrandyf 	int8_t	loaded;
782df1fe9cSrandyf 	uint8_t	day_alrm;
792df1fe9cSrandyf 	uint8_t mon_alrm;
802df1fe9cSrandyf 	uint8_t	century;
812df1fe9cSrandyf };
822df1fe9cSrandyf 
832df1fe9cSrandyf static struct rtc_offset pc_rtc_offset = {0, 0, 0, 0};
842df1fe9cSrandyf 
852df1fe9cSrandyf 
862df1fe9cSrandyf /*
872df1fe9cSrandyf  * Entry point for ACPI to pass RTC or other clock values that
882df1fe9cSrandyf  * are useful to TOD.
892df1fe9cSrandyf  */
902df1fe9cSrandyf void
pc_tod_set_rtc_offsets(ACPI_TABLE_FADT * fadt)91db2bae30SDana Myers pc_tod_set_rtc_offsets(ACPI_TABLE_FADT *fadt) {
922df1fe9cSrandyf 	int		ok = 0;
932df1fe9cSrandyf 
942df1fe9cSrandyf 	/*
952df1fe9cSrandyf 	 * ASSERT is for debugging, but we don't want the machine
962df1fe9cSrandyf 	 * falling over because for some reason we didn't get a valid
972df1fe9cSrandyf 	 * pointer.
982df1fe9cSrandyf 	 */
992df1fe9cSrandyf 	ASSERT(fadt);
1002df1fe9cSrandyf 	if (fadt == NULL) {
1012df1fe9cSrandyf 		return;
1022df1fe9cSrandyf 	}
1032df1fe9cSrandyf 
104db2bae30SDana Myers 	if (fadt->DayAlarm) {
105db2bae30SDana Myers 		pc_rtc_offset.day_alrm = fadt->DayAlarm;
1062df1fe9cSrandyf 		ok = 1;
1072df1fe9cSrandyf 	}
1082df1fe9cSrandyf 
109db2bae30SDana Myers 	if (fadt->MonthAlarm) {
110db2bae30SDana Myers 		pc_rtc_offset.mon_alrm = fadt->MonthAlarm;
1112df1fe9cSrandyf 		ok = 1;
1122df1fe9cSrandyf 	}
1132df1fe9cSrandyf 
1142df1fe9cSrandyf 	if (fadt->Century) {
1152df1fe9cSrandyf 		pc_rtc_offset.century = fadt->Century;
1162df1fe9cSrandyf 		ok = 1;
1172df1fe9cSrandyf 	}
1182df1fe9cSrandyf 
1192df1fe9cSrandyf 	pc_rtc_offset.loaded = ok;
1202df1fe9cSrandyf }
1212df1fe9cSrandyf 
1222df1fe9cSrandyf 
123ae115bc7Smrj /*
124ae115bc7Smrj  * Write the specified time into the clock chip.
125ae115bc7Smrj  * Must be called with tod_lock held.
126ae115bc7Smrj  */
127ae115bc7Smrj /*ARGSUSED*/
128ae115bc7Smrj static void
todpc_set(tod_ops_t * top,timestruc_t ts)129ae115bc7Smrj todpc_set(tod_ops_t *top, timestruc_t ts)
130ae115bc7Smrj {
131ae115bc7Smrj 	todinfo_t tod = utc_to_tod(ts.tv_sec - ggmtl());
132ae115bc7Smrj 	struct rtc_t rtc;
133ae115bc7Smrj 
134ae115bc7Smrj 	ASSERT(MUTEX_HELD(&tod_lock));
135ae115bc7Smrj 
136ae115bc7Smrj 	if (todpc_rtcget((unsigned char *)&rtc))
137ae115bc7Smrj 		return;
138ae115bc7Smrj 
139ae115bc7Smrj 	/*
140ae115bc7Smrj 	 * rtc bytes are in binary-coded decimal, so we have to convert.
141ae115bc7Smrj 	 * We assume that we wrap the rtc year back to zero at 2000.
142ae115bc7Smrj 	 */
143ae115bc7Smrj 	/* LINTED: YRBASE = 0 for x86 */
144ae115bc7Smrj 	tod.tod_year -= YRBASE;
145ae115bc7Smrj 	if (tod.tod_year >= 100) {
146ae115bc7Smrj 		tod.tod_year -= 100;
147ae115bc7Smrj 		rtc.rtc_century = BYTE_TO_BCD(20); /* 20xx year */
148ae115bc7Smrj 	} else
149ae115bc7Smrj 		rtc.rtc_century = BYTE_TO_BCD(19); /* 19xx year */
150ae115bc7Smrj 	rtc.rtc_yr	= BYTE_TO_BCD(tod.tod_year);
151ae115bc7Smrj 	rtc.rtc_mon	= BYTE_TO_BCD(tod.tod_month);
152ae115bc7Smrj 	rtc.rtc_dom	= BYTE_TO_BCD(tod.tod_day);
153ae115bc7Smrj 	/* dow < 10, so no conversion */
154ae115bc7Smrj 	rtc.rtc_dow	= (unsigned char)tod.tod_dow;
155ae115bc7Smrj 	rtc.rtc_hr	= BYTE_TO_BCD(tod.tod_hour);
156ae115bc7Smrj 	rtc.rtc_min	= BYTE_TO_BCD(tod.tod_min);
157ae115bc7Smrj 	rtc.rtc_sec	= BYTE_TO_BCD(tod.tod_sec);
158ae115bc7Smrj 
159ae115bc7Smrj 	todpc_rtcput((unsigned char *)&rtc);
160ae115bc7Smrj }
161ae115bc7Smrj 
162ae115bc7Smrj /*
163ae115bc7Smrj  * Read the current time from the clock chip and convert to UNIX form.
164ae115bc7Smrj  * Assumes that the year in the clock chip is valid.
165ae115bc7Smrj  * Must be called with tod_lock held.
166ae115bc7Smrj  */
167ae115bc7Smrj /*ARGSUSED*/
168ae115bc7Smrj static timestruc_t
todpc_get(tod_ops_t * top)169ae115bc7Smrj todpc_get(tod_ops_t *top)
170ae115bc7Smrj {
171ae115bc7Smrj 	timestruc_t ts;
172ae115bc7Smrj 	todinfo_t tod;
173ae115bc7Smrj 	struct rtc_t rtc;
174ae115bc7Smrj 	int compute_century;
175ae115bc7Smrj 	static int century_warn = 1; /* only warn once, not each time called */
176ae115bc7Smrj 	static int range_warn = 1;
177ae115bc7Smrj 
178ae115bc7Smrj 	ASSERT(MUTEX_HELD(&tod_lock));
179ae115bc7Smrj 
180ae115bc7Smrj 	if (todpc_rtcget((unsigned char *)&rtc)) {
1818fc99e42STrevor Thompson 		tod_status_set(TOD_GET_FAILED);
182*8500cbf9SDan Kruchinin 		return (hrestime);
183ae115bc7Smrj 	}
184ae115bc7Smrj 
185ae115bc7Smrj 	/* assume that we wrap the rtc year back to zero at 2000 */
186ae115bc7Smrj 	tod.tod_year	= BCD_TO_BYTE(rtc.rtc_yr);
187ae115bc7Smrj 	if (tod.tod_year < 69) {
188ae115bc7Smrj 		if (range_warn && tod.tod_year > 38) {
189ae115bc7Smrj 			cmn_err(CE_WARN, "hardware real-time clock is out "
190ae115bc7Smrj 			    "of range -- time needs to be reset");
191ae115bc7Smrj 			range_warn = 0;
192ae115bc7Smrj 		}
193ae115bc7Smrj 		tod.tod_year += 100 + YRBASE; /* 20xx year */
194ae115bc7Smrj 		compute_century = 20;
195ae115bc7Smrj 	} else {
196ae115bc7Smrj 		/* LINTED: YRBASE = 0 for x86 */
197ae115bc7Smrj 		tod.tod_year += YRBASE; /* 19xx year */
198ae115bc7Smrj 		compute_century = 19;
199ae115bc7Smrj 	}
200ae115bc7Smrj 	if (century_warn && BCD_TO_BYTE(rtc.rtc_century) != compute_century) {
201ae115bc7Smrj 		cmn_err(CE_NOTE,
202ae115bc7Smrj 		    "The hardware real-time clock appears to have the "
203ae115bc7Smrj 		    "wrong century: %d.\nSolaris will still operate "
204ae115bc7Smrj 		    "correctly, but other OS's/firmware agents may "
205ae115bc7Smrj 		    "not.\nUse date(1) to set the date to the current "
206ae115bc7Smrj 		    "time to correct the RTC.",
207ae115bc7Smrj 		    BCD_TO_BYTE(rtc.rtc_century));
208ae115bc7Smrj 		century_warn = 0;
209ae115bc7Smrj 	}
210ae115bc7Smrj 	tod.tod_month	= BCD_TO_BYTE(rtc.rtc_mon);
211ae115bc7Smrj 	tod.tod_day	= BCD_TO_BYTE(rtc.rtc_dom);
212ae115bc7Smrj 	tod.tod_dow	= rtc.rtc_dow;	/* dow < 10, so no conversion needed */
213ae115bc7Smrj 	tod.tod_hour	= BCD_TO_BYTE(rtc.rtc_hr);
214ae115bc7Smrj 	tod.tod_min	= BCD_TO_BYTE(rtc.rtc_min);
215ae115bc7Smrj 	tod.tod_sec	= BCD_TO_BYTE(rtc.rtc_sec);
216ae115bc7Smrj 
2178fc99e42STrevor Thompson 	/* read was successful so ensure failure flag is clear */
2188fc99e42STrevor Thompson 	tod_status_clear(TOD_GET_FAILED);
2198fc99e42STrevor Thompson 
220ae115bc7Smrj 	ts.tv_sec = tod_to_utc(tod) + ggmtl();
221ae115bc7Smrj 	ts.tv_nsec = 0;
222ae115bc7Smrj 
223ae115bc7Smrj 	return (ts);
224ae115bc7Smrj }
225ae115bc7Smrj 
2262df1fe9cSrandyf #include <sys/promif.h>
2272df1fe9cSrandyf /*
2282df1fe9cSrandyf  * Write the specified wakeup alarm into the clock chip.
2292df1fe9cSrandyf  * Must be called with tod_lock held.
2302df1fe9cSrandyf  */
2312df1fe9cSrandyf void
2322df1fe9cSrandyf /*ARGSUSED*/
todpc_setalarm(tod_ops_t * top,int nsecs)2332df1fe9cSrandyf todpc_setalarm(tod_ops_t *top, int nsecs)
2342df1fe9cSrandyf {
2352df1fe9cSrandyf 	struct rtc_t rtc;
2362df1fe9cSrandyf 	int delta, asec, amin, ahr, adom, amon;
2372df1fe9cSrandyf 	int day_alrm = pc_rtc_offset.day_alrm;
2382df1fe9cSrandyf 	int mon_alrm = pc_rtc_offset.mon_alrm;
2392df1fe9cSrandyf 
2402df1fe9cSrandyf 	ASSERT(MUTEX_HELD(&tod_lock));
2412df1fe9cSrandyf 
2422df1fe9cSrandyf 	/* A delay of zero is not allowed */
2432df1fe9cSrandyf 	if (nsecs == 0)
2442df1fe9cSrandyf 		return;
2452df1fe9cSrandyf 
2462df1fe9cSrandyf 	/* Make sure that we delay no less than the minimum time */
2472df1fe9cSrandyf 	if (nsecs < clock_min_alarm)
2482df1fe9cSrandyf 		nsecs = clock_min_alarm;
2492df1fe9cSrandyf 
2502df1fe9cSrandyf 	if (todpc_rtcget((unsigned char *)&rtc))
2512df1fe9cSrandyf 		return;
2522df1fe9cSrandyf 
2532df1fe9cSrandyf 	/*
2542df1fe9cSrandyf 	 * Compute alarm secs, mins and hrs, and where appropriate, dom
2552df1fe9cSrandyf 	 * and mon.  rtc bytes are in binary-coded decimal, so we have
2562df1fe9cSrandyf 	 * to convert.
2572df1fe9cSrandyf 	 */
2582df1fe9cSrandyf 	delta = nsecs + BCD_TO_BYTE(rtc.rtc_sec);
2592df1fe9cSrandyf 	asec = delta % 60;
2602df1fe9cSrandyf 
2612df1fe9cSrandyf 	delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_min);
2622df1fe9cSrandyf 	amin = delta % 60;
2632df1fe9cSrandyf 
2642df1fe9cSrandyf 	delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_hr);
2652df1fe9cSrandyf 	ahr  = delta % 24;
2662df1fe9cSrandyf 
2672df1fe9cSrandyf 	if (day_alrm == 0 && delta >= 24) {
2682df1fe9cSrandyf 		prom_printf("No day alarm - set to end of today!\n");
2692df1fe9cSrandyf 		asec = 59;
2702df1fe9cSrandyf 		amin = 59;
2712df1fe9cSrandyf 		ahr  = 23;
2722df1fe9cSrandyf 	} else {
2732df1fe9cSrandyf 		int mon = BCD_TO_BYTE(rtc.rtc_mon);
2742df1fe9cSrandyf 		static int dpm[] =
2752df1fe9cSrandyf 		    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
2762df1fe9cSrandyf 
2772df1fe9cSrandyf 		adom = (delta / 24) + BCD_TO_BYTE(rtc.rtc_dom);
2782df1fe9cSrandyf 
2792df1fe9cSrandyf 		if (mon_alrm == 0) {
2802df1fe9cSrandyf 			if (adom > dpm[mon]) {
2812df1fe9cSrandyf 				prom_printf("No mon alarm - "
2822df1fe9cSrandyf 				    "set to end of current month!\n");
2832df1fe9cSrandyf 				asec = 59;
2842df1fe9cSrandyf 				amin = 59;
2852df1fe9cSrandyf 				ahr  = 23;
2862df1fe9cSrandyf 				adom = dpm[mon];
2872df1fe9cSrandyf 			}
2882df1fe9cSrandyf 		} else {
2892df1fe9cSrandyf 			for (amon = mon;
2902df1fe9cSrandyf 			    amon <= 12 && adom > dpm[amon]; amon++) {
2912df1fe9cSrandyf 				adom -= dpm[amon];
2922df1fe9cSrandyf 			}
2932df1fe9cSrandyf 			if (amon > 12) {
2942df1fe9cSrandyf 				prom_printf("Alarm too far in future - "
2952df1fe9cSrandyf 				    "set to end of current year!\n");
2962df1fe9cSrandyf 				asec = 59;
2972df1fe9cSrandyf 				amin = 59;
2982df1fe9cSrandyf 				ahr  = 23;
2992df1fe9cSrandyf 				adom = dpm[12];
3002df1fe9cSrandyf 				amon = 12;
3012df1fe9cSrandyf 			}
3022df1fe9cSrandyf 			rtc.rtc_amon = BYTE_TO_BCD(amon);
3032df1fe9cSrandyf 		}
3042df1fe9cSrandyf 
3052df1fe9cSrandyf 		rtc.rtc_adom = BYTE_TO_BCD(adom);
3062df1fe9cSrandyf 	}
3072df1fe9cSrandyf 
3082df1fe9cSrandyf 	rtc.rtc_asec = BYTE_TO_BCD(asec);
3092df1fe9cSrandyf 	rtc.rtc_amin = BYTE_TO_BCD(amin);
3102df1fe9cSrandyf 	rtc.rtc_ahr  = BYTE_TO_BCD(ahr);
3112df1fe9cSrandyf 
3122df1fe9cSrandyf 	rtc.rtc_statusb |= RTC_AIE;	/* Enable alarm interrupt */
3132df1fe9cSrandyf 
3142df1fe9cSrandyf 	todpc_rtcput((unsigned char *)&rtc);
3152df1fe9cSrandyf }
3162df1fe9cSrandyf 
3172df1fe9cSrandyf /*
3182df1fe9cSrandyf  * Clear an alarm.  This is effectively setting an alarm of 0.
3192df1fe9cSrandyf  */
3202df1fe9cSrandyf void
3212df1fe9cSrandyf /*ARGSUSED*/
todpc_clralarm(tod_ops_t * top)3222df1fe9cSrandyf todpc_clralarm(tod_ops_t *top)
3232df1fe9cSrandyf {
3242df1fe9cSrandyf 	mutex_enter(&tod_lock);
3252df1fe9cSrandyf 	todpc_setalarm(top, 0);
3262df1fe9cSrandyf 	mutex_exit(&tod_lock);
3272df1fe9cSrandyf }
3282df1fe9cSrandyf 
329ae115bc7Smrj /*
330ae115bc7Smrj  * Routine to read contents of real time clock to the specified buffer.
331ae115bc7Smrj  * Returns ENXIO if clock not valid, or EAGAIN if clock data cannot be read
332ae115bc7Smrj  * else 0.
333ae115bc7Smrj  * The routine will busy wait for the Update-In-Progress flag to clear.
334ae115bc7Smrj  * On completion of the reads the Seconds register is re-read and the
335ae115bc7Smrj  * UIP flag is rechecked to confirm that an clock update did not occur
336ae115bc7Smrj  * during the accesses.  Routine will error exit after 256 attempts.
337ae115bc7Smrj  * (See bugid 1158298.)
338ae115bc7Smrj  * Routine returns RTC_NREG (which is 15) bytes of data, as given in the
339ae115bc7Smrj  * technical reference.  This data includes both time and status registers.
340ae115bc7Smrj  */
341ae115bc7Smrj 
342ae115bc7Smrj static int
todpc_rtcget(unsigned char * buf)343ae115bc7Smrj todpc_rtcget(unsigned char *buf)
344ae115bc7Smrj {
345ae115bc7Smrj 	unsigned char	reg;
346ae115bc7Smrj 	int		i;
347ae115bc7Smrj 	int		retries = 256;
348ae115bc7Smrj 	unsigned char	*rawp;
3492df1fe9cSrandyf 	unsigned char	century = RTC_CENTURY;
3502df1fe9cSrandyf 	unsigned char	day_alrm;
3512df1fe9cSrandyf 	unsigned char	mon_alrm;
352ae115bc7Smrj 
353ae115bc7Smrj 	ASSERT(MUTEX_HELD(&tod_lock));
354ae115bc7Smrj 
3552df1fe9cSrandyf 	day_alrm = pc_rtc_offset.day_alrm;
3562df1fe9cSrandyf 	mon_alrm = pc_rtc_offset.mon_alrm;
3572df1fe9cSrandyf 	if (pc_rtc_offset.century != 0) {
3582df1fe9cSrandyf 		century = pc_rtc_offset.century;
3592df1fe9cSrandyf 	}
3602df1fe9cSrandyf 
361ae115bc7Smrj 	outb(RTC_ADDR, RTC_D);		/* check if clock valid */
362ae115bc7Smrj 	reg = inb(RTC_DATA);
363ae115bc7Smrj 	if ((reg & RTC_VRT) == 0)
364ae115bc7Smrj 		return (ENXIO);
365ae115bc7Smrj 
366ae115bc7Smrj checkuip:
367ae115bc7Smrj 	if (retries-- < 0)
368ae115bc7Smrj 		return (EAGAIN);
369ae115bc7Smrj 	outb(RTC_ADDR, RTC_A);		/* check if update in progress */
370ae115bc7Smrj 	reg = inb(RTC_DATA);
371ae115bc7Smrj 	if (reg & RTC_UIP) {
372ae115bc7Smrj 		tenmicrosec();
373ae115bc7Smrj 		goto checkuip;
374ae115bc7Smrj 	}
375ae115bc7Smrj 
376ae115bc7Smrj 	for (i = 0, rawp = buf; i < RTC_NREG; i++) {
377ae115bc7Smrj 		outb(RTC_ADDR, i);
378ae115bc7Smrj 		*rawp++ = inb(RTC_DATA);
379ae115bc7Smrj 	}
3802df1fe9cSrandyf 	outb(RTC_ADDR, century); /* do century */
381ae115bc7Smrj 	((struct rtc_t *)buf)->rtc_century = inb(RTC_DATA);
3822df1fe9cSrandyf 
3832df1fe9cSrandyf 	if (day_alrm > 0) {
3842df1fe9cSrandyf 		outb(RTC_ADDR, day_alrm);
3852df1fe9cSrandyf 		((struct rtc_t *)buf)->rtc_adom = inb(RTC_DATA) & 0x3f;
3862df1fe9cSrandyf 	}
3872df1fe9cSrandyf 	if (mon_alrm > 0) {
3882df1fe9cSrandyf 		outb(RTC_ADDR, mon_alrm);
3892df1fe9cSrandyf 		((struct rtc_t *)buf)->rtc_amon = inb(RTC_DATA);
3902df1fe9cSrandyf 	}
3912df1fe9cSrandyf 
392ae115bc7Smrj 	outb(RTC_ADDR, 0);		/* re-read Seconds register */
393ae115bc7Smrj 	reg = inb(RTC_DATA);
394ae115bc7Smrj 	if (reg != ((struct rtc_t *)buf)->rtc_sec ||
395ae115bc7Smrj 	    (((struct rtc_t *)buf)->rtc_statusa & RTC_UIP))
396ae115bc7Smrj 		/* update occured during reads */
397ae115bc7Smrj 		goto checkuip;
398ae115bc7Smrj 
399ae115bc7Smrj 	return (0);
400ae115bc7Smrj }
401ae115bc7Smrj 
402ae115bc7Smrj /*
403ae115bc7Smrj  * This routine writes the contents of the given buffer to the real time
404ae115bc7Smrj  * clock.  It is given RTC_NREGP bytes of data, which are the 10 bytes used
405ae115bc7Smrj  * to write the time and set the alarm.  It should be called with the priority
406ae115bc7Smrj  * raised to 5.
407ae115bc7Smrj  */
408ae115bc7Smrj static void
todpc_rtcput(unsigned char * buf)409ae115bc7Smrj todpc_rtcput(unsigned char *buf)
410ae115bc7Smrj {
411ae115bc7Smrj 	unsigned char	reg;
412ae115bc7Smrj 	int		i;
4132df1fe9cSrandyf 	unsigned char	century = RTC_CENTURY;
4142df1fe9cSrandyf 	unsigned char	day_alrm = pc_rtc_offset.day_alrm;
4152df1fe9cSrandyf 	unsigned char	mon_alrm = pc_rtc_offset.mon_alrm;
4165b3a1fb0SDan Mick 	unsigned char	tmp;
4172df1fe9cSrandyf 
4182df1fe9cSrandyf 	if (pc_rtc_offset.century != 0) {
4192df1fe9cSrandyf 		century = pc_rtc_offset.century;
4202df1fe9cSrandyf 	}
421ae115bc7Smrj 
422ae115bc7Smrj 	outb(RTC_ADDR, RTC_B);
423ae115bc7Smrj 	reg = inb(RTC_DATA);
424ae115bc7Smrj 	outb(RTC_ADDR, RTC_B);
425ae115bc7Smrj 	outb(RTC_DATA, reg | RTC_SET);	/* allow time set now */
426ae115bc7Smrj 	for (i = 0; i < RTC_NREGP; i++) { /* set the time */
427ae115bc7Smrj 		outb(RTC_ADDR, i);
428ae115bc7Smrj 		outb(RTC_DATA, buf[i]);
429ae115bc7Smrj 	}
4302df1fe9cSrandyf 	outb(RTC_ADDR, century); /* do century */
431ae115bc7Smrj 	outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_century);
4322df1fe9cSrandyf 
4332df1fe9cSrandyf 	if (day_alrm > 0) {
4342df1fe9cSrandyf 		outb(RTC_ADDR, day_alrm);
4355b3a1fb0SDan Mick 		/* preserve RTC_VRT bit; some virt envs accept writes there */
4365b3a1fb0SDan Mick 		tmp = inb(RTC_DATA) & RTC_VRT;
4375b3a1fb0SDan Mick 		tmp |= ((struct rtc_t *)buf)->rtc_adom & ~RTC_VRT;
4385b3a1fb0SDan Mick 		outb(RTC_DATA, tmp);
4392df1fe9cSrandyf 	}
4402df1fe9cSrandyf 	if (mon_alrm > 0) {
4412df1fe9cSrandyf 		outb(RTC_ADDR, mon_alrm);
4422df1fe9cSrandyf 		outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_amon);
4432df1fe9cSrandyf 	}
4442df1fe9cSrandyf 
4452df1fe9cSrandyf 	outb(RTC_ADDR, RTC_B);
4462df1fe9cSrandyf 	reg = inb(RTC_DATA);
447ae115bc7Smrj 	outb(RTC_ADDR, RTC_B);
448ae115bc7Smrj 	outb(RTC_DATA, reg & ~RTC_SET);	/* allow time update */
449ae115bc7Smrj }
450ae115bc7Smrj 
451ae115bc7Smrj static tod_ops_t todpc_ops = {
452ae115bc7Smrj 	TOD_OPS_VERSION,
453ae115bc7Smrj 	todpc_get,
454ae115bc7Smrj 	todpc_set,
4552df1fe9cSrandyf 	NULL,
4562df1fe9cSrandyf 	NULL,
4572df1fe9cSrandyf 	todpc_setalarm,
4582df1fe9cSrandyf 	todpc_clralarm,
459ae115bc7Smrj 	NULL
460ae115bc7Smrj };
461ae115bc7Smrj 
462ae115bc7Smrj /*
463ae115bc7Smrj  * Initialize for the default TOD ops vector for use on hardware.
464ae115bc7Smrj  */
465ae115bc7Smrj 
466ae115bc7Smrj tod_ops_t *tod_ops = &todpc_ops;
467