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