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