19454b2d8SWarner Losh /*-
251369649SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni *
4d7f7792eSThomas Moestl * Copyright (c) 1988 University of Utah.
5d7f7792eSThomas Moestl * Copyright (c) 1982, 1990, 1993
6b0fdc837SLawrence Stewart * The Regents of the University of California.
7b0fdc837SLawrence Stewart * Copyright (c) 2011 The FreeBSD Foundation
8b0fdc837SLawrence Stewart * All rights reserved.
9d7f7792eSThomas Moestl *
10d7f7792eSThomas Moestl * This code is derived from software contributed to Berkeley by
11d7f7792eSThomas Moestl * the Systems Programming Group of the University of Utah Computer
12d7f7792eSThomas Moestl * Science Department.
13d7f7792eSThomas Moestl *
14b0fdc837SLawrence Stewart * Portions of this software were developed by Julien Ridoux at the University
15b0fdc837SLawrence Stewart * of Melbourne under sponsorship from the FreeBSD Foundation.
16b0fdc837SLawrence Stewart *
17d7f7792eSThomas Moestl * Redistribution and use in source and binary forms, with or without
18d7f7792eSThomas Moestl * modification, are permitted provided that the following conditions
19d7f7792eSThomas Moestl * are met:
20d7f7792eSThomas Moestl * 1. Redistributions of source code must retain the above copyright
21d7f7792eSThomas Moestl * notice, this list of conditions and the following disclaimer.
22d7f7792eSThomas Moestl * 2. Redistributions in binary form must reproduce the above copyright
23d7f7792eSThomas Moestl * notice, this list of conditions and the following disclaimer in the
24d7f7792eSThomas Moestl * documentation and/or other materials provided with the distribution.
2569a28758SEd Maste * 3. Neither the name of the University nor the names of its contributors
26d7f7792eSThomas Moestl * may be used to endorse or promote products derived from this software
27d7f7792eSThomas Moestl * without specific prior written permission.
28d7f7792eSThomas Moestl *
29d7f7792eSThomas Moestl * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30d7f7792eSThomas Moestl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31d7f7792eSThomas Moestl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32d7f7792eSThomas Moestl * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33d7f7792eSThomas Moestl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34d7f7792eSThomas Moestl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35d7f7792eSThomas Moestl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36d7f7792eSThomas Moestl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37d7f7792eSThomas Moestl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38d7f7792eSThomas Moestl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39d7f7792eSThomas Moestl * SUCH DAMAGE.
40d7f7792eSThomas Moestl *
41d7f7792eSThomas Moestl * from: Utah $Hdr: clock.c 1.18 91/01/21$
42d7f7792eSThomas Moestl * from: NetBSD: clock_subr.c,v 1.6 2001/07/07 17:04:02 thorpej Exp
43d7f7792eSThomas Moestl * and
44d7f7792eSThomas Moestl * from: src/sys/i386/isa/clock.c,v 1.176 2001/09/04
45d7f7792eSThomas Moestl */
46d7f7792eSThomas Moestl
47d7f7792eSThomas Moestl /*
48d7f7792eSThomas Moestl * Helpers for time-of-day clocks. This is useful for architectures that need
49d7f7792eSThomas Moestl * support multiple models of such clocks, and generally serves to make the
50d7f7792eSThomas Moestl * code more machine-independent.
51d7f7792eSThomas Moestl * If the clock in question can also be used as a time counter, the driver
52d7f7792eSThomas Moestl * needs to initiate this.
53d7f7792eSThomas Moestl * This code is not yet used by all architectures.
54d7f7792eSThomas Moestl */
55d7f7792eSThomas Moestl
56677b542eSDavid E. O'Brien #include <sys/cdefs.h>
57b0fdc837SLawrence Stewart #include "opt_ffclock.h"
58b0fdc837SLawrence Stewart
59d7f7792eSThomas Moestl #include <sys/param.h>
60d7f7792eSThomas Moestl #include <sys/systm.h>
61d7f7792eSThomas Moestl #include <sys/kernel.h>
62d7f7792eSThomas Moestl #include <sys/bus.h>
63d7f7792eSThomas Moestl #include <sys/clock.h>
643a0e6f92SKonstantin Belousov #include <sys/lock.h>
65a8d7b9d3SIan Lepore #include <sys/malloc.h>
66a8d7b9d3SIan Lepore #include <sys/sx.h>
67d7f7792eSThomas Moestl #include <sys/sysctl.h>
68a8d7b9d3SIan Lepore #include <sys/taskqueue.h>
69b0fdc837SLawrence Stewart #ifdef FFCLOCK
70b0fdc837SLawrence Stewart #include <sys/timeffc.h>
71b0fdc837SLawrence Stewart #endif
72d7f7792eSThomas Moestl #include <sys/timetc.h>
73d7f7792eSThomas Moestl
74d7f7792eSThomas Moestl #include "clock_if.h"
75d7f7792eSThomas Moestl
7645eee6dbSIan Lepore static int show_io;
7745eee6dbSIan Lepore SYSCTL_INT(_debug, OID_AUTO, clock_show_io, CTLFLAG_RWTUN, &show_io, 0,
7845eee6dbSIan Lepore "Enable debug printing of RTC clock I/O; 1=reads, 2=writes, 3=both.");
7945eee6dbSIan Lepore
80bd54c5acSIan Lepore static int sysctl_clock_do_io(SYSCTL_HANDLER_ARGS);
81*7029da5cSPawel Biernacki SYSCTL_PROC(_debug, OID_AUTO, clock_do_io,
82*7029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 0, sysctl_clock_do_io, "I",
83bd54c5acSIan Lepore "Trigger one-time IO on RTC clocks; 1=read (and discard), 2=write");
84bd54c5acSIan Lepore
859b4a8ab7SPoul-Henning Kamp /* XXX: should be kern. now, it's no longer machdep. */
869b4a8ab7SPoul-Henning Kamp static int disable_rtc_set;
870674ea6fSJung-uk Kim SYSCTL_INT(_machdep, OID_AUTO, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set,
880674ea6fSJung-uk Kim 0, "Disallow adjusting time-of-day clock");
899b4a8ab7SPoul-Henning Kamp
90a8d7b9d3SIan Lepore /*
91a8d7b9d3SIan Lepore * An instance of a realtime clock. A list of these tracks all the registered
92a8d7b9d3SIan Lepore * clocks in the system.
93a8d7b9d3SIan Lepore *
94a8d7b9d3SIan Lepore * The resadj member is used to apply a "resolution adjustment" equal to half
95a8d7b9d3SIan Lepore * the clock's resolution, which is useful mainly on clocks with a whole-second
96a8d7b9d3SIan Lepore * resolution. Because the clock truncates the fractional part, adding half the
97a8d7b9d3SIan Lepore * resolution performs 4/5 rounding. The same adjustment is applied to the
98a8d7b9d3SIan Lepore * times returned from clock_gettime(), because the fraction returned will
99a8d7b9d3SIan Lepore * always be zero, but on average the actual fraction at the time of the call
100a8d7b9d3SIan Lepore * should be about .5.
101a8d7b9d3SIan Lepore */
102a8d7b9d3SIan Lepore struct rtc_instance {
103a8d7b9d3SIan Lepore device_t clockdev;
104a8d7b9d3SIan Lepore int resolution;
105a8d7b9d3SIan Lepore int flags;
106cd9d9e54SIan Lepore u_int schedns;
107a8d7b9d3SIan Lepore struct timespec resadj;
108cd9d9e54SIan Lepore struct timeout_task
109cd9d9e54SIan Lepore stask;
110a8d7b9d3SIan Lepore LIST_ENTRY(rtc_instance)
111a8d7b9d3SIan Lepore rtc_entries;
112a8d7b9d3SIan Lepore };
113a8d7b9d3SIan Lepore
114a8d7b9d3SIan Lepore /*
115a8d7b9d3SIan Lepore * Clocks are updated using a task running on taskqueue_thread.
116a8d7b9d3SIan Lepore */
117a8d7b9d3SIan Lepore static void settime_task_func(void *arg, int pending);
118a8d7b9d3SIan Lepore
119a8d7b9d3SIan Lepore /*
120a8d7b9d3SIan Lepore * Registered clocks are kept in a list which is sorted by resolution; the more
121a8d7b9d3SIan Lepore * accurate clocks get the first shot at providing the time.
122a8d7b9d3SIan Lepore */
123a8d7b9d3SIan Lepore LIST_HEAD(rtc_listhead, rtc_instance);
124a8d7b9d3SIan Lepore static struct rtc_listhead rtc_list = LIST_HEAD_INITIALIZER(rtc_list);
125a8d7b9d3SIan Lepore static struct sx rtc_list_lock;
126a8d7b9d3SIan Lepore SX_SYSINIT(rtc_list_lock_init, &rtc_list_lock, "rtc list");
127a8d7b9d3SIan Lepore
128a8d7b9d3SIan Lepore /*
129cd9d9e54SIan Lepore * On the task thread, invoke the clock_settime() method of the clock. Do so
130cd9d9e54SIan Lepore * holding no locks, so that clock drivers are free to do whatever kind of
131cd9d9e54SIan Lepore * locking or sleeping they need to.
132a8d7b9d3SIan Lepore */
133a8d7b9d3SIan Lepore static void
settime_task_func(void * arg,int pending)134a8d7b9d3SIan Lepore settime_task_func(void *arg, int pending)
135a8d7b9d3SIan Lepore {
136a8d7b9d3SIan Lepore struct timespec ts;
137a8d7b9d3SIan Lepore struct rtc_instance *rtc;
13842038236SKonstantin Belousov int error;
139a8d7b9d3SIan Lepore
140cd9d9e54SIan Lepore rtc = arg;
141a8d7b9d3SIan Lepore if (!(rtc->flags & CLOCKF_SETTIME_NO_TS)) {
142a8d7b9d3SIan Lepore getnanotime(&ts);
143a8d7b9d3SIan Lepore if (!(rtc->flags & CLOCKF_SETTIME_NO_ADJ)) {
144a8d7b9d3SIan Lepore ts.tv_sec -= utc_offset();
1456040822cSAlan Somers timespecadd(&ts, &rtc->resadj, &ts);
146a8d7b9d3SIan Lepore }
147a8d7b9d3SIan Lepore } else {
148a8d7b9d3SIan Lepore ts.tv_sec = 0;
149a8d7b9d3SIan Lepore ts.tv_nsec = 0;
150a8d7b9d3SIan Lepore }
15142038236SKonstantin Belousov error = CLOCK_SETTIME(rtc->clockdev, &ts);
15242038236SKonstantin Belousov if (error != 0 && bootverbose)
15342038236SKonstantin Belousov device_printf(rtc->clockdev, "CLOCK_SETTIME error %d\n", error);
154a8d7b9d3SIan Lepore }
155a8d7b9d3SIan Lepore
15645eee6dbSIan Lepore static void
clock_dbgprint_hdr(device_t dev,int rw)15745eee6dbSIan Lepore clock_dbgprint_hdr(device_t dev, int rw)
15845eee6dbSIan Lepore {
15945eee6dbSIan Lepore struct timespec now;
16045eee6dbSIan Lepore
16145eee6dbSIan Lepore getnanotime(&now);
16245eee6dbSIan Lepore device_printf(dev, "%s at ", (rw & CLOCK_DBG_READ) ? "read " : "write");
16345eee6dbSIan Lepore clock_print_ts(&now, 9);
16445eee6dbSIan Lepore printf(": ");
16545eee6dbSIan Lepore }
16645eee6dbSIan Lepore
16745eee6dbSIan Lepore void
clock_dbgprint_bcd(device_t dev,int rw,const struct bcd_clocktime * bct)16845eee6dbSIan Lepore clock_dbgprint_bcd(device_t dev, int rw, const struct bcd_clocktime *bct)
16945eee6dbSIan Lepore {
17045eee6dbSIan Lepore
17145eee6dbSIan Lepore if (show_io & rw) {
17245eee6dbSIan Lepore clock_dbgprint_hdr(dev, rw);
17345eee6dbSIan Lepore clock_print_bcd(bct, 9);
17445eee6dbSIan Lepore printf("\n");
17545eee6dbSIan Lepore }
17645eee6dbSIan Lepore }
17745eee6dbSIan Lepore
17845eee6dbSIan Lepore void
clock_dbgprint_ct(device_t dev,int rw,const struct clocktime * ct)17945eee6dbSIan Lepore clock_dbgprint_ct(device_t dev, int rw, const struct clocktime *ct)
18045eee6dbSIan Lepore {
18145eee6dbSIan Lepore
18245eee6dbSIan Lepore if (show_io & rw) {
18345eee6dbSIan Lepore clock_dbgprint_hdr(dev, rw);
18445eee6dbSIan Lepore clock_print_ct(ct, 9);
18545eee6dbSIan Lepore printf("\n");
18645eee6dbSIan Lepore }
18745eee6dbSIan Lepore }
18845eee6dbSIan Lepore
18945eee6dbSIan Lepore void
clock_dbgprint_err(device_t dev,int rw,int err)19045eee6dbSIan Lepore clock_dbgprint_err(device_t dev, int rw, int err)
19145eee6dbSIan Lepore {
19245eee6dbSIan Lepore
19345eee6dbSIan Lepore if (show_io & rw) {
19445eee6dbSIan Lepore clock_dbgprint_hdr(dev, rw);
19545eee6dbSIan Lepore printf("error = %d\n", err);
19645eee6dbSIan Lepore }
19745eee6dbSIan Lepore }
19845eee6dbSIan Lepore
19945eee6dbSIan Lepore void
clock_dbgprint_ts(device_t dev,int rw,const struct timespec * ts)20045eee6dbSIan Lepore clock_dbgprint_ts(device_t dev, int rw, const struct timespec *ts)
20145eee6dbSIan Lepore {
20245eee6dbSIan Lepore
20345eee6dbSIan Lepore if (show_io & rw) {
20445eee6dbSIan Lepore clock_dbgprint_hdr(dev, rw);
20545eee6dbSIan Lepore clock_print_ts(ts, 9);
20645eee6dbSIan Lepore printf("\n");
20745eee6dbSIan Lepore }
20845eee6dbSIan Lepore }
20945eee6dbSIan Lepore
210d7f7792eSThomas Moestl void
clock_register_flags(device_t clockdev,long resolution,int flags)211a8d7b9d3SIan Lepore clock_register_flags(device_t clockdev, long resolution, int flags)
212a8d7b9d3SIan Lepore {
213a8d7b9d3SIan Lepore struct rtc_instance *rtc, *newrtc;
214a8d7b9d3SIan Lepore
215a8d7b9d3SIan Lepore newrtc = malloc(sizeof(*newrtc), M_DEVBUF, M_WAITOK);
216a8d7b9d3SIan Lepore newrtc->clockdev = clockdev;
217a8d7b9d3SIan Lepore newrtc->resolution = (int)resolution;
218a8d7b9d3SIan Lepore newrtc->flags = flags;
219cd9d9e54SIan Lepore newrtc->schedns = 0;
220a8d7b9d3SIan Lepore newrtc->resadj.tv_sec = newrtc->resolution / 2 / 1000000;
221a8d7b9d3SIan Lepore newrtc->resadj.tv_nsec = newrtc->resolution / 2 % 1000000 * 1000;
222cd9d9e54SIan Lepore TIMEOUT_TASK_INIT(taskqueue_thread, &newrtc->stask, 0,
223cd9d9e54SIan Lepore settime_task_func, newrtc);
224a8d7b9d3SIan Lepore
225a8d7b9d3SIan Lepore sx_xlock(&rtc_list_lock);
226a8d7b9d3SIan Lepore if (LIST_EMPTY(&rtc_list)) {
227a8d7b9d3SIan Lepore LIST_INSERT_HEAD(&rtc_list, newrtc, rtc_entries);
228a8d7b9d3SIan Lepore } else {
229a8d7b9d3SIan Lepore LIST_FOREACH(rtc, &rtc_list, rtc_entries) {
230a8d7b9d3SIan Lepore if (rtc->resolution > newrtc->resolution) {
231a8d7b9d3SIan Lepore LIST_INSERT_BEFORE(rtc, newrtc, rtc_entries);
232a8d7b9d3SIan Lepore break;
233a8d7b9d3SIan Lepore } else if (LIST_NEXT(rtc, rtc_entries) == NULL) {
234a8d7b9d3SIan Lepore LIST_INSERT_AFTER(rtc, newrtc, rtc_entries);
235a8d7b9d3SIan Lepore break;
236a8d7b9d3SIan Lepore }
237a8d7b9d3SIan Lepore }
238a8d7b9d3SIan Lepore }
239a8d7b9d3SIan Lepore sx_xunlock(&rtc_list_lock);
240a8d7b9d3SIan Lepore
241a8d7b9d3SIan Lepore device_printf(clockdev,
242a8d7b9d3SIan Lepore "registered as a time-of-day clock, resolution %d.%6.6ds\n",
243a8d7b9d3SIan Lepore newrtc->resolution / 1000000, newrtc->resolution % 1000000);
244a8d7b9d3SIan Lepore }
245a8d7b9d3SIan Lepore
246a8d7b9d3SIan Lepore void
clock_register(device_t dev,long res)247a8d7b9d3SIan Lepore clock_register(device_t dev, long res)
248d7f7792eSThomas Moestl {
249d7f7792eSThomas Moestl
250a8d7b9d3SIan Lepore clock_register_flags(dev, res, 0);
2510674ea6fSJung-uk Kim }
252a8d7b9d3SIan Lepore
253a8d7b9d3SIan Lepore void
clock_unregister(device_t clockdev)254a8d7b9d3SIan Lepore clock_unregister(device_t clockdev)
255a8d7b9d3SIan Lepore {
256a8d7b9d3SIan Lepore struct rtc_instance *rtc, *tmp;
257a8d7b9d3SIan Lepore
258a8d7b9d3SIan Lepore sx_xlock(&rtc_list_lock);
259a8d7b9d3SIan Lepore LIST_FOREACH_SAFE(rtc, &rtc_list, rtc_entries, tmp) {
260a8d7b9d3SIan Lepore if (rtc->clockdev == clockdev) {
261a8d7b9d3SIan Lepore LIST_REMOVE(rtc, rtc_entries);
262cd9d9e54SIan Lepore break;
263cd9d9e54SIan Lepore }
264cd9d9e54SIan Lepore }
265cd9d9e54SIan Lepore sx_xunlock(&rtc_list_lock);
266cd9d9e54SIan Lepore if (rtc != NULL) {
267cd9d9e54SIan Lepore taskqueue_cancel_timeout(taskqueue_thread, &rtc->stask, NULL);
268cd9d9e54SIan Lepore taskqueue_drain_timeout(taskqueue_thread, &rtc->stask);
269a8d7b9d3SIan Lepore free(rtc, M_DEVBUF);
270d7f7792eSThomas Moestl }
271a8d7b9d3SIan Lepore }
272cd9d9e54SIan Lepore
273cd9d9e54SIan Lepore void
clock_schedule(device_t clockdev,u_int offsetns)274cd9d9e54SIan Lepore clock_schedule(device_t clockdev, u_int offsetns)
275cd9d9e54SIan Lepore {
276cd9d9e54SIan Lepore struct rtc_instance *rtc;
277cd9d9e54SIan Lepore
278cd9d9e54SIan Lepore sx_xlock(&rtc_list_lock);
279cd9d9e54SIan Lepore LIST_FOREACH(rtc, &rtc_list, rtc_entries) {
280cd9d9e54SIan Lepore if (rtc->clockdev == clockdev) {
281cd9d9e54SIan Lepore rtc->schedns = offsetns;
282cd9d9e54SIan Lepore break;
283cd9d9e54SIan Lepore }
284cd9d9e54SIan Lepore }
285a8d7b9d3SIan Lepore sx_xunlock(&rtc_list_lock);
286d7f7792eSThomas Moestl }
287d7f7792eSThomas Moestl
288bd54c5acSIan Lepore static int
read_clocks(struct timespec * ts,bool debug_read)289bd54c5acSIan Lepore read_clocks(struct timespec *ts, bool debug_read)
290bd54c5acSIan Lepore {
291bd54c5acSIan Lepore struct rtc_instance *rtc;
292bd54c5acSIan Lepore int error;
293bd54c5acSIan Lepore
294bd54c5acSIan Lepore error = ENXIO;
295bd54c5acSIan Lepore sx_xlock(&rtc_list_lock);
296bd54c5acSIan Lepore LIST_FOREACH(rtc, &rtc_list, rtc_entries) {
297bd54c5acSIan Lepore if ((error = CLOCK_GETTIME(rtc->clockdev, ts)) != 0)
298bd54c5acSIan Lepore continue;
299bd54c5acSIan Lepore if (ts->tv_sec < 0 || ts->tv_nsec < 0) {
300bd54c5acSIan Lepore error = EINVAL;
301bd54c5acSIan Lepore continue;
302bd54c5acSIan Lepore }
303bd54c5acSIan Lepore if (!(rtc->flags & CLOCKF_GETTIME_NO_ADJ)) {
3046040822cSAlan Somers timespecadd(ts, &rtc->resadj, ts);
305bd54c5acSIan Lepore ts->tv_sec += utc_offset();
306bd54c5acSIan Lepore }
307bd54c5acSIan Lepore if (!debug_read) {
308bd54c5acSIan Lepore if (bootverbose)
309bd54c5acSIan Lepore device_printf(rtc->clockdev,
310bd54c5acSIan Lepore "providing initial system time\n");
311bd54c5acSIan Lepore break;
312bd54c5acSIan Lepore }
313bd54c5acSIan Lepore }
314bd54c5acSIan Lepore sx_xunlock(&rtc_list_lock);
315bd54c5acSIan Lepore return (error);
316bd54c5acSIan Lepore }
317bd54c5acSIan Lepore
318d7f7792eSThomas Moestl /*
319a8d7b9d3SIan Lepore * Initialize the system time. Must be called from a context which does not
320a8d7b9d3SIan Lepore * restrict any locking or sleeping that clock drivers may need to do.
321a8d7b9d3SIan Lepore *
322a8d7b9d3SIan Lepore * First attempt to get the time from a registered realtime clock. The clocks
323a8d7b9d3SIan Lepore * are queried in order of resolution until one provides the time. If no clock
324a8d7b9d3SIan Lepore * can provide the current time, use the 'base' time provided by the caller, if
325a8d7b9d3SIan Lepore * non-zero. The 'base' time is potentially highly inaccurate, such as the last
326a8d7b9d3SIan Lepore * known good value of the system clock, or even a filesystem last-updated
327a8d7b9d3SIan Lepore * timestamp. It is used to prevent system time from appearing to move
328a8d7b9d3SIan Lepore * backwards in logs.
329d7f7792eSThomas Moestl */
330d7f7792eSThomas Moestl void
inittodr(time_t base)331d7f7792eSThomas Moestl inittodr(time_t base)
332d7f7792eSThomas Moestl {
3330674ea6fSJung-uk Kim struct timespec ts;
334d7f7792eSThomas Moestl int error;
335d7f7792eSThomas Moestl
336bd54c5acSIan Lepore error = read_clocks(&ts, false);
337a8d7b9d3SIan Lepore
338a8d7b9d3SIan Lepore /*
339a8d7b9d3SIan Lepore * Do not report errors from each clock; it is expected that some clocks
340a8d7b9d3SIan Lepore * cannot provide results in some situations. Only report problems when
341a8d7b9d3SIan Lepore * no clocks could provide the time.
342a8d7b9d3SIan Lepore */
343a8d7b9d3SIan Lepore if (error != 0) {
344a8d7b9d3SIan Lepore switch (error) {
345a8d7b9d3SIan Lepore case ENXIO:
346a8d7b9d3SIan Lepore printf("Warning: no time-of-day clock registered, ");
347a8d7b9d3SIan Lepore break;
348a8d7b9d3SIan Lepore case EINVAL:
349a8d7b9d3SIan Lepore printf("Warning: bad time from time-of-day clock, ");
350a8d7b9d3SIan Lepore break;
351a8d7b9d3SIan Lepore default:
352a8d7b9d3SIan Lepore printf("Error reading time-of-day clock (%d), ", error);
353a8d7b9d3SIan Lepore break;
354a8d7b9d3SIan Lepore }
355a8d7b9d3SIan Lepore printf("system time will not be set accurately\n");
356a8d7b9d3SIan Lepore ts.tv_sec = (base > 0) ? base : -1;
357a8d7b9d3SIan Lepore ts.tv_nsec = 0;
358d7f7792eSThomas Moestl }
359d7f7792eSThomas Moestl
360a8d7b9d3SIan Lepore if (ts.tv_sec >= 0) {
361d7f7792eSThomas Moestl tc_setclock(&ts);
362b0fdc837SLawrence Stewart #ifdef FFCLOCK
363b0fdc837SLawrence Stewart ffclock_reset_clock(&ts);
364b0fdc837SLawrence Stewart #endif
365d7f7792eSThomas Moestl }
366d7f7792eSThomas Moestl }
367d7f7792eSThomas Moestl
368d7f7792eSThomas Moestl /*
369a8d7b9d3SIan Lepore * Write system time back to all registered clocks, unless disabled by admin.
370a8d7b9d3SIan Lepore * This can be called from a context that restricts locking and/or sleeping; the
371a8d7b9d3SIan Lepore * actual updating is done asynchronously on a task thread.
372d7f7792eSThomas Moestl */
373d7f7792eSThomas Moestl void
resettodr(void)3749483543dSWarner Losh resettodr(void)
375d7f7792eSThomas Moestl {
376cd9d9e54SIan Lepore struct timespec now;
377cd9d9e54SIan Lepore struct rtc_instance *rtc;
378cd9d9e54SIan Lepore sbintime_t sbt;
379cd9d9e54SIan Lepore long waitns;
380d7f7792eSThomas Moestl
381a8d7b9d3SIan Lepore if (disable_rtc_set)
382d7f7792eSThomas Moestl return;
383d7f7792eSThomas Moestl
384cd9d9e54SIan Lepore sx_xlock(&rtc_list_lock);
385cd9d9e54SIan Lepore LIST_FOREACH(rtc, &rtc_list, rtc_entries) {
386cd9d9e54SIan Lepore if (rtc->schedns != 0) {
387cd9d9e54SIan Lepore getnanotime(&now);
388cd9d9e54SIan Lepore waitns = rtc->schedns - now.tv_nsec;
389cd9d9e54SIan Lepore if (waitns < 0)
390cd9d9e54SIan Lepore waitns += 1000000000;
391cd9d9e54SIan Lepore sbt = nstosbt(waitns);
392cd9d9e54SIan Lepore } else
393cd9d9e54SIan Lepore sbt = 0;
394cd9d9e54SIan Lepore taskqueue_enqueue_timeout_sbt(taskqueue_thread,
395cd9d9e54SIan Lepore &rtc->stask, -sbt, 0, C_PREL(31));
396cd9d9e54SIan Lepore }
397cd9d9e54SIan Lepore sx_xunlock(&rtc_list_lock);
398d7f7792eSThomas Moestl }
399bd54c5acSIan Lepore
400bd54c5acSIan Lepore static int
sysctl_clock_do_io(SYSCTL_HANDLER_ARGS)401bd54c5acSIan Lepore sysctl_clock_do_io(SYSCTL_HANDLER_ARGS)
402bd54c5acSIan Lepore {
403bd54c5acSIan Lepore struct timespec ts_discard;
404bd54c5acSIan Lepore int error, value;
405bd54c5acSIan Lepore
406bd54c5acSIan Lepore value = 0;
407bd54c5acSIan Lepore error = sysctl_handle_int(oidp, &value, 0, req);
408bd54c5acSIan Lepore if (error != 0 || req->newptr == NULL)
409bd54c5acSIan Lepore return (error);
410bd54c5acSIan Lepore
411bd54c5acSIan Lepore switch (value) {
412bd54c5acSIan Lepore case CLOCK_DBG_READ:
413bd54c5acSIan Lepore if (read_clocks(&ts_discard, true) == ENXIO)
414bd54c5acSIan Lepore printf("No registered RTC clocks\n");
415bd54c5acSIan Lepore break;
416bd54c5acSIan Lepore case CLOCK_DBG_WRITE:
417bd54c5acSIan Lepore resettodr();
418bd54c5acSIan Lepore break;
419bd54c5acSIan Lepore default:
420bd54c5acSIan Lepore return (EINVAL);
421bd54c5acSIan Lepore }
422bd54c5acSIan Lepore
423bd54c5acSIan Lepore return (0);
424bd54c5acSIan Lepore }
425