xref: /onnv-gate/usr/src/uts/sun4u/io/todopl.c (revision 1772:78cca3d2cc4b)
1*1772Sjl139090 /*
2*1772Sjl139090  * CDDL HEADER START
3*1772Sjl139090  *
4*1772Sjl139090  * The contents of this file are subject to the terms of the
5*1772Sjl139090  * Common Development and Distribution License (the "License").
6*1772Sjl139090  * You may not use this file except in compliance with the License.
7*1772Sjl139090  *
8*1772Sjl139090  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*1772Sjl139090  * or http://www.opensolaris.org/os/licensing.
10*1772Sjl139090  * See the License for the specific language governing permissions
11*1772Sjl139090  * and limitations under the License.
12*1772Sjl139090  *
13*1772Sjl139090  * When distributing Covered Code, include this CDDL HEADER in each
14*1772Sjl139090  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*1772Sjl139090  * If applicable, add the following below this CDDL HEADER, with the
16*1772Sjl139090  * fields enclosed by brackets "[]" replaced with your own identifying
17*1772Sjl139090  * information: Portions Copyright [yyyy] [name of copyright owner]
18*1772Sjl139090  *
19*1772Sjl139090  * CDDL HEADER END
20*1772Sjl139090  */
21*1772Sjl139090 /*
22*1772Sjl139090  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*1772Sjl139090  * Use is subject to license terms.
24*1772Sjl139090  */
25*1772Sjl139090 
26*1772Sjl139090 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*1772Sjl139090 
28*1772Sjl139090 /*
29*1772Sjl139090  * tod driver module for OPL (implements a soft tod)
30*1772Sjl139090  */
31*1772Sjl139090 
32*1772Sjl139090 #include <sys/types.h>
33*1772Sjl139090 #include <sys/param.h>
34*1772Sjl139090 #include <sys/sysmacros.h>
35*1772Sjl139090 #include <sys/systm.h>
36*1772Sjl139090 #include <sys/errno.h>
37*1772Sjl139090 #include <sys/modctl.h>
38*1772Sjl139090 #include <sys/autoconf.h>
39*1772Sjl139090 #include <sys/debug.h>
40*1772Sjl139090 #include <sys/clock.h>
41*1772Sjl139090 #include <sys/cmn_err.h>
42*1772Sjl139090 #include <sys/prom_plat.h>
43*1772Sjl139090 #include <sys/cpuvar.h>
44*1772Sjl139090 #include <sys/opl_module.h>
45*1772Sjl139090 
46*1772Sjl139090 /*
47*1772Sjl139090  * Debug stuff
48*1772Sjl139090  */
49*1772Sjl139090 #ifdef DEBUG
50*1772Sjl139090 int todopl_debug = 0;
51*1772Sjl139090 #define	TODOPL_DEBUG(args)  if (todopl_debug) cmn_err args
52*1772Sjl139090 #else
53*1772Sjl139090 #define	TODOPL_DEBUG(args)
54*1772Sjl139090 #endif
55*1772Sjl139090 
56*1772Sjl139090 #define	abs(x)	((x) < 0 ? -(x) : (x))
57*1772Sjl139090 
58*1772Sjl139090 #define	TODOPL_SET_THRESHOLD	30
59*1772Sjl139090 
60*1772Sjl139090 static timestruc_t	todopl_get(void);
61*1772Sjl139090 static void		todopl_set(timestruc_t);
62*1772Sjl139090 static uint_t		todopl_set_watchdog_timer(uint_t);
63*1772Sjl139090 static uint_t		todopl_clear_watchdog_timer(void);
64*1772Sjl139090 static void		todopl_set_power_alarm(timestruc_t);
65*1772Sjl139090 static void		todopl_clear_power_alarm(void);
66*1772Sjl139090 static uint64_t		todopl_get_cpufrequency(void);
67*1772Sjl139090 
68*1772Sjl139090 /*
69*1772Sjl139090  * Module linkage information for the kernel.
70*1772Sjl139090  */
71*1772Sjl139090 static struct modlmisc modlmisc = {
72*1772Sjl139090 	&mod_miscops, "Soft tod module for OPL 1.11"
73*1772Sjl139090 };
74*1772Sjl139090 
75*1772Sjl139090 static struct modlinkage modlinkage = {
76*1772Sjl139090 	MODREV_1, (void *)&modlmisc, NULL
77*1772Sjl139090 };
78*1772Sjl139090 
79*1772Sjl139090 /*
80*1772Sjl139090  * The TOD OPL logic description.
81*1772Sjl139090  *
82*1772Sjl139090  * The todopl driver uses promif functions prom_opl_get_tod() and
83*1772Sjl139090  * prom_opl_set_diff(). These functions call FJSV,get-tod and
84*1772Sjl139090  * FJSV,set-domain-time OBP client services.
85*1772Sjl139090  *
86*1772Sjl139090  * At the system boot or reboot:
87*1772Sjl139090  *
88*1772Sjl139090  *    FJSV,tod-get
89*1772Sjl139090  * OS  --------->   OBP     SCF I/F
90*1772Sjl139090  *                         ----------->  XSCF
91*1772Sjl139090  *                         <-----------
92*1772Sjl139090  *     <--------            time, diff
93*1772Sjl139090  *    time+diff, stick
94*1772Sjl139090  *
95*1772Sjl139090  * Note that on first powerup domain boot, diff is zero.
96*1772Sjl139090  *
97*1772Sjl139090  * When system updates the time via date(1m):
98*1772Sjl139090  *
99*1772Sjl139090  *   FJSV,set-domain-time
100*1772Sjl139090  * OS   --------->   OBP                      SRAM
101*1772Sjl139090  *      diff_delta        diff += diff_delta ------------->  XSCF
102*1772Sjl139090  *
103*1772Sjl139090  * diff_delta = new time -  current domain time (hrestime)
104*1772Sjl139090  *
105*1772Sjl139090  *
106*1772Sjl139090  * In theory, FJSV,get-tod and FJSV,set-domain-time should never fails.
107*1772Sjl139090  * But, if call to FJSV,get-tod fails on boot, the domain will be unable
108*1772Sjl139090  * to calculate "diff" properly and synchronization between Domain and
109*1772Sjl139090  * SP will be broken. In this particular case, we notify users that
110*1772Sjl139090  * "there is no time synchronization" and the logic will attempt to
111*1772Sjl139090  * resync with the SP whenever the OS tries to do a TOD update.
112*1772Sjl139090  * (e.g. via date(1m) or NTP).
113*1772Sjl139090  */
114*1772Sjl139090 
115*1772Sjl139090 static	int enable_time_sync = 1;
116*1772Sjl139090 
117*1772Sjl139090 int
_init(void)118*1772Sjl139090 _init(void)
119*1772Sjl139090 {
120*1772Sjl139090 	int64_t	stick;
121*1772Sjl139090 	time_t	obp_time = 0;
122*1772Sjl139090 	int64_t obp_stick;
123*1772Sjl139090 
124*1772Sjl139090 	if (strcmp(tod_module_name, "todopl") == 0) {
125*1772Sjl139090 		/*
126*1772Sjl139090 		 * Get TOD time from OBP and adjust it.
127*1772Sjl139090 		 */
128*1772Sjl139090 		prom_opl_get_tod(&obp_time, &obp_stick);
129*1772Sjl139090 
130*1772Sjl139090 		TODOPL_DEBUG((CE_NOTE, "todopl: OBP time 0x%lx stick 0x%lx\n",
131*1772Sjl139090 			obp_time, obp_stick));
132*1772Sjl139090 
133*1772Sjl139090 		if (obp_time != 0) {
134*1772Sjl139090 			/*
135*1772Sjl139090 			 * adjust OBP time by stick counts
136*1772Sjl139090 			 */
137*1772Sjl139090 			stick_timestamp(&stick);
138*1772Sjl139090 			obp_time += ((stick - obp_stick) / system_clock_freq);
139*1772Sjl139090 
140*1772Sjl139090 			TODOPL_DEBUG((CE_NOTE,
141*1772Sjl139090 				"todopl: cpu stick 0x%lx sys_time 0x%lx\n",
142*1772Sjl139090 				stick, obp_time));
143*1772Sjl139090 		} else {
144*1772Sjl139090 			/*
145*1772Sjl139090 			 * A date of zero causes the root filesystem driver
146*1772Sjl139090 			 * to try to set the date from the last shutdown.
147*1772Sjl139090 			 */
148*1772Sjl139090 			enable_time_sync = 0;
149*1772Sjl139090 			cmn_err(CE_WARN, "Initial date is invalid.");
150*1772Sjl139090 			cmn_err(CE_CONT, "Attempting to set the date and time "
151*1772Sjl139090 				"based on the last shutdown.\n");
152*1772Sjl139090 			cmn_err(CE_CONT, "The time could not be synchronized "
153*1772Sjl139090 				"between Domain and Service Processor.\n");
154*1772Sjl139090 			cmn_err(CE_CONT, "Please inspect the date and time and "
155*1772Sjl139090 				"correct if necessary.\n");
156*1772Sjl139090 		}
157*1772Sjl139090 
158*1772Sjl139090 		hrestime.tv_sec = obp_time;
159*1772Sjl139090 
160*1772Sjl139090 		/*
161*1772Sjl139090 		 * Check that the date has not overflowed a 32-bit integer.
162*1772Sjl139090 		 */
163*1772Sjl139090 		if (TIMESPEC_OVERFLOW(&hrestime)) {
164*1772Sjl139090 			cmn_err(CE_WARN, "Date overflow detected.");
165*1772Sjl139090 			cmn_err(CE_CONT, "Attempting to set the date and time "
166*1772Sjl139090 				"based on the last shutdown.\n");
167*1772Sjl139090 			cmn_err(CE_CONT, "Please inspect the date and time and "
168*1772Sjl139090 				"correct if necessary.\n");
169*1772Sjl139090 
170*1772Sjl139090 			hrestime.tv_sec = (time_t)0;
171*1772Sjl139090 		}
172*1772Sjl139090 
173*1772Sjl139090 		tod_ops.tod_get = todopl_get;
174*1772Sjl139090 		tod_ops.tod_set = todopl_set;
175*1772Sjl139090 		tod_ops.tod_set_watchdog_timer = todopl_set_watchdog_timer;
176*1772Sjl139090 		tod_ops.tod_clear_watchdog_timer = todopl_clear_watchdog_timer;
177*1772Sjl139090 		tod_ops.tod_set_power_alarm = todopl_set_power_alarm;
178*1772Sjl139090 		tod_ops.tod_clear_power_alarm = todopl_clear_power_alarm;
179*1772Sjl139090 		tod_ops.tod_get_cpufrequency = todopl_get_cpufrequency;
180*1772Sjl139090 
181*1772Sjl139090 		/*
182*1772Sjl139090 		 * Flag warning if user tried to use hardware watchdog
183*1772Sjl139090 		 */
184*1772Sjl139090 		if (watchdog_enable) {
185*1772Sjl139090 			cmn_err(CE_WARN, "Hardware watchdog unavailable");
186*1772Sjl139090 		}
187*1772Sjl139090 	}
188*1772Sjl139090 
189*1772Sjl139090 	return (mod_install(&modlinkage));
190*1772Sjl139090 }
191*1772Sjl139090 
192*1772Sjl139090 int
_fini(void)193*1772Sjl139090 _fini(void)
194*1772Sjl139090 {
195*1772Sjl139090 	if (strcmp(tod_module_name, "todopl") == 0)
196*1772Sjl139090 		return (EBUSY);
197*1772Sjl139090 	else
198*1772Sjl139090 		return (mod_remove(&modlinkage));
199*1772Sjl139090 }
200*1772Sjl139090 
201*1772Sjl139090 int
_info(struct modinfo * modinfop)202*1772Sjl139090 _info(struct modinfo *modinfop)
203*1772Sjl139090 {
204*1772Sjl139090 	return (mod_info(&modlinkage, modinfop));
205*1772Sjl139090 }
206*1772Sjl139090 
207*1772Sjl139090 
208*1772Sjl139090 /*
209*1772Sjl139090  * OPL tod_get is simplified to return hrestime
210*1772Sjl139090  * Must be called with tod_lock held.
211*1772Sjl139090  */
212*1772Sjl139090 static timestruc_t
todopl_get(void)213*1772Sjl139090 todopl_get(void)
214*1772Sjl139090 {
215*1772Sjl139090 	ASSERT(MUTEX_HELD(&tod_lock));
216*1772Sjl139090 	return (hrestime);
217*1772Sjl139090 }
218*1772Sjl139090 
219*1772Sjl139090 /*
220*1772Sjl139090  * Must be called with tod_lock held.
221*1772Sjl139090  *
222*1772Sjl139090  * When running NTP, tod_set is called at least once per second in order
223*1772Sjl139090  * to update the hardware clock. To minimize pressure on SP, we want only
224*1772Sjl139090  * to record significant time changes on the SP (when date(1M) is run).
225*1772Sjl139090  * We have 30 seconds threshold requirement before recording the time change.
226*1772Sjl139090  */
227*1772Sjl139090 /* ARGSUSED */
228*1772Sjl139090 static void
todopl_set(timestruc_t ts)229*1772Sjl139090 todopl_set(timestruc_t ts)
230*1772Sjl139090 {
231*1772Sjl139090 	ASSERT(MUTEX_HELD(&tod_lock));
232*1772Sjl139090 
233*1772Sjl139090 	if (abs(ts.tv_sec - hrestime.tv_sec) > TODOPL_SET_THRESHOLD) {
234*1772Sjl139090 		/*
235*1772Sjl139090 		 * Send time difference to SP
236*1772Sjl139090 		 */
237*1772Sjl139090 		if (enable_time_sync)
238*1772Sjl139090 			prom_opl_set_diff(ts.tv_sec - hrestime.tv_sec);
239*1772Sjl139090 		else {
240*1772Sjl139090 			/*
241*1772Sjl139090 			 * We did not get a successful initial time
242*1772Sjl139090 			 * update/sync from the SP via OBP during boot.
243*1772Sjl139090 			 * Try again here.
244*1772Sjl139090 			 */
245*1772Sjl139090 			time_t  obp_time = 0;
246*1772Sjl139090 			int64_t obp_stick;
247*1772Sjl139090 			int64_t stick;
248*1772Sjl139090 
249*1772Sjl139090 			prom_opl_get_tod(&obp_time, &obp_stick);
250*1772Sjl139090 
251*1772Sjl139090 			if (obp_time != 0) {
252*1772Sjl139090 				/*
253*1772Sjl139090 				 * adjust OBP time by stick counts
254*1772Sjl139090 				 */
255*1772Sjl139090 				stick_timestamp(&stick);
256*1772Sjl139090 				obp_time += ((stick - obp_stick) /
257*1772Sjl139090 					system_clock_freq);
258*1772Sjl139090 
259*1772Sjl139090 				/*
260*1772Sjl139090 				 * Sync up by computing the diff using the
261*1772Sjl139090 				 * newly acquired SP/OBP reference time
262*1772Sjl139090 				 */
263*1772Sjl139090 				prom_opl_set_diff(ts.tv_sec - obp_time);
264*1772Sjl139090 
265*1772Sjl139090 				enable_time_sync = 1;
266*1772Sjl139090 			}
267*1772Sjl139090 		}
268*1772Sjl139090 		TODOPL_DEBUG((CE_NOTE, "todopl_set: new domain time 0x%lx\n",
269*1772Sjl139090 			ts.tv_sec));
270*1772Sjl139090 	}
271*1772Sjl139090 }
272*1772Sjl139090 
273*1772Sjl139090 /*
274*1772Sjl139090  * No watchdog function.
275*1772Sjl139090  */
276*1772Sjl139090 /* ARGSUSED */
277*1772Sjl139090 static uint_t
todopl_set_watchdog_timer(uint_t timeoutval)278*1772Sjl139090 todopl_set_watchdog_timer(uint_t timeoutval)
279*1772Sjl139090 {
280*1772Sjl139090 	ASSERT(MUTEX_HELD(&tod_lock));
281*1772Sjl139090 	return (0);
282*1772Sjl139090 }
283*1772Sjl139090 
284*1772Sjl139090 /*
285*1772Sjl139090  * No watchdog function
286*1772Sjl139090  */
287*1772Sjl139090 static uint_t
todopl_clear_watchdog_timer(void)288*1772Sjl139090 todopl_clear_watchdog_timer(void)
289*1772Sjl139090 {
290*1772Sjl139090 	ASSERT(MUTEX_HELD(&tod_lock));
291*1772Sjl139090 	return (0);
292*1772Sjl139090 }
293*1772Sjl139090 
294*1772Sjl139090 /*
295*1772Sjl139090  * Null function.
296*1772Sjl139090  */
297*1772Sjl139090 /* ARGSUSED */
298*1772Sjl139090 static void
todopl_set_power_alarm(timestruc_t ts)299*1772Sjl139090 todopl_set_power_alarm(timestruc_t ts)
300*1772Sjl139090 {
301*1772Sjl139090 	ASSERT(MUTEX_HELD(&tod_lock));
302*1772Sjl139090 }
303*1772Sjl139090 
304*1772Sjl139090 /*
305*1772Sjl139090  * Null function
306*1772Sjl139090  */
307*1772Sjl139090 static void
todopl_clear_power_alarm()308*1772Sjl139090 todopl_clear_power_alarm()
309*1772Sjl139090 {
310*1772Sjl139090 	ASSERT(MUTEX_HELD(&tod_lock));
311*1772Sjl139090 }
312*1772Sjl139090 
313*1772Sjl139090 /*
314*1772Sjl139090  * Get clock freq from the cpunode.  This function is only called
315*1772Sjl139090  * when use_stick = 0, otherwise, system_clock_freq gets used instead.
316*1772Sjl139090  */
317*1772Sjl139090 uint64_t
todopl_get_cpufrequency(void)318*1772Sjl139090 todopl_get_cpufrequency(void)
319*1772Sjl139090 {
320*1772Sjl139090 	return (cpunodes[CPU->cpu_id].clock_freq);
321*1772Sjl139090 }
322