xref: /onnv-gate/usr/src/uts/i86pc/io/todpc_subr.c (revision 11752:9c475fee0b48)
13446Smrj /*
23446Smrj  * CDDL HEADER START
33446Smrj  *
43446Smrj  * The contents of this file are subject to the terms of the
53446Smrj  * Common Development and Distribution License (the "License").
63446Smrj  * You may not use this file except in compliance with the License.
73446Smrj  *
83446Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93446Smrj  * or http://www.opensolaris.org/os/licensing.
103446Smrj  * See the License for the specific language governing permissions
113446Smrj  * and limitations under the License.
123446Smrj  *
133446Smrj  * When distributing Covered Code, include this CDDL HEADER in each
143446Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153446Smrj  * If applicable, add the following below this CDDL HEADER, with the
163446Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
173446Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
183446Smrj  *
193446Smrj  * CDDL HEADER END
203446Smrj  */
213446Smrj 
223446Smrj /*
23*11752STrevor.Thompson@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
243446Smrj  * Use is subject to license terms.
253446Smrj  */
263446Smrj 
273446Smrj /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
283446Smrj /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
293446Smrj /*	  All Rights Reserved  	*/
303446Smrj 
313446Smrj /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
323446Smrj /*	  All Rights Reserved	*/
333446Smrj 
343446Smrj #include <sys/param.h>
353446Smrj #include <sys/time.h>
363446Smrj #include <sys/systm.h>
373446Smrj 
383446Smrj #include <sys/cpuvar.h>
393446Smrj #include <sys/clock.h>
403446Smrj #include <sys/debug.h>
413446Smrj #include <sys/rtc.h>
423446Smrj #include <sys/archsystm.h>
433446Smrj #include <sys/sysmacros.h>
443446Smrj #include <sys/lockstat.h>
453446Smrj #include <sys/stat.h>
463446Smrj #include <sys/sunddi.h>
473446Smrj 
485295Srandyf #include <sys/acpi/acpi.h>
495295Srandyf #include <sys/acpica.h>
505295Srandyf 
513446Smrj static int todpc_rtcget(unsigned char *buf);
523446Smrj static void todpc_rtcput(unsigned char *buf);
533446Smrj 
545295Srandyf #define	CLOCK_RES	1000		/* 1 microsec in nanosecs */
555295Srandyf 
565295Srandyf int clock_res = CLOCK_RES;
575295Srandyf 
585295Srandyf /*
595295Srandyf  * The minimum sleep time till an alarm can be fired.
605295Srandyf  * This can be tuned in /etc/system, but if the value is too small,
615295Srandyf  * there is a danger that it will be missed if it takes too long to
625295Srandyf  * get from the set point to sleep.  Or that it can fire quickly, and
635295Srandyf  * generate a power spike on the hardware.  And small values are
645295Srandyf  * probably only usefull for test setups.
655295Srandyf  */
665295Srandyf int clock_min_alarm = 4;
675295Srandyf 
683446Smrj /*
693446Smrj  * Machine-dependent clock routines.
703446Smrj  */
713446Smrj 
725295Srandyf extern long gmt_lag;
735295Srandyf 
745295Srandyf struct rtc_offset {
755295Srandyf 	int8_t	loaded;
765295Srandyf 	uint8_t	day_alrm;
775295Srandyf 	uint8_t mon_alrm;
785295Srandyf 	uint8_t	century;
795295Srandyf };
805295Srandyf 
815295Srandyf static struct rtc_offset pc_rtc_offset = {0, 0, 0, 0};
825295Srandyf 
835295Srandyf 
845295Srandyf /*
855295Srandyf  * Entry point for ACPI to pass RTC or other clock values that
865295Srandyf  * are useful to TOD.
875295Srandyf  */
885295Srandyf void
pc_tod_set_rtc_offsets(ACPI_TABLE_FADT * fadt)897851SDana.Myers@Sun.COM pc_tod_set_rtc_offsets(ACPI_TABLE_FADT *fadt) {
905295Srandyf 	int		ok = 0;
915295Srandyf 
925295Srandyf 	/*
935295Srandyf 	 * ASSERT is for debugging, but we don't want the machine
945295Srandyf 	 * falling over because for some reason we didn't get a valid
955295Srandyf 	 * pointer.
965295Srandyf 	 */
975295Srandyf 	ASSERT(fadt);
985295Srandyf 	if (fadt == NULL) {
995295Srandyf 		return;
1005295Srandyf 	}
1015295Srandyf 
1027851SDana.Myers@Sun.COM 	if (fadt->DayAlarm) {
1037851SDana.Myers@Sun.COM 		pc_rtc_offset.day_alrm = fadt->DayAlarm;
1045295Srandyf 		ok = 1;
1055295Srandyf 	}
1065295Srandyf 
1077851SDana.Myers@Sun.COM 	if (fadt->MonthAlarm) {
1087851SDana.Myers@Sun.COM 		pc_rtc_offset.mon_alrm = fadt->MonthAlarm;
1095295Srandyf 		ok = 1;
1105295Srandyf 	}
1115295Srandyf 
1125295Srandyf 	if (fadt->Century) {
1135295Srandyf 		pc_rtc_offset.century = fadt->Century;
1145295Srandyf 		ok = 1;
1155295Srandyf 	}
1165295Srandyf 
1175295Srandyf 	pc_rtc_offset.loaded = ok;
1185295Srandyf }
1195295Srandyf 
1205295Srandyf 
1213446Smrj /*
1223446Smrj  * Write the specified time into the clock chip.
1233446Smrj  * Must be called with tod_lock held.
1243446Smrj  */
1253446Smrj /*ARGSUSED*/
1263446Smrj static void
todpc_set(tod_ops_t * top,timestruc_t ts)1273446Smrj todpc_set(tod_ops_t *top, timestruc_t ts)
1283446Smrj {
1293446Smrj 	todinfo_t tod = utc_to_tod(ts.tv_sec - ggmtl());
1303446Smrj 	struct rtc_t rtc;
1313446Smrj 
1323446Smrj 	ASSERT(MUTEX_HELD(&tod_lock));
1333446Smrj 
1343446Smrj 	if (todpc_rtcget((unsigned char *)&rtc))
1353446Smrj 		return;
1363446Smrj 
1373446Smrj 	/*
1383446Smrj 	 * rtc bytes are in binary-coded decimal, so we have to convert.
1393446Smrj 	 * We assume that we wrap the rtc year back to zero at 2000.
1403446Smrj 	 */
1413446Smrj 	/* LINTED: YRBASE = 0 for x86 */
1423446Smrj 	tod.tod_year -= YRBASE;
1433446Smrj 	if (tod.tod_year >= 100) {
1443446Smrj 		tod.tod_year -= 100;
1453446Smrj 		rtc.rtc_century = BYTE_TO_BCD(20); /* 20xx year */
1463446Smrj 	} else
1473446Smrj 		rtc.rtc_century = BYTE_TO_BCD(19); /* 19xx year */
1483446Smrj 	rtc.rtc_yr	= BYTE_TO_BCD(tod.tod_year);
1493446Smrj 	rtc.rtc_mon	= BYTE_TO_BCD(tod.tod_month);
1503446Smrj 	rtc.rtc_dom	= BYTE_TO_BCD(tod.tod_day);
1513446Smrj 	/* dow < 10, so no conversion */
1523446Smrj 	rtc.rtc_dow	= (unsigned char)tod.tod_dow;
1533446Smrj 	rtc.rtc_hr	= BYTE_TO_BCD(tod.tod_hour);
1543446Smrj 	rtc.rtc_min	= BYTE_TO_BCD(tod.tod_min);
1553446Smrj 	rtc.rtc_sec	= BYTE_TO_BCD(tod.tod_sec);
1563446Smrj 
1573446Smrj 	todpc_rtcput((unsigned char *)&rtc);
1583446Smrj }
1593446Smrj 
1603446Smrj /*
1613446Smrj  * Read the current time from the clock chip and convert to UNIX form.
1623446Smrj  * Assumes that the year in the clock chip is valid.
1633446Smrj  * Must be called with tod_lock held.
1643446Smrj  */
1653446Smrj /*ARGSUSED*/
1663446Smrj static timestruc_t
todpc_get(tod_ops_t * top)1673446Smrj todpc_get(tod_ops_t *top)
1683446Smrj {
1693446Smrj 	timestruc_t ts;
1703446Smrj 	todinfo_t tod;
1713446Smrj 	struct rtc_t rtc;
1723446Smrj 	int compute_century;
1733446Smrj 	static int century_warn = 1; /* only warn once, not each time called */
1743446Smrj 	static int range_warn = 1;
1753446Smrj 
1763446Smrj 	ASSERT(MUTEX_HELD(&tod_lock));
1773446Smrj 
1783446Smrj 	if (todpc_rtcget((unsigned char *)&rtc)) {
1793446Smrj 		ts.tv_sec = 0;
1803446Smrj 		ts.tv_nsec = 0;
181*11752STrevor.Thompson@Sun.COM 		tod_status_set(TOD_GET_FAILED);
1823446Smrj 		return (ts);
1833446Smrj 	}
1843446Smrj 
1853446Smrj 	/* assume that we wrap the rtc year back to zero at 2000 */
1863446Smrj 	tod.tod_year	= BCD_TO_BYTE(rtc.rtc_yr);
1873446Smrj 	if (tod.tod_year < 69) {
1883446Smrj 		if (range_warn && tod.tod_year > 38) {
1893446Smrj 			cmn_err(CE_WARN, "hardware real-time clock is out "
1905295Srandyf 			    "of range -- time needs to be reset");
1913446Smrj 			range_warn = 0;
1923446Smrj 		}
1933446Smrj 		tod.tod_year += 100 + YRBASE; /* 20xx year */
1943446Smrj 		compute_century = 20;
1953446Smrj 	} else {
1963446Smrj 		/* LINTED: YRBASE = 0 for x86 */
1973446Smrj 		tod.tod_year += YRBASE; /* 19xx year */
1983446Smrj 		compute_century = 19;
1993446Smrj 	}
2003446Smrj 	if (century_warn && BCD_TO_BYTE(rtc.rtc_century) != compute_century) {
2013446Smrj 		cmn_err(CE_NOTE,
2025295Srandyf 		    "The hardware real-time clock appears to have the "
2035295Srandyf 		    "wrong century: %d.\nSolaris will still operate "
2045295Srandyf 		    "correctly, but other OS's/firmware agents may "
2055295Srandyf 		    "not.\nUse date(1) to set the date to the current "
2065295Srandyf 		    "time to correct the RTC.",
2075295Srandyf 		    BCD_TO_BYTE(rtc.rtc_century));
2083446Smrj 		century_warn = 0;
2093446Smrj 	}
2103446Smrj 	tod.tod_month	= BCD_TO_BYTE(rtc.rtc_mon);
2113446Smrj 	tod.tod_day	= BCD_TO_BYTE(rtc.rtc_dom);
2123446Smrj 	tod.tod_dow	= rtc.rtc_dow;	/* dow < 10, so no conversion needed */
2133446Smrj 	tod.tod_hour	= BCD_TO_BYTE(rtc.rtc_hr);
2143446Smrj 	tod.tod_min	= BCD_TO_BYTE(rtc.rtc_min);
2153446Smrj 	tod.tod_sec	= BCD_TO_BYTE(rtc.rtc_sec);
2163446Smrj 
217*11752STrevor.Thompson@Sun.COM 	/* read was successful so ensure failure flag is clear */
218*11752STrevor.Thompson@Sun.COM 	tod_status_clear(TOD_GET_FAILED);
219*11752STrevor.Thompson@Sun.COM 
2203446Smrj 	ts.tv_sec = tod_to_utc(tod) + ggmtl();
2213446Smrj 	ts.tv_nsec = 0;
2223446Smrj 
2233446Smrj 	return (ts);
2243446Smrj }
2253446Smrj 
2265295Srandyf #include <sys/promif.h>
2275295Srandyf /*
2285295Srandyf  * Write the specified wakeup alarm into the clock chip.
2295295Srandyf  * Must be called with tod_lock held.
2305295Srandyf  */
2315295Srandyf void
2325295Srandyf /*ARGSUSED*/
todpc_setalarm(tod_ops_t * top,int nsecs)2335295Srandyf todpc_setalarm(tod_ops_t *top, int nsecs)
2345295Srandyf {
2355295Srandyf 	struct rtc_t rtc;
2365295Srandyf 	int delta, asec, amin, ahr, adom, amon;
2375295Srandyf 	int day_alrm = pc_rtc_offset.day_alrm;
2385295Srandyf 	int mon_alrm = pc_rtc_offset.mon_alrm;
2395295Srandyf 
2405295Srandyf 	ASSERT(MUTEX_HELD(&tod_lock));
2415295Srandyf 
2425295Srandyf 	/* A delay of zero is not allowed */
2435295Srandyf 	if (nsecs == 0)
2445295Srandyf 		return;
2455295Srandyf 
2465295Srandyf 	/* Make sure that we delay no less than the minimum time */
2475295Srandyf 	if (nsecs < clock_min_alarm)
2485295Srandyf 		nsecs = clock_min_alarm;
2495295Srandyf 
2505295Srandyf 	if (todpc_rtcget((unsigned char *)&rtc))
2515295Srandyf 		return;
2525295Srandyf 
2535295Srandyf 	/*
2545295Srandyf 	 * Compute alarm secs, mins and hrs, and where appropriate, dom
2555295Srandyf 	 * and mon.  rtc bytes are in binary-coded decimal, so we have
2565295Srandyf 	 * to convert.
2575295Srandyf 	 */
2585295Srandyf 	delta = nsecs + BCD_TO_BYTE(rtc.rtc_sec);
2595295Srandyf 	asec = delta % 60;
2605295Srandyf 
2615295Srandyf 	delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_min);
2625295Srandyf 	amin = delta % 60;
2635295Srandyf 
2645295Srandyf 	delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_hr);
2655295Srandyf 	ahr  = delta % 24;
2665295Srandyf 
2675295Srandyf 	if (day_alrm == 0 && delta >= 24) {
2685295Srandyf 		prom_printf("No day alarm - set to end of today!\n");
2695295Srandyf 		asec = 59;
2705295Srandyf 		amin = 59;
2715295Srandyf 		ahr  = 23;
2725295Srandyf 	} else {
2735295Srandyf 		int mon = BCD_TO_BYTE(rtc.rtc_mon);
2745295Srandyf 		static int dpm[] =
2755295Srandyf 		    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
2765295Srandyf 
2775295Srandyf 		adom = (delta / 24) + BCD_TO_BYTE(rtc.rtc_dom);
2785295Srandyf 
2795295Srandyf 		if (mon_alrm == 0) {
2805295Srandyf 			if (adom > dpm[mon]) {
2815295Srandyf 				prom_printf("No mon alarm - "
2825295Srandyf 				    "set to end of current month!\n");
2835295Srandyf 				asec = 59;
2845295Srandyf 				amin = 59;
2855295Srandyf 				ahr  = 23;
2865295Srandyf 				adom = dpm[mon];
2875295Srandyf 			}
2885295Srandyf 		} else {
2895295Srandyf 			for (amon = mon;
2905295Srandyf 			    amon <= 12 && adom > dpm[amon]; amon++) {
2915295Srandyf 				adom -= dpm[amon];
2925295Srandyf 			}
2935295Srandyf 			if (amon > 12) {
2945295Srandyf 				prom_printf("Alarm too far in future - "
2955295Srandyf 				    "set to end of current year!\n");
2965295Srandyf 				asec = 59;
2975295Srandyf 				amin = 59;
2985295Srandyf 				ahr  = 23;
2995295Srandyf 				adom = dpm[12];
3005295Srandyf 				amon = 12;
3015295Srandyf 			}
3025295Srandyf 			rtc.rtc_amon = BYTE_TO_BCD(amon);
3035295Srandyf 		}
3045295Srandyf 
3055295Srandyf 		rtc.rtc_adom = BYTE_TO_BCD(adom);
3065295Srandyf 	}
3075295Srandyf 
3085295Srandyf 	rtc.rtc_asec = BYTE_TO_BCD(asec);
3095295Srandyf 	rtc.rtc_amin = BYTE_TO_BCD(amin);
3105295Srandyf 	rtc.rtc_ahr  = BYTE_TO_BCD(ahr);
3115295Srandyf 
3125295Srandyf 	rtc.rtc_statusb |= RTC_AIE;	/* Enable alarm interrupt */
3135295Srandyf 
3145295Srandyf 	todpc_rtcput((unsigned char *)&rtc);
3155295Srandyf }
3165295Srandyf 
3175295Srandyf /*
3185295Srandyf  * Clear an alarm.  This is effectively setting an alarm of 0.
3195295Srandyf  */
3205295Srandyf void
3215295Srandyf /*ARGSUSED*/
todpc_clralarm(tod_ops_t * top)3225295Srandyf todpc_clralarm(tod_ops_t *top)
3235295Srandyf {
3245295Srandyf 	mutex_enter(&tod_lock);
3255295Srandyf 	todpc_setalarm(top, 0);
3265295Srandyf 	mutex_exit(&tod_lock);
3275295Srandyf }
3285295Srandyf 
3293446Smrj /*
3303446Smrj  * Routine to read contents of real time clock to the specified buffer.
3313446Smrj  * Returns ENXIO if clock not valid, or EAGAIN if clock data cannot be read
3323446Smrj  * else 0.
3333446Smrj  * The routine will busy wait for the Update-In-Progress flag to clear.
3343446Smrj  * On completion of the reads the Seconds register is re-read and the
3353446Smrj  * UIP flag is rechecked to confirm that an clock update did not occur
3363446Smrj  * during the accesses.  Routine will error exit after 256 attempts.
3373446Smrj  * (See bugid 1158298.)
3383446Smrj  * Routine returns RTC_NREG (which is 15) bytes of data, as given in the
3393446Smrj  * technical reference.  This data includes both time and status registers.
3403446Smrj  */
3413446Smrj 
3423446Smrj static int
todpc_rtcget(unsigned char * buf)3433446Smrj todpc_rtcget(unsigned char *buf)
3443446Smrj {
3453446Smrj 	unsigned char	reg;
3463446Smrj 	int		i;
3473446Smrj 	int		retries = 256;
3483446Smrj 	unsigned char	*rawp;
3495295Srandyf 	unsigned char	century = RTC_CENTURY;
3505295Srandyf 	unsigned char	day_alrm;
3515295Srandyf 	unsigned char	mon_alrm;
3523446Smrj 
3533446Smrj 	ASSERT(MUTEX_HELD(&tod_lock));
3543446Smrj 
3555295Srandyf 	day_alrm = pc_rtc_offset.day_alrm;
3565295Srandyf 	mon_alrm = pc_rtc_offset.mon_alrm;
3575295Srandyf 	if (pc_rtc_offset.century != 0) {
3585295Srandyf 		century = pc_rtc_offset.century;
3595295Srandyf 	}
3605295Srandyf 
3613446Smrj 	outb(RTC_ADDR, RTC_D);		/* check if clock valid */
3623446Smrj 	reg = inb(RTC_DATA);
3633446Smrj 	if ((reg & RTC_VRT) == 0)
3643446Smrj 		return (ENXIO);
3653446Smrj 
3663446Smrj checkuip:
3673446Smrj 	if (retries-- < 0)
3683446Smrj 		return (EAGAIN);
3693446Smrj 	outb(RTC_ADDR, RTC_A);		/* check if update in progress */
3703446Smrj 	reg = inb(RTC_DATA);
3713446Smrj 	if (reg & RTC_UIP) {
3723446Smrj 		tenmicrosec();
3733446Smrj 		goto checkuip;
3743446Smrj 	}
3753446Smrj 
3763446Smrj 	for (i = 0, rawp = buf; i < RTC_NREG; i++) {
3773446Smrj 		outb(RTC_ADDR, i);
3783446Smrj 		*rawp++ = inb(RTC_DATA);
3793446Smrj 	}
3805295Srandyf 	outb(RTC_ADDR, century); /* do century */
3813446Smrj 	((struct rtc_t *)buf)->rtc_century = inb(RTC_DATA);
3825295Srandyf 
3835295Srandyf 	if (day_alrm > 0) {
3845295Srandyf 		outb(RTC_ADDR, day_alrm);
3855295Srandyf 		((struct rtc_t *)buf)->rtc_adom = inb(RTC_DATA) & 0x3f;
3865295Srandyf 	}
3875295Srandyf 	if (mon_alrm > 0) {
3885295Srandyf 		outb(RTC_ADDR, mon_alrm);
3895295Srandyf 		((struct rtc_t *)buf)->rtc_amon = inb(RTC_DATA);
3905295Srandyf 	}
3915295Srandyf 
3923446Smrj 	outb(RTC_ADDR, 0);		/* re-read Seconds register */
3933446Smrj 	reg = inb(RTC_DATA);
3943446Smrj 	if (reg != ((struct rtc_t *)buf)->rtc_sec ||
3953446Smrj 	    (((struct rtc_t *)buf)->rtc_statusa & RTC_UIP))
3963446Smrj 		/* update occured during reads */
3973446Smrj 		goto checkuip;
3983446Smrj 
3993446Smrj 	return (0);
4003446Smrj }
4013446Smrj 
4023446Smrj /*
4033446Smrj  * This routine writes the contents of the given buffer to the real time
4043446Smrj  * clock.  It is given RTC_NREGP bytes of data, which are the 10 bytes used
4053446Smrj  * to write the time and set the alarm.  It should be called with the priority
4063446Smrj  * raised to 5.
4073446Smrj  */
4083446Smrj static void
todpc_rtcput(unsigned char * buf)4093446Smrj todpc_rtcput(unsigned char *buf)
4103446Smrj {
4113446Smrj 	unsigned char	reg;
4123446Smrj 	int		i;
4135295Srandyf 	unsigned char	century = RTC_CENTURY;
4145295Srandyf 	unsigned char	day_alrm = pc_rtc_offset.day_alrm;
4155295Srandyf 	unsigned char	mon_alrm = pc_rtc_offset.mon_alrm;
4168936SDan.Mick@Sun.COM 	unsigned char	tmp;
4175295Srandyf 
4185295Srandyf 	if (pc_rtc_offset.century != 0) {
4195295Srandyf 		century = pc_rtc_offset.century;
4205295Srandyf 	}
4213446Smrj 
4223446Smrj 	outb(RTC_ADDR, RTC_B);
4233446Smrj 	reg = inb(RTC_DATA);
4243446Smrj 	outb(RTC_ADDR, RTC_B);
4253446Smrj 	outb(RTC_DATA, reg | RTC_SET);	/* allow time set now */
4263446Smrj 	for (i = 0; i < RTC_NREGP; i++) { /* set the time */
4273446Smrj 		outb(RTC_ADDR, i);
4283446Smrj 		outb(RTC_DATA, buf[i]);
4293446Smrj 	}
4305295Srandyf 	outb(RTC_ADDR, century); /* do century */
4313446Smrj 	outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_century);
4325295Srandyf 
4335295Srandyf 	if (day_alrm > 0) {
4345295Srandyf 		outb(RTC_ADDR, day_alrm);
4358936SDan.Mick@Sun.COM 		/* preserve RTC_VRT bit; some virt envs accept writes there */
4368936SDan.Mick@Sun.COM 		tmp = inb(RTC_DATA) & RTC_VRT;
4378936SDan.Mick@Sun.COM 		tmp |= ((struct rtc_t *)buf)->rtc_adom & ~RTC_VRT;
4388936SDan.Mick@Sun.COM 		outb(RTC_DATA, tmp);
4395295Srandyf 	}
4405295Srandyf 	if (mon_alrm > 0) {
4415295Srandyf 		outb(RTC_ADDR, mon_alrm);
4425295Srandyf 		outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_amon);
4435295Srandyf 	}
4445295Srandyf 
4455295Srandyf 	outb(RTC_ADDR, RTC_B);
4465295Srandyf 	reg = inb(RTC_DATA);
4473446Smrj 	outb(RTC_ADDR, RTC_B);
4483446Smrj 	outb(RTC_DATA, reg & ~RTC_SET);	/* allow time update */
4493446Smrj }
4503446Smrj 
4513446Smrj static tod_ops_t todpc_ops = {
4523446Smrj 	TOD_OPS_VERSION,
4533446Smrj 	todpc_get,
4543446Smrj 	todpc_set,
4555295Srandyf 	NULL,
4565295Srandyf 	NULL,
4575295Srandyf 	todpc_setalarm,
4585295Srandyf 	todpc_clralarm,
4593446Smrj 	NULL
4603446Smrj };
4613446Smrj 
4623446Smrj /*
4633446Smrj  * Initialize for the default TOD ops vector for use on hardware.
4643446Smrj  */
4653446Smrj 
4663446Smrj tod_ops_t *tod_ops = &todpc_ops;
467