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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 /* 29 * tod driver module for Serengeti 30 * This module implements a soft tod since 31 * Serengeti has no tod part. 32 */ 33 34 #include <sys/modctl.h> 35 #include <sys/systm.h> 36 #include <sys/cpuvar.h> 37 #include <sys/promif.h> 38 #include <sys/sgsbbc_iosram.h> 39 #include <sys/todsg.h> 40 #include <sys/cmn_err.h> 41 #include <sys/time.h> 42 #include <sys/sysmacros.h> 43 #include <sys/clock.h> 44 45 #if defined(DEBUG) || defined(lint) 46 static int todsg_debug = 0; 47 #define DCMNERR if (todsg_debug) cmn_err 48 #else 49 #define DCMNERR 50 #endif /* DEBUG */ 51 52 #define OFFSET(base, field) ((char *)&base.field - (char *)&base) 53 #define SC_DOWN_COUNT_THRESHOLD 2 54 #define SC_TOD_MIN_REV 2 55 56 static timestruc_t todsg_get(void); 57 static void todsg_set(timestruc_t); 58 static uint32_t todsg_set_watchdog_timer(uint_t); 59 static uint32_t todsg_clear_watchdog_timer(void); 60 static void todsg_set_power_alarm(timestruc_t); 61 static void todsg_clear_power_alarm(void); 62 static uint64_t todsg_get_cpufrequency(void); 63 static int update_heartbeat(void); 64 static int verify_sc_tod_version(void); 65 static int update_tod_skew(time_t skew); 66 67 static uint32_t i_am_alive = 0; 68 static uint32_t sc_tod_version = 0; 69 static time_t skew_adjust = 0; 70 static int is_sc_down = 0; 71 static int adjust_sc_down = 0; 72 73 /* 74 * Module linkage information for the kernel. 75 */ 76 static struct modlmisc modlmisc = { 77 &mod_miscops, "Serengeti tod module v.%I%" 78 }; 79 80 static struct modlinkage modlinkage = { 81 MODREV_1, (void *)&modlmisc, NULL 82 }; 83 84 int 85 _init(void) 86 { 87 88 DCMNERR(CE_NOTE, "todsg:_init(): begins"); 89 90 if (strcmp(tod_module_name, "todsg") == 0) { 91 time_t ssc_time = (time_t)0; 92 char obp_string[80]; 93 94 /* 95 * To obtain the initial start of day time, we use an 96 * OBP callback; this is because the iosram is not yet 97 * accessible from the OS at this early stage of startup. 98 */ 99 100 /* 101 * Set the string to pass to OBP 102 */ 103 (void) sprintf(obp_string, 104 "h# %p \" unix-get-tod\" $find if execute else " 105 "3drop then", 106 (void *)&ssc_time); 107 108 prom_interpret(obp_string, 0, 0, 0, 0, 0); 109 110 if (ssc_time == (time_t)0) { 111 cmn_err(CE_WARN, "Initial date is invalid. " 112 "This can be caused by older firmware."); 113 cmn_err(CE_CONT, "Please flashupdate the System " 114 "Controller firmware to the latest version.\n"); 115 cmn_err(CE_CONT, "Attempting to set the date and time " 116 "based on the last shutdown.\n"); 117 cmn_err(CE_CONT, "Please inspect the date and time and " 118 "correct if necessary.\n"); 119 } 120 121 hrestime.tv_sec = ssc_time; 122 123 DCMNERR(CE_NOTE, "todsg: _init(): time from OBP 0x%lX", 124 ssc_time); 125 /* 126 * Verify whether the received date/clock has overflowed 127 * an integer(32bit), so that we capture any corrupted 128 * date from SC, thereby preventing boot failure. 129 */ 130 if (TIMESPEC_OVERFLOW(&hrestime)) { 131 cmn_err(CE_WARN, "Date overflow detected."); 132 cmn_err(CE_CONT, "Attempting to set the date and time " 133 "based on the last shutdown.\n"); 134 cmn_err(CE_CONT, "Please inspect the date and time and " 135 "correct if necessary.\n"); 136 137 /* 138 * By setting hrestime.tv_sec to zero 139 * we force the vfs_mountroot() to set 140 * the date from the last shutdown. 141 */ 142 hrestime.tv_sec = (time_t)0; 143 /* 144 * Save the skew so that we can update 145 * IOSRAM when it becomes accessible. 146 */ 147 skew_adjust = -ssc_time; 148 } 149 150 DCMNERR(CE_NOTE, "todsg:_init(): set tod_ops"); 151 152 tod_ops.tod_get = todsg_get; 153 tod_ops.tod_set = todsg_set; 154 tod_ops.tod_set_watchdog_timer = todsg_set_watchdog_timer; 155 tod_ops.tod_clear_watchdog_timer = todsg_clear_watchdog_timer; 156 tod_ops.tod_set_power_alarm = todsg_set_power_alarm; 157 tod_ops.tod_clear_power_alarm = todsg_clear_power_alarm; 158 tod_ops.tod_get_cpufrequency = todsg_get_cpufrequency; 159 } 160 161 return (mod_install(&modlinkage)); 162 163 } 164 165 int 166 _fini(void) 167 { 168 if (strcmp(tod_module_name, "todsg") == 0) 169 return (EBUSY); 170 else 171 return (mod_remove(&modlinkage)); 172 } 173 174 int 175 _info(struct modinfo *modinfop) 176 { 177 return (mod_info(&modlinkage, modinfop)); 178 } 179 180 static int 181 update_heartbeat(void) 182 { 183 tod_iosram_t tod_buf; 184 int complained = 0; 185 186 /* Update the heartbeat */ 187 if (i_am_alive == UINT32_MAX) 188 i_am_alive = 0; 189 else 190 i_am_alive++; 191 if (iosram_write(SBBC_TOD_KEY, OFFSET(tod_buf, tod_i_am_alive), 192 (char *)&i_am_alive, sizeof (uint32_t))) { 193 complained++; 194 cmn_err(CE_WARN, "update_heartbeat(): write heartbeat failed"); 195 } 196 return (complained); 197 } 198 199 static int 200 verify_sc_tod_version(void) 201 { 202 uint32_t magic; 203 tod_iosram_t tod_buf; 204 205 if (!todsg_use_sc) 206 return (FALSE); 207 /* 208 * read tod_version only when the first time and 209 * when there has been a previous sc down time 210 */ 211 if (!sc_tod_version || is_sc_down >= SC_DOWN_COUNT_THRESHOLD) { 212 if (iosram_read(SBBC_TOD_KEY, OFFSET(tod_buf, tod_magic), 213 (char *)&magic, sizeof (uint32_t)) || 214 magic != TODSG_MAGIC) { 215 cmn_err(CE_WARN, "get_sc_tod_version(): " 216 "TOD SRAM magic error"); 217 return (FALSE); 218 } 219 if (iosram_read(SBBC_TOD_KEY, OFFSET(tod_buf, tod_version), 220 (char *)&sc_tod_version, sizeof (uint32_t))) { 221 cmn_err(CE_WARN, "get_sc_tod_version(): " 222 "read tod version failed"); 223 sc_tod_version = 0; 224 return (FALSE); 225 } 226 } 227 if (sc_tod_version >= SC_TOD_MIN_REV) { 228 return (TRUE); 229 } else { 230 todsg_use_sc = 0; 231 cmn_err(CE_WARN, 232 "todsg_get(): incorrect firmware version, " 233 "(%d): expected version >= %d.", 234 sc_tod_version, SC_TOD_MIN_REV); 235 } 236 return (FALSE); 237 } 238 239 static int 240 update_tod_skew(time_t skew) 241 { 242 time_t domain_skew; 243 tod_iosram_t tod_buf; 244 int complained = 0; 245 246 DCMNERR(CE_NOTE, "update_tod_skew(): skew 0x%lX", skew); 247 248 if (iosram_read(SBBC_TOD_KEY, OFFSET(tod_buf, tod_domain_skew), 249 (char *)&domain_skew, sizeof (time_t))) { 250 complained++; 251 cmn_err(CE_WARN, "update_tod_skew(): " 252 "read tod domain skew failed"); 253 } 254 domain_skew += skew; 255 /* we shall update the skew_adjust too now */ 256 domain_skew += skew_adjust; 257 if (!complained && iosram_write(SBBC_TOD_KEY, 258 OFFSET(tod_buf, tod_domain_skew), 259 (char *)&domain_skew, sizeof (time_t))) { 260 complained++; 261 cmn_err(CE_WARN, "update_tod_skew(): " 262 "write domain skew failed"); 263 } 264 if (!complained) 265 skew_adjust = 0; 266 return (complained); 267 } 268 269 270 /* 271 * Return time value read from IOSRAM. 272 * Must be called with tod_lock held. 273 */ 274 static timestruc_t 275 todsg_get(void) 276 { 277 tod_iosram_t tod_buf; 278 time_t seconds; 279 time_t domain_skew; 280 int complained = 0; 281 static time_t pre_seconds = (time_t)0; 282 283 ASSERT(MUTEX_HELD(&tod_lock)); 284 285 if (!verify_sc_tod_version()) { 286 /* if we can't use SC */ 287 goto return_hrestime; 288 } 289 if (watchdog_activated != 0 || watchdog_enable != 0) 290 complained = update_heartbeat(); 291 if (!complained && (iosram_read(SBBC_TOD_KEY, 292 OFFSET(tod_buf, tod_get_value), 293 (char *)&seconds, sizeof (time_t)))) { 294 complained++; 295 cmn_err(CE_WARN, "todsg_get(): read 64-bit tod value failed"); 296 } 297 if (!complained && skew_adjust) { 298 /* 299 * This is our first chance to update IOSRAM 300 * with local copy of the skew, so update it. 301 */ 302 complained = update_tod_skew(0); 303 } 304 if (!complained && iosram_read(SBBC_TOD_KEY, 305 OFFSET(tod_buf, tod_domain_skew), 306 (char *)&domain_skew, sizeof (time_t))) { 307 complained++; 308 cmn_err(CE_WARN, "todsg_get(): read tod domain skew failed"); 309 } 310 311 if (complained) { 312 cmn_err(CE_WARN, "todsg_get(): turned off using tod"); 313 todsg_use_sc = 0; 314 goto return_hrestime; 315 } 316 317 /* 318 * If the SC gets rebooted, and we are using NTP, then we need 319 * to sync the IOSRAM to hrestime when the SC comes back. We 320 * can determine that either NTP slew (or date -a) was called if 321 * the global timedelta was non-zero at any point while the SC 322 * was away. If timedelta remains zero throughout, then the 323 * default action will be to sync hrestime to IOSRAM 324 */ 325 if (seconds != pre_seconds) { /* SC still alive */ 326 pre_seconds = seconds; 327 if (is_sc_down >= SC_DOWN_COUNT_THRESHOLD && adjust_sc_down) { 328 skew_adjust = hrestime.tv_sec - (seconds + domain_skew); 329 complained = update_tod_skew(0); 330 if (!complained && (iosram_read(SBBC_TOD_KEY, 331 OFFSET(tod_buf, tod_domain_skew), 332 (char *)&domain_skew, sizeof (time_t)))) { 333 complained++; 334 cmn_err(CE_WARN, "todsg_get(): " 335 "read tod domain skew failed"); 336 } 337 } 338 is_sc_down = 0; 339 adjust_sc_down = 0; 340 341 /* 342 * If complained then domain_skew is invalid. 343 * Hand back hrestime instead. 344 */ 345 if (!complained) { 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 till 363 * SC come back up again. Note that we will return hrestime below 364 * which can be different that the previous TOD value we returned 365 */ 366 tod_fault_reset(); 367 return (hrestime); 368 } 369 370 static void 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), 410 (char *)&seconds, 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), 417 (char *)&domain_skew, 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 435 if (complained) { 436 cmn_err(CE_WARN, "todsg_set(): turned off using tod"); 437 todsg_use_sc = 0; 438 } 439 } 440 441 static uint32_t 442 todsg_set_watchdog_timer(uint32_t timeoutval) 443 { 444 tod_iosram_t tod_buf; 445 446 ASSERT(MUTEX_HELD(&tod_lock)); 447 448 if (!verify_sc_tod_version()) { 449 DCMNERR(CE_NOTE, "todsg_set_watchdog_timer(): " 450 "verify_sc_tod_version failed"); 451 return (0); 452 } 453 DCMNERR(CE_NOTE, "todsg_set_watchdog_timer(): " 454 "set watchdog timer value = %d", timeoutval); 455 456 if (iosram_write(SBBC_TOD_KEY, OFFSET(tod_buf, tod_timeout_period), 457 (char *)&timeoutval, sizeof (uint32_t))) { 458 DCMNERR(CE_NOTE, "todsg_set_watchdog_timer(): " 459 "write new timeout value failed"); 460 return (0); 461 } 462 watchdog_activated = 1; 463 return (timeoutval); 464 } 465 466 static uint32_t 467 todsg_clear_watchdog_timer(void) 468 { 469 tod_iosram_t tod_buf; 470 uint32_t r_timeout_period; 471 uint32_t w_timeout_period; 472 473 ASSERT(MUTEX_HELD(&tod_lock)); 474 475 if ((watchdog_activated == 0) || !verify_sc_tod_version()) { 476 DCMNERR(CE_NOTE, "todsg_set_watchdog_timer(): " 477 "either watchdog not activated or " 478 "verify_sc_tod_version failed"); 479 return (0); 480 } 481 if (iosram_read(SBBC_TOD_KEY, OFFSET(tod_buf, tod_timeout_period), 482 (char *)&r_timeout_period, sizeof (uint32_t))) { 483 DCMNERR(CE_NOTE, "todsg_clear_watchdog_timer(): " 484 "read timeout value failed"); 485 return (0); 486 } 487 DCMNERR(CE_NOTE, "todsg_clear_watchdog_timer(): " 488 "clear watchdog timer (old value=%d)", r_timeout_period); 489 w_timeout_period = 0; 490 if (iosram_write(SBBC_TOD_KEY, OFFSET(tod_buf, tod_timeout_period), 491 (char *)&w_timeout_period, sizeof (uint32_t))) { 492 DCMNERR(CE_NOTE, "todsg_clear_watchdog_timer(): " 493 "write zero timeout value failed"); 494 return (0); 495 } 496 watchdog_activated = 0; 497 return (r_timeout_period); 498 } 499 500 /* 501 * Null function. 502 */ 503 /* ARGSUSED */ 504 static void 505 todsg_set_power_alarm(timestruc_t ts) 506 { 507 ASSERT(MUTEX_HELD(&tod_lock)); 508 } 509 510 /* 511 * Null function 512 */ 513 static void 514 todsg_clear_power_alarm() 515 { 516 ASSERT(MUTEX_HELD(&tod_lock)); 517 } 518 519 /* 520 * Get clock freq from the cpunode 521 */ 522 uint64_t 523 todsg_get_cpufrequency(void) 524 { 525 526 DCMNERR(CE_NOTE, "todsg_get_cpufrequency(): frequency=%ldMHz", 527 cpunodes[CPU->cpu_id].clock_freq/1000000); 528 529 return (cpunodes[CPU->cpu_id].clock_freq); 530 } 531