kern_todr.c revision 1.46 1 1.46 thorpej /* $NetBSD: kern_todr.c,v 1.46 2020/01/03 01:24:48 thorpej Exp $ */
2 1.44 thorpej
3 1.44 thorpej /*-
4 1.44 thorpej * Copyright (c) 2020 The NetBSD Foundation, Inc.
5 1.44 thorpej * All rights reserved.
6 1.44 thorpej *
7 1.44 thorpej * This code is derived from software contributed to The NetBSD Foundation
8 1.44 thorpej * by Jason R. Thorpe.
9 1.44 thorpej *
10 1.44 thorpej * Redistribution and use in source and binary forms, with or without
11 1.44 thorpej * modification, are permitted provided that the following conditions
12 1.44 thorpej * are met:
13 1.44 thorpej * 1. Redistributions of source code must retain the above copyright
14 1.44 thorpej * notice, this list of conditions and the following disclaimer.
15 1.44 thorpej * 2. Redistributions in binary form must reproduce the above copyright
16 1.44 thorpej * notice, this list of conditions and the following disclaimer in the
17 1.44 thorpej * documentation and/or other materials provided with the distribution.
18 1.44 thorpej *
19 1.44 thorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.44 thorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.44 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.44 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.44 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.44 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.44 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.44 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.44 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.44 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.44 thorpej * POSSIBILITY OF SUCH DAMAGE.
30 1.44 thorpej */
31 1.1 gdamore
32 1.1 gdamore /*
33 1.34 rmind * Copyright (c) 1988 University of Utah.
34 1.1 gdamore * Copyright (c) 1992, 1993
35 1.1 gdamore * The Regents of the University of California. All rights reserved.
36 1.1 gdamore *
37 1.1 gdamore * This code is derived from software contributed to Berkeley by
38 1.1 gdamore * the Systems Programming Group of the University of Utah Computer
39 1.1 gdamore * Science Department and Ralph Campbell.
40 1.1 gdamore *
41 1.1 gdamore * Redistribution and use in source and binary forms, with or without
42 1.1 gdamore * modification, are permitted provided that the following conditions
43 1.1 gdamore * are met:
44 1.1 gdamore * 1. Redistributions of source code must retain the above copyright
45 1.1 gdamore * notice, this list of conditions and the following disclaimer.
46 1.1 gdamore * 2. Redistributions in binary form must reproduce the above copyright
47 1.1 gdamore * notice, this list of conditions and the following disclaimer in the
48 1.1 gdamore * documentation and/or other materials provided with the distribution.
49 1.1 gdamore * 3. Neither the name of the University nor the names of its contributors
50 1.1 gdamore * may be used to endorse or promote products derived from this software
51 1.1 gdamore * without specific prior written permission.
52 1.1 gdamore *
53 1.1 gdamore * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54 1.1 gdamore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55 1.1 gdamore * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56 1.1 gdamore * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57 1.1 gdamore * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58 1.1 gdamore * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59 1.1 gdamore * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60 1.1 gdamore * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61 1.1 gdamore * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62 1.1 gdamore * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 1.1 gdamore * SUCH DAMAGE.
64 1.1 gdamore *
65 1.1 gdamore * from: Utah Hdr: clock.c 1.18 91/01/21
66 1.1 gdamore *
67 1.1 gdamore * @(#)clock.c 8.1 (Berkeley) 6/10/93
68 1.1 gdamore */
69 1.34 rmind
70 1.38 jmcneill #include "opt_todr.h"
71 1.38 jmcneill
72 1.1 gdamore #include <sys/cdefs.h>
73 1.46 thorpej __KERNEL_RCSID(0, "$NetBSD: kern_todr.c,v 1.46 2020/01/03 01:24:48 thorpej Exp $");
74 1.1 gdamore
75 1.1 gdamore #include <sys/param.h>
76 1.1 gdamore #include <sys/kernel.h>
77 1.1 gdamore #include <sys/systm.h>
78 1.1 gdamore #include <sys/device.h>
79 1.1 gdamore #include <sys/timetc.h>
80 1.25 ad #include <sys/intr.h>
81 1.39 riastrad #include <sys/rndsource.h>
82 1.44 thorpej #include <sys/mutex.h>
83 1.25 ad
84 1.1 gdamore #include <dev/clock_subr.h> /* hmm.. this should probably move to sys */
85 1.1 gdamore
86 1.42 thorpej static int todr_gettime(todr_chip_handle_t, struct timeval *);
87 1.42 thorpej static int todr_settime(todr_chip_handle_t, struct timeval *);
88 1.42 thorpej
89 1.44 thorpej static kmutex_t todr_mutex;
90 1.44 thorpej static todr_chip_handle_t todr_handle;
91 1.44 thorpej static bool todr_initialized;
92 1.44 thorpej
93 1.44 thorpej /*
94 1.44 thorpej * todr_init:
95 1.44 thorpej * Initialize TOD clock data.
96 1.44 thorpej */
97 1.44 thorpej void
98 1.44 thorpej todr_init(void)
99 1.44 thorpej {
100 1.44 thorpej
101 1.44 thorpej mutex_init(&todr_mutex, MUTEX_DEFAULT, IPL_NONE);
102 1.44 thorpej todr_initialized = true;
103 1.44 thorpej }
104 1.44 thorpej
105 1.44 thorpej /*
106 1.44 thorpej * todr_lock:
107 1.44 thorpej * Acquire the TODR lock.
108 1.44 thorpej */
109 1.44 thorpej void
110 1.44 thorpej todr_lock(void)
111 1.44 thorpej {
112 1.44 thorpej
113 1.44 thorpej mutex_enter(&todr_mutex);
114 1.44 thorpej }
115 1.44 thorpej
116 1.44 thorpej /*
117 1.44 thorpej * todr_unlock:
118 1.44 thorpej * Release the TODR lock.
119 1.44 thorpej */
120 1.44 thorpej void
121 1.44 thorpej todr_unlock(void)
122 1.44 thorpej {
123 1.44 thorpej
124 1.44 thorpej mutex_exit(&todr_mutex);
125 1.44 thorpej }
126 1.44 thorpej
127 1.44 thorpej /*
128 1.44 thorpej * todr_lock_owned:
129 1.44 thorpej * Return true if the current thread owns the TODR lock.
130 1.44 thorpej * This is to be used by diagnostic assertions only.
131 1.44 thorpej */
132 1.44 thorpej bool
133 1.44 thorpej todr_lock_owned(void)
134 1.44 thorpej {
135 1.44 thorpej
136 1.44 thorpej return mutex_owned(&todr_mutex) ? true : false;
137 1.44 thorpej }
138 1.1 gdamore
139 1.1 gdamore /*
140 1.44 thorpej * todr_attach:
141 1.44 thorpej * Attach the clock device to todr_handle.
142 1.1 gdamore */
143 1.1 gdamore void
144 1.1 gdamore todr_attach(todr_chip_handle_t todr)
145 1.1 gdamore {
146 1.1 gdamore
147 1.44 thorpej /*
148 1.44 thorpej * todr_init() is called very early in main(), but this is
149 1.44 thorpej * here to catch a case where todr_attach() is called before
150 1.44 thorpej * main().
151 1.44 thorpej */
152 1.44 thorpej KASSERT(todr_initialized);
153 1.44 thorpej
154 1.44 thorpej todr_lock();
155 1.24 peter if (todr_handle) {
156 1.44 thorpej todr_unlock();
157 1.24 peter printf("todr_attach: TOD already configured\n");
158 1.1 gdamore return;
159 1.1 gdamore }
160 1.24 peter todr_handle = todr;
161 1.44 thorpej todr_unlock();
162 1.1 gdamore }
163 1.1 gdamore
164 1.31 tsutsui static bool timeset = false;
165 1.2 gdamore
166 1.1 gdamore /*
167 1.44 thorpej * todr_set_systime:
168 1.44 thorpej * Set up the system's time. The "base" argument is a best-guess
169 1.44 thorpej * close-enough value to use if the TOD clock is unavailable or
170 1.44 thorpej * contains garbage. Must be called with the TODR lock held.
171 1.1 gdamore */
172 1.1 gdamore void
173 1.44 thorpej todr_set_systime(time_t base)
174 1.1 gdamore {
175 1.31 tsutsui bool badbase = false;
176 1.31 tsutsui bool waszero = (base == 0);
177 1.31 tsutsui bool goodtime = false;
178 1.31 tsutsui bool badrtc = false;
179 1.1 gdamore struct timespec ts;
180 1.2 gdamore struct timeval tv;
181 1.1 gdamore
182 1.44 thorpej KASSERT(todr_lock_owned());
183 1.44 thorpej
184 1.35 tls rnd_add_data(NULL, &base, sizeof(base), 0);
185 1.35 tls
186 1.37 christos if (base < 5 * SECS_PER_COMMON_YEAR) {
187 1.4 gdamore struct clock_ymdhms basedate;
188 1.4 gdamore
189 1.1 gdamore /*
190 1.1 gdamore * If base is 0, assume filesystem time is just unknown
191 1.2 gdamore * instead of preposterous. Don't bark.
192 1.1 gdamore */
193 1.1 gdamore if (base != 0)
194 1.1 gdamore printf("WARNING: preposterous time in file system\n");
195 1.1 gdamore /* not going to use it anyway, if the chip is readable */
196 1.33 tsutsui basedate.dt_year = 2010;
197 1.4 gdamore basedate.dt_mon = 1;
198 1.4 gdamore basedate.dt_day = 1;
199 1.4 gdamore basedate.dt_hour = 12;
200 1.4 gdamore basedate.dt_min = 0;
201 1.4 gdamore basedate.dt_sec = 0;
202 1.4 gdamore base = clock_ymdhms_to_secs(&basedate);
203 1.31 tsutsui badbase = true;
204 1.1 gdamore }
205 1.1 gdamore
206 1.5 matt /*
207 1.5 matt * Some ports need to be supplied base in order to fabricate a time_t.
208 1.5 matt */
209 1.22 gdamore if (todr_handle)
210 1.22 gdamore todr_handle->base_time = base;
211 1.5 matt
212 1.40 maxv memset(&tv, 0, sizeof(tv));
213 1.40 maxv
214 1.1 gdamore if ((todr_handle == NULL) ||
215 1.2 gdamore (todr_gettime(todr_handle, &tv) != 0) ||
216 1.37 christos (tv.tv_sec < (25 * SECS_PER_COMMON_YEAR))) {
217 1.1 gdamore
218 1.24 peter if (todr_handle != NULL)
219 1.2 gdamore printf("WARNING: preposterous TOD clock time\n");
220 1.1 gdamore else
221 1.2 gdamore printf("WARNING: no TOD clock present\n");
222 1.31 tsutsui badrtc = true;
223 1.1 gdamore } else {
224 1.30 tsutsui time_t deltat = tv.tv_sec - base;
225 1.1 gdamore
226 1.1 gdamore if (deltat < 0)
227 1.1 gdamore deltat = -deltat;
228 1.2 gdamore
229 1.37 christos if (!badbase && deltat >= 2 * SECS_PER_DAY) {
230 1.2 gdamore
231 1.2 gdamore if (tv.tv_sec < base) {
232 1.2 gdamore /*
233 1.2 gdamore * The clock should never go backwards
234 1.2 gdamore * relative to filesystem time. If it
235 1.2 gdamore * does by more than the threshold,
236 1.2 gdamore * believe the filesystem.
237 1.2 gdamore */
238 1.30 tsutsui printf("WARNING: clock lost %" PRId64 " days\n",
239 1.37 christos deltat / SECS_PER_DAY);
240 1.31 tsutsui badrtc = true;
241 1.24 peter } else {
242 1.30 tsutsui aprint_verbose("WARNING: clock gained %" PRId64
243 1.37 christos " days\n", deltat / SECS_PER_DAY);
244 1.31 tsutsui goodtime = true;
245 1.2 gdamore }
246 1.2 gdamore } else {
247 1.31 tsutsui goodtime = true;
248 1.2 gdamore }
249 1.35 tls
250 1.35 tls rnd_add_data(NULL, &tv, sizeof(tv), 0);
251 1.2 gdamore }
252 1.2 gdamore
253 1.2 gdamore /* if the rtc time is bad, use the filesystem time */
254 1.2 gdamore if (badrtc) {
255 1.2 gdamore if (badbase) {
256 1.2 gdamore printf("WARNING: using default initial time\n");
257 1.2 gdamore } else {
258 1.2 gdamore printf("WARNING: using filesystem time\n");
259 1.2 gdamore }
260 1.2 gdamore tv.tv_sec = base;
261 1.2 gdamore tv.tv_usec = 0;
262 1.1 gdamore }
263 1.2 gdamore
264 1.31 tsutsui timeset = true;
265 1.2 gdamore
266 1.2 gdamore ts.tv_sec = tv.tv_sec;
267 1.2 gdamore ts.tv_nsec = tv.tv_usec * 1000;
268 1.2 gdamore tc_setclock(&ts);
269 1.2 gdamore
270 1.2 gdamore if (waszero || goodtime)
271 1.2 gdamore return;
272 1.2 gdamore
273 1.2 gdamore printf("WARNING: CHECK AND RESET THE DATE!\n");
274 1.1 gdamore }
275 1.1 gdamore
276 1.1 gdamore /*
277 1.44 thorpej * todr_save_systime:
278 1.44 thorpej * Save the current system time back to the TOD clock.
279 1.44 thorpej * Must be called with the TODR lock held.
280 1.1 gdamore */
281 1.1 gdamore void
282 1.44 thorpej todr_save_systime(void)
283 1.1 gdamore {
284 1.26 yamt struct timeval tv;
285 1.1 gdamore
286 1.44 thorpej KASSERT(todr_lock_owned());
287 1.44 thorpej
288 1.2 gdamore /*
289 1.2 gdamore * We might have been called by boot() due to a crash early
290 1.2 gdamore * on. Don't reset the clock chip if we don't know what time
291 1.2 gdamore * it is.
292 1.2 gdamore */
293 1.2 gdamore if (!timeset)
294 1.2 gdamore return;
295 1.2 gdamore
296 1.26 yamt getmicrotime(&tv);
297 1.1 gdamore
298 1.26 yamt if (tv.tv_sec == 0)
299 1.1 gdamore return;
300 1.1 gdamore
301 1.1 gdamore if (todr_handle)
302 1.26 yamt if (todr_settime(todr_handle, &tv) != 0)
303 1.1 gdamore printf("Cannot set TOD clock time\n");
304 1.1 gdamore }
305 1.1 gdamore
306 1.44 thorpej /*
307 1.44 thorpej * inittodr:
308 1.44 thorpej * Legacy wrapper around todr_set_systime().
309 1.44 thorpej */
310 1.44 thorpej void
311 1.44 thorpej inittodr(time_t base)
312 1.44 thorpej {
313 1.44 thorpej
314 1.44 thorpej todr_lock();
315 1.44 thorpej todr_set_systime(base);
316 1.44 thorpej todr_unlock();
317 1.44 thorpej }
318 1.44 thorpej
319 1.44 thorpej /*
320 1.44 thorpej * resettodr:
321 1.44 thorpej * Legacy wrapper around todr_save_systime().
322 1.44 thorpej */
323 1.44 thorpej void
324 1.44 thorpej resettodr(void)
325 1.44 thorpej {
326 1.44 thorpej
327 1.44 thorpej /*
328 1.44 thorpej * If we're shutting down, we don't want to get stuck in case
329 1.44 thorpej * someone was already fiddling with the TOD clock.
330 1.44 thorpej */
331 1.44 thorpej if (shutting_down) {
332 1.44 thorpej if (mutex_tryenter(&todr_mutex) == 0) {
333 1.44 thorpej printf("WARNING: Cannot set TOD clock time (busy)\n");
334 1.44 thorpej return;
335 1.44 thorpej }
336 1.44 thorpej } else {
337 1.44 thorpej todr_lock();
338 1.44 thorpej }
339 1.44 thorpej todr_save_systime();
340 1.44 thorpej todr_unlock();
341 1.44 thorpej }
342 1.44 thorpej
343 1.18 gdamore #ifdef TODR_DEBUG
344 1.18 gdamore static void
345 1.18 gdamore todr_debug(const char *prefix, int rv, struct clock_ymdhms *dt,
346 1.32 tsutsui struct timeval *tvp)
347 1.18 gdamore {
348 1.18 gdamore struct timeval tv_val;
349 1.18 gdamore struct clock_ymdhms dt_val;
350 1.18 gdamore
351 1.18 gdamore if (dt == NULL) {
352 1.18 gdamore clock_secs_to_ymdhms(tvp->tv_sec, &dt_val);
353 1.18 gdamore dt = &dt_val;
354 1.18 gdamore }
355 1.18 gdamore if (tvp == NULL) {
356 1.18 gdamore tvp = &tv_val;
357 1.18 gdamore tvp->tv_sec = clock_ymdhms_to_secs(dt);
358 1.18 gdamore tvp->tv_usec = 0;
359 1.18 gdamore }
360 1.18 gdamore printf("%s: rv = %d\n", prefix, rv);
361 1.18 gdamore printf("%s: rtc_offset = %d\n", prefix, rtc_offset);
362 1.23 tsutsui printf("%s: %4u/%02u/%02u %02u:%02u:%02u, (wday %d) (epoch %u.%06u)\n",
363 1.18 gdamore prefix,
364 1.36 jmcneill (unsigned)dt->dt_year, dt->dt_mon, dt->dt_day,
365 1.18 gdamore dt->dt_hour, dt->dt_min, dt->dt_sec,
366 1.18 gdamore dt->dt_wday, (unsigned)tvp->tv_sec, (unsigned)tvp->tv_usec);
367 1.18 gdamore }
368 1.18 gdamore #else /* !TODR_DEBUG */
369 1.18 gdamore #define todr_debug(prefix, rv, dt, tvp)
370 1.18 gdamore #endif /* TODR_DEBUG */
371 1.18 gdamore
372 1.42 thorpej static int
373 1.43 thorpej todr_wenable(todr_chip_handle_t todr, int onoff)
374 1.43 thorpej {
375 1.43 thorpej
376 1.43 thorpej if (todr->todr_setwen != NULL)
377 1.43 thorpej return todr->todr_setwen(todr, onoff);
378 1.43 thorpej
379 1.43 thorpej return 0;
380 1.43 thorpej }
381 1.43 thorpej
382 1.43 thorpej #define ENABLE_TODR_WRITES() \
383 1.43 thorpej do { \
384 1.43 thorpej if ((rv = todr_wenable(tch, 1)) != 0) { \
385 1.43 thorpej printf("%s: cannot enable TODR writes\n", __func__); \
386 1.43 thorpej return rv; \
387 1.43 thorpej } \
388 1.43 thorpej } while (/*CONSTCOND*/0)
389 1.43 thorpej
390 1.43 thorpej #define DISABLE_TODR_WRITES() \
391 1.43 thorpej do { \
392 1.43 thorpej if (todr_wenable(tch, 0) != 0) \
393 1.43 thorpej printf("%s: WARNING: cannot disable TODR writes\n", \
394 1.43 thorpej __func__); \
395 1.43 thorpej } while (/*CONSTCOND*/0)
396 1.43 thorpej
397 1.43 thorpej static int
398 1.32 tsutsui todr_gettime(todr_chip_handle_t tch, struct timeval *tvp)
399 1.6 gdamore {
400 1.46 thorpej int rv;
401 1.6 gdamore
402 1.43 thorpej /*
403 1.43 thorpej * Write-enable is used even when reading the TODR because
404 1.43 thorpej * writing to registers may be required in order to do so.
405 1.43 thorpej */
406 1.43 thorpej
407 1.17 gdamore if (tch->todr_gettime) {
408 1.43 thorpej ENABLE_TODR_WRITES();
409 1.17 gdamore rv = tch->todr_gettime(tch, tvp);
410 1.43 thorpej DISABLE_TODR_WRITES();
411 1.20 gdamore /*
412 1.20 gdamore * Some unconverted ports have their own references to
413 1.20 gdamore * rtc_offset. A converted port must not do that.
414 1.20 gdamore */
415 1.20 gdamore if (rv == 0)
416 1.20 gdamore tvp->tv_sec += rtc_offset * 60;
417 1.23 tsutsui todr_debug("TODR-GET-SECS", rv, NULL, tvp);
418 1.17 gdamore return rv;
419 1.17 gdamore } else if (tch->todr_gettime_ymdhms) {
420 1.46 thorpej struct clock_ymdhms dt = { 0 };
421 1.43 thorpej ENABLE_TODR_WRITES();
422 1.17 gdamore rv = tch->todr_gettime_ymdhms(tch, &dt);
423 1.43 thorpej DISABLE_TODR_WRITES();
424 1.23 tsutsui todr_debug("TODR-GET-YMDHMS", rv, &dt, NULL);
425 1.17 gdamore if (rv)
426 1.6 gdamore return rv;
427 1.10 gdamore
428 1.6 gdamore /*
429 1.19 gdamore * Simple sanity checks. Note that this includes a
430 1.19 gdamore * value for clocks that can return a leap second.
431 1.19 gdamore * Note that we don't support double leap seconds,
432 1.19 gdamore * since this was apparently an error/misunderstanding
433 1.19 gdamore * on the part of the ISO C committee, and can never
434 1.19 gdamore * actually occur. If your clock issues us a double
435 1.19 gdamore * leap second, it must be broken. Ultimately, you'd
436 1.19 gdamore * have to be trying to read time at precisely that
437 1.19 gdamore * instant to even notice, so even broken clocks will
438 1.19 gdamore * work the vast majority of the time. In such a case
439 1.21 gdamore * it is recommended correction be applied in the
440 1.21 gdamore * clock driver.
441 1.19 gdamore */
442 1.13 tsutsui if (dt.dt_mon < 1 || dt.dt_mon > 12 ||
443 1.13 tsutsui dt.dt_day < 1 || dt.dt_day > 31 ||
444 1.19 gdamore dt.dt_hour > 23 || dt.dt_min > 59 || dt.dt_sec > 60) {
445 1.17 gdamore return EINVAL;
446 1.17 gdamore }
447 1.6 gdamore tvp->tv_sec = clock_ymdhms_to_secs(&dt) + rtc_offset * 60;
448 1.6 gdamore tvp->tv_usec = 0;
449 1.17 gdamore return tvp->tv_sec < 0 ? EINVAL : 0;
450 1.6 gdamore }
451 1.6 gdamore
452 1.17 gdamore return ENXIO;
453 1.6 gdamore }
454 1.6 gdamore
455 1.42 thorpej static int
456 1.32 tsutsui todr_settime(todr_chip_handle_t tch, struct timeval *tvp)
457 1.6 gdamore {
458 1.46 thorpej int rv;
459 1.6 gdamore
460 1.17 gdamore if (tch->todr_settime) {
461 1.46 thorpej struct timeval copy = *tvp;
462 1.20 gdamore copy.tv_sec -= rtc_offset * 60;
463 1.43 thorpej ENABLE_TODR_WRITES();
464 1.20 gdamore rv = tch->todr_settime(tch, ©);
465 1.43 thorpej DISABLE_TODR_WRITES();
466 1.17 gdamore todr_debug("TODR-SET-SECS", rv, NULL, tvp);
467 1.17 gdamore return rv;
468 1.17 gdamore } else if (tch->todr_settime_ymdhms) {
469 1.46 thorpej struct clock_ymdhms dt;
470 1.46 thorpej time_t sec = tvp->tv_sec - rtc_offset * 60;
471 1.10 gdamore if (tvp->tv_usec >= 500000)
472 1.10 gdamore sec++;
473 1.10 gdamore clock_secs_to_ymdhms(sec, &dt);
474 1.43 thorpej ENABLE_TODR_WRITES();
475 1.17 gdamore rv = tch->todr_settime_ymdhms(tch, &dt);
476 1.43 thorpej DISABLE_TODR_WRITES();
477 1.17 gdamore todr_debug("TODR-SET-YMDHMS", rv, &dt, NULL);
478 1.17 gdamore return rv;
479 1.6 gdamore }
480 1.46 thorpej
481 1.46 thorpej return ENXIO;
482 1.6 gdamore }
483