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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)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
todopl_get(void)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
todopl_set(timestruc_t ts)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
todopl_set_watchdog_timer(uint_t timeoutval)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
todopl_clear_watchdog_timer(void)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
todopl_set_power_alarm(timestruc_t ts)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
todopl_clear_power_alarm()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
todopl_get_cpufrequency(void)316 todopl_get_cpufrequency(void)
317 {
318 return (cpunodes[CPU->cpu_id].clock_freq);
319 }
320