xref: /illumos-gate/usr/src/uts/sun4u/io/todsg.c (revision 8fc99e42)
103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
588294e09SRichard Bean  * Common Development and Distribution License (the "License").
688294e09SRichard Bean  * You may not use this file except in compliance with the License.
703831d35Sstevel  *
803831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel  * See the License for the specific language governing permissions
1103831d35Sstevel  * and limitations under the License.
1203831d35Sstevel  *
1303831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel  *
1903831d35Sstevel  * CDDL HEADER END
2003831d35Sstevel  */
2103831d35Sstevel /*
22*8fc99e42STrevor Thompson  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2303831d35Sstevel  * Use is subject to license terms.
2403831d35Sstevel  */
2503831d35Sstevel 
2603831d35Sstevel /*
2703831d35Sstevel  * tod driver module for Serengeti
2803831d35Sstevel  * This module implements a soft tod since
2903831d35Sstevel  * Serengeti has no tod part.
3003831d35Sstevel  */
3103831d35Sstevel 
3203831d35Sstevel #include <sys/modctl.h>
3303831d35Sstevel #include <sys/systm.h>
3403831d35Sstevel #include <sys/cpuvar.h>
3503831d35Sstevel #include <sys/promif.h>
3603831d35Sstevel #include <sys/sgsbbc_iosram.h>
3703831d35Sstevel #include <sys/todsg.h>
3803831d35Sstevel #include <sys/cmn_err.h>
3903831d35Sstevel #include <sys/time.h>
4003831d35Sstevel #include <sys/sysmacros.h>
4103831d35Sstevel #include <sys/clock.h>
4203831d35Sstevel 
4303831d35Sstevel #if defined(DEBUG) || defined(lint)
4403831d35Sstevel static int todsg_debug = 0;
4503831d35Sstevel #define	DCMNERR if (todsg_debug) cmn_err
4603831d35Sstevel #else
4703831d35Sstevel #define	DCMNERR
4803831d35Sstevel #endif /* DEBUG */
4903831d35Sstevel 
5003831d35Sstevel #define	OFFSET(base, field)	((char *)&base.field - (char *)&base)
5103831d35Sstevel #define	SC_DOWN_COUNT_THRESHOLD	2
5203831d35Sstevel #define	SC_TOD_MIN_REV		2
5303831d35Sstevel 
5403831d35Sstevel static timestruc_t	todsg_get(void);
5503831d35Sstevel static void		todsg_set(timestruc_t);
5603831d35Sstevel static uint32_t		todsg_set_watchdog_timer(uint_t);
5703831d35Sstevel static uint32_t		todsg_clear_watchdog_timer(void);
5803831d35Sstevel static void		todsg_set_power_alarm(timestruc_t);
5903831d35Sstevel static void		todsg_clear_power_alarm(void);
6003831d35Sstevel static uint64_t		todsg_get_cpufrequency(void);
6103831d35Sstevel static int 		update_heartbeat(void);
6203831d35Sstevel static int		verify_sc_tod_version(void);
6303831d35Sstevel static int 		update_tod_skew(time_t skew);
6403831d35Sstevel 
6503831d35Sstevel static uint32_t i_am_alive = 0;
6603831d35Sstevel static uint32_t sc_tod_version = 0;
6703831d35Sstevel static time_t 	skew_adjust = 0;
6803831d35Sstevel static int 	is_sc_down = 0;
6903831d35Sstevel static int	adjust_sc_down = 0;
7003831d35Sstevel 
7103831d35Sstevel /*
7203831d35Sstevel  * Module linkage information for the kernel.
7303831d35Sstevel  */
7403831d35Sstevel static struct modlmisc modlmisc = {
7588294e09SRichard Bean 	&mod_miscops, "Serengeti tod module"
7603831d35Sstevel };
7703831d35Sstevel 
7803831d35Sstevel static struct modlinkage modlinkage = {
7903831d35Sstevel 	MODREV_1, (void *)&modlmisc, NULL
8003831d35Sstevel };
8103831d35Sstevel 
8203831d35Sstevel int
_init(void)8303831d35Sstevel _init(void)
8403831d35Sstevel {
8503831d35Sstevel 
8603831d35Sstevel 	DCMNERR(CE_NOTE, "todsg:_init(): begins");
8703831d35Sstevel 
8803831d35Sstevel 	if (strcmp(tod_module_name, "todsg") == 0) {
8903831d35Sstevel 		time_t ssc_time = (time_t)0;
9003831d35Sstevel 		char obp_string[80];
9103831d35Sstevel 
9203831d35Sstevel 		/*
9303831d35Sstevel 		 * To obtain the initial start of day time, we use an
9403831d35Sstevel 		 * OBP callback; this is because the iosram is not yet
9503831d35Sstevel 		 * accessible from the OS at this early stage of startup.
9603831d35Sstevel 		 */
9703831d35Sstevel 
9803831d35Sstevel 		/*
9903831d35Sstevel 		 * Set the string to pass to OBP
10003831d35Sstevel 		 */
10103831d35Sstevel 		(void) sprintf(obp_string,
102*8fc99e42STrevor Thompson 		    "h# %p \" unix-get-tod\" $find if execute else 3drop then",
103*8fc99e42STrevor Thompson 		    (void *)&ssc_time);
10403831d35Sstevel 
10503831d35Sstevel 		prom_interpret(obp_string, 0, 0, 0, 0, 0);
10603831d35Sstevel 
10703831d35Sstevel 		if (ssc_time == (time_t)0) {
10803831d35Sstevel 			cmn_err(CE_WARN, "Initial date is invalid. "
109*8fc99e42STrevor Thompson 			    "This can be caused by older firmware.");
11003831d35Sstevel 			cmn_err(CE_CONT, "Please flashupdate the System "
111*8fc99e42STrevor Thompson 			    "Controller firmware to the latest version.\n");
11203831d35Sstevel 			cmn_err(CE_CONT, "Attempting to set the date and time "
113*8fc99e42STrevor Thompson 			    "based on the last shutdown.\n");
11403831d35Sstevel 			cmn_err(CE_CONT, "Please inspect the date and time and "
115*8fc99e42STrevor Thompson 			    "correct if necessary.\n");
11603831d35Sstevel 		}
11703831d35Sstevel 
11803831d35Sstevel 		hrestime.tv_sec = ssc_time;
11903831d35Sstevel 
12003831d35Sstevel 		DCMNERR(CE_NOTE, "todsg: _init(): time from OBP 0x%lX",
121*8fc99e42STrevor Thompson 		    ssc_time);
12203831d35Sstevel 		/*
12303831d35Sstevel 		 * Verify whether the received date/clock has overflowed
12403831d35Sstevel 		 * an integer(32bit), so that we capture any corrupted
12503831d35Sstevel 		 * date from SC, thereby preventing boot failure.
12603831d35Sstevel 		 */
12703831d35Sstevel 		if (TIMESPEC_OVERFLOW(&hrestime)) {
12803831d35Sstevel 			cmn_err(CE_WARN, "Date overflow detected.");
12903831d35Sstevel 			cmn_err(CE_CONT, "Attempting to set the date and time "
130*8fc99e42STrevor Thompson 			    "based on the last shutdown.\n");
13103831d35Sstevel 			cmn_err(CE_CONT, "Please inspect the date and time and "
132*8fc99e42STrevor Thompson 			    "correct if necessary.\n");
13303831d35Sstevel 
13403831d35Sstevel 			/*
13503831d35Sstevel 			 * By setting hrestime.tv_sec to zero
13603831d35Sstevel 			 * we force the vfs_mountroot() to set
13703831d35Sstevel 			 * the date from the last shutdown.
13803831d35Sstevel 			 */
13903831d35Sstevel 			hrestime.tv_sec = (time_t)0;
14003831d35Sstevel 			/*
14103831d35Sstevel 			 * Save the skew so that we can update
14203831d35Sstevel 			 * IOSRAM when it becomes accessible.
14303831d35Sstevel 			 */
14403831d35Sstevel 			skew_adjust = -ssc_time;
14503831d35Sstevel 		}
14603831d35Sstevel 
14703831d35Sstevel 		DCMNERR(CE_NOTE, "todsg:_init(): set tod_ops");
14803831d35Sstevel 
14903831d35Sstevel 		tod_ops.tod_get = todsg_get;
15003831d35Sstevel 		tod_ops.tod_set = todsg_set;
15103831d35Sstevel 		tod_ops.tod_set_watchdog_timer = todsg_set_watchdog_timer;
15203831d35Sstevel 		tod_ops.tod_clear_watchdog_timer = todsg_clear_watchdog_timer;
15303831d35Sstevel 		tod_ops.tod_set_power_alarm = todsg_set_power_alarm;
15403831d35Sstevel 		tod_ops.tod_clear_power_alarm = todsg_clear_power_alarm;
15503831d35Sstevel 		tod_ops.tod_get_cpufrequency = todsg_get_cpufrequency;
15603831d35Sstevel 	}
15703831d35Sstevel 
15803831d35Sstevel 	return (mod_install(&modlinkage));
15903831d35Sstevel 
16003831d35Sstevel }
16103831d35Sstevel 
16203831d35Sstevel int
_fini(void)16303831d35Sstevel _fini(void)
16403831d35Sstevel {
16503831d35Sstevel 	if (strcmp(tod_module_name, "todsg") == 0)
16603831d35Sstevel 		return (EBUSY);
16703831d35Sstevel 	else
16803831d35Sstevel 		return (mod_remove(&modlinkage));
16903831d35Sstevel }
17003831d35Sstevel 
17103831d35Sstevel int
_info(struct modinfo * modinfop)17203831d35Sstevel _info(struct modinfo *modinfop)
17303831d35Sstevel {
17403831d35Sstevel 	return (mod_info(&modlinkage, modinfop));
17503831d35Sstevel }
17603831d35Sstevel 
17703831d35Sstevel static int
update_heartbeat(void)17803831d35Sstevel update_heartbeat(void)
17903831d35Sstevel {
18003831d35Sstevel 	tod_iosram_t tod_buf;
18103831d35Sstevel 	int complained = 0;
18203831d35Sstevel 
18303831d35Sstevel 	/* Update the heartbeat */
18403831d35Sstevel 	if (i_am_alive == UINT32_MAX)
18503831d35Sstevel 		i_am_alive = 0;
18603831d35Sstevel 	else
18703831d35Sstevel 		i_am_alive++;
18803831d35Sstevel 	if (iosram_write(SBBC_TOD_KEY, OFFSET(tod_buf, tod_i_am_alive),
189*8fc99e42STrevor Thompson 	    (char *)&i_am_alive, sizeof (uint32_t))) {
19003831d35Sstevel 		complained++;
19103831d35Sstevel 		cmn_err(CE_WARN, "update_heartbeat(): write heartbeat failed");
19203831d35Sstevel 	}
19303831d35Sstevel 	return (complained);
19403831d35Sstevel }
19503831d35Sstevel 
19603831d35Sstevel static int
verify_sc_tod_version(void)19703831d35Sstevel verify_sc_tod_version(void)
19803831d35Sstevel {
19903831d35Sstevel 	uint32_t magic;
20003831d35Sstevel 	tod_iosram_t tod_buf;
20103831d35Sstevel 
20203831d35Sstevel 	if (!todsg_use_sc)
20303831d35Sstevel 		return (FALSE);
20403831d35Sstevel 	/*
20503831d35Sstevel 	 * read tod_version only when the first time and
20603831d35Sstevel 	 * when there has been a previous sc down time
20703831d35Sstevel 	 */
20803831d35Sstevel 	if (!sc_tod_version || is_sc_down >= SC_DOWN_COUNT_THRESHOLD) {
20903831d35Sstevel 		if (iosram_read(SBBC_TOD_KEY, OFFSET(tod_buf, tod_magic),
210*8fc99e42STrevor Thompson 		    (char *)&magic, sizeof (uint32_t)) ||
211*8fc99e42STrevor Thompson 		    magic != TODSG_MAGIC) {
21203831d35Sstevel 			cmn_err(CE_WARN, "get_sc_tod_version(): "
213*8fc99e42STrevor Thompson 			    "TOD SRAM magic error");
21403831d35Sstevel 			return (FALSE);
21503831d35Sstevel 		}
21603831d35Sstevel 		if (iosram_read(SBBC_TOD_KEY, OFFSET(tod_buf, tod_version),
217*8fc99e42STrevor Thompson 		    (char *)&sc_tod_version, sizeof (uint32_t))) {
21803831d35Sstevel 			cmn_err(CE_WARN, "get_sc_tod_version(): "
219*8fc99e42STrevor Thompson 			    "read tod version failed");
22003831d35Sstevel 			sc_tod_version = 0;
22103831d35Sstevel 			return (FALSE);
22203831d35Sstevel 		}
22303831d35Sstevel 	}
22403831d35Sstevel 	if (sc_tod_version >= SC_TOD_MIN_REV) {
22503831d35Sstevel 		return (TRUE);
22603831d35Sstevel 	} else {
22703831d35Sstevel 		todsg_use_sc = 0;
228*8fc99e42STrevor Thompson 		cmn_err(CE_WARN, "todsg_get(): incorrect firmware version, "
229*8fc99e42STrevor Thompson 		    "(%d): expected version >= %d.", sc_tod_version,
230*8fc99e42STrevor Thompson 		    SC_TOD_MIN_REV);
23103831d35Sstevel 	}
23203831d35Sstevel 	return (FALSE);
23303831d35Sstevel }
23403831d35Sstevel 
23503831d35Sstevel static int
update_tod_skew(time_t skew)23603831d35Sstevel update_tod_skew(time_t skew)
23703831d35Sstevel {
23803831d35Sstevel 	time_t domain_skew;
23903831d35Sstevel 	tod_iosram_t tod_buf;
24003831d35Sstevel 	int complained = 0;
24103831d35Sstevel 
24203831d35Sstevel 	DCMNERR(CE_NOTE, "update_tod_skew(): skew  0x%lX", skew);
24303831d35Sstevel 
24403831d35Sstevel 	if (iosram_read(SBBC_TOD_KEY, OFFSET(tod_buf, tod_domain_skew),
245*8fc99e42STrevor Thompson 	    (char *)&domain_skew, sizeof (time_t))) {
24603831d35Sstevel 		complained++;
247*8fc99e42STrevor Thompson 		cmn_err(CE_WARN,
248*8fc99e42STrevor Thompson 		    "update_tod_skew(): read tod domain skew failed");
24903831d35Sstevel 	}
25003831d35Sstevel 	domain_skew += skew;
25103831d35Sstevel 	/* we shall update the skew_adjust too now */
25203831d35Sstevel 	domain_skew += skew_adjust;
25303831d35Sstevel 	if (!complained && iosram_write(SBBC_TOD_KEY,
254*8fc99e42STrevor Thompson 	    OFFSET(tod_buf, tod_domain_skew), (char *)&domain_skew,
255*8fc99e42STrevor Thompson 	    sizeof (time_t))) {
25603831d35Sstevel 		complained++;
257*8fc99e42STrevor Thompson 		cmn_err(CE_WARN,
258*8fc99e42STrevor Thompson 		    "update_tod_skew(): write domain skew failed");
25903831d35Sstevel 	}
26003831d35Sstevel 	if (!complained)
26103831d35Sstevel 		skew_adjust = 0;
26203831d35Sstevel 	return (complained);
26303831d35Sstevel }
26403831d35Sstevel 
26503831d35Sstevel /*
26603831d35Sstevel  * Return time value read from IOSRAM.
26703831d35Sstevel  * Must be called with tod_lock held.
26803831d35Sstevel  */
26903831d35Sstevel static timestruc_t
todsg_get(void)27003831d35Sstevel todsg_get(void)
27103831d35Sstevel {
27203831d35Sstevel 	tod_iosram_t tod_buf;
27303831d35Sstevel 	time_t seconds;
27403831d35Sstevel 	time_t domain_skew;
27503831d35Sstevel 	int complained = 0;
27603831d35Sstevel 	static time_t pre_seconds = (time_t)0;
27703831d35Sstevel 
27803831d35Sstevel 	ASSERT(MUTEX_HELD(&tod_lock));
27903831d35Sstevel 
28003831d35Sstevel 	if (!verify_sc_tod_version()) {
28103831d35Sstevel 		/* if we can't use SC */
28203831d35Sstevel 		goto return_hrestime;
28303831d35Sstevel 	}
28403831d35Sstevel 	if (watchdog_activated != 0 || watchdog_enable != 0)
28503831d35Sstevel 		complained = update_heartbeat();
28603831d35Sstevel 	if (!complained && (iosram_read(SBBC_TOD_KEY,
287*8fc99e42STrevor Thompson 	    OFFSET(tod_buf, tod_get_value), (char *)&seconds,
288*8fc99e42STrevor Thompson 	    sizeof (time_t)))) {
28903831d35Sstevel 		complained++;
29003831d35Sstevel 		cmn_err(CE_WARN, "todsg_get(): read 64-bit tod value failed");
29103831d35Sstevel 	}
29203831d35Sstevel 	if (!complained && skew_adjust)  {
29303831d35Sstevel 		/*
29403831d35Sstevel 		 * This is our first chance to update IOSRAM
29503831d35Sstevel 		 * with local copy of the skew,  so update it.
29603831d35Sstevel 		 */
29703831d35Sstevel 		complained = update_tod_skew(0);
29803831d35Sstevel 	}
29903831d35Sstevel 	if (!complained && iosram_read(SBBC_TOD_KEY,
300*8fc99e42STrevor Thompson 	    OFFSET(tod_buf, tod_domain_skew), (char *)&domain_skew,
301*8fc99e42STrevor Thompson 	    sizeof (time_t))) {
30203831d35Sstevel 		complained++;
30303831d35Sstevel 		cmn_err(CE_WARN, "todsg_get(): read tod domain skew failed");
30403831d35Sstevel 	}
30503831d35Sstevel 
30603831d35Sstevel 	if (complained) {
30703831d35Sstevel 		cmn_err(CE_WARN, "todsg_get(): turned off using tod");
30803831d35Sstevel 		todsg_use_sc = 0;
30903831d35Sstevel 		goto return_hrestime;
31003831d35Sstevel 	}
31103831d35Sstevel 
31203831d35Sstevel 	/*
31303831d35Sstevel 	 * If the SC gets rebooted, and we are using NTP, then we need
31403831d35Sstevel 	 * to sync the IOSRAM to hrestime when the SC comes back.  We
31503831d35Sstevel 	 * can determine that either NTP slew (or date -a) was called if
31603831d35Sstevel 	 * the global timedelta was non-zero at any point while the SC
31703831d35Sstevel 	 * was away.  If timedelta remains zero throughout, then the
31803831d35Sstevel 	 * default action will be to sync hrestime to IOSRAM
31903831d35Sstevel 	 */
32003831d35Sstevel 	if (seconds != pre_seconds) {	/* SC still alive */
32103831d35Sstevel 		pre_seconds = seconds;
32203831d35Sstevel 		if (is_sc_down >= SC_DOWN_COUNT_THRESHOLD && adjust_sc_down) {
32303831d35Sstevel 			skew_adjust = hrestime.tv_sec - (seconds + domain_skew);
32403831d35Sstevel 			complained = update_tod_skew(0);
32503831d35Sstevel 			if (!complained && (iosram_read(SBBC_TOD_KEY,
326*8fc99e42STrevor Thompson 			    OFFSET(tod_buf, tod_domain_skew),
327*8fc99e42STrevor Thompson 			    (char *)&domain_skew, sizeof (time_t)))) {
32803831d35Sstevel 				complained++;
32903831d35Sstevel 				cmn_err(CE_WARN, "todsg_get(): "
330*8fc99e42STrevor Thompson 				    "read tod domain skew failed");
33103831d35Sstevel 			}
33203831d35Sstevel 		}
33303831d35Sstevel 		is_sc_down = 0;
33403831d35Sstevel 		adjust_sc_down = 0;
33503831d35Sstevel 
33603831d35Sstevel 		/*
33703831d35Sstevel 		 * If complained then domain_skew is invalid.
33803831d35Sstevel 		 * Hand back hrestime instead.
33903831d35Sstevel 		 */
34003831d35Sstevel 		if (!complained) {
341*8fc99e42STrevor Thompson 			/*
342*8fc99e42STrevor Thompson 			 * The read was successful so ensure the failure
343*8fc99e42STrevor Thompson 			 * flag is clear.
344*8fc99e42STrevor Thompson 			 */
345*8fc99e42STrevor Thompson 			tod_status_clear(TOD_GET_FAILED);
34603831d35Sstevel 			timestruc_t ts = {0, 0};
34703831d35Sstevel 			ts.tv_sec = seconds + domain_skew;
34803831d35Sstevel 			return (ts);
34903831d35Sstevel 		} else {
35003831d35Sstevel 			goto return_hrestime;
35103831d35Sstevel 		}
35203831d35Sstevel 	}
35303831d35Sstevel 
35403831d35Sstevel 	/* SC/TOD is down */
35503831d35Sstevel 	is_sc_down++;
35603831d35Sstevel 	if (timedelta != 0) {
35703831d35Sstevel 		adjust_sc_down = 1;
35803831d35Sstevel 	}
35903831d35Sstevel 
36003831d35Sstevel return_hrestime:
36103831d35Sstevel 	/*
362*8fc99e42STrevor Thompson 	 * We need to inform the tod_validate() code to stop checking until
363*8fc99e42STrevor Thompson 	 * the SC comes back up again.  Note we will return hrestime below
364*8fc99e42STrevor Thompson 	 * which may be different to the previous TOD value we returned.
36503831d35Sstevel 	 */
366*8fc99e42STrevor Thompson 	tod_status_set(TOD_GET_FAILED);
36703831d35Sstevel 	return (hrestime);
36803831d35Sstevel }
36903831d35Sstevel 
37003831d35Sstevel static void
todsg_set(timestruc_t ts)37103831d35Sstevel todsg_set(timestruc_t ts)
37203831d35Sstevel {
37303831d35Sstevel 	int complained = 0;
37403831d35Sstevel 	tod_iosram_t tod_buf;
37503831d35Sstevel 	time_t domain_skew;
37603831d35Sstevel 	time_t seconds;
37703831d35Sstevel 	time_t hwtod;
37803831d35Sstevel 
37903831d35Sstevel 	ASSERT(MUTEX_HELD(&tod_lock));
38003831d35Sstevel 
38103831d35Sstevel 	if (!verify_sc_tod_version()) {
38203831d35Sstevel 		/* if we can't use SC */
38303831d35Sstevel 		return;
38403831d35Sstevel 	}
38503831d35Sstevel 	/*
38603831d35Sstevel 	 * If the SC is down just note the fact that we should
38703831d35Sstevel 	 * have adjusted the hardware skew which caters for calls
38803831d35Sstevel 	 * to stime(). (eg NTP step, as opposed to NTP skew)
38903831d35Sstevel 	 */
39003831d35Sstevel 	if (is_sc_down) {
39103831d35Sstevel 		adjust_sc_down = 1;
39203831d35Sstevel 		return;
39303831d35Sstevel 	}
39403831d35Sstevel 	/*
39503831d35Sstevel 	 * reason to update i_am_alive here:
39603831d35Sstevel 	 * To work around a generic Solaris bug that can
39703831d35Sstevel 	 * cause tod_get() to be starved by too frequent
39803831d35Sstevel 	 * calls to the stime() system call.
39903831d35Sstevel 	 */
40003831d35Sstevel 	if (watchdog_activated != 0 || watchdog_enable != 0)
40103831d35Sstevel 		complained = update_heartbeat();
40203831d35Sstevel 
40303831d35Sstevel 	/*
40403831d35Sstevel 	 * We are passed hrestime from clock.c so we need to read the
40503831d35Sstevel 	 * IOSRAM for the hardware's idea of the time to see if we need
40603831d35Sstevel 	 * to update the skew.
40703831d35Sstevel 	 */
40803831d35Sstevel 	if (!complained && (iosram_read(SBBC_TOD_KEY,
409*8fc99e42STrevor Thompson 	    OFFSET(tod_buf, tod_get_value), (char *)&seconds,
410*8fc99e42STrevor Thompson 	    sizeof (time_t)))) {
41103831d35Sstevel 		complained++;
41203831d35Sstevel 		cmn_err(CE_WARN, "todsg_set(): read 64-bit tod value failed");
41303831d35Sstevel 	}
41403831d35Sstevel 
41503831d35Sstevel 	if (!complained && iosram_read(SBBC_TOD_KEY,
416*8fc99e42STrevor Thompson 	    OFFSET(tod_buf, tod_domain_skew), (char *)&domain_skew,
417*8fc99e42STrevor Thompson 	    sizeof (time_t))) {
41803831d35Sstevel 		complained++;
41903831d35Sstevel 		cmn_err(CE_WARN, "todsg_set(): read tod domain skew failed");
42003831d35Sstevel 	}
42103831d35Sstevel 
42203831d35Sstevel 	/*
42303831d35Sstevel 	 * Only update the skew if the time passed differs from
42403831d35Sstevel 	 * what the hardware thinks & no errors talking to SC
42503831d35Sstevel 	 */
42603831d35Sstevel 	if (!complained && (ts.tv_sec != (seconds + domain_skew))) {
42703831d35Sstevel 		hwtod = seconds + domain_skew;
42803831d35Sstevel 		complained = update_tod_skew(ts.tv_sec - hwtod);
42903831d35Sstevel 
43003831d35Sstevel 		DCMNERR(CE_NOTE, "todsg_set(): set time %lX (%lX)%s",
431*8fc99e42STrevor Thompson 		    ts.tv_sec, hwtod, complained ? " failed" : "");
43203831d35Sstevel 	}
43303831d35Sstevel 
43403831d35Sstevel 	if (complained) {
43503831d35Sstevel 		cmn_err(CE_WARN, "todsg_set(): turned off using tod");
43603831d35Sstevel 		todsg_use_sc = 0;
43703831d35Sstevel 	}
43803831d35Sstevel }
43903831d35Sstevel 
44003831d35Sstevel static uint32_t
todsg_set_watchdog_timer(uint32_t timeoutval)44103831d35Sstevel todsg_set_watchdog_timer(uint32_t timeoutval)
44203831d35Sstevel {
44303831d35Sstevel 	tod_iosram_t tod_buf;
44403831d35Sstevel 
44503831d35Sstevel 	ASSERT(MUTEX_HELD(&tod_lock));
44603831d35Sstevel 
44703831d35Sstevel 	if (!verify_sc_tod_version()) {
44803831d35Sstevel 		DCMNERR(CE_NOTE, "todsg_set_watchdog_timer(): "
449*8fc99e42STrevor Thompson 		    "verify_sc_tod_version failed");
45003831d35Sstevel 		return (0);
45103831d35Sstevel 	}
45203831d35Sstevel 	DCMNERR(CE_NOTE, "todsg_set_watchdog_timer(): "
453*8fc99e42STrevor Thompson 	    "set watchdog timer value = %d", timeoutval);
45403831d35Sstevel 
45503831d35Sstevel 	if (iosram_write(SBBC_TOD_KEY, OFFSET(tod_buf, tod_timeout_period),
456*8fc99e42STrevor Thompson 	    (char *)&timeoutval, sizeof (uint32_t))) {
45703831d35Sstevel 		DCMNERR(CE_NOTE, "todsg_set_watchdog_timer(): "
458*8fc99e42STrevor Thompson 		    "write new timeout value failed");
45903831d35Sstevel 		return (0);
46003831d35Sstevel 	}
46103831d35Sstevel 	watchdog_activated = 1;
46203831d35Sstevel 	return (timeoutval);
46303831d35Sstevel }
46403831d35Sstevel 
46503831d35Sstevel static uint32_t
todsg_clear_watchdog_timer(void)46603831d35Sstevel todsg_clear_watchdog_timer(void)
46703831d35Sstevel {
46803831d35Sstevel 	tod_iosram_t tod_buf;
46903831d35Sstevel 	uint32_t r_timeout_period;
47003831d35Sstevel 	uint32_t w_timeout_period;
47103831d35Sstevel 
47203831d35Sstevel 	ASSERT(MUTEX_HELD(&tod_lock));
47303831d35Sstevel 
47403831d35Sstevel 	if ((watchdog_activated == 0) || !verify_sc_tod_version()) {
47503831d35Sstevel 		DCMNERR(CE_NOTE, "todsg_set_watchdog_timer(): "
476*8fc99e42STrevor Thompson 		    "either watchdog not activated or "
477*8fc99e42STrevor Thompson 		    "verify_sc_tod_version failed");
47803831d35Sstevel 		return (0);
47903831d35Sstevel 	}
48003831d35Sstevel 	if (iosram_read(SBBC_TOD_KEY, OFFSET(tod_buf, tod_timeout_period),
481*8fc99e42STrevor Thompson 	    (char *)&r_timeout_period, sizeof (uint32_t))) {
48203831d35Sstevel 		DCMNERR(CE_NOTE, "todsg_clear_watchdog_timer(): "
483*8fc99e42STrevor Thompson 		    "read timeout value failed");
48403831d35Sstevel 		return (0);
48503831d35Sstevel 	}
48603831d35Sstevel 	DCMNERR(CE_NOTE, "todsg_clear_watchdog_timer(): "
487*8fc99e42STrevor Thompson 	    "clear watchdog timer (old value=%d)", r_timeout_period);
48803831d35Sstevel 	w_timeout_period = 0;
48903831d35Sstevel 	if (iosram_write(SBBC_TOD_KEY, OFFSET(tod_buf, tod_timeout_period),
490*8fc99e42STrevor Thompson 	    (char *)&w_timeout_period, sizeof (uint32_t))) {
49103831d35Sstevel 		DCMNERR(CE_NOTE, "todsg_clear_watchdog_timer(): "
492*8fc99e42STrevor Thompson 		    "write zero timeout value failed");
49303831d35Sstevel 		return (0);
49403831d35Sstevel 	}
49503831d35Sstevel 	watchdog_activated = 0;
49603831d35Sstevel 	return (r_timeout_period);
49703831d35Sstevel }
49803831d35Sstevel 
49903831d35Sstevel /*
50003831d35Sstevel  * Null function.
50103831d35Sstevel  */
50203831d35Sstevel /* ARGSUSED */
50303831d35Sstevel static void
todsg_set_power_alarm(timestruc_t ts)50403831d35Sstevel todsg_set_power_alarm(timestruc_t ts)
50503831d35Sstevel {
50603831d35Sstevel 	ASSERT(MUTEX_HELD(&tod_lock));
50703831d35Sstevel }
50803831d35Sstevel 
50903831d35Sstevel /*
51003831d35Sstevel  * Null function
51103831d35Sstevel  */
51203831d35Sstevel static void
todsg_clear_power_alarm()51303831d35Sstevel todsg_clear_power_alarm()
51403831d35Sstevel {
51503831d35Sstevel 	ASSERT(MUTEX_HELD(&tod_lock));
51603831d35Sstevel }
51703831d35Sstevel 
51803831d35Sstevel /*
51903831d35Sstevel  * Get clock freq from the cpunode
52003831d35Sstevel  */
52103831d35Sstevel uint64_t
todsg_get_cpufrequency(void)52203831d35Sstevel todsg_get_cpufrequency(void)
52303831d35Sstevel {
52403831d35Sstevel 
52503831d35Sstevel 	DCMNERR(CE_NOTE, "todsg_get_cpufrequency(): frequency=%ldMHz",
526*8fc99e42STrevor Thompson 	    cpunodes[CPU->cpu_id].clock_freq/1000000);
52703831d35Sstevel 
52803831d35Sstevel 	return (cpunodes[CPU->cpu_id].clock_freq);
52903831d35Sstevel }
530