xref: /freebsd/sys/kern/subr_rtc.c (revision 29363fb446372cb3f10bc98664e9767c53fbb457)
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