xref: /freebsd/sys/x86/isa/atrtc.c (revision df615735960370255d3acc4ac2a6f4fd297b7461)
132580301SAttilio Rao /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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 
3032580301SAttilio Rao #include <sys/cdefs.h>
312b4ee398SVladimir Kondratyev #include "opt_acpi.h"
3232580301SAttilio Rao #include "opt_isa.h"
3332580301SAttilio Rao 
3432580301SAttilio Rao #include <sys/param.h>
3532580301SAttilio Rao #include <sys/systm.h>
3632580301SAttilio Rao #include <sys/bus.h>
3732580301SAttilio Rao #include <sys/clock.h>
3832580301SAttilio Rao #include <sys/lock.h>
3932580301SAttilio Rao #include <sys/mutex.h>
4022e61bc2SWarner Losh #include <sys/kdb.h>
4132580301SAttilio Rao #include <sys/kernel.h>
4232580301SAttilio Rao #include <sys/module.h>
43875b8844SAlexander Motin #include <sys/proc.h>
44875b8844SAlexander Motin #include <sys/rman.h>
45c63dda46SMark Johnston #include <sys/sysctl.h>
46875b8844SAlexander Motin #include <sys/timeet.h>
4732580301SAttilio Rao 
4832580301SAttilio Rao #include <isa/rtc.h>
4932580301SAttilio Rao #ifdef DEV_ISA
5032580301SAttilio Rao #include <isa/isareg.h>
5132580301SAttilio Rao #include <isa/isavar.h>
5232580301SAttilio Rao #endif
53875b8844SAlexander Motin #include <machine/intr_machdep.h>
54875b8844SAlexander Motin #include "clock_if.h"
552b4ee398SVladimir Kondratyev #ifdef DEV_ACPI
564a6d4e7bSRoger Pau Monné #include <contrib/dev/acpica/include/acpi.h>
572b4ee398SVladimir Kondratyev #include <contrib/dev/acpica/include/accommon.h>
582b4ee398SVladimir Kondratyev #include <dev/acpica/acpivar.h>
597705dd4dSKonstantin Belousov #include <machine/md_var.h>
602b4ee398SVladimir Kondratyev #endif
614a6d4e7bSRoger Pau Monné 
6255f1ca20SCorvin Köhne /* tunable to detect a power loss of the rtc */
6355f1ca20SCorvin Köhne static bool atrtc_power_lost = false;
6455f1ca20SCorvin Köhne SYSCTL_BOOL(_machdep, OID_AUTO, atrtc_power_lost, CTLFLAG_RD, &atrtc_power_lost,
65*38cc6c3dSGordon Bergling     false, "RTC lost power on last power cycle (probably caused by an empty cmos battery)");
6655f1ca20SCorvin Köhne 
67b524a315SIan Lepore /*
68c7053bbeSIan Lepore  * atrtc_lock protects low-level access to individual hardware registers.
69c7053bbeSIan Lepore  * atrtc_time_lock protects the entire sequence of accessing multiple registers
70c7053bbeSIan Lepore  * to read or write the date and time.
71b524a315SIan Lepore  */
72c7053bbeSIan Lepore static struct mtx atrtc_lock;
734b502f00SIan Lepore MTX_SYSINIT(atrtc_lock_init, &atrtc_lock, "atrtc", MTX_SPIN);
7432580301SAttilio Rao 
754a6d4e7bSRoger Pau Monné /* Force RTC enabled/disabled. */
764a6d4e7bSRoger Pau Monné static int atrtc_enabled = -1;
774a6d4e7bSRoger Pau Monné TUNABLE_INT("hw.atrtc.enabled", &atrtc_enabled);
784a6d4e7bSRoger Pau Monné 
79c7053bbeSIan Lepore struct mtx atrtc_time_lock;
8022b3d71eSIan Lepore MTX_SYSINIT(atrtc_time_lock_init, &atrtc_time_lock, "atrtc_time", MTX_DEF);
81c7053bbeSIan Lepore 
82df471e06SAlexander Motin int	atrtcclock_disable = 0;
83df471e06SAlexander Motin 
84e1ef6c0eSAustin Zhang static	int	rtc_century = 0;
8532580301SAttilio Rao static	int	rtc_reg = -1;
8632580301SAttilio Rao static	u_char	rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
8732580301SAttilio Rao static	u_char	rtc_statusb = RTCSB_24HR;
8832580301SAttilio Rao 
8976cefcd8SVladimir Kondratyev #ifdef DEV_ACPI
9076cefcd8SVladimir Kondratyev #define	_COMPONENT	ACPI_TIMER
9176cefcd8SVladimir Kondratyev ACPI_MODULE_NAME("ATRTC")
9276cefcd8SVladimir Kondratyev #endif
9376cefcd8SVladimir Kondratyev 
9432580301SAttilio Rao /*
9532580301SAttilio Rao  * RTC support routines
9632580301SAttilio Rao  */
9732580301SAttilio Rao 
98e5ef0142SIan Lepore static inline u_char
rtcin_locked(int reg)99e5ef0142SIan Lepore rtcin_locked(int reg)
10032580301SAttilio Rao {
10132580301SAttilio Rao 
10232580301SAttilio Rao 	if (rtc_reg != reg) {
10332580301SAttilio Rao 		inb(0x84);
10432580301SAttilio Rao 		outb(IO_RTC, reg);
10532580301SAttilio Rao 		rtc_reg = reg;
10632580301SAttilio Rao 		inb(0x84);
10732580301SAttilio Rao 	}
108e5ef0142SIan Lepore 	return (inb(IO_RTC + 1));
109e5ef0142SIan Lepore }
110e5ef0142SIan Lepore 
111e5ef0142SIan Lepore static inline void
rtcout_locked(int reg,u_char val)112e5ef0142SIan Lepore rtcout_locked(int reg, u_char val)
113e5ef0142SIan Lepore {
114e5ef0142SIan Lepore 
115e5ef0142SIan Lepore 	if (rtc_reg != reg) {
116e5ef0142SIan Lepore 		inb(0x84);
117e5ef0142SIan Lepore 		outb(IO_RTC, reg);
118e5ef0142SIan Lepore 		rtc_reg = reg;
119e5ef0142SIan Lepore 		inb(0x84);
120e5ef0142SIan Lepore 	}
121e5ef0142SIan Lepore 	outb(IO_RTC + 1, val);
122e5ef0142SIan Lepore 	inb(0x84);
123e5ef0142SIan Lepore }
124e5ef0142SIan Lepore 
125e5ef0142SIan Lepore int
rtcin(int reg)126e5ef0142SIan Lepore rtcin(int reg)
127e5ef0142SIan Lepore {
128e5ef0142SIan Lepore 	u_char val;
129e5ef0142SIan Lepore 
1308355852fSIan Lepore 	mtx_lock_spin(&atrtc_lock);
131e5ef0142SIan Lepore 	val = rtcin_locked(reg);
1328355852fSIan Lepore 	mtx_unlock_spin(&atrtc_lock);
13332580301SAttilio Rao 	return (val);
13432580301SAttilio Rao }
13532580301SAttilio Rao 
13632580301SAttilio Rao void
writertc(int reg,u_char val)13732580301SAttilio Rao writertc(int reg, u_char val)
13832580301SAttilio Rao {
13932580301SAttilio Rao 
1408355852fSIan Lepore 	mtx_lock_spin(&atrtc_lock);
141e5ef0142SIan Lepore 	rtcout_locked(reg, val);
1428355852fSIan Lepore 	mtx_unlock_spin(&atrtc_lock);
14332580301SAttilio Rao }
14432580301SAttilio Rao 
145875b8844SAlexander Motin static void
atrtc_start(void)14632580301SAttilio Rao atrtc_start(void)
14732580301SAttilio Rao {
14832580301SAttilio Rao 
14967e2a292SIan Lepore 	mtx_lock_spin(&atrtc_lock);
15067e2a292SIan Lepore 	rtcout_locked(RTC_STATUSA, rtc_statusa);
15167e2a292SIan Lepore 	rtcout_locked(RTC_STATUSB, RTCSB_24HR);
15267e2a292SIan Lepore 	mtx_unlock_spin(&atrtc_lock);
15332580301SAttilio Rao }
15432580301SAttilio Rao 
155875b8844SAlexander Motin static void
atrtc_rate(unsigned rate)15632580301SAttilio Rao atrtc_rate(unsigned rate)
15732580301SAttilio Rao {
15832580301SAttilio Rao 
15932580301SAttilio Rao 	rtc_statusa = RTCSA_DIVIDER | rate;
16032580301SAttilio Rao 	writertc(RTC_STATUSA, rtc_statusa);
16132580301SAttilio Rao }
16232580301SAttilio Rao 
163875b8844SAlexander Motin static void
atrtc_enable_intr(void)16432580301SAttilio Rao atrtc_enable_intr(void)
16532580301SAttilio Rao {
16632580301SAttilio Rao 
16732580301SAttilio Rao 	rtc_statusb |= RTCSB_PINTR;
16867e2a292SIan Lepore 	mtx_lock_spin(&atrtc_lock);
16967e2a292SIan Lepore 	rtcout_locked(RTC_STATUSB, rtc_statusb);
17067e2a292SIan Lepore 	rtcin_locked(RTC_INTR);
17167e2a292SIan Lepore 	mtx_unlock_spin(&atrtc_lock);
17232580301SAttilio Rao }
17332580301SAttilio Rao 
174875b8844SAlexander Motin static void
atrtc_disable_intr(void)175875b8844SAlexander Motin atrtc_disable_intr(void)
176875b8844SAlexander Motin {
177875b8844SAlexander Motin 
178875b8844SAlexander Motin 	rtc_statusb &= ~RTCSB_PINTR;
17967e2a292SIan Lepore 	mtx_lock_spin(&atrtc_lock);
18067e2a292SIan Lepore 	rtcout_locked(RTC_STATUSB, rtc_statusb);
18167e2a292SIan Lepore 	rtcin_locked(RTC_INTR);
18267e2a292SIan Lepore 	mtx_unlock_spin(&atrtc_lock);
183875b8844SAlexander Motin }
184875b8844SAlexander Motin 
18532580301SAttilio Rao void
atrtc_restore(void)18632580301SAttilio Rao atrtc_restore(void)
18732580301SAttilio Rao {
18832580301SAttilio Rao 
18932580301SAttilio Rao 	/* Restore all of the RTC's "status" (actually, control) registers. */
19067e2a292SIan Lepore 	mtx_lock_spin(&atrtc_lock);
19167e2a292SIan Lepore 	rtcin_locked(RTC_STATUSA);	/* dummy to get rtc_reg set */
19267e2a292SIan Lepore 	rtcout_locked(RTC_STATUSB, RTCSB_24HR);
19367e2a292SIan Lepore 	rtcout_locked(RTC_STATUSA, rtc_statusa);
19467e2a292SIan Lepore 	rtcout_locked(RTC_STATUSB, rtc_statusb);
19567e2a292SIan Lepore 	rtcin_locked(RTC_INTR);
19667e2a292SIan Lepore 	mtx_unlock_spin(&atrtc_lock);
19732580301SAttilio Rao }
19832580301SAttilio Rao 
19932580301SAttilio Rao /**********************************************************************
20032580301SAttilio Rao  * RTC driver for subr_rtc
20132580301SAttilio Rao  */
20232580301SAttilio Rao 
20332580301SAttilio Rao struct atrtc_softc {
20432580301SAttilio Rao 	int port_rid, intr_rid;
20532580301SAttilio Rao 	struct resource *port_res;
20632580301SAttilio Rao 	struct resource *intr_res;
207875b8844SAlexander Motin 	void *intr_handler;
208875b8844SAlexander Motin 	struct eventtimer et;
2092b4ee398SVladimir Kondratyev #ifdef DEV_ACPI
2102b4ee398SVladimir Kondratyev 	ACPI_HANDLE acpi_handle;
2112b4ee398SVladimir Kondratyev #endif
21232580301SAttilio Rao };
21332580301SAttilio Rao 
214875b8844SAlexander Motin static int
rtc_start(struct eventtimer * et,sbintime_t first,sbintime_t period)215fdc5dd2dSAlexander Motin rtc_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
216875b8844SAlexander Motin {
217875b8844SAlexander Motin 
218fdc5dd2dSAlexander Motin 	atrtc_rate(max(fls(period + (period >> 1)) - 17, 1));
219875b8844SAlexander Motin 	atrtc_enable_intr();
220875b8844SAlexander Motin 	return (0);
221875b8844SAlexander Motin }
222875b8844SAlexander Motin 
223875b8844SAlexander Motin static int
rtc_stop(struct eventtimer * et)224875b8844SAlexander Motin rtc_stop(struct eventtimer *et)
225875b8844SAlexander Motin {
226875b8844SAlexander Motin 
227875b8844SAlexander Motin 	atrtc_disable_intr();
228875b8844SAlexander Motin 	return (0);
229875b8844SAlexander Motin }
230875b8844SAlexander Motin 
231875b8844SAlexander Motin /*
232875b8844SAlexander Motin  * This routine receives statistical clock interrupts from the RTC.
233875b8844SAlexander Motin  * As explained above, these occur at 128 interrupts per second.
234875b8844SAlexander Motin  * When profiling, we receive interrupts at a rate of 1024 Hz.
235875b8844SAlexander Motin  *
236875b8844SAlexander Motin  * This does not actually add as much overhead as it sounds, because
237875b8844SAlexander Motin  * when the statistical clock is active, the hardclock driver no longer
238875b8844SAlexander Motin  * needs to keep (inaccurate) statistics on its own.  This decouples
239875b8844SAlexander Motin  * statistics gathering from scheduling interrupts.
240875b8844SAlexander Motin  *
241875b8844SAlexander Motin  * The RTC chip requires that we read status register C (RTC_INTR)
242875b8844SAlexander Motin  * to acknowledge an interrupt, before it will generate the next one.
243875b8844SAlexander Motin  * Under high interrupt load, rtcintr() can be indefinitely delayed and
244875b8844SAlexander Motin  * the clock can tick immediately after the read from RTC_INTR.  In this
245875b8844SAlexander Motin  * case, the mc146818A interrupt signal will not drop for long enough
246875b8844SAlexander Motin  * to register with the 8259 PIC.  If an interrupt is missed, the stat
247875b8844SAlexander Motin  * clock will halt, considerably degrading system performance.  This is
248875b8844SAlexander Motin  * why we use 'while' rather than a more straightforward 'if' below.
249875b8844SAlexander Motin  * Stat clock ticks can still be lost, causing minor loss of accuracy
250875b8844SAlexander Motin  * in the statistics, but the stat clock will no longer stop.
251875b8844SAlexander Motin  */
252875b8844SAlexander Motin static int
rtc_intr(void * arg)253875b8844SAlexander Motin rtc_intr(void *arg)
254875b8844SAlexander Motin {
255875b8844SAlexander Motin 	struct atrtc_softc *sc = (struct atrtc_softc *)arg;
256875b8844SAlexander Motin 	int flag = 0;
257875b8844SAlexander Motin 
258875b8844SAlexander Motin 	while (rtcin(RTC_INTR) & RTCIR_PERIOD) {
259875b8844SAlexander Motin 		flag = 1;
2608a687080SAlexander Motin 		if (sc->et.et_active)
2618a687080SAlexander Motin 			sc->et.et_event_cb(&sc->et, sc->et.et_arg);
262875b8844SAlexander Motin 	}
263875b8844SAlexander Motin 	return(flag ? FILTER_HANDLED : FILTER_STRAY);
264875b8844SAlexander Motin }
265875b8844SAlexander Motin 
2662b4ee398SVladimir Kondratyev #ifdef DEV_ACPI
2672b4ee398SVladimir Kondratyev /*
2682b4ee398SVladimir Kondratyev  *  ACPI RTC CMOS address space handler
2692b4ee398SVladimir Kondratyev  */
2702b4ee398SVladimir Kondratyev #define	ATRTC_LAST_REG	0x40
2712b4ee398SVladimir Kondratyev 
2722b4ee398SVladimir Kondratyev static void
rtcin_region(int reg,void * buf,int len)2732b4ee398SVladimir Kondratyev rtcin_region(int reg, void *buf, int len)
2742b4ee398SVladimir Kondratyev {
2752b4ee398SVladimir Kondratyev 	u_char *ptr = buf;
2762b4ee398SVladimir Kondratyev 
2772b4ee398SVladimir Kondratyev 	/* Drop lock after each IO as intr and settime have greater priority */
2782b4ee398SVladimir Kondratyev 	while (len-- > 0)
2792b4ee398SVladimir Kondratyev 		*ptr++ = rtcin(reg++) & 0xff;
2802b4ee398SVladimir Kondratyev }
2812b4ee398SVladimir Kondratyev 
2822b4ee398SVladimir Kondratyev static void
rtcout_region(int reg,const void * buf,int len)2832b4ee398SVladimir Kondratyev rtcout_region(int reg, const void *buf, int len)
2842b4ee398SVladimir Kondratyev {
2852b4ee398SVladimir Kondratyev 	const u_char *ptr = buf;
2862b4ee398SVladimir Kondratyev 
2872b4ee398SVladimir Kondratyev 	while (len-- > 0)
2882b4ee398SVladimir Kondratyev 		writertc(reg++, *ptr++);
2892b4ee398SVladimir Kondratyev }
2902b4ee398SVladimir Kondratyev 
2912b4ee398SVladimir Kondratyev static bool
atrtc_check_cmos_access(bool is_read,ACPI_PHYSICAL_ADDRESS addr,UINT32 len)2922b4ee398SVladimir Kondratyev atrtc_check_cmos_access(bool is_read, ACPI_PHYSICAL_ADDRESS addr, UINT32 len)
2932b4ee398SVladimir Kondratyev {
2942b4ee398SVladimir Kondratyev 
2952b4ee398SVladimir Kondratyev 	/* Block address space wrapping on out-of-bound access */
2962b4ee398SVladimir Kondratyev 	if (addr >= ATRTC_LAST_REG || addr + len > ATRTC_LAST_REG)
2972b4ee398SVladimir Kondratyev 		return (false);
2982b4ee398SVladimir Kondratyev 
2992b4ee398SVladimir Kondratyev 	if (is_read) {
3002b4ee398SVladimir Kondratyev 		/* Reading 0x0C will muck with interrupts */
3012b4ee398SVladimir Kondratyev 		if (addr <= RTC_INTR && addr + len > RTC_INTR)
3022b4ee398SVladimir Kondratyev 			return (false);
3032b4ee398SVladimir Kondratyev 	} else {
3042b4ee398SVladimir Kondratyev 		/*
3052b4ee398SVladimir Kondratyev 		 * Allow single-byte writes to alarm registers and
3062b4ee398SVladimir Kondratyev 		 * multi-byte writes to addr >= 0x30, else deny.
3072b4ee398SVladimir Kondratyev 		 */
3082b4ee398SVladimir Kondratyev 		if (!((len == 1 && (addr == RTC_SECALRM ||
3092b4ee398SVladimir Kondratyev 				    addr == RTC_MINALRM ||
3102b4ee398SVladimir Kondratyev 				    addr == RTC_HRSALRM)) ||
3112b4ee398SVladimir Kondratyev 		      addr >= 0x30))
3122b4ee398SVladimir Kondratyev 			return (false);
3132b4ee398SVladimir Kondratyev 	}
3142b4ee398SVladimir Kondratyev 	return (true);
3152b4ee398SVladimir Kondratyev }
3162b4ee398SVladimir Kondratyev 
3172b4ee398SVladimir Kondratyev static ACPI_STATUS
atrtc_acpi_cmos_handler(UINT32 func,ACPI_PHYSICAL_ADDRESS addr,UINT32 bitwidth,UINT64 * value,void * context,void * region_context)3182b4ee398SVladimir Kondratyev atrtc_acpi_cmos_handler(UINT32 func, ACPI_PHYSICAL_ADDRESS addr,
3192b4ee398SVladimir Kondratyev     UINT32 bitwidth, UINT64 *value, void *context, void *region_context)
3202b4ee398SVladimir Kondratyev {
3212b4ee398SVladimir Kondratyev 	device_t dev = context;
3222b4ee398SVladimir Kondratyev 	UINT32 bytewidth = howmany(bitwidth, 8);
3232b4ee398SVladimir Kondratyev 	bool is_read = func == ACPI_READ;
3242b4ee398SVladimir Kondratyev 
3252b4ee398SVladimir Kondratyev 	/* ACPICA is very verbose on CMOS handler failures, so we, too */
3262b4ee398SVladimir Kondratyev #define	CMOS_HANDLER_ERR(fmt, ...) \
3272b4ee398SVladimir Kondratyev 	device_printf(dev, "ACPI [SystemCMOS] handler: " fmt, ##__VA_ARGS__)
3282b4ee398SVladimir Kondratyev 
3292b4ee398SVladimir Kondratyev 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
3302b4ee398SVladimir Kondratyev 
3312b4ee398SVladimir Kondratyev 	if (value == NULL) {
3322b4ee398SVladimir Kondratyev 		CMOS_HANDLER_ERR("NULL parameter\n");
3332b4ee398SVladimir Kondratyev 		return (AE_BAD_PARAMETER);
3342b4ee398SVladimir Kondratyev 	}
3352b4ee398SVladimir Kondratyev 	if (bitwidth == 0 || (bitwidth & 0x07) != 0) {
3362b4ee398SVladimir Kondratyev 		CMOS_HANDLER_ERR("Invalid bitwidth: %u\n", bitwidth);
3372b4ee398SVladimir Kondratyev 		return (AE_BAD_PARAMETER);
3382b4ee398SVladimir Kondratyev 	}
3392b4ee398SVladimir Kondratyev 	if (!atrtc_check_cmos_access(is_read, addr, bytewidth)) {
3402b4ee398SVladimir Kondratyev 		CMOS_HANDLER_ERR("%s access rejected: addr=%#04jx, len=%u\n",
3412b4ee398SVladimir Kondratyev 		    is_read ? "Read" : "Write", (uintmax_t)addr, bytewidth);
3422b4ee398SVladimir Kondratyev 		return (AE_BAD_PARAMETER);
3432b4ee398SVladimir Kondratyev 	}
3442b4ee398SVladimir Kondratyev 
3452b4ee398SVladimir Kondratyev 	switch (func) {
3462b4ee398SVladimir Kondratyev 	case ACPI_READ:
3472b4ee398SVladimir Kondratyev 		rtcin_region(addr, value, bytewidth);
3482b4ee398SVladimir Kondratyev 		break;
3492b4ee398SVladimir Kondratyev 	case ACPI_WRITE:
3502b4ee398SVladimir Kondratyev 		rtcout_region(addr, value, bytewidth);
3512b4ee398SVladimir Kondratyev 		break;
3522b4ee398SVladimir Kondratyev 	default:
3532b4ee398SVladimir Kondratyev 		CMOS_HANDLER_ERR("Invalid function: %u\n", func);
3542b4ee398SVladimir Kondratyev 		return (AE_BAD_PARAMETER);
3552b4ee398SVladimir Kondratyev 	}
3562b4ee398SVladimir Kondratyev 
3572b4ee398SVladimir Kondratyev 	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
3582b4ee398SVladimir Kondratyev 	    "ACPI RTC CMOS %s access: addr=%#04x, len=%u, val=%*D\n",
3592b4ee398SVladimir Kondratyev 	    is_read ? "read" : "write", (unsigned)addr, bytewidth,
3602b4ee398SVladimir Kondratyev 	    bytewidth, value, " ");
3612b4ee398SVladimir Kondratyev 
3622b4ee398SVladimir Kondratyev 	return (AE_OK);
3632b4ee398SVladimir Kondratyev }
3642b4ee398SVladimir Kondratyev 
3652b4ee398SVladimir Kondratyev static int
atrtc_reg_acpi_cmos_handler(device_t dev)3662b4ee398SVladimir Kondratyev atrtc_reg_acpi_cmos_handler(device_t dev)
3672b4ee398SVladimir Kondratyev {
3682b4ee398SVladimir Kondratyev 	struct atrtc_softc *sc = device_get_softc(dev);
3692b4ee398SVladimir Kondratyev 
3702b4ee398SVladimir Kondratyev 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
3712b4ee398SVladimir Kondratyev 
3722b4ee398SVladimir Kondratyev 	/* Don't handle address space events if driver is disabled. */
3732b4ee398SVladimir Kondratyev 	if (acpi_disabled("atrtc"))
3742b4ee398SVladimir Kondratyev 		return (ENXIO);
3752b4ee398SVladimir Kondratyev 
376eb815a74STakanori Watanabe 	if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &sc->acpi_handle))) {
377eb815a74STakanori Watanabe 		return (ENXIO);
378eb815a74STakanori Watanabe 	}
379eb815a74STakanori Watanabe 
3802b4ee398SVladimir Kondratyev 	if (sc->acpi_handle == NULL ||
3812b4ee398SVladimir Kondratyev 	    ACPI_FAILURE(AcpiInstallAddressSpaceHandler(sc->acpi_handle,
3822b4ee398SVladimir Kondratyev 	      ACPI_ADR_SPACE_CMOS, atrtc_acpi_cmos_handler, NULL, dev))) {
3832b4ee398SVladimir Kondratyev 		sc->acpi_handle = NULL;
3842b4ee398SVladimir Kondratyev 		device_printf(dev,
3852b4ee398SVladimir Kondratyev 		    "Can't register ACPI CMOS address space handler\n");
3862b4ee398SVladimir Kondratyev 		return (ENXIO);
3872b4ee398SVladimir Kondratyev         }
3882b4ee398SVladimir Kondratyev 
3892b4ee398SVladimir Kondratyev         return (0);
3902b4ee398SVladimir Kondratyev }
3912b4ee398SVladimir Kondratyev 
3922b4ee398SVladimir Kondratyev static int
atrtc_unreg_acpi_cmos_handler(device_t dev)3932b4ee398SVladimir Kondratyev atrtc_unreg_acpi_cmos_handler(device_t dev)
3942b4ee398SVladimir Kondratyev {
3952b4ee398SVladimir Kondratyev 	struct atrtc_softc *sc = device_get_softc(dev);
3962b4ee398SVladimir Kondratyev 
3972b4ee398SVladimir Kondratyev 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
3982b4ee398SVladimir Kondratyev 
3992b4ee398SVladimir Kondratyev 	if (sc->acpi_handle != NULL)
4002b4ee398SVladimir Kondratyev 		AcpiRemoveAddressSpaceHandler(sc->acpi_handle,
4012b4ee398SVladimir Kondratyev 		    ACPI_ADR_SPACE_CMOS, atrtc_acpi_cmos_handler);
4022b4ee398SVladimir Kondratyev 
4032b4ee398SVladimir Kondratyev 	return (0);
4042b4ee398SVladimir Kondratyev }
4052b4ee398SVladimir Kondratyev #endif	/* DEV_ACPI */
4062b4ee398SVladimir Kondratyev 
40732580301SAttilio Rao /*
40832580301SAttilio Rao  * Attach to the ISA PnP descriptors for the timer and realtime clock.
40932580301SAttilio Rao  */
41032580301SAttilio Rao static struct isa_pnp_id atrtc_ids[] = {
41132580301SAttilio Rao 	{ 0x000bd041 /* PNP0B00 */, "AT realtime clock" },
41232580301SAttilio Rao 	{ 0 }
41332580301SAttilio Rao };
41432580301SAttilio Rao 
4154a6d4e7bSRoger Pau Monné static bool
atrtc_acpi_disabled(void)4164a6d4e7bSRoger Pau Monné atrtc_acpi_disabled(void)
4174a6d4e7bSRoger Pau Monné {
4182b4ee398SVladimir Kondratyev #ifdef DEV_ACPI
4194a6d4e7bSRoger Pau Monné 	uint16_t flags;
4204a6d4e7bSRoger Pau Monné 
4217705dd4dSKonstantin Belousov 	if (!acpi_get_fadt_bootflags(&flags))
4224a6d4e7bSRoger Pau Monné 		return (false);
4237705dd4dSKonstantin Belousov 	return ((flags & ACPI_FADT_NO_CMOS_RTC) != 0);
4242b4ee398SVladimir Kondratyev #else
4252b4ee398SVladimir Kondratyev 	return (false);
4262b4ee398SVladimir Kondratyev #endif
4274a6d4e7bSRoger Pau Monné }
4284a6d4e7bSRoger Pau Monné 
42932580301SAttilio Rao static int
rtc_acpi_century_get(void)430e1ef6c0eSAustin Zhang rtc_acpi_century_get(void)
431e1ef6c0eSAustin Zhang {
432e1ef6c0eSAustin Zhang #ifdef DEV_ACPI
433e1ef6c0eSAustin Zhang 	ACPI_TABLE_FADT *fadt;
434e1ef6c0eSAustin Zhang 	vm_paddr_t physaddr;
435e1ef6c0eSAustin Zhang 	int century;
436e1ef6c0eSAustin Zhang 
437e1ef6c0eSAustin Zhang 	physaddr = acpi_find_table(ACPI_SIG_FADT);
438e1ef6c0eSAustin Zhang 	if (physaddr == 0)
439e1ef6c0eSAustin Zhang 		return (0);
440e1ef6c0eSAustin Zhang 
441e1ef6c0eSAustin Zhang 	fadt = acpi_map_table(physaddr, ACPI_SIG_FADT);
442e1ef6c0eSAustin Zhang 	if (fadt == NULL)
443e1ef6c0eSAustin Zhang 		return (0);
444e1ef6c0eSAustin Zhang 
445e1ef6c0eSAustin Zhang 	century = fadt->Century;
446e1ef6c0eSAustin Zhang 	acpi_unmap_table(fadt);
447e1ef6c0eSAustin Zhang 
448e1ef6c0eSAustin Zhang 	return (century);
449e1ef6c0eSAustin Zhang #else
450e1ef6c0eSAustin Zhang 	return (0);
451e1ef6c0eSAustin Zhang #endif
452e1ef6c0eSAustin Zhang }
453e1ef6c0eSAustin Zhang 
454e1ef6c0eSAustin Zhang static int
atrtc_probe(device_t dev)45532580301SAttilio Rao atrtc_probe(device_t dev)
45632580301SAttilio Rao {
45732580301SAttilio Rao 	int result;
45832580301SAttilio Rao 
4594a6d4e7bSRoger Pau Monné 	if ((atrtc_enabled == -1 && atrtc_acpi_disabled()) ||
4604a6d4e7bSRoger Pau Monné 	    (atrtc_enabled == 0))
4614a6d4e7bSRoger Pau Monné 		return (ENXIO);
4624a6d4e7bSRoger Pau Monné 
46332580301SAttilio Rao 	result = ISA_PNP_PROBE(device_get_parent(dev), dev, atrtc_ids);
464a7d6757cSAlexander Motin 	/* ENOENT means no PnP-ID, device is hinted. */
465a7d6757cSAlexander Motin 	if (result == ENOENT) {
466a7d6757cSAlexander Motin 		device_set_desc(dev, "AT realtime clock");
46732580301SAttilio Rao 		return (BUS_PROBE_LOW_PRIORITY);
46832580301SAttilio Rao 	}
469e1ef6c0eSAustin Zhang 	rtc_century = rtc_acpi_century_get();
470a7d6757cSAlexander Motin 	return (result);
471a7d6757cSAlexander Motin }
47232580301SAttilio Rao 
47332580301SAttilio Rao static int
atrtc_attach(device_t dev)47432580301SAttilio Rao atrtc_attach(device_t dev)
47532580301SAttilio Rao {
47632580301SAttilio Rao 	struct atrtc_softc *sc;
4772dd1bdf1SJustin Hibbits 	rman_res_t s;
4782b89f1fcSAndriy Gapon 	int i;
47932580301SAttilio Rao 
48032580301SAttilio Rao 	sc = device_get_softc(dev);
481686b1e6bSJohn Baldwin 	sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->port_rid,
482686b1e6bSJohn Baldwin 	    IO_RTC, IO_RTC + 1, 2, RF_ACTIVE);
483686b1e6bSJohn Baldwin 	if (sc->port_res == NULL)
48432580301SAttilio Rao 		device_printf(dev, "Warning: Couldn't map I/O.\n");
485875b8844SAlexander Motin 	atrtc_start();
48632580301SAttilio Rao 	clock_register(dev, 1000000);
487875b8844SAlexander Motin 	bzero(&sc->et, sizeof(struct eventtimer));
48849ed68bbSAlexander Motin 	if (!atrtcclock_disable &&
489875b8844SAlexander Motin 	    (resource_int_value(device_get_name(dev), device_get_unit(dev),
490875b8844SAlexander Motin 	     "clock", &i) != 0 || i != 0)) {
4916019ba4eSAlexander Motin 		sc->intr_rid = 0;
49291751b1aSAlexander Motin 		while (bus_get_resource(dev, SYS_RES_IRQ, sc->intr_rid,
49391751b1aSAlexander Motin 		    &s, NULL) == 0 && s != 8)
49491751b1aSAlexander Motin 			sc->intr_rid++;
495686b1e6bSJohn Baldwin 		sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ,
496686b1e6bSJohn Baldwin 		    &sc->intr_rid, 8, 8, 1, RF_ACTIVE);
497686b1e6bSJohn Baldwin 		if (sc->intr_res == NULL) {
49849ed68bbSAlexander Motin 			device_printf(dev, "Can't map interrupt.\n");
49949ed68bbSAlexander Motin 			return (0);
500686b1e6bSJohn Baldwin 		} else if ((bus_setup_intr(dev, sc->intr_res, INTR_TYPE_CLK,
501686b1e6bSJohn Baldwin 		    rtc_intr, NULL, sc, &sc->intr_handler))) {
50249ed68bbSAlexander Motin 			device_printf(dev, "Can't setup interrupt.\n");
50349ed68bbSAlexander Motin 			return (0);
50449ed68bbSAlexander Motin 		} else {
50549ed68bbSAlexander Motin 			/* Bind IRQ to BSP to avoid live migration. */
50649ed68bbSAlexander Motin 			bus_bind_intr(dev, sc->intr_res, 0);
50749ed68bbSAlexander Motin 		}
508875b8844SAlexander Motin 		sc->et.et_name = "RTC";
50951636352SAlexander Motin 		sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_POW2DIV;
510875b8844SAlexander Motin 		sc->et.et_quality = 0;
511875b8844SAlexander Motin 		sc->et.et_frequency = 32768;
512fdc5dd2dSAlexander Motin 		sc->et.et_min_period = 0x00080000;
513fdc5dd2dSAlexander Motin 		sc->et.et_max_period = 0x80000000;
514875b8844SAlexander Motin 		sc->et.et_start = rtc_start;
515875b8844SAlexander Motin 		sc->et.et_stop = rtc_stop;
516875b8844SAlexander Motin 		sc->et.et_priv = dev;
517875b8844SAlexander Motin 		et_register(&sc->et);
518875b8844SAlexander Motin 	}
51932580301SAttilio Rao 	return(0);
52032580301SAttilio Rao }
52132580301SAttilio Rao 
52232580301SAttilio Rao static int
atrtc_isa_attach(device_t dev)5232b4ee398SVladimir Kondratyev atrtc_isa_attach(device_t dev)
5242b4ee398SVladimir Kondratyev {
5252b4ee398SVladimir Kondratyev 
5262b4ee398SVladimir Kondratyev 	return (atrtc_attach(dev));
5272b4ee398SVladimir Kondratyev }
5282b4ee398SVladimir Kondratyev 
5292b4ee398SVladimir Kondratyev #ifdef DEV_ACPI
5302b4ee398SVladimir Kondratyev static int
atrtc_acpi_attach(device_t dev)5312b4ee398SVladimir Kondratyev atrtc_acpi_attach(device_t dev)
5322b4ee398SVladimir Kondratyev {
5332b4ee398SVladimir Kondratyev 	int ret;
5342b4ee398SVladimir Kondratyev 
5352b4ee398SVladimir Kondratyev 	ret = atrtc_attach(dev);
5362b4ee398SVladimir Kondratyev 	if (ret)
5372b4ee398SVladimir Kondratyev 		return (ret);
5382b4ee398SVladimir Kondratyev 
5392b4ee398SVladimir Kondratyev 	(void)atrtc_reg_acpi_cmos_handler(dev);
5402b4ee398SVladimir Kondratyev 
5412b4ee398SVladimir Kondratyev 	return (0);
5422b4ee398SVladimir Kondratyev }
5432b4ee398SVladimir Kondratyev 
5442b4ee398SVladimir Kondratyev static int
atrtc_acpi_detach(device_t dev)5452b4ee398SVladimir Kondratyev atrtc_acpi_detach(device_t dev)
5462b4ee398SVladimir Kondratyev {
5472b4ee398SVladimir Kondratyev 
5482b4ee398SVladimir Kondratyev 	(void)atrtc_unreg_acpi_cmos_handler(dev);
5492b4ee398SVladimir Kondratyev 	return (0);
5502b4ee398SVladimir Kondratyev }
5512b4ee398SVladimir Kondratyev #endif	/* DEV_ACPI */
5522b4ee398SVladimir Kondratyev 
5532b4ee398SVladimir Kondratyev static int
atrtc_resume(device_t dev)55432580301SAttilio Rao atrtc_resume(device_t dev)
55532580301SAttilio Rao {
55632580301SAttilio Rao 
55732580301SAttilio Rao 	atrtc_restore();
55832580301SAttilio Rao 	return(0);
55932580301SAttilio Rao }
56032580301SAttilio Rao 
56132580301SAttilio Rao static int
atrtc_settime(device_t dev __unused,struct timespec * ts)56232580301SAttilio Rao atrtc_settime(device_t dev __unused, struct timespec *ts)
56332580301SAttilio Rao {
564428cdf02SIan Lepore 	struct bcd_clocktime bct;
56532580301SAttilio Rao 
566428cdf02SIan Lepore 	clock_ts_to_bcd(ts, &bct, false);
56714d08b45SIan Lepore 	clock_dbgprint_bcd(dev, CLOCK_DBG_WRITE, &bct);
568428cdf02SIan Lepore 
569c7053bbeSIan Lepore 	mtx_lock(&atrtc_time_lock);
5708355852fSIan Lepore 	mtx_lock_spin(&atrtc_lock);
571428cdf02SIan Lepore 
572428cdf02SIan Lepore 	/* Disable RTC updates and interrupts.  */
573428cdf02SIan Lepore 	rtcout_locked(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR);
574428cdf02SIan Lepore 
575428cdf02SIan Lepore 	/* Write all the time registers. */
576428cdf02SIan Lepore 	rtcout_locked(RTC_SEC,   bct.sec);
577428cdf02SIan Lepore 	rtcout_locked(RTC_MIN,   bct.min);
578428cdf02SIan Lepore 	rtcout_locked(RTC_HRS,   bct.hour);
579428cdf02SIan Lepore 	rtcout_locked(RTC_WDAY,  bct.dow + 1);
580428cdf02SIan Lepore 	rtcout_locked(RTC_DAY,   bct.day);
581428cdf02SIan Lepore 	rtcout_locked(RTC_MONTH, bct.mon);
582428cdf02SIan Lepore 	rtcout_locked(RTC_YEAR,  bct.year & 0xff);
583e1ef6c0eSAustin Zhang 	if (rtc_century)
584e1ef6c0eSAustin Zhang 		rtcout_locked(rtc_century, bct.year >> 8);
585428cdf02SIan Lepore 
586428cdf02SIan Lepore 	/*
587428cdf02SIan Lepore 	 * Re-enable RTC updates and interrupts.
588428cdf02SIan Lepore 	 */
589428cdf02SIan Lepore 	rtcout_locked(RTC_STATUSB, rtc_statusb);
590428cdf02SIan Lepore 	rtcin_locked(RTC_INTR);
591428cdf02SIan Lepore 
5928355852fSIan Lepore 	mtx_unlock_spin(&atrtc_lock);
593c7053bbeSIan Lepore 	mtx_unlock(&atrtc_time_lock);
594428cdf02SIan Lepore 
59532580301SAttilio Rao 	return (0);
59632580301SAttilio Rao }
59732580301SAttilio Rao 
59832580301SAttilio Rao static int
atrtc_gettime(device_t dev,struct timespec * ts)59932580301SAttilio Rao atrtc_gettime(device_t dev, struct timespec *ts)
60032580301SAttilio Rao {
601428cdf02SIan Lepore 	struct bcd_clocktime bct;
60232580301SAttilio Rao 
60332580301SAttilio Rao 	/* Look if we have a RTC present and the time is valid */
60432580301SAttilio Rao 	if (!(rtcin(RTC_STATUSD) & RTCSD_PWR)) {
60555f1ca20SCorvin Köhne 		atrtc_power_lost = true;
60632580301SAttilio Rao 		device_printf(dev, "WARNING: Battery failure indication\n");
60732580301SAttilio Rao 		return (EINVAL);
60832580301SAttilio Rao 	}
60932580301SAttilio Rao 
61022e61bc2SWarner Losh 	/*
61122e61bc2SWarner Losh 	 * wait for time update to complete
61222e61bc2SWarner Losh 	 * If RTCSA_TUP is zero, we have at least 244us before next update.
61322e61bc2SWarner Losh 	 * This is fast enough on most hardware, but a refinement would be
61422e61bc2SWarner Losh 	 * to make sure that no more than 240us pass after we start reading,
61522e61bc2SWarner Losh 	 * and try again if so.
61622e61bc2SWarner Losh 	 */
617c7053bbeSIan Lepore 	mtx_lock(&atrtc_time_lock);
6187fe82634SWarner Losh 	while (rtcin(RTC_STATUSA) & RTCSA_TUP)
6197fe82634SWarner Losh 		continue;
6208355852fSIan Lepore 	mtx_lock_spin(&atrtc_lock);
621428cdf02SIan Lepore 	bct.sec  = rtcin_locked(RTC_SEC);
622428cdf02SIan Lepore 	bct.min  = rtcin_locked(RTC_MIN);
623428cdf02SIan Lepore 	bct.hour = rtcin_locked(RTC_HRS);
624428cdf02SIan Lepore 	bct.day  = rtcin_locked(RTC_DAY);
625428cdf02SIan Lepore 	bct.mon  = rtcin_locked(RTC_MONTH);
626428cdf02SIan Lepore 	bct.year = rtcin_locked(RTC_YEAR);
627e1ef6c0eSAustin Zhang 	if (rtc_century)
628e1ef6c0eSAustin Zhang 		bct.year |= rtcin_locked(rtc_century) << 8;
6298355852fSIan Lepore 	mtx_unlock_spin(&atrtc_lock);
630c7053bbeSIan Lepore 	mtx_unlock(&atrtc_time_lock);
6317c63e501SIan Lepore 	/* dow is unused in timespec conversion and we have no nsec info. */
632428cdf02SIan Lepore 	bct.dow  = 0;
633428cdf02SIan Lepore 	bct.nsec = 0;
63414d08b45SIan Lepore 	clock_dbgprint_bcd(dev, CLOCK_DBG_READ, &bct);
635428cdf02SIan Lepore 	return (clock_bcd_to_ts(&bct, ts, false));
63632580301SAttilio Rao }
63732580301SAttilio Rao 
6382b4ee398SVladimir Kondratyev static device_method_t atrtc_isa_methods[] = {
63932580301SAttilio Rao 	/* Device interface */
64032580301SAttilio Rao 	DEVMETHOD(device_probe,		atrtc_probe),
6412b4ee398SVladimir Kondratyev 	DEVMETHOD(device_attach,	atrtc_isa_attach),
64232580301SAttilio Rao 		/* XXX stop statclock? */
64332580301SAttilio Rao 	DEVMETHOD(device_resume,	atrtc_resume),
64432580301SAttilio Rao 
64532580301SAttilio Rao 	/* clock interface */
64632580301SAttilio Rao 	DEVMETHOD(clock_gettime,	atrtc_gettime),
64732580301SAttilio Rao 	DEVMETHOD(clock_settime,	atrtc_settime),
64832580301SAttilio Rao 	{ 0, 0 }
64932580301SAttilio Rao };
65032580301SAttilio Rao 
6512b4ee398SVladimir Kondratyev static driver_t atrtc_isa_driver = {
65232580301SAttilio Rao 	"atrtc",
6532b4ee398SVladimir Kondratyev 	atrtc_isa_methods,
65432580301SAttilio Rao 	sizeof(struct atrtc_softc),
65532580301SAttilio Rao };
65632580301SAttilio Rao 
6572b4ee398SVladimir Kondratyev #ifdef DEV_ACPI
6582b4ee398SVladimir Kondratyev static device_method_t atrtc_acpi_methods[] = {
6592b4ee398SVladimir Kondratyev 	/* Device interface */
6602b4ee398SVladimir Kondratyev 	DEVMETHOD(device_probe,		atrtc_probe),
6612b4ee398SVladimir Kondratyev 	DEVMETHOD(device_attach,	atrtc_acpi_attach),
6622b4ee398SVladimir Kondratyev 	DEVMETHOD(device_detach,	atrtc_acpi_detach),
6632b4ee398SVladimir Kondratyev 		/* XXX stop statclock? */
6642b4ee398SVladimir Kondratyev 	DEVMETHOD(device_resume,	atrtc_resume),
6652b4ee398SVladimir Kondratyev 
6662b4ee398SVladimir Kondratyev 	/* clock interface */
6672b4ee398SVladimir Kondratyev 	DEVMETHOD(clock_gettime,	atrtc_gettime),
6682b4ee398SVladimir Kondratyev 	DEVMETHOD(clock_settime,	atrtc_settime),
6692b4ee398SVladimir Kondratyev 	{ 0, 0 }
6702b4ee398SVladimir Kondratyev };
6712b4ee398SVladimir Kondratyev 
6722b4ee398SVladimir Kondratyev static driver_t atrtc_acpi_driver = {
6732b4ee398SVladimir Kondratyev 	"atrtc",
6742b4ee398SVladimir Kondratyev 	atrtc_acpi_methods,
6752b4ee398SVladimir Kondratyev 	sizeof(struct atrtc_softc),
6762b4ee398SVladimir Kondratyev };
6772b4ee398SVladimir Kondratyev #endif	/* DEV_ACPI */
6782b4ee398SVladimir Kondratyev 
67980d2b3deSJohn Baldwin DRIVER_MODULE(atrtc, isa, atrtc_isa_driver, 0, 0);
6802b4ee398SVladimir Kondratyev #ifdef DEV_ACPI
68180d2b3deSJohn Baldwin DRIVER_MODULE(atrtc, acpi, atrtc_acpi_driver, 0, 0);
6822b4ee398SVladimir Kondratyev #endif
683d6b66397SWarner Losh ISA_PNP_INFO(atrtc_ids);
684