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