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 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 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 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 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 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 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 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 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 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 318 todopl_get_cpufrequency(void) 319 { 320 return (cpunodes[CPU->cpu_id].clock_freq); 321 } 322