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