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