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 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 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 172 _info(struct modinfo *modinfop) 173 { 174 return (mod_info(&modlinkage, modinfop)); 175 } 176 177 static int 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 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 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 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 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 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 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 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 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 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