kern_todr.c revision 1.46.10.1 1 1.46.10.1 thorpej /* $NetBSD: kern_todr.c,v 1.46.10.1 2021/04/03 21:45:00 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.10.1 thorpej __KERNEL_RCSID(0, "$NetBSD: kern_todr.c,v 1.46.10.1 2021/04/03 21:45:00 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.46.10.1 thorpej /* The minimum reasonable RTC date before preposterousness */
94 1.46.10.1 thorpej #define PREPOSTEROUS_YEARS (2021 - POSIX_BASE_YEAR)
95 1.46.10.1 thorpej
96 1.44 thorpej /*
97 1.44 thorpej * todr_init:
98 1.44 thorpej * Initialize TOD clock data.
99 1.44 thorpej */
100 1.44 thorpej void
101 1.44 thorpej todr_init(void)
102 1.44 thorpej {
103 1.44 thorpej
104 1.44 thorpej mutex_init(&todr_mutex, MUTEX_DEFAULT, IPL_NONE);
105 1.44 thorpej todr_initialized = true;
106 1.44 thorpej }
107 1.44 thorpej
108 1.44 thorpej /*
109 1.44 thorpej * todr_lock:
110 1.44 thorpej * Acquire the TODR lock.
111 1.44 thorpej */
112 1.44 thorpej void
113 1.44 thorpej todr_lock(void)
114 1.44 thorpej {
115 1.44 thorpej
116 1.44 thorpej mutex_enter(&todr_mutex);
117 1.44 thorpej }
118 1.44 thorpej
119 1.44 thorpej /*
120 1.44 thorpej * todr_unlock:
121 1.44 thorpej * Release the TODR lock.
122 1.44 thorpej */
123 1.44 thorpej void
124 1.44 thorpej todr_unlock(void)
125 1.44 thorpej {
126 1.44 thorpej
127 1.44 thorpej mutex_exit(&todr_mutex);
128 1.44 thorpej }
129 1.44 thorpej
130 1.44 thorpej /*
131 1.44 thorpej * todr_lock_owned:
132 1.44 thorpej * Return true if the current thread owns the TODR lock.
133 1.44 thorpej * This is to be used by diagnostic assertions only.
134 1.44 thorpej */
135 1.44 thorpej bool
136 1.44 thorpej todr_lock_owned(void)
137 1.44 thorpej {
138 1.44 thorpej
139 1.44 thorpej return mutex_owned(&todr_mutex) ? true : false;
140 1.44 thorpej }
141 1.1 gdamore
142 1.1 gdamore /*
143 1.44 thorpej * todr_attach:
144 1.44 thorpej * Attach the clock device to todr_handle.
145 1.1 gdamore */
146 1.1 gdamore void
147 1.1 gdamore todr_attach(todr_chip_handle_t todr)
148 1.1 gdamore {
149 1.1 gdamore
150 1.44 thorpej /*
151 1.44 thorpej * todr_init() is called very early in main(), but this is
152 1.44 thorpej * here to catch a case where todr_attach() is called before
153 1.44 thorpej * main().
154 1.44 thorpej */
155 1.44 thorpej KASSERT(todr_initialized);
156 1.44 thorpej
157 1.44 thorpej todr_lock();
158 1.24 peter if (todr_handle) {
159 1.44 thorpej todr_unlock();
160 1.24 peter printf("todr_attach: TOD already configured\n");
161 1.1 gdamore return;
162 1.1 gdamore }
163 1.24 peter todr_handle = todr;
164 1.44 thorpej todr_unlock();
165 1.1 gdamore }
166 1.1 gdamore
167 1.31 tsutsui static bool timeset = false;
168 1.2 gdamore
169 1.1 gdamore /*
170 1.44 thorpej * todr_set_systime:
171 1.44 thorpej * Set up the system's time. The "base" argument is a best-guess
172 1.44 thorpej * close-enough value to use if the TOD clock is unavailable or
173 1.44 thorpej * contains garbage. Must be called with the TODR lock held.
174 1.1 gdamore */
175 1.1 gdamore void
176 1.44 thorpej todr_set_systime(time_t base)
177 1.1 gdamore {
178 1.31 tsutsui bool badbase = false;
179 1.31 tsutsui bool waszero = (base == 0);
180 1.31 tsutsui bool goodtime = false;
181 1.31 tsutsui bool badrtc = false;
182 1.1 gdamore struct timespec ts;
183 1.2 gdamore struct timeval tv;
184 1.1 gdamore
185 1.44 thorpej KASSERT(todr_lock_owned());
186 1.44 thorpej
187 1.35 tls rnd_add_data(NULL, &base, sizeof(base), 0);
188 1.35 tls
189 1.37 christos if (base < 5 * SECS_PER_COMMON_YEAR) {
190 1.4 gdamore struct clock_ymdhms basedate;
191 1.4 gdamore
192 1.1 gdamore /*
193 1.1 gdamore * If base is 0, assume filesystem time is just unknown
194 1.2 gdamore * instead of preposterous. Don't bark.
195 1.1 gdamore */
196 1.1 gdamore if (base != 0)
197 1.1 gdamore printf("WARNING: preposterous time in file system\n");
198 1.1 gdamore /* not going to use it anyway, if the chip is readable */
199 1.33 tsutsui basedate.dt_year = 2010;
200 1.4 gdamore basedate.dt_mon = 1;
201 1.4 gdamore basedate.dt_day = 1;
202 1.4 gdamore basedate.dt_hour = 12;
203 1.4 gdamore basedate.dt_min = 0;
204 1.4 gdamore basedate.dt_sec = 0;
205 1.4 gdamore base = clock_ymdhms_to_secs(&basedate);
206 1.31 tsutsui badbase = true;
207 1.1 gdamore }
208 1.1 gdamore
209 1.5 matt /*
210 1.5 matt * Some ports need to be supplied base in order to fabricate a time_t.
211 1.5 matt */
212 1.22 gdamore if (todr_handle)
213 1.22 gdamore todr_handle->base_time = base;
214 1.5 matt
215 1.40 maxv memset(&tv, 0, sizeof(tv));
216 1.40 maxv
217 1.1 gdamore if ((todr_handle == NULL) ||
218 1.2 gdamore (todr_gettime(todr_handle, &tv) != 0) ||
219 1.46.10.1 thorpej (tv.tv_sec < (PREPOSTEROUS_YEARS * SECS_PER_COMMON_YEAR))) {
220 1.1 gdamore
221 1.24 peter if (todr_handle != NULL)
222 1.2 gdamore printf("WARNING: preposterous TOD clock time\n");
223 1.1 gdamore else
224 1.2 gdamore printf("WARNING: no TOD clock present\n");
225 1.31 tsutsui badrtc = true;
226 1.1 gdamore } else {
227 1.30 tsutsui time_t deltat = tv.tv_sec - base;
228 1.1 gdamore
229 1.1 gdamore if (deltat < 0)
230 1.1 gdamore deltat = -deltat;
231 1.2 gdamore
232 1.37 christos if (!badbase && deltat >= 2 * SECS_PER_DAY) {
233 1.2 gdamore
234 1.2 gdamore if (tv.tv_sec < base) {
235 1.2 gdamore /*
236 1.2 gdamore * The clock should never go backwards
237 1.2 gdamore * relative to filesystem time. If it
238 1.2 gdamore * does by more than the threshold,
239 1.2 gdamore * believe the filesystem.
240 1.2 gdamore */
241 1.30 tsutsui printf("WARNING: clock lost %" PRId64 " days\n",
242 1.37 christos deltat / SECS_PER_DAY);
243 1.31 tsutsui badrtc = true;
244 1.24 peter } else {
245 1.30 tsutsui aprint_verbose("WARNING: clock gained %" PRId64
246 1.37 christos " days\n", deltat / SECS_PER_DAY);
247 1.31 tsutsui goodtime = true;
248 1.2 gdamore }
249 1.2 gdamore } else {
250 1.31 tsutsui goodtime = true;
251 1.2 gdamore }
252 1.35 tls
253 1.35 tls rnd_add_data(NULL, &tv, sizeof(tv), 0);
254 1.2 gdamore }
255 1.2 gdamore
256 1.2 gdamore /* if the rtc time is bad, use the filesystem time */
257 1.2 gdamore if (badrtc) {
258 1.2 gdamore if (badbase) {
259 1.2 gdamore printf("WARNING: using default initial time\n");
260 1.2 gdamore } else {
261 1.2 gdamore printf("WARNING: using filesystem time\n");
262 1.2 gdamore }
263 1.2 gdamore tv.tv_sec = base;
264 1.2 gdamore tv.tv_usec = 0;
265 1.1 gdamore }
266 1.2 gdamore
267 1.31 tsutsui timeset = true;
268 1.2 gdamore
269 1.2 gdamore ts.tv_sec = tv.tv_sec;
270 1.2 gdamore ts.tv_nsec = tv.tv_usec * 1000;
271 1.2 gdamore tc_setclock(&ts);
272 1.2 gdamore
273 1.2 gdamore if (waszero || goodtime)
274 1.2 gdamore return;
275 1.2 gdamore
276 1.2 gdamore printf("WARNING: CHECK AND RESET THE DATE!\n");
277 1.1 gdamore }
278 1.1 gdamore
279 1.1 gdamore /*
280 1.44 thorpej * todr_save_systime:
281 1.44 thorpej * Save the current system time back to the TOD clock.
282 1.44 thorpej * Must be called with the TODR lock held.
283 1.1 gdamore */
284 1.1 gdamore void
285 1.44 thorpej todr_save_systime(void)
286 1.1 gdamore {
287 1.26 yamt struct timeval tv;
288 1.1 gdamore
289 1.44 thorpej KASSERT(todr_lock_owned());
290 1.44 thorpej
291 1.2 gdamore /*
292 1.2 gdamore * We might have been called by boot() due to a crash early
293 1.2 gdamore * on. Don't reset the clock chip if we don't know what time
294 1.2 gdamore * it is.
295 1.2 gdamore */
296 1.2 gdamore if (!timeset)
297 1.2 gdamore return;
298 1.2 gdamore
299 1.26 yamt getmicrotime(&tv);
300 1.1 gdamore
301 1.26 yamt if (tv.tv_sec == 0)
302 1.1 gdamore return;
303 1.1 gdamore
304 1.1 gdamore if (todr_handle)
305 1.26 yamt if (todr_settime(todr_handle, &tv) != 0)
306 1.1 gdamore printf("Cannot set TOD clock time\n");
307 1.1 gdamore }
308 1.1 gdamore
309 1.44 thorpej /*
310 1.44 thorpej * inittodr:
311 1.44 thorpej * Legacy wrapper around todr_set_systime().
312 1.44 thorpej */
313 1.44 thorpej void
314 1.44 thorpej inittodr(time_t base)
315 1.44 thorpej {
316 1.44 thorpej
317 1.44 thorpej todr_lock();
318 1.44 thorpej todr_set_systime(base);
319 1.44 thorpej todr_unlock();
320 1.44 thorpej }
321 1.44 thorpej
322 1.44 thorpej /*
323 1.44 thorpej * resettodr:
324 1.44 thorpej * Legacy wrapper around todr_save_systime().
325 1.44 thorpej */
326 1.44 thorpej void
327 1.44 thorpej resettodr(void)
328 1.44 thorpej {
329 1.44 thorpej
330 1.44 thorpej /*
331 1.44 thorpej * If we're shutting down, we don't want to get stuck in case
332 1.44 thorpej * someone was already fiddling with the TOD clock.
333 1.44 thorpej */
334 1.44 thorpej if (shutting_down) {
335 1.44 thorpej if (mutex_tryenter(&todr_mutex) == 0) {
336 1.44 thorpej printf("WARNING: Cannot set TOD clock time (busy)\n");
337 1.44 thorpej return;
338 1.44 thorpej }
339 1.44 thorpej } else {
340 1.44 thorpej todr_lock();
341 1.44 thorpej }
342 1.44 thorpej todr_save_systime();
343 1.44 thorpej todr_unlock();
344 1.44 thorpej }
345 1.44 thorpej
346 1.18 gdamore #ifdef TODR_DEBUG
347 1.18 gdamore static void
348 1.18 gdamore todr_debug(const char *prefix, int rv, struct clock_ymdhms *dt,
349 1.32 tsutsui struct timeval *tvp)
350 1.18 gdamore {
351 1.18 gdamore struct timeval tv_val;
352 1.18 gdamore struct clock_ymdhms dt_val;
353 1.18 gdamore
354 1.18 gdamore if (dt == NULL) {
355 1.18 gdamore clock_secs_to_ymdhms(tvp->tv_sec, &dt_val);
356 1.18 gdamore dt = &dt_val;
357 1.18 gdamore }
358 1.18 gdamore if (tvp == NULL) {
359 1.18 gdamore tvp = &tv_val;
360 1.18 gdamore tvp->tv_sec = clock_ymdhms_to_secs(dt);
361 1.18 gdamore tvp->tv_usec = 0;
362 1.18 gdamore }
363 1.18 gdamore printf("%s: rv = %d\n", prefix, rv);
364 1.18 gdamore printf("%s: rtc_offset = %d\n", prefix, rtc_offset);
365 1.23 tsutsui printf("%s: %4u/%02u/%02u %02u:%02u:%02u, (wday %d) (epoch %u.%06u)\n",
366 1.18 gdamore prefix,
367 1.36 jmcneill (unsigned)dt->dt_year, dt->dt_mon, dt->dt_day,
368 1.18 gdamore dt->dt_hour, dt->dt_min, dt->dt_sec,
369 1.18 gdamore dt->dt_wday, (unsigned)tvp->tv_sec, (unsigned)tvp->tv_usec);
370 1.18 gdamore }
371 1.18 gdamore #else /* !TODR_DEBUG */
372 1.18 gdamore #define todr_debug(prefix, rv, dt, tvp)
373 1.18 gdamore #endif /* TODR_DEBUG */
374 1.18 gdamore
375 1.42 thorpej static int
376 1.43 thorpej todr_wenable(todr_chip_handle_t todr, int onoff)
377 1.43 thorpej {
378 1.43 thorpej
379 1.43 thorpej if (todr->todr_setwen != NULL)
380 1.43 thorpej return todr->todr_setwen(todr, onoff);
381 1.43 thorpej
382 1.43 thorpej return 0;
383 1.43 thorpej }
384 1.43 thorpej
385 1.43 thorpej #define ENABLE_TODR_WRITES() \
386 1.43 thorpej do { \
387 1.43 thorpej if ((rv = todr_wenable(tch, 1)) != 0) { \
388 1.43 thorpej printf("%s: cannot enable TODR writes\n", __func__); \
389 1.43 thorpej return rv; \
390 1.43 thorpej } \
391 1.43 thorpej } while (/*CONSTCOND*/0)
392 1.43 thorpej
393 1.43 thorpej #define DISABLE_TODR_WRITES() \
394 1.43 thorpej do { \
395 1.43 thorpej if (todr_wenable(tch, 0) != 0) \
396 1.43 thorpej printf("%s: WARNING: cannot disable TODR writes\n", \
397 1.43 thorpej __func__); \
398 1.43 thorpej } while (/*CONSTCOND*/0)
399 1.43 thorpej
400 1.43 thorpej static int
401 1.32 tsutsui todr_gettime(todr_chip_handle_t tch, struct timeval *tvp)
402 1.6 gdamore {
403 1.46 thorpej int rv;
404 1.6 gdamore
405 1.43 thorpej /*
406 1.43 thorpej * Write-enable is used even when reading the TODR because
407 1.43 thorpej * writing to registers may be required in order to do so.
408 1.43 thorpej */
409 1.43 thorpej
410 1.17 gdamore if (tch->todr_gettime) {
411 1.43 thorpej ENABLE_TODR_WRITES();
412 1.17 gdamore rv = tch->todr_gettime(tch, tvp);
413 1.43 thorpej DISABLE_TODR_WRITES();
414 1.20 gdamore /*
415 1.20 gdamore * Some unconverted ports have their own references to
416 1.20 gdamore * rtc_offset. A converted port must not do that.
417 1.20 gdamore */
418 1.20 gdamore if (rv == 0)
419 1.20 gdamore tvp->tv_sec += rtc_offset * 60;
420 1.23 tsutsui todr_debug("TODR-GET-SECS", rv, NULL, tvp);
421 1.17 gdamore return rv;
422 1.17 gdamore } else if (tch->todr_gettime_ymdhms) {
423 1.46 thorpej struct clock_ymdhms dt = { 0 };
424 1.43 thorpej ENABLE_TODR_WRITES();
425 1.17 gdamore rv = tch->todr_gettime_ymdhms(tch, &dt);
426 1.43 thorpej DISABLE_TODR_WRITES();
427 1.23 tsutsui todr_debug("TODR-GET-YMDHMS", rv, &dt, NULL);
428 1.17 gdamore if (rv)
429 1.6 gdamore return rv;
430 1.10 gdamore
431 1.6 gdamore /*
432 1.19 gdamore * Simple sanity checks. Note that this includes a
433 1.19 gdamore * value for clocks that can return a leap second.
434 1.19 gdamore * Note that we don't support double leap seconds,
435 1.19 gdamore * since this was apparently an error/misunderstanding
436 1.19 gdamore * on the part of the ISO C committee, and can never
437 1.19 gdamore * actually occur. If your clock issues us a double
438 1.19 gdamore * leap second, it must be broken. Ultimately, you'd
439 1.19 gdamore * have to be trying to read time at precisely that
440 1.19 gdamore * instant to even notice, so even broken clocks will
441 1.19 gdamore * work the vast majority of the time. In such a case
442 1.21 gdamore * it is recommended correction be applied in the
443 1.21 gdamore * clock driver.
444 1.19 gdamore */
445 1.13 tsutsui if (dt.dt_mon < 1 || dt.dt_mon > 12 ||
446 1.13 tsutsui dt.dt_day < 1 || dt.dt_day > 31 ||
447 1.19 gdamore dt.dt_hour > 23 || dt.dt_min > 59 || dt.dt_sec > 60) {
448 1.17 gdamore return EINVAL;
449 1.17 gdamore }
450 1.6 gdamore tvp->tv_sec = clock_ymdhms_to_secs(&dt) + rtc_offset * 60;
451 1.6 gdamore tvp->tv_usec = 0;
452 1.17 gdamore return tvp->tv_sec < 0 ? EINVAL : 0;
453 1.6 gdamore }
454 1.6 gdamore
455 1.17 gdamore return ENXIO;
456 1.6 gdamore }
457 1.6 gdamore
458 1.42 thorpej static int
459 1.32 tsutsui todr_settime(todr_chip_handle_t tch, struct timeval *tvp)
460 1.6 gdamore {
461 1.46 thorpej int rv;
462 1.6 gdamore
463 1.17 gdamore if (tch->todr_settime) {
464 1.46 thorpej struct timeval copy = *tvp;
465 1.20 gdamore copy.tv_sec -= rtc_offset * 60;
466 1.43 thorpej ENABLE_TODR_WRITES();
467 1.20 gdamore rv = tch->todr_settime(tch, ©);
468 1.43 thorpej DISABLE_TODR_WRITES();
469 1.17 gdamore todr_debug("TODR-SET-SECS", rv, NULL, tvp);
470 1.17 gdamore return rv;
471 1.17 gdamore } else if (tch->todr_settime_ymdhms) {
472 1.46 thorpej struct clock_ymdhms dt;
473 1.46 thorpej time_t sec = tvp->tv_sec - rtc_offset * 60;
474 1.10 gdamore if (tvp->tv_usec >= 500000)
475 1.10 gdamore sec++;
476 1.10 gdamore clock_secs_to_ymdhms(sec, &dt);
477 1.43 thorpej ENABLE_TODR_WRITES();
478 1.17 gdamore rv = tch->todr_settime_ymdhms(tch, &dt);
479 1.43 thorpej DISABLE_TODR_WRITES();
480 1.17 gdamore todr_debug("TODR-SET-YMDHMS", rv, &dt, NULL);
481 1.17 gdamore return rv;
482 1.6 gdamore }
483 1.46 thorpej
484 1.46 thorpej return ENXIO;
485 1.6 gdamore }
486