xref: /freebsd/sys/x86/isa/atrtc.c (revision e5ef01427cf92fa9482e9338144886d39c44f3a5)
132580301SAttilio Rao /*-
2ebf5747bSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3ebf5747bSPedro F. Giffuni  *
432580301SAttilio Rao  * Copyright (c) 2008 Poul-Henning Kamp
5875b8844SAlexander Motin  * Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org>
632580301SAttilio Rao  * All rights reserved.
732580301SAttilio Rao  *
832580301SAttilio Rao  * Redistribution and use in source and binary forms, with or without
932580301SAttilio Rao  * modification, are permitted provided that the following conditions
1032580301SAttilio Rao  * are met:
1132580301SAttilio Rao  * 1. Redistributions of source code must retain the above copyright
1232580301SAttilio Rao  *    notice, this list of conditions and the following disclaimer.
1332580301SAttilio Rao  * 2. Redistributions in binary form must reproduce the above copyright
1432580301SAttilio Rao  *    notice, this list of conditions and the following disclaimer in the
1532580301SAttilio Rao  *    documentation and/or other materials provided with the distribution.
1632580301SAttilio Rao  *
1732580301SAttilio Rao  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1832580301SAttilio Rao  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1932580301SAttilio Rao  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2032580301SAttilio Rao  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2132580301SAttilio Rao  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2232580301SAttilio Rao  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2332580301SAttilio Rao  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2432580301SAttilio Rao  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2532580301SAttilio Rao  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2632580301SAttilio Rao  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2732580301SAttilio Rao  * SUCH DAMAGE.
2832580301SAttilio Rao  *
2932580301SAttilio Rao  * $FreeBSD$
3032580301SAttilio Rao  */
3132580301SAttilio Rao 
3232580301SAttilio Rao #include <sys/cdefs.h>
3332580301SAttilio Rao __FBSDID("$FreeBSD$");
3432580301SAttilio Rao 
3532580301SAttilio Rao #include "opt_isa.h"
3632580301SAttilio Rao 
3732580301SAttilio Rao #include <sys/param.h>
3832580301SAttilio Rao #include <sys/systm.h>
3932580301SAttilio Rao #include <sys/bus.h>
4032580301SAttilio Rao #include <sys/clock.h>
4132580301SAttilio Rao #include <sys/lock.h>
4232580301SAttilio Rao #include <sys/mutex.h>
4322e61bc2SWarner Losh #include <sys/kdb.h>
4432580301SAttilio Rao #include <sys/kernel.h>
4532580301SAttilio Rao #include <sys/module.h>
46875b8844SAlexander Motin #include <sys/proc.h>
47875b8844SAlexander Motin #include <sys/rman.h>
48875b8844SAlexander Motin #include <sys/timeet.h>
4932580301SAttilio Rao 
5032580301SAttilio Rao #include <isa/rtc.h>
5132580301SAttilio Rao #ifdef DEV_ISA
5232580301SAttilio Rao #include <isa/isareg.h>
5332580301SAttilio Rao #include <isa/isavar.h>
5432580301SAttilio Rao #endif
55875b8844SAlexander Motin #include <machine/intr_machdep.h>
56875b8844SAlexander Motin #include "clock_if.h"
5732580301SAttilio Rao 
58b524a315SIan Lepore /*
59b524a315SIan Lepore  * clock_lock protects low-level access to individual hardware registers.
60b524a315SIan Lepore  * atrtc_time_lock protects the entire sequence of accessing multiple registers
61b524a315SIan Lepore  * to read or write the date and time.
62b524a315SIan Lepore  */
6322e61bc2SWarner Losh #define	RTC_LOCK	do { if (!kdb_active) mtx_lock_spin(&clock_lock); } while (0)
6422e61bc2SWarner Losh #define	RTC_UNLOCK	do { if (!kdb_active) mtx_unlock_spin(&clock_lock); } while (0)
6532580301SAttilio Rao 
66b524a315SIan Lepore struct mtx atrtc_time_lock;
67b524a315SIan Lepore MTX_SYSINIT(atrtc_lock_init, &atrtc_time_lock, "atrtc", MTX_DEF);
68b524a315SIan Lepore 
69df471e06SAlexander Motin int	atrtcclock_disable = 0;
70df471e06SAlexander Motin 
7132580301SAttilio Rao static	int	rtc_reg = -1;
7232580301SAttilio Rao static	u_char	rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
7332580301SAttilio Rao static	u_char	rtc_statusb = RTCSB_24HR;
7432580301SAttilio Rao 
7532580301SAttilio Rao /*
7632580301SAttilio Rao  * RTC support routines
7732580301SAttilio Rao  */
7832580301SAttilio Rao 
79*e5ef0142SIan Lepore static inline u_char
80*e5ef0142SIan Lepore rtcin_locked(int reg)
8132580301SAttilio Rao {
8232580301SAttilio Rao 
8332580301SAttilio Rao 	if (rtc_reg != reg) {
8432580301SAttilio Rao 		inb(0x84);
8532580301SAttilio Rao 		outb(IO_RTC, reg);
8632580301SAttilio Rao 		rtc_reg = reg;
8732580301SAttilio Rao 		inb(0x84);
8832580301SAttilio Rao 	}
89*e5ef0142SIan Lepore 	return (inb(IO_RTC + 1));
90*e5ef0142SIan Lepore }
91*e5ef0142SIan Lepore 
92*e5ef0142SIan Lepore static inline void
93*e5ef0142SIan Lepore rtcout_locked(int reg, u_char val)
94*e5ef0142SIan Lepore {
95*e5ef0142SIan Lepore 
96*e5ef0142SIan Lepore 	if (rtc_reg != reg) {
97*e5ef0142SIan Lepore 		inb(0x84);
98*e5ef0142SIan Lepore 		outb(IO_RTC, reg);
99*e5ef0142SIan Lepore 		rtc_reg = reg;
100*e5ef0142SIan Lepore 		inb(0x84);
101*e5ef0142SIan Lepore 	}
102*e5ef0142SIan Lepore 	outb(IO_RTC + 1, val);
103*e5ef0142SIan Lepore 	inb(0x84);
104*e5ef0142SIan Lepore }
105*e5ef0142SIan Lepore 
106*e5ef0142SIan Lepore int
107*e5ef0142SIan Lepore rtcin(int reg)
108*e5ef0142SIan Lepore {
109*e5ef0142SIan Lepore 	u_char val;
110*e5ef0142SIan Lepore 
111*e5ef0142SIan Lepore 	RTC_LOCK;
112*e5ef0142SIan Lepore 	val = rtcin_locked(reg);
11332580301SAttilio Rao 	RTC_UNLOCK;
11432580301SAttilio Rao 	return (val);
11532580301SAttilio Rao }
11632580301SAttilio Rao 
11732580301SAttilio Rao void
11832580301SAttilio Rao writertc(int reg, u_char val)
11932580301SAttilio Rao {
12032580301SAttilio Rao 
12132580301SAttilio Rao 	RTC_LOCK;
122*e5ef0142SIan Lepore 	rtcout_locked(reg, val);
12332580301SAttilio Rao 	RTC_UNLOCK;
12432580301SAttilio Rao }
12532580301SAttilio Rao 
126875b8844SAlexander Motin static void
12732580301SAttilio Rao atrtc_start(void)
12832580301SAttilio Rao {
12932580301SAttilio Rao 
13032580301SAttilio Rao 	writertc(RTC_STATUSA, rtc_statusa);
13132580301SAttilio Rao 	writertc(RTC_STATUSB, RTCSB_24HR);
13232580301SAttilio Rao }
13332580301SAttilio Rao 
134875b8844SAlexander Motin static void
13532580301SAttilio Rao atrtc_rate(unsigned rate)
13632580301SAttilio Rao {
13732580301SAttilio Rao 
13832580301SAttilio Rao 	rtc_statusa = RTCSA_DIVIDER | rate;
13932580301SAttilio Rao 	writertc(RTC_STATUSA, rtc_statusa);
14032580301SAttilio Rao }
14132580301SAttilio Rao 
142875b8844SAlexander Motin static void
14332580301SAttilio Rao atrtc_enable_intr(void)
14432580301SAttilio Rao {
14532580301SAttilio Rao 
14632580301SAttilio Rao 	rtc_statusb |= RTCSB_PINTR;
14732580301SAttilio Rao 	writertc(RTC_STATUSB, rtc_statusb);
14832580301SAttilio Rao 	rtcin(RTC_INTR);
14932580301SAttilio Rao }
15032580301SAttilio Rao 
151875b8844SAlexander Motin static void
152875b8844SAlexander Motin atrtc_disable_intr(void)
153875b8844SAlexander Motin {
154875b8844SAlexander Motin 
155875b8844SAlexander Motin 	rtc_statusb &= ~RTCSB_PINTR;
156875b8844SAlexander Motin 	writertc(RTC_STATUSB, rtc_statusb);
157875b8844SAlexander Motin 	rtcin(RTC_INTR);
158875b8844SAlexander Motin }
159875b8844SAlexander Motin 
16032580301SAttilio Rao void
16132580301SAttilio Rao atrtc_restore(void)
16232580301SAttilio Rao {
16332580301SAttilio Rao 
16432580301SAttilio Rao 	/* Restore all of the RTC's "status" (actually, control) registers. */
16532580301SAttilio Rao 	rtcin(RTC_STATUSA);	/* dummy to get rtc_reg set */
16632580301SAttilio Rao 	writertc(RTC_STATUSB, RTCSB_24HR);
16732580301SAttilio Rao 	writertc(RTC_STATUSA, rtc_statusa);
16832580301SAttilio Rao 	writertc(RTC_STATUSB, rtc_statusb);
16932580301SAttilio Rao 	rtcin(RTC_INTR);
17032580301SAttilio Rao }
17132580301SAttilio Rao 
172c82d887dSIan Lepore static void
173f65466ebSRoger Pau Monné atrtc_set(struct timespec *ts)
174f65466ebSRoger Pau Monné {
1757c63e501SIan Lepore 	struct bcd_clocktime ct;
176f65466ebSRoger Pau Monné 
1777c63e501SIan Lepore 	clock_ts_to_bcd(ts, &ct, false);
178f65466ebSRoger Pau Monné 
179b524a315SIan Lepore 	mtx_lock(&atrtc_time_lock);
180*e5ef0142SIan Lepore 	RTC_LOCK;
181b524a315SIan Lepore 
182f65466ebSRoger Pau Monné 	/* Disable RTC updates and interrupts. */
183*e5ef0142SIan Lepore 	rtcout_locked(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR);
184f65466ebSRoger Pau Monné 
185*e5ef0142SIan Lepore 	/* Write all the time registers. */
186*e5ef0142SIan Lepore 	rtcout_locked(RTC_SEC,   ct.sec);
187*e5ef0142SIan Lepore 	rtcout_locked(RTC_MIN,   ct.min);
188*e5ef0142SIan Lepore 	rtcout_locked(RTC_HRS,   ct.hour);
189*e5ef0142SIan Lepore 	rtcout_locked(RTC_WDAY,  ct.dow + 1);
190*e5ef0142SIan Lepore 	rtcout_locked(RTC_DAY,   ct.day);
191*e5ef0142SIan Lepore 	rtcout_locked(RTC_MONTH, ct.mon);
192*e5ef0142SIan Lepore 	rtcout_locked(RTC_YEAR,  ct.year & 0xff);
193f65466ebSRoger Pau Monné #ifdef USE_RTC_CENTURY
194*e5ef0142SIan Lepore 	rtcout_locked(RTC_CENTURY, ct.year >> 8);
195f65466ebSRoger Pau Monné #endif
196f65466ebSRoger Pau Monné 
197f65466ebSRoger Pau Monné 	/* Re-enable RTC updates and interrupts. */
198*e5ef0142SIan Lepore 	rtcout_locked(RTC_STATUSB, rtc_statusb);
199*e5ef0142SIan Lepore 	rtcin_locked(RTC_INTR);
200b524a315SIan Lepore 
201*e5ef0142SIan Lepore 	RTC_UNLOCK;
202b524a315SIan Lepore 	mtx_unlock(&atrtc_time_lock);
203f65466ebSRoger Pau Monné }
204f65466ebSRoger Pau Monné 
20532580301SAttilio Rao /**********************************************************************
20632580301SAttilio Rao  * RTC driver for subr_rtc
20732580301SAttilio Rao  */
20832580301SAttilio Rao 
20932580301SAttilio Rao struct atrtc_softc {
21032580301SAttilio Rao 	int port_rid, intr_rid;
21132580301SAttilio Rao 	struct resource *port_res;
21232580301SAttilio Rao 	struct resource *intr_res;
213875b8844SAlexander Motin 	void *intr_handler;
214875b8844SAlexander Motin 	struct eventtimer et;
21532580301SAttilio Rao };
21632580301SAttilio Rao 
217875b8844SAlexander Motin static int
218fdc5dd2dSAlexander Motin rtc_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
219875b8844SAlexander Motin {
220875b8844SAlexander Motin 
221fdc5dd2dSAlexander Motin 	atrtc_rate(max(fls(period + (period >> 1)) - 17, 1));
222875b8844SAlexander Motin 	atrtc_enable_intr();
223875b8844SAlexander Motin 	return (0);
224875b8844SAlexander Motin }
225875b8844SAlexander Motin 
226875b8844SAlexander Motin static int
227875b8844SAlexander Motin rtc_stop(struct eventtimer *et)
228875b8844SAlexander Motin {
229875b8844SAlexander Motin 
230875b8844SAlexander Motin 	atrtc_disable_intr();
231875b8844SAlexander Motin 	return (0);
232875b8844SAlexander Motin }
233875b8844SAlexander Motin 
234875b8844SAlexander Motin /*
235875b8844SAlexander Motin  * This routine receives statistical clock interrupts from the RTC.
236875b8844SAlexander Motin  * As explained above, these occur at 128 interrupts per second.
237875b8844SAlexander Motin  * When profiling, we receive interrupts at a rate of 1024 Hz.
238875b8844SAlexander Motin  *
239875b8844SAlexander Motin  * This does not actually add as much overhead as it sounds, because
240875b8844SAlexander Motin  * when the statistical clock is active, the hardclock driver no longer
241875b8844SAlexander Motin  * needs to keep (inaccurate) statistics on its own.  This decouples
242875b8844SAlexander Motin  * statistics gathering from scheduling interrupts.
243875b8844SAlexander Motin  *
244875b8844SAlexander Motin  * The RTC chip requires that we read status register C (RTC_INTR)
245875b8844SAlexander Motin  * to acknowledge an interrupt, before it will generate the next one.
246875b8844SAlexander Motin  * Under high interrupt load, rtcintr() can be indefinitely delayed and
247875b8844SAlexander Motin  * the clock can tick immediately after the read from RTC_INTR.  In this
248875b8844SAlexander Motin  * case, the mc146818A interrupt signal will not drop for long enough
249875b8844SAlexander Motin  * to register with the 8259 PIC.  If an interrupt is missed, the stat
250875b8844SAlexander Motin  * clock will halt, considerably degrading system performance.  This is
251875b8844SAlexander Motin  * why we use 'while' rather than a more straightforward 'if' below.
252875b8844SAlexander Motin  * Stat clock ticks can still be lost, causing minor loss of accuracy
253875b8844SAlexander Motin  * in the statistics, but the stat clock will no longer stop.
254875b8844SAlexander Motin  */
255875b8844SAlexander Motin static int
256875b8844SAlexander Motin rtc_intr(void *arg)
257875b8844SAlexander Motin {
258875b8844SAlexander Motin 	struct atrtc_softc *sc = (struct atrtc_softc *)arg;
259875b8844SAlexander Motin 	int flag = 0;
260875b8844SAlexander Motin 
261875b8844SAlexander Motin 	while (rtcin(RTC_INTR) & RTCIR_PERIOD) {
262875b8844SAlexander Motin 		flag = 1;
2638a687080SAlexander Motin 		if (sc->et.et_active)
2648a687080SAlexander Motin 			sc->et.et_event_cb(&sc->et, sc->et.et_arg);
265875b8844SAlexander Motin 	}
266875b8844SAlexander Motin 	return(flag ? FILTER_HANDLED : FILTER_STRAY);
267875b8844SAlexander Motin }
268875b8844SAlexander Motin 
26932580301SAttilio Rao /*
27032580301SAttilio Rao  * Attach to the ISA PnP descriptors for the timer and realtime clock.
27132580301SAttilio Rao  */
27232580301SAttilio Rao static struct isa_pnp_id atrtc_ids[] = {
27332580301SAttilio Rao 	{ 0x000bd041 /* PNP0B00 */, "AT realtime clock" },
27432580301SAttilio Rao 	{ 0 }
27532580301SAttilio Rao };
27632580301SAttilio Rao 
27732580301SAttilio Rao static int
27832580301SAttilio Rao atrtc_probe(device_t dev)
27932580301SAttilio Rao {
28032580301SAttilio Rao 	int result;
28132580301SAttilio Rao 
28232580301SAttilio Rao 	result = ISA_PNP_PROBE(device_get_parent(dev), dev, atrtc_ids);
283a7d6757cSAlexander Motin 	/* ENOENT means no PnP-ID, device is hinted. */
284a7d6757cSAlexander Motin 	if (result == ENOENT) {
285a7d6757cSAlexander Motin 		device_set_desc(dev, "AT realtime clock");
28632580301SAttilio Rao 		return (BUS_PROBE_LOW_PRIORITY);
28732580301SAttilio Rao 	}
288a7d6757cSAlexander Motin 	return (result);
289a7d6757cSAlexander Motin }
29032580301SAttilio Rao 
29132580301SAttilio Rao static int
29232580301SAttilio Rao atrtc_attach(device_t dev)
29332580301SAttilio Rao {
29432580301SAttilio Rao 	struct atrtc_softc *sc;
2952dd1bdf1SJustin Hibbits 	rman_res_t s;
2962b89f1fcSAndriy Gapon 	int i;
29732580301SAttilio Rao 
29832580301SAttilio Rao 	sc = device_get_softc(dev);
299686b1e6bSJohn Baldwin 	sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->port_rid,
300686b1e6bSJohn Baldwin 	    IO_RTC, IO_RTC + 1, 2, RF_ACTIVE);
301686b1e6bSJohn Baldwin 	if (sc->port_res == NULL)
30232580301SAttilio Rao 		device_printf(dev, "Warning: Couldn't map I/O.\n");
303875b8844SAlexander Motin 	atrtc_start();
30432580301SAttilio Rao 	clock_register(dev, 1000000);
305875b8844SAlexander Motin 	bzero(&sc->et, sizeof(struct eventtimer));
30649ed68bbSAlexander Motin 	if (!atrtcclock_disable &&
307875b8844SAlexander Motin 	    (resource_int_value(device_get_name(dev), device_get_unit(dev),
308875b8844SAlexander Motin 	     "clock", &i) != 0 || i != 0)) {
3096019ba4eSAlexander Motin 		sc->intr_rid = 0;
31091751b1aSAlexander Motin 		while (bus_get_resource(dev, SYS_RES_IRQ, sc->intr_rid,
31191751b1aSAlexander Motin 		    &s, NULL) == 0 && s != 8)
31291751b1aSAlexander Motin 			sc->intr_rid++;
313686b1e6bSJohn Baldwin 		sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ,
314686b1e6bSJohn Baldwin 		    &sc->intr_rid, 8, 8, 1, RF_ACTIVE);
315686b1e6bSJohn Baldwin 		if (sc->intr_res == NULL) {
31649ed68bbSAlexander Motin 			device_printf(dev, "Can't map interrupt.\n");
31749ed68bbSAlexander Motin 			return (0);
318686b1e6bSJohn Baldwin 		} else if ((bus_setup_intr(dev, sc->intr_res, INTR_TYPE_CLK,
319686b1e6bSJohn Baldwin 		    rtc_intr, NULL, sc, &sc->intr_handler))) {
32049ed68bbSAlexander Motin 			device_printf(dev, "Can't setup interrupt.\n");
32149ed68bbSAlexander Motin 			return (0);
32249ed68bbSAlexander Motin 		} else {
32349ed68bbSAlexander Motin 			/* Bind IRQ to BSP to avoid live migration. */
32449ed68bbSAlexander Motin 			bus_bind_intr(dev, sc->intr_res, 0);
32549ed68bbSAlexander Motin 		}
326875b8844SAlexander Motin 		sc->et.et_name = "RTC";
32751636352SAlexander Motin 		sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_POW2DIV;
328875b8844SAlexander Motin 		sc->et.et_quality = 0;
329875b8844SAlexander Motin 		sc->et.et_frequency = 32768;
330fdc5dd2dSAlexander Motin 		sc->et.et_min_period = 0x00080000;
331fdc5dd2dSAlexander Motin 		sc->et.et_max_period = 0x80000000;
332875b8844SAlexander Motin 		sc->et.et_start = rtc_start;
333875b8844SAlexander Motin 		sc->et.et_stop = rtc_stop;
334875b8844SAlexander Motin 		sc->et.et_priv = dev;
335875b8844SAlexander Motin 		et_register(&sc->et);
336875b8844SAlexander Motin 	}
33732580301SAttilio Rao 	return(0);
33832580301SAttilio Rao }
33932580301SAttilio Rao 
34032580301SAttilio Rao static int
34132580301SAttilio Rao atrtc_resume(device_t dev)
34232580301SAttilio Rao {
34332580301SAttilio Rao 
34432580301SAttilio Rao 	atrtc_restore();
34532580301SAttilio Rao 	return(0);
34632580301SAttilio Rao }
34732580301SAttilio Rao 
34832580301SAttilio Rao static int
34932580301SAttilio Rao atrtc_settime(device_t dev __unused, struct timespec *ts)
35032580301SAttilio Rao {
35132580301SAttilio Rao 
352f65466ebSRoger Pau Monné 	atrtc_set(ts);
35332580301SAttilio Rao 	return (0);
35432580301SAttilio Rao }
35532580301SAttilio Rao 
35632580301SAttilio Rao static int
35732580301SAttilio Rao atrtc_gettime(device_t dev, struct timespec *ts)
35832580301SAttilio Rao {
3597c63e501SIan Lepore 	struct bcd_clocktime ct;
36032580301SAttilio Rao 
36132580301SAttilio Rao 	/* Look if we have a RTC present and the time is valid */
36232580301SAttilio Rao 	if (!(rtcin(RTC_STATUSD) & RTCSD_PWR)) {
36332580301SAttilio Rao 		device_printf(dev, "WARNING: Battery failure indication\n");
36432580301SAttilio Rao 		return (EINVAL);
36532580301SAttilio Rao 	}
36632580301SAttilio Rao 
36722e61bc2SWarner Losh 	/*
36822e61bc2SWarner Losh 	 * wait for time update to complete
36922e61bc2SWarner Losh 	 * If RTCSA_TUP is zero, we have at least 244us before next update.
37022e61bc2SWarner Losh 	 * This is fast enough on most hardware, but a refinement would be
37122e61bc2SWarner Losh 	 * to make sure that no more than 240us pass after we start reading,
37222e61bc2SWarner Losh 	 * and try again if so.
37322e61bc2SWarner Losh 	 */
374b524a315SIan Lepore 	mtx_lock(&atrtc_time_lock);
3757fe82634SWarner Losh 	while (rtcin(RTC_STATUSA) & RTCSA_TUP)
3767fe82634SWarner Losh 		continue;
37722e61bc2SWarner Losh 	critical_enter();
378*e5ef0142SIan Lepore 	RTC_LOCK;
379*e5ef0142SIan Lepore 	ct.sec  = rtcin_locked(RTC_SEC);
380*e5ef0142SIan Lepore 	ct.min  = rtcin_locked(RTC_MIN);
381*e5ef0142SIan Lepore 	ct.hour = rtcin_locked(RTC_HRS);
382*e5ef0142SIan Lepore 	ct.day  = rtcin_locked(RTC_DAY);
383*e5ef0142SIan Lepore 	ct.mon  = rtcin_locked(RTC_MONTH);
384*e5ef0142SIan Lepore 	ct.year = rtcin_locked(RTC_YEAR);
38532580301SAttilio Rao #ifdef USE_RTC_CENTURY
386*e5ef0142SIan Lepore 	ct.year |= rtcin_locked(RTC_CENTURY) << 8;
38732580301SAttilio Rao #endif
388*e5ef0142SIan Lepore 	RTC_UNLOCK;
38922e61bc2SWarner Losh 	critical_exit();
390b524a315SIan Lepore 	mtx_unlock(&atrtc_time_lock);
3917c63e501SIan Lepore 	/* dow is unused in timespec conversion and we have no nsec info. */
3927c63e501SIan Lepore 	ct.dow  = 0;
3937c63e501SIan Lepore 	ct.nsec = 0;
3947c63e501SIan Lepore 	return (clock_bcd_to_ts(&ct, ts, false));
39532580301SAttilio Rao }
39632580301SAttilio Rao 
39732580301SAttilio Rao static device_method_t atrtc_methods[] = {
39832580301SAttilio Rao 	/* Device interface */
39932580301SAttilio Rao 	DEVMETHOD(device_probe,		atrtc_probe),
40032580301SAttilio Rao 	DEVMETHOD(device_attach,	atrtc_attach),
40132580301SAttilio Rao 	DEVMETHOD(device_detach,	bus_generic_detach),
40232580301SAttilio Rao 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
40332580301SAttilio Rao 	DEVMETHOD(device_suspend,	bus_generic_suspend),
40432580301SAttilio Rao 		/* XXX stop statclock? */
40532580301SAttilio Rao 	DEVMETHOD(device_resume,	atrtc_resume),
40632580301SAttilio Rao 
40732580301SAttilio Rao 	/* clock interface */
40832580301SAttilio Rao 	DEVMETHOD(clock_gettime,	atrtc_gettime),
40932580301SAttilio Rao 	DEVMETHOD(clock_settime,	atrtc_settime),
41032580301SAttilio Rao 
41132580301SAttilio Rao 	{ 0, 0 }
41232580301SAttilio Rao };
41332580301SAttilio Rao 
41432580301SAttilio Rao static driver_t atrtc_driver = {
41532580301SAttilio Rao 	"atrtc",
41632580301SAttilio Rao 	atrtc_methods,
41732580301SAttilio Rao 	sizeof(struct atrtc_softc),
41832580301SAttilio Rao };
41932580301SAttilio Rao 
42032580301SAttilio Rao static devclass_t atrtc_devclass;
42132580301SAttilio Rao 
42232580301SAttilio Rao DRIVER_MODULE(atrtc, isa, atrtc_driver, atrtc_devclass, 0, 0);
42332580301SAttilio Rao DRIVER_MODULE(atrtc, acpi, atrtc_driver, atrtc_devclass, 0, 0);
42432580301SAttilio Rao 
42532580301SAttilio Rao #include "opt_ddb.h"
42632580301SAttilio Rao #ifdef DDB
42732580301SAttilio Rao #include <ddb/ddb.h>
42832580301SAttilio Rao 
42932580301SAttilio Rao DB_SHOW_COMMAND(rtc, rtc)
43032580301SAttilio Rao {
43132580301SAttilio Rao 	printf("%02x/%02x/%02x %02x:%02x:%02x, A = %02x, B = %02x, C = %02x\n",
43232580301SAttilio Rao 		rtcin(RTC_YEAR), rtcin(RTC_MONTH), rtcin(RTC_DAY),
43332580301SAttilio Rao 		rtcin(RTC_HRS), rtcin(RTC_MIN), rtcin(RTC_SEC),
43432580301SAttilio Rao 		rtcin(RTC_STATUSA), rtcin(RTC_STATUSB), rtcin(RTC_INTR));
43532580301SAttilio Rao }
43632580301SAttilio Rao #endif /* DDB */
437