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 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * tod driver module for Serengeti
28 * This module implements a soft tod since
29 * Serengeti has no tod part.
30 */
31
32 #include <sys/modctl.h>
33 #include <sys/systm.h>
34 #include <sys/cpuvar.h>
35 #include <sys/promif.h>
36 #include <sys/sgsbbc_iosram.h>
37 #include <sys/todsg.h>
38 #include <sys/cmn_err.h>
39 #include <sys/time.h>
40 #include <sys/sysmacros.h>
41 #include <sys/clock.h>
42
43 #if defined(DEBUG) || defined(lint)
44 static int todsg_debug = 0;
45 #define DCMNERR if (todsg_debug) cmn_err
46 #else
47 #define DCMNERR
48 #endif /* DEBUG */
49
50 #define OFFSET(base, field) ((char *)&base.field - (char *)&base)
51 #define SC_DOWN_COUNT_THRESHOLD 2
52 #define SC_TOD_MIN_REV 2
53
54 static timestruc_t todsg_get(void);
55 static void todsg_set(timestruc_t);
56 static uint32_t todsg_set_watchdog_timer(uint_t);
57 static uint32_t todsg_clear_watchdog_timer(void);
58 static void todsg_set_power_alarm(timestruc_t);
59 static void todsg_clear_power_alarm(void);
60 static uint64_t todsg_get_cpufrequency(void);
61 static int update_heartbeat(void);
62 static int verify_sc_tod_version(void);
63 static int update_tod_skew(time_t skew);
64
65 static uint32_t i_am_alive = 0;
66 static uint32_t sc_tod_version = 0;
67 static time_t skew_adjust = 0;
68 static int is_sc_down = 0;
69 static int adjust_sc_down = 0;
70
71 /*
72 * Module linkage information for the kernel.
73 */
74 static struct modlmisc modlmisc = {
75 &mod_miscops, "Serengeti tod module"
76 };
77
78 static struct modlinkage modlinkage = {
79 MODREV_1, (void *)&modlmisc, NULL
80 };
81
82 int
_init(void)83 _init(void)
84 {
85
86 DCMNERR(CE_NOTE, "todsg:_init(): begins");
87
88 if (strcmp(tod_module_name, "todsg") == 0) {
89 time_t ssc_time = (time_t)0;
90 char obp_string[80];
91
92 /*
93 * To obtain the initial start of day time, we use an
94 * OBP callback; this is because the iosram is not yet
95 * accessible from the OS at this early stage of startup.
96 */
97
98 /*
99 * Set the string to pass to OBP
100 */
101 (void) sprintf(obp_string,
102 "h# %p \" unix-get-tod\" $find if execute else 3drop then",
103 (void *)&ssc_time);
104
105 prom_interpret(obp_string, 0, 0, 0, 0, 0);
106
107 if (ssc_time == (time_t)0) {
108 cmn_err(CE_WARN, "Initial date is invalid. "
109 "This can be caused by older firmware.");
110 cmn_err(CE_CONT, "Please flashupdate the System "
111 "Controller firmware to the latest version.\n");
112 cmn_err(CE_CONT, "Attempting to set the date and time "
113 "based on the last shutdown.\n");
114 cmn_err(CE_CONT, "Please inspect the date and time and "
115 "correct if necessary.\n");
116 }
117
118 hrestime.tv_sec = ssc_time;
119
120 DCMNERR(CE_NOTE, "todsg: _init(): time from OBP 0x%lX",
121 ssc_time);
122 /*
123 * Verify whether the received date/clock has overflowed
124 * an integer(32bit), so that we capture any corrupted
125 * date from SC, thereby preventing boot failure.
126 */
127 if (TIMESPEC_OVERFLOW(&hrestime)) {
128 cmn_err(CE_WARN, "Date overflow detected.");
129 cmn_err(CE_CONT, "Attempting to set the date and time "
130 "based on the last shutdown.\n");
131 cmn_err(CE_CONT, "Please inspect the date and time and "
132 "correct if necessary.\n");
133
134 /*
135 * By setting hrestime.tv_sec to zero
136 * we force the vfs_mountroot() to set
137 * the date from the last shutdown.
138 */
139 hrestime.tv_sec = (time_t)0;
140 /*
141 * Save the skew so that we can update
142 * IOSRAM when it becomes accessible.
143 */
144 skew_adjust = -ssc_time;
145 }
146
147 DCMNERR(CE_NOTE, "todsg:_init(): set tod_ops");
148
149 tod_ops.tod_get = todsg_get;
150 tod_ops.tod_set = todsg_set;
151 tod_ops.tod_set_watchdog_timer = todsg_set_watchdog_timer;
152 tod_ops.tod_clear_watchdog_timer = todsg_clear_watchdog_timer;
153 tod_ops.tod_set_power_alarm = todsg_set_power_alarm;
154 tod_ops.tod_clear_power_alarm = todsg_clear_power_alarm;
155 tod_ops.tod_get_cpufrequency = todsg_get_cpufrequency;
156 }
157
158 return (mod_install(&modlinkage));
159
160 }
161
162 int
_fini(void)163 _fini(void)
164 {
165 if (strcmp(tod_module_name, "todsg") == 0)
166 return (EBUSY);
167 else
168 return (mod_remove(&modlinkage));
169 }
170
171 int
_info(struct modinfo * modinfop)172 _info(struct modinfo *modinfop)
173 {
174 return (mod_info(&modlinkage, modinfop));
175 }
176
177 static int
update_heartbeat(void)178 update_heartbeat(void)
179 {
180 tod_iosram_t tod_buf;
181 int complained = 0;
182
183 /* Update the heartbeat */
184 if (i_am_alive == UINT32_MAX)
185 i_am_alive = 0;
186 else
187 i_am_alive++;
188 if (iosram_write(SBBC_TOD_KEY, OFFSET(tod_buf, tod_i_am_alive),
189 (char *)&i_am_alive, sizeof (uint32_t))) {
190 complained++;
191 cmn_err(CE_WARN, "update_heartbeat(): write heartbeat failed");
192 }
193 return (complained);
194 }
195
196 static int
verify_sc_tod_version(void)197 verify_sc_tod_version(void)
198 {
199 uint32_t magic;
200 tod_iosram_t tod_buf;
201
202 if (!todsg_use_sc)
203 return (FALSE);
204 /*
205 * read tod_version only when the first time and
206 * when there has been a previous sc down time
207 */
208 if (!sc_tod_version || is_sc_down >= SC_DOWN_COUNT_THRESHOLD) {
209 if (iosram_read(SBBC_TOD_KEY, OFFSET(tod_buf, tod_magic),
210 (char *)&magic, sizeof (uint32_t)) ||
211 magic != TODSG_MAGIC) {
212 cmn_err(CE_WARN, "get_sc_tod_version(): "
213 "TOD SRAM magic error");
214 return (FALSE);
215 }
216 if (iosram_read(SBBC_TOD_KEY, OFFSET(tod_buf, tod_version),
217 (char *)&sc_tod_version, sizeof (uint32_t))) {
218 cmn_err(CE_WARN, "get_sc_tod_version(): "
219 "read tod version failed");
220 sc_tod_version = 0;
221 return (FALSE);
222 }
223 }
224 if (sc_tod_version >= SC_TOD_MIN_REV) {
225 return (TRUE);
226 } else {
227 todsg_use_sc = 0;
228 cmn_err(CE_WARN, "todsg_get(): incorrect firmware version, "
229 "(%d): expected version >= %d.", sc_tod_version,
230 SC_TOD_MIN_REV);
231 }
232 return (FALSE);
233 }
234
235 static int
update_tod_skew(time_t skew)236 update_tod_skew(time_t skew)
237 {
238 time_t domain_skew;
239 tod_iosram_t tod_buf;
240 int complained = 0;
241
242 DCMNERR(CE_NOTE, "update_tod_skew(): skew 0x%lX", skew);
243
244 if (iosram_read(SBBC_TOD_KEY, OFFSET(tod_buf, tod_domain_skew),
245 (char *)&domain_skew, sizeof (time_t))) {
246 complained++;
247 cmn_err(CE_WARN,
248 "update_tod_skew(): read tod domain skew failed");
249 }
250 domain_skew += skew;
251 /* we shall update the skew_adjust too now */
252 domain_skew += skew_adjust;
253 if (!complained && iosram_write(SBBC_TOD_KEY,
254 OFFSET(tod_buf, tod_domain_skew), (char *)&domain_skew,
255 sizeof (time_t))) {
256 complained++;
257 cmn_err(CE_WARN,
258 "update_tod_skew(): write domain skew failed");
259 }
260 if (!complained)
261 skew_adjust = 0;
262 return (complained);
263 }
264
265 /*
266 * Return time value read from IOSRAM.
267 * Must be called with tod_lock held.
268 */
269 static timestruc_t
todsg_get(void)270 todsg_get(void)
271 {
272 tod_iosram_t tod_buf;
273 time_t seconds;
274 time_t domain_skew;
275 int complained = 0;
276 static time_t pre_seconds = (time_t)0;
277
278 ASSERT(MUTEX_HELD(&tod_lock));
279
280 if (!verify_sc_tod_version()) {
281 /* if we can't use SC */
282 goto return_hrestime;
283 }
284 if (watchdog_activated != 0 || watchdog_enable != 0)
285 complained = update_heartbeat();
286 if (!complained && (iosram_read(SBBC_TOD_KEY,
287 OFFSET(tod_buf, tod_get_value), (char *)&seconds,
288 sizeof (time_t)))) {
289 complained++;
290 cmn_err(CE_WARN, "todsg_get(): read 64-bit tod value failed");
291 }
292 if (!complained && skew_adjust) {
293 /*
294 * This is our first chance to update IOSRAM
295 * with local copy of the skew, so update it.
296 */
297 complained = update_tod_skew(0);
298 }
299 if (!complained && iosram_read(SBBC_TOD_KEY,
300 OFFSET(tod_buf, tod_domain_skew), (char *)&domain_skew,
301 sizeof (time_t))) {
302 complained++;
303 cmn_err(CE_WARN, "todsg_get(): read tod domain skew failed");
304 }
305
306 if (complained) {
307 cmn_err(CE_WARN, "todsg_get(): turned off using tod");
308 todsg_use_sc = 0;
309 goto return_hrestime;
310 }
311
312 /*
313 * If the SC gets rebooted, and we are using NTP, then we need
314 * to sync the IOSRAM to hrestime when the SC comes back. We
315 * can determine that either NTP slew (or date -a) was called if
316 * the global timedelta was non-zero at any point while the SC
317 * was away. If timedelta remains zero throughout, then the
318 * default action will be to sync hrestime to IOSRAM
319 */
320 if (seconds != pre_seconds) { /* SC still alive */
321 pre_seconds = seconds;
322 if (is_sc_down >= SC_DOWN_COUNT_THRESHOLD && adjust_sc_down) {
323 skew_adjust = hrestime.tv_sec - (seconds + domain_skew);
324 complained = update_tod_skew(0);
325 if (!complained && (iosram_read(SBBC_TOD_KEY,
326 OFFSET(tod_buf, tod_domain_skew),
327 (char *)&domain_skew, sizeof (time_t)))) {
328 complained++;
329 cmn_err(CE_WARN, "todsg_get(): "
330 "read tod domain skew failed");
331 }
332 }
333 is_sc_down = 0;
334 adjust_sc_down = 0;
335
336 /*
337 * If complained then domain_skew is invalid.
338 * Hand back hrestime instead.
339 */
340 if (!complained) {
341 /*
342 * The read was successful so ensure the failure
343 * flag is clear.
344 */
345 tod_status_clear(TOD_GET_FAILED);
346 timestruc_t ts = {0, 0};
347 ts.tv_sec = seconds + domain_skew;
348 return (ts);
349 } else {
350 goto return_hrestime;
351 }
352 }
353
354 /* SC/TOD is down */
355 is_sc_down++;
356 if (timedelta != 0) {
357 adjust_sc_down = 1;
358 }
359
360 return_hrestime:
361 /*
362 * We need to inform the tod_validate() code to stop checking until
363 * the SC comes back up again. Note we will return hrestime below
364 * which may be different to the previous TOD value we returned.
365 */
366 tod_status_set(TOD_GET_FAILED);
367 return (hrestime);
368 }
369
370 static void
todsg_set(timestruc_t ts)371 todsg_set(timestruc_t ts)
372 {
373 int complained = 0;
374 tod_iosram_t tod_buf;
375 time_t domain_skew;
376 time_t seconds;
377 time_t hwtod;
378
379 ASSERT(MUTEX_HELD(&tod_lock));
380
381 if (!verify_sc_tod_version()) {
382 /* if we can't use SC */
383 return;
384 }
385 /*
386 * If the SC is down just note the fact that we should
387 * have adjusted the hardware skew which caters for calls
388 * to stime(). (eg NTP step, as opposed to NTP skew)
389 */
390 if (is_sc_down) {
391 adjust_sc_down = 1;
392 return;
393 }
394 /*
395 * reason to update i_am_alive here:
396 * To work around a generic Solaris bug that can
397 * cause tod_get() to be starved by too frequent
398 * calls to the stime() system call.
399 */
400 if (watchdog_activated != 0 || watchdog_enable != 0)
401 complained = update_heartbeat();
402
403 /*
404 * We are passed hrestime from clock.c so we need to read the
405 * IOSRAM for the hardware's idea of the time to see if we need
406 * to update the skew.
407 */
408 if (!complained && (iosram_read(SBBC_TOD_KEY,
409 OFFSET(tod_buf, tod_get_value), (char *)&seconds,
410 sizeof (time_t)))) {
411 complained++;
412 cmn_err(CE_WARN, "todsg_set(): read 64-bit tod value failed");
413 }
414
415 if (!complained && iosram_read(SBBC_TOD_KEY,
416 OFFSET(tod_buf, tod_domain_skew), (char *)&domain_skew,
417 sizeof (time_t))) {
418 complained++;
419 cmn_err(CE_WARN, "todsg_set(): read tod domain skew failed");
420 }
421
422 /*
423 * Only update the skew if the time passed differs from
424 * what the hardware thinks & no errors talking to SC
425 */
426 if (!complained && (ts.tv_sec != (seconds + domain_skew))) {
427 hwtod = seconds + domain_skew;
428 complained = update_tod_skew(ts.tv_sec - hwtod);
429
430 DCMNERR(CE_NOTE, "todsg_set(): set time %lX (%lX)%s",
431 ts.tv_sec, hwtod, complained ? " failed" : "");
432 }
433
434 if (complained) {
435 cmn_err(CE_WARN, "todsg_set(): turned off using tod");
436 todsg_use_sc = 0;
437 }
438 }
439
440 static uint32_t
todsg_set_watchdog_timer(uint32_t timeoutval)441 todsg_set_watchdog_timer(uint32_t timeoutval)
442 {
443 tod_iosram_t tod_buf;
444
445 ASSERT(MUTEX_HELD(&tod_lock));
446
447 if (!verify_sc_tod_version()) {
448 DCMNERR(CE_NOTE, "todsg_set_watchdog_timer(): "
449 "verify_sc_tod_version failed");
450 return (0);
451 }
452 DCMNERR(CE_NOTE, "todsg_set_watchdog_timer(): "
453 "set watchdog timer value = %d", timeoutval);
454
455 if (iosram_write(SBBC_TOD_KEY, OFFSET(tod_buf, tod_timeout_period),
456 (char *)&timeoutval, sizeof (uint32_t))) {
457 DCMNERR(CE_NOTE, "todsg_set_watchdog_timer(): "
458 "write new timeout value failed");
459 return (0);
460 }
461 watchdog_activated = 1;
462 return (timeoutval);
463 }
464
465 static uint32_t
todsg_clear_watchdog_timer(void)466 todsg_clear_watchdog_timer(void)
467 {
468 tod_iosram_t tod_buf;
469 uint32_t r_timeout_period;
470 uint32_t w_timeout_period;
471
472 ASSERT(MUTEX_HELD(&tod_lock));
473
474 if ((watchdog_activated == 0) || !verify_sc_tod_version()) {
475 DCMNERR(CE_NOTE, "todsg_set_watchdog_timer(): "
476 "either watchdog not activated or "
477 "verify_sc_tod_version failed");
478 return (0);
479 }
480 if (iosram_read(SBBC_TOD_KEY, OFFSET(tod_buf, tod_timeout_period),
481 (char *)&r_timeout_period, sizeof (uint32_t))) {
482 DCMNERR(CE_NOTE, "todsg_clear_watchdog_timer(): "
483 "read timeout value failed");
484 return (0);
485 }
486 DCMNERR(CE_NOTE, "todsg_clear_watchdog_timer(): "
487 "clear watchdog timer (old value=%d)", r_timeout_period);
488 w_timeout_period = 0;
489 if (iosram_write(SBBC_TOD_KEY, OFFSET(tod_buf, tod_timeout_period),
490 (char *)&w_timeout_period, sizeof (uint32_t))) {
491 DCMNERR(CE_NOTE, "todsg_clear_watchdog_timer(): "
492 "write zero timeout value failed");
493 return (0);
494 }
495 watchdog_activated = 0;
496 return (r_timeout_period);
497 }
498
499 /*
500 * Null function.
501 */
502 /* ARGSUSED */
503 static void
todsg_set_power_alarm(timestruc_t ts)504 todsg_set_power_alarm(timestruc_t ts)
505 {
506 ASSERT(MUTEX_HELD(&tod_lock));
507 }
508
509 /*
510 * Null function
511 */
512 static void
todsg_clear_power_alarm()513 todsg_clear_power_alarm()
514 {
515 ASSERT(MUTEX_HELD(&tod_lock));
516 }
517
518 /*
519 * Get clock freq from the cpunode
520 */
521 uint64_t
todsg_get_cpufrequency(void)522 todsg_get_cpufrequency(void)
523 {
524
525 DCMNERR(CE_NOTE, "todsg_get_cpufrequency(): frequency=%ldMHz",
526 cpunodes[CPU->cpu_id].clock_freq/1000000);
527
528 return (cpunodes[CPU->cpu_id].clock_freq);
529 }
530