xref: /illumos-gate/usr/src/uts/i86pc/io/todpc_subr.c (revision 4414ceb1)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * 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.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
218500cbf9SDan Kruchinin /*
22*4414ceb1SGary Mills  * Copyright 2018 Gary Mills
238500cbf9SDan Kruchinin  * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
248500cbf9SDan Kruchinin  */
257c478bd9Sstevel@tonic-gate /*
268fc99e42STrevor Thompson  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
277c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
317c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
327c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
357c478bd9Sstevel@tonic-gate /*	  All Rights Reserved	*/
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate #include <sys/param.h>
387c478bd9Sstevel@tonic-gate #include <sys/time.h>
397c478bd9Sstevel@tonic-gate #include <sys/systm.h>
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
427c478bd9Sstevel@tonic-gate #include <sys/clock.h>
437c478bd9Sstevel@tonic-gate #include <sys/debug.h>
447c478bd9Sstevel@tonic-gate #include <sys/rtc.h>
457c478bd9Sstevel@tonic-gate #include <sys/archsystm.h>
467c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
477c478bd9Sstevel@tonic-gate #include <sys/lockstat.h>
487c478bd9Sstevel@tonic-gate #include <sys/stat.h>
497c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
50*4414ceb1SGary Mills #include <sys/ddi.h>
517c478bd9Sstevel@tonic-gate 
522df1fe9cSrandyf #include <sys/acpi/acpi.h>
532df1fe9cSrandyf #include <sys/acpica.h>
542df1fe9cSrandyf 
55ae115bc7Smrj static int todpc_rtcget(unsigned char *buf);
56ae115bc7Smrj static void todpc_rtcput(unsigned char *buf);
577c478bd9Sstevel@tonic-gate 
582df1fe9cSrandyf #define	CLOCK_RES	1000		/* 1 microsec in nanosecs */
592df1fe9cSrandyf 
602df1fe9cSrandyf int clock_res = CLOCK_RES;
612df1fe9cSrandyf 
622df1fe9cSrandyf /*
632df1fe9cSrandyf  * The minimum sleep time till an alarm can be fired.
642df1fe9cSrandyf  * This can be tuned in /etc/system, but if the value is too small,
652df1fe9cSrandyf  * there is a danger that it will be missed if it takes too long to
662df1fe9cSrandyf  * get from the set point to sleep.  Or that it can fire quickly, and
672df1fe9cSrandyf  * generate a power spike on the hardware.  And small values are
682df1fe9cSrandyf  * probably only usefull for test setups.
692df1fe9cSrandyf  */
702df1fe9cSrandyf int clock_min_alarm = 4;
712df1fe9cSrandyf 
727c478bd9Sstevel@tonic-gate /*
737c478bd9Sstevel@tonic-gate  * Machine-dependent clock routines.
747c478bd9Sstevel@tonic-gate  */
757c478bd9Sstevel@tonic-gate 
762df1fe9cSrandyf extern long gmt_lag;
772df1fe9cSrandyf 
782df1fe9cSrandyf struct rtc_offset {
792df1fe9cSrandyf 	int8_t	loaded;
802df1fe9cSrandyf 	uint8_t	day_alrm;
812df1fe9cSrandyf 	uint8_t mon_alrm;
822df1fe9cSrandyf 	uint8_t	century;
832df1fe9cSrandyf };
842df1fe9cSrandyf 
852df1fe9cSrandyf static struct rtc_offset pc_rtc_offset = {0, 0, 0, 0};
862df1fe9cSrandyf 
872df1fe9cSrandyf 
882df1fe9cSrandyf /*
892df1fe9cSrandyf  * Entry point for ACPI to pass RTC or other clock values that
902df1fe9cSrandyf  * are useful to TOD.
912df1fe9cSrandyf  */
922df1fe9cSrandyf void
pc_tod_set_rtc_offsets(ACPI_TABLE_FADT * fadt)93*4414ceb1SGary Mills pc_tod_set_rtc_offsets(ACPI_TABLE_FADT *fadt)
94*4414ceb1SGary Mills {
952df1fe9cSrandyf 	int		ok = 0;
962df1fe9cSrandyf 
972df1fe9cSrandyf 	/*
982df1fe9cSrandyf 	 * ASSERT is for debugging, but we don't want the machine
992df1fe9cSrandyf 	 * falling over because for some reason we didn't get a valid
1002df1fe9cSrandyf 	 * pointer.
1012df1fe9cSrandyf 	 */
1022df1fe9cSrandyf 	ASSERT(fadt);
1032df1fe9cSrandyf 	if (fadt == NULL) {
1042df1fe9cSrandyf 		return;
1052df1fe9cSrandyf 	}
1062df1fe9cSrandyf 
107db2bae30SDana Myers 	if (fadt->DayAlarm) {
108db2bae30SDana Myers 		pc_rtc_offset.day_alrm = fadt->DayAlarm;
1092df1fe9cSrandyf 		ok = 1;
1102df1fe9cSrandyf 	}
1112df1fe9cSrandyf 
112db2bae30SDana Myers 	if (fadt->MonthAlarm) {
113db2bae30SDana Myers 		pc_rtc_offset.mon_alrm = fadt->MonthAlarm;
1142df1fe9cSrandyf 		ok = 1;
1152df1fe9cSrandyf 	}
1162df1fe9cSrandyf 
1172df1fe9cSrandyf 	if (fadt->Century) {
1182df1fe9cSrandyf 		pc_rtc_offset.century = fadt->Century;
1192df1fe9cSrandyf 		ok = 1;
1202df1fe9cSrandyf 	}
1212df1fe9cSrandyf 
1222df1fe9cSrandyf 	pc_rtc_offset.loaded = ok;
1232df1fe9cSrandyf }
1242df1fe9cSrandyf 
1252df1fe9cSrandyf 
1267c478bd9Sstevel@tonic-gate /*
1277c478bd9Sstevel@tonic-gate  * Write the specified time into the clock chip.
1287c478bd9Sstevel@tonic-gate  * Must be called with tod_lock held.
1297c478bd9Sstevel@tonic-gate  */
130ae115bc7Smrj /*ARGSUSED*/
131ae115bc7Smrj static void
todpc_set(tod_ops_t * top,timestruc_t ts)132ae115bc7Smrj todpc_set(tod_ops_t *top, timestruc_t ts)
1337c478bd9Sstevel@tonic-gate {
134ae115bc7Smrj 	todinfo_t tod = utc_to_tod(ts.tv_sec - ggmtl());
1357c478bd9Sstevel@tonic-gate 	struct rtc_t rtc;
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
1387c478bd9Sstevel@tonic-gate 
139ae115bc7Smrj 	if (todpc_rtcget((unsigned char *)&rtc))
1407c478bd9Sstevel@tonic-gate 		return;
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate 	/*
1437c478bd9Sstevel@tonic-gate 	 * rtc bytes are in binary-coded decimal, so we have to convert.
1447c478bd9Sstevel@tonic-gate 	 * We assume that we wrap the rtc year back to zero at 2000.
1457c478bd9Sstevel@tonic-gate 	 */
1467c478bd9Sstevel@tonic-gate 	/* LINTED: YRBASE = 0 for x86 */
1477c478bd9Sstevel@tonic-gate 	tod.tod_year -= YRBASE;
1487c478bd9Sstevel@tonic-gate 	if (tod.tod_year >= 100) {
1497c478bd9Sstevel@tonic-gate 		tod.tod_year -= 100;
1507c478bd9Sstevel@tonic-gate 		rtc.rtc_century = BYTE_TO_BCD(20); /* 20xx year */
1517c478bd9Sstevel@tonic-gate 	} else
1527c478bd9Sstevel@tonic-gate 		rtc.rtc_century = BYTE_TO_BCD(19); /* 19xx year */
1537c478bd9Sstevel@tonic-gate 	rtc.rtc_yr	= BYTE_TO_BCD(tod.tod_year);
1547c478bd9Sstevel@tonic-gate 	rtc.rtc_mon	= BYTE_TO_BCD(tod.tod_month);
1557c478bd9Sstevel@tonic-gate 	rtc.rtc_dom	= BYTE_TO_BCD(tod.tod_day);
1567c478bd9Sstevel@tonic-gate 	/* dow < 10, so no conversion */
1577c478bd9Sstevel@tonic-gate 	rtc.rtc_dow	= (unsigned char)tod.tod_dow;
1587c478bd9Sstevel@tonic-gate 	rtc.rtc_hr	= BYTE_TO_BCD(tod.tod_hour);
1597c478bd9Sstevel@tonic-gate 	rtc.rtc_min	= BYTE_TO_BCD(tod.tod_min);
1607c478bd9Sstevel@tonic-gate 	rtc.rtc_sec	= BYTE_TO_BCD(tod.tod_sec);
1617c478bd9Sstevel@tonic-gate 
162ae115bc7Smrj 	todpc_rtcput((unsigned char *)&rtc);
1637c478bd9Sstevel@tonic-gate }
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate /*
1667c478bd9Sstevel@tonic-gate  * Read the current time from the clock chip and convert to UNIX form.
1677c478bd9Sstevel@tonic-gate  * Assumes that the year in the clock chip is valid.
1687c478bd9Sstevel@tonic-gate  * Must be called with tod_lock held.
1697c478bd9Sstevel@tonic-gate  */
170ae115bc7Smrj /*ARGSUSED*/
171ae115bc7Smrj static timestruc_t
todpc_get(tod_ops_t * top)172ae115bc7Smrj todpc_get(tod_ops_t *top)
1737c478bd9Sstevel@tonic-gate {
1747c478bd9Sstevel@tonic-gate 	timestruc_t ts;
1757c478bd9Sstevel@tonic-gate 	todinfo_t tod;
1767c478bd9Sstevel@tonic-gate 	struct rtc_t rtc;
1777c478bd9Sstevel@tonic-gate 	int compute_century;
1787c478bd9Sstevel@tonic-gate 	static int century_warn = 1; /* only warn once, not each time called */
1797c478bd9Sstevel@tonic-gate 	static int range_warn = 1;
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
1827c478bd9Sstevel@tonic-gate 
183ae115bc7Smrj 	if (todpc_rtcget((unsigned char *)&rtc)) {
1848fc99e42STrevor Thompson 		tod_status_set(TOD_GET_FAILED);
1858500cbf9SDan Kruchinin 		return (hrestime);
1867c478bd9Sstevel@tonic-gate 	}
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 	/* assume that we wrap the rtc year back to zero at 2000 */
1897c478bd9Sstevel@tonic-gate 	tod.tod_year	= BCD_TO_BYTE(rtc.rtc_yr);
1907c478bd9Sstevel@tonic-gate 	if (tod.tod_year < 69) {
1917c478bd9Sstevel@tonic-gate 		if (range_warn && tod.tod_year > 38) {
1927c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "hardware real-time clock is out "
1932df1fe9cSrandyf 			    "of range -- time needs to be reset");
1947c478bd9Sstevel@tonic-gate 			range_warn = 0;
1957c478bd9Sstevel@tonic-gate 		}
1967c478bd9Sstevel@tonic-gate 		tod.tod_year += 100 + YRBASE; /* 20xx year */
1977c478bd9Sstevel@tonic-gate 		compute_century = 20;
1987c478bd9Sstevel@tonic-gate 	} else {
1997c478bd9Sstevel@tonic-gate 		/* LINTED: YRBASE = 0 for x86 */
2007c478bd9Sstevel@tonic-gate 		tod.tod_year += YRBASE; /* 19xx year */
2017c478bd9Sstevel@tonic-gate 		compute_century = 19;
2027c478bd9Sstevel@tonic-gate 	}
2037c478bd9Sstevel@tonic-gate 	if (century_warn && BCD_TO_BYTE(rtc.rtc_century) != compute_century) {
2047c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE,
2052df1fe9cSrandyf 		    "The hardware real-time clock appears to have the "
2062df1fe9cSrandyf 		    "wrong century: %d.\nSolaris will still operate "
2072df1fe9cSrandyf 		    "correctly, but other OS's/firmware agents may "
2082df1fe9cSrandyf 		    "not.\nUse date(1) to set the date to the current "
2092df1fe9cSrandyf 		    "time to correct the RTC.",
2102df1fe9cSrandyf 		    BCD_TO_BYTE(rtc.rtc_century));
2117c478bd9Sstevel@tonic-gate 		century_warn = 0;
2127c478bd9Sstevel@tonic-gate 	}
2137c478bd9Sstevel@tonic-gate 	tod.tod_month	= BCD_TO_BYTE(rtc.rtc_mon);
2147c478bd9Sstevel@tonic-gate 	tod.tod_day	= BCD_TO_BYTE(rtc.rtc_dom);
2157c478bd9Sstevel@tonic-gate 	tod.tod_dow	= rtc.rtc_dow;	/* dow < 10, so no conversion needed */
2167c478bd9Sstevel@tonic-gate 	tod.tod_hour	= BCD_TO_BYTE(rtc.rtc_hr);
2177c478bd9Sstevel@tonic-gate 	tod.tod_min	= BCD_TO_BYTE(rtc.rtc_min);
2187c478bd9Sstevel@tonic-gate 	tod.tod_sec	= BCD_TO_BYTE(rtc.rtc_sec);
2197c478bd9Sstevel@tonic-gate 
2208fc99e42STrevor Thompson 	/* read was successful so ensure failure flag is clear */
2218fc99e42STrevor Thompson 	tod_status_clear(TOD_GET_FAILED);
2228fc99e42STrevor Thompson 
223ae115bc7Smrj 	ts.tv_sec = tod_to_utc(tod) + ggmtl();
2247c478bd9Sstevel@tonic-gate 	ts.tv_nsec = 0;
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate 	return (ts);
2277c478bd9Sstevel@tonic-gate }
2287c478bd9Sstevel@tonic-gate 
2292df1fe9cSrandyf #include <sys/promif.h>
2302df1fe9cSrandyf /*
2312df1fe9cSrandyf  * Write the specified wakeup alarm into the clock chip.
2322df1fe9cSrandyf  * Must be called with tod_lock held.
2332df1fe9cSrandyf  */
2342df1fe9cSrandyf void
2352df1fe9cSrandyf /*ARGSUSED*/
todpc_setalarm(tod_ops_t * top,int nsecs)2362df1fe9cSrandyf todpc_setalarm(tod_ops_t *top, int nsecs)
2372df1fe9cSrandyf {
2382df1fe9cSrandyf 	struct rtc_t rtc;
2392df1fe9cSrandyf 	int delta, asec, amin, ahr, adom, amon;
2402df1fe9cSrandyf 	int day_alrm = pc_rtc_offset.day_alrm;
2412df1fe9cSrandyf 	int mon_alrm = pc_rtc_offset.mon_alrm;
2422df1fe9cSrandyf 
2432df1fe9cSrandyf 	ASSERT(MUTEX_HELD(&tod_lock));
2442df1fe9cSrandyf 
2452df1fe9cSrandyf 	/* A delay of zero is not allowed */
2462df1fe9cSrandyf 	if (nsecs == 0)
2472df1fe9cSrandyf 		return;
2482df1fe9cSrandyf 
2492df1fe9cSrandyf 	/* Make sure that we delay no less than the minimum time */
2502df1fe9cSrandyf 	if (nsecs < clock_min_alarm)
2512df1fe9cSrandyf 		nsecs = clock_min_alarm;
2522df1fe9cSrandyf 
2532df1fe9cSrandyf 	if (todpc_rtcget((unsigned char *)&rtc))
2542df1fe9cSrandyf 		return;
2552df1fe9cSrandyf 
2562df1fe9cSrandyf 	/*
2572df1fe9cSrandyf 	 * Compute alarm secs, mins and hrs, and where appropriate, dom
2582df1fe9cSrandyf 	 * and mon.  rtc bytes are in binary-coded decimal, so we have
2592df1fe9cSrandyf 	 * to convert.
2602df1fe9cSrandyf 	 */
2612df1fe9cSrandyf 	delta = nsecs + BCD_TO_BYTE(rtc.rtc_sec);
2622df1fe9cSrandyf 	asec = delta % 60;
2632df1fe9cSrandyf 
2642df1fe9cSrandyf 	delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_min);
2652df1fe9cSrandyf 	amin = delta % 60;
2662df1fe9cSrandyf 
2672df1fe9cSrandyf 	delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_hr);
2682df1fe9cSrandyf 	ahr  = delta % 24;
2692df1fe9cSrandyf 
2702df1fe9cSrandyf 	if (day_alrm == 0 && delta >= 24) {
2712df1fe9cSrandyf 		prom_printf("No day alarm - set to end of today!\n");
2722df1fe9cSrandyf 		asec = 59;
2732df1fe9cSrandyf 		amin = 59;
2742df1fe9cSrandyf 		ahr  = 23;
2752df1fe9cSrandyf 	} else {
2762df1fe9cSrandyf 		int mon = BCD_TO_BYTE(rtc.rtc_mon);
2772df1fe9cSrandyf 		static int dpm[] =
2782df1fe9cSrandyf 		    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
2792df1fe9cSrandyf 
2802df1fe9cSrandyf 		adom = (delta / 24) + BCD_TO_BYTE(rtc.rtc_dom);
2812df1fe9cSrandyf 
2822df1fe9cSrandyf 		if (mon_alrm == 0) {
2832df1fe9cSrandyf 			if (adom > dpm[mon]) {
2842df1fe9cSrandyf 				prom_printf("No mon alarm - "
2852df1fe9cSrandyf 				    "set to end of current month!\n");
2862df1fe9cSrandyf 				asec = 59;
2872df1fe9cSrandyf 				amin = 59;
2882df1fe9cSrandyf 				ahr  = 23;
2892df1fe9cSrandyf 				adom = dpm[mon];
2902df1fe9cSrandyf 			}
2912df1fe9cSrandyf 		} else {
2922df1fe9cSrandyf 			for (amon = mon;
2932df1fe9cSrandyf 			    amon <= 12 && adom > dpm[amon]; amon++) {
2942df1fe9cSrandyf 				adom -= dpm[amon];
2952df1fe9cSrandyf 			}
2962df1fe9cSrandyf 			if (amon > 12) {
2972df1fe9cSrandyf 				prom_printf("Alarm too far in future - "
2982df1fe9cSrandyf 				    "set to end of current year!\n");
2992df1fe9cSrandyf 				asec = 59;
3002df1fe9cSrandyf 				amin = 59;
3012df1fe9cSrandyf 				ahr  = 23;
3022df1fe9cSrandyf 				adom = dpm[12];
3032df1fe9cSrandyf 				amon = 12;
3042df1fe9cSrandyf 			}
3052df1fe9cSrandyf 			rtc.rtc_amon = BYTE_TO_BCD(amon);
3062df1fe9cSrandyf 		}
3072df1fe9cSrandyf 
3082df1fe9cSrandyf 		rtc.rtc_adom = BYTE_TO_BCD(adom);
3092df1fe9cSrandyf 	}
3102df1fe9cSrandyf 
3112df1fe9cSrandyf 	rtc.rtc_asec = BYTE_TO_BCD(asec);
3122df1fe9cSrandyf 	rtc.rtc_amin = BYTE_TO_BCD(amin);
3132df1fe9cSrandyf 	rtc.rtc_ahr  = BYTE_TO_BCD(ahr);
3142df1fe9cSrandyf 
3152df1fe9cSrandyf 	rtc.rtc_statusb |= RTC_AIE;	/* Enable alarm interrupt */
3162df1fe9cSrandyf 
3172df1fe9cSrandyf 	todpc_rtcput((unsigned char *)&rtc);
3182df1fe9cSrandyf }
3192df1fe9cSrandyf 
3202df1fe9cSrandyf /*
3212df1fe9cSrandyf  * Clear an alarm.  This is effectively setting an alarm of 0.
3222df1fe9cSrandyf  */
3232df1fe9cSrandyf void
3242df1fe9cSrandyf /*ARGSUSED*/
todpc_clralarm(tod_ops_t * top)3252df1fe9cSrandyf todpc_clralarm(tod_ops_t *top)
3262df1fe9cSrandyf {
3272df1fe9cSrandyf 	mutex_enter(&tod_lock);
3282df1fe9cSrandyf 	todpc_setalarm(top, 0);
3292df1fe9cSrandyf 	mutex_exit(&tod_lock);
3302df1fe9cSrandyf }
3312df1fe9cSrandyf 
3327c478bd9Sstevel@tonic-gate /*
3337c478bd9Sstevel@tonic-gate  * Routine to read contents of real time clock to the specified buffer.
334ae115bc7Smrj  * Returns ENXIO if clock not valid, or EAGAIN if clock data cannot be read
3357c478bd9Sstevel@tonic-gate  * else 0.
336*4414ceb1SGary Mills  * Some RTC hardware is very slow at asserting the validity flag on
337*4414ceb1SGary Mills  * startup.  The routine will busy wait for the RTC to become valid.
338*4414ceb1SGary Mills  * The routine will also busy wait for the Update-In-Progress flag to clear.
3397c478bd9Sstevel@tonic-gate  * On completion of the reads the Seconds register is re-read and the
3407c478bd9Sstevel@tonic-gate  * UIP flag is rechecked to confirm that an clock update did not occur
3417c478bd9Sstevel@tonic-gate  * during the accesses.  Routine will error exit after 256 attempts.
3427c478bd9Sstevel@tonic-gate  * (See bugid 1158298.)
3437c478bd9Sstevel@tonic-gate  * Routine returns RTC_NREG (which is 15) bytes of data, as given in the
3447c478bd9Sstevel@tonic-gate  * technical reference.  This data includes both time and status registers.
3457c478bd9Sstevel@tonic-gate  */
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate static int
todpc_rtcget(unsigned char * buf)348ae115bc7Smrj todpc_rtcget(unsigned char *buf)
3497c478bd9Sstevel@tonic-gate {
3507c478bd9Sstevel@tonic-gate 	unsigned char	reg;
3517c478bd9Sstevel@tonic-gate 	int		i;
352*4414ceb1SGary Mills 	int		uip_try = 256;
353*4414ceb1SGary Mills 	int		vrt_try = 512;
3547c478bd9Sstevel@tonic-gate 	unsigned char	*rawp;
3552df1fe9cSrandyf 	unsigned char	century = RTC_CENTURY;
3562df1fe9cSrandyf 	unsigned char	day_alrm;
3572df1fe9cSrandyf 	unsigned char	mon_alrm;
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
3607c478bd9Sstevel@tonic-gate 
3612df1fe9cSrandyf 	day_alrm = pc_rtc_offset.day_alrm;
3622df1fe9cSrandyf 	mon_alrm = pc_rtc_offset.mon_alrm;
3632df1fe9cSrandyf 	if (pc_rtc_offset.century != 0) {
3642df1fe9cSrandyf 		century = pc_rtc_offset.century;
3652df1fe9cSrandyf 	}
3662df1fe9cSrandyf 
367*4414ceb1SGary Mills 	for (;;) {
368*4414ceb1SGary Mills 		if (vrt_try-- < 0)
369*4414ceb1SGary Mills 			return (ENXIO);
370*4414ceb1SGary Mills 		outb(RTC_ADDR, RTC_D);		/* check if clock valid */
371*4414ceb1SGary Mills 		reg = inb(RTC_DATA);
372*4414ceb1SGary Mills 		if ((reg & RTC_VRT) != 0)
373*4414ceb1SGary Mills 			break;
374*4414ceb1SGary Mills 		drv_usecwait(5000);		/* Delay for 5000 us */
375*4414ceb1SGary Mills 	}
376*4414ceb1SGary Mills 
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate checkuip:
379*4414ceb1SGary Mills 	if (uip_try-- < 0)
380ae115bc7Smrj 		return (EAGAIN);
3817c478bd9Sstevel@tonic-gate 	outb(RTC_ADDR, RTC_A);		/* check if update in progress */
3827c478bd9Sstevel@tonic-gate 	reg = inb(RTC_DATA);
3837c478bd9Sstevel@tonic-gate 	if (reg & RTC_UIP) {
3847c478bd9Sstevel@tonic-gate 		tenmicrosec();
3857c478bd9Sstevel@tonic-gate 		goto checkuip;
3867c478bd9Sstevel@tonic-gate 	}
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 	for (i = 0, rawp = buf; i < RTC_NREG; i++) {
3897c478bd9Sstevel@tonic-gate 		outb(RTC_ADDR, i);
3907c478bd9Sstevel@tonic-gate 		*rawp++ = inb(RTC_DATA);
3917c478bd9Sstevel@tonic-gate 	}
3922df1fe9cSrandyf 	outb(RTC_ADDR, century); /* do century */
3937c478bd9Sstevel@tonic-gate 	((struct rtc_t *)buf)->rtc_century = inb(RTC_DATA);
3942df1fe9cSrandyf 
3952df1fe9cSrandyf 	if (day_alrm > 0) {
3962df1fe9cSrandyf 		outb(RTC_ADDR, day_alrm);
3972df1fe9cSrandyf 		((struct rtc_t *)buf)->rtc_adom = inb(RTC_DATA) & 0x3f;
3982df1fe9cSrandyf 	}
3992df1fe9cSrandyf 	if (mon_alrm > 0) {
4002df1fe9cSrandyf 		outb(RTC_ADDR, mon_alrm);
4012df1fe9cSrandyf 		((struct rtc_t *)buf)->rtc_amon = inb(RTC_DATA);
4022df1fe9cSrandyf 	}
4032df1fe9cSrandyf 
4047c478bd9Sstevel@tonic-gate 	outb(RTC_ADDR, 0);		/* re-read Seconds register */
4057c478bd9Sstevel@tonic-gate 	reg = inb(RTC_DATA);
4067c478bd9Sstevel@tonic-gate 	if (reg != ((struct rtc_t *)buf)->rtc_sec ||
4077c478bd9Sstevel@tonic-gate 	    (((struct rtc_t *)buf)->rtc_statusa & RTC_UIP))
4087c478bd9Sstevel@tonic-gate 		/* update occured during reads */
4097c478bd9Sstevel@tonic-gate 		goto checkuip;
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 	return (0);
4127c478bd9Sstevel@tonic-gate }
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate /*
4157c478bd9Sstevel@tonic-gate  * This routine writes the contents of the given buffer to the real time
4167c478bd9Sstevel@tonic-gate  * clock.  It is given RTC_NREGP bytes of data, which are the 10 bytes used
4177c478bd9Sstevel@tonic-gate  * to write the time and set the alarm.  It should be called with the priority
4187c478bd9Sstevel@tonic-gate  * raised to 5.
4197c478bd9Sstevel@tonic-gate  */
4207c478bd9Sstevel@tonic-gate static void
todpc_rtcput(unsigned char * buf)421ae115bc7Smrj todpc_rtcput(unsigned char *buf)
4227c478bd9Sstevel@tonic-gate {
4237c478bd9Sstevel@tonic-gate 	unsigned char	reg;
4247c478bd9Sstevel@tonic-gate 	int		i;
4252df1fe9cSrandyf 	unsigned char	century = RTC_CENTURY;
4262df1fe9cSrandyf 	unsigned char	day_alrm = pc_rtc_offset.day_alrm;
4272df1fe9cSrandyf 	unsigned char	mon_alrm = pc_rtc_offset.mon_alrm;
4285b3a1fb0SDan Mick 	unsigned char	tmp;
4292df1fe9cSrandyf 
4302df1fe9cSrandyf 	if (pc_rtc_offset.century != 0) {
4312df1fe9cSrandyf 		century = pc_rtc_offset.century;
4322df1fe9cSrandyf 	}
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 	outb(RTC_ADDR, RTC_B);
4357c478bd9Sstevel@tonic-gate 	reg = inb(RTC_DATA);
4367c478bd9Sstevel@tonic-gate 	outb(RTC_ADDR, RTC_B);
4377c478bd9Sstevel@tonic-gate 	outb(RTC_DATA, reg | RTC_SET);	/* allow time set now */
4387c478bd9Sstevel@tonic-gate 	for (i = 0; i < RTC_NREGP; i++) { /* set the time */
4397c478bd9Sstevel@tonic-gate 		outb(RTC_ADDR, i);
4407c478bd9Sstevel@tonic-gate 		outb(RTC_DATA, buf[i]);
4417c478bd9Sstevel@tonic-gate 	}
4422df1fe9cSrandyf 	outb(RTC_ADDR, century); /* do century */
4437c478bd9Sstevel@tonic-gate 	outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_century);
4442df1fe9cSrandyf 
4452df1fe9cSrandyf 	if (day_alrm > 0) {
4462df1fe9cSrandyf 		outb(RTC_ADDR, day_alrm);
4475b3a1fb0SDan Mick 		/* preserve RTC_VRT bit; some virt envs accept writes there */
4485b3a1fb0SDan Mick 		tmp = inb(RTC_DATA) & RTC_VRT;
4495b3a1fb0SDan Mick 		tmp |= ((struct rtc_t *)buf)->rtc_adom & ~RTC_VRT;
4505b3a1fb0SDan Mick 		outb(RTC_DATA, tmp);
4512df1fe9cSrandyf 	}
4522df1fe9cSrandyf 	if (mon_alrm > 0) {
4532df1fe9cSrandyf 		outb(RTC_ADDR, mon_alrm);
4542df1fe9cSrandyf 		outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_amon);
4552df1fe9cSrandyf 	}
4562df1fe9cSrandyf 
4572df1fe9cSrandyf 	outb(RTC_ADDR, RTC_B);
4582df1fe9cSrandyf 	reg = inb(RTC_DATA);
4597c478bd9Sstevel@tonic-gate 	outb(RTC_ADDR, RTC_B);
4607c478bd9Sstevel@tonic-gate 	outb(RTC_DATA, reg & ~RTC_SET);	/* allow time update */
4617c478bd9Sstevel@tonic-gate }
4627c478bd9Sstevel@tonic-gate 
463ae115bc7Smrj static tod_ops_t todpc_ops = {
464ae115bc7Smrj 	TOD_OPS_VERSION,
465ae115bc7Smrj 	todpc_get,
466ae115bc7Smrj 	todpc_set,
4672df1fe9cSrandyf 	NULL,
4682df1fe9cSrandyf 	NULL,
4692df1fe9cSrandyf 	todpc_setalarm,
4702df1fe9cSrandyf 	todpc_clralarm,
471ae115bc7Smrj 	NULL
472ae115bc7Smrj };
473ae115bc7Smrj 
4747c478bd9Sstevel@tonic-gate /*
475ae115bc7Smrj  * Initialize for the default TOD ops vector for use on hardware.
4767c478bd9Sstevel@tonic-gate  */
4777c478bd9Sstevel@tonic-gate 
478ae115bc7Smrj tod_ops_t *tod_ops = &todpc_ops;
479