Index: stable/11/sys/dev/xen/timer/timer.c =================================================================== --- stable/11/sys/dev/xen/timer/timer.c (revision 323471) +++ stable/11/sys/dev/xen/timer/timer.c (revision 323472) @@ -1,562 +1,557 @@ /*- * Copyright (c) 2009 Adrian Chadd * Copyright (c) 2012 Spectra Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /** * \file dev/xen/timer/timer.c * \brief A timer driver for the Xen hypervisor's PV clock. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include - #include "clock_if.h" static devclass_t xentimer_devclass; #define NSEC_IN_SEC 1000000000ULL #define NSEC_IN_USEC 1000ULL /* 18446744073 = int(2^64 / NSEC_IN_SC) = 1 ns in 64-bit fractions */ #define FRAC_IN_NSEC 18446744073LL /* Xen timers may fire up to 100us off */ #define XENTIMER_MIN_PERIOD_IN_NSEC 100*NSEC_IN_USEC /* * The real resolution of the PV clock is 1ns, but the highest * resolution that FreeBSD supports is 1us, so just use that. */ #define XENCLOCK_RESOLUTION 1 #define XENTIMER_QUALITY 950 struct xentimer_pcpu_data { uint64_t timer; uint64_t last_processed; void *irq_handle; }; DPCPU_DEFINE(struct xentimer_pcpu_data, xentimer_pcpu); DPCPU_DECLARE(struct vcpu_info *, vcpu_info); struct xentimer_softc { device_t dev; struct timecounter tc; struct eventtimer et; }; static void xentimer_identify(driver_t *driver, device_t parent) { if (!xen_domain()) return; /* Handle all Xen PV timers in one device instance. */ if (devclass_get_device(xentimer_devclass, 0)) return; BUS_ADD_CHILD(parent, 0, "xen_et", 0); } static int xentimer_probe(device_t dev) { KASSERT((xen_domain()), ("Trying to use Xen timer on bare metal")); /* * In order to attach, this driver requires the following: * - Vector callback support by the hypervisor, in order to deliver * timer interrupts to the correct CPU for CPUs other than 0. * - Access to the hypervisor shared info page, in order to look up * each VCPU's timer information and the Xen wallclock time. * - The hypervisor must say its PV clock is "safe" to use. * - The hypervisor must support VCPUOP hypercalls. * - The maximum number of CPUs supported by FreeBSD must not exceed * the number of VCPUs supported by the hypervisor. */ #define XTREQUIRES(condition, reason...) \ if (!(condition)) { \ device_printf(dev, ## reason); \ device_detach(dev); \ return (ENXIO); \ } if (xen_hvm_domain()) { XTREQUIRES(xen_vector_callback_enabled, "vector callbacks unavailable\n"); XTREQUIRES(xen_feature(XENFEAT_hvm_safe_pvclock), "HVM safe pvclock unavailable\n"); } XTREQUIRES(HYPERVISOR_shared_info != NULL, "shared info page unavailable\n"); XTREQUIRES(HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, 0, NULL) == 0, "VCPUOPs interface unavailable\n"); #undef XTREQUIRES device_set_desc(dev, "Xen PV Clock"); return (BUS_PROBE_NOWILDCARD); } /** * \brief Get the current time, in nanoseconds, since the hypervisor booted. * * \param vcpu vcpu_info structure to fetch the time from. * */ static uint64_t xen_fetch_vcpu_time(struct vcpu_info *vcpu) { struct pvclock_vcpu_time_info *time; time = (struct pvclock_vcpu_time_info *) &vcpu->time; return (pvclock_get_timecount(time)); } static uint32_t xentimer_get_timecount(struct timecounter *tc) { uint64_t vcpu_time; /* * We don't disable preemption here because the worst that can * happen is reading the vcpu_info area of a different CPU than * the one we are currently running on, but that would also * return a valid tc (and we avoid the overhead of * critical_{enter/exit} calls). */ vcpu_time = xen_fetch_vcpu_time(DPCPU_GET(vcpu_info)); return (vcpu_time & UINT32_MAX); } /** * \brief Fetch the hypervisor boot time, known as the "Xen wallclock". * * \param ts Timespec to store the current stable value. * \param version Pointer to store the corresponding wallclock version. * * \note This value is updated when Domain-0 shifts its clock to follow * clock drift, e.g. as detected by NTP. */ static void xen_fetch_wallclock(struct timespec *ts) { shared_info_t *src = HYPERVISOR_shared_info; struct pvclock_wall_clock *wc; wc = (struct pvclock_wall_clock *) &src->wc_version; pvclock_get_wallclock(wc, ts); } static void xen_fetch_uptime(struct timespec *ts) { uint64_t uptime; uptime = xen_fetch_vcpu_time(DPCPU_GET(vcpu_info)); ts->tv_sec = uptime / NSEC_IN_SEC; ts->tv_nsec = uptime % NSEC_IN_SEC; } static int xentimer_settime(device_t dev __unused, struct timespec *ts) { struct xen_platform_op settime; int ret; /* * Don't return EINVAL here; just silently fail if the domain isn't * privileged enough to set the TOD. */ if (!xen_initial_domain()) return (0); - - /* Set the native RTC. */ - atrtc_set(ts); settime.cmd = XENPF_settime64; settime.u.settime64.mbz = 0; settime.u.settime64.secs = ts->tv_sec; settime.u.settime64.nsecs = ts->tv_nsec; settime.u.settime64.system_time = xen_fetch_vcpu_time(DPCPU_GET(vcpu_info)); ret = HYPERVISOR_platform_op(&settime); ret = ret != 0 ? xen_translate_error(ret) : 0; if (ret != 0 && bootverbose) device_printf(dev, "failed to set Xen PV clock: %d\n", ret); return (ret); } /** * \brief Return current time according to the Xen Hypervisor wallclock. * * \param dev Xentimer device. * \param ts Pointer to store the wallclock time. * * \note The Xen time structures document the hypervisor start time and the * uptime-since-hypervisor-start (in nsec.) They need to be combined * in order to calculate a TOD clock. */ static int xentimer_gettime(device_t dev, struct timespec *ts) { struct timespec u_ts; timespecclear(ts); xen_fetch_wallclock(ts); xen_fetch_uptime(&u_ts); timespecadd(ts, &u_ts); return (0); } /** * \brief Handle a timer interrupt for the Xen PV timer driver. * * \param arg Xen timer driver softc that is expecting the interrupt. */ static int xentimer_intr(void *arg) { struct xentimer_softc *sc = (struct xentimer_softc *)arg; struct xentimer_pcpu_data *pcpu = DPCPU_PTR(xentimer_pcpu); pcpu->last_processed = xen_fetch_vcpu_time(DPCPU_GET(vcpu_info)); if (pcpu->timer != 0 && sc->et.et_active) sc->et.et_event_cb(&sc->et, sc->et.et_arg); return (FILTER_HANDLED); } static int xentimer_vcpu_start_timer(int vcpu, uint64_t next_time) { struct vcpu_set_singleshot_timer single; single.timeout_abs_ns = next_time; /* Get an event anyway, even if the timeout is already expired */ single.flags = 0; return (HYPERVISOR_vcpu_op(VCPUOP_set_singleshot_timer, vcpu, &single)); } static int xentimer_vcpu_stop_timer(int vcpu) { return (HYPERVISOR_vcpu_op(VCPUOP_stop_singleshot_timer, vcpu, NULL)); } /** * \brief Set the next oneshot time for the current CPU. * * \param et Xen timer driver event timer to schedule on. * \param first Delta to the next time to schedule the interrupt for. * \param period Not used. * * \note See eventtimers(9) for more information. * \note * * \returns 0 */ static int xentimer_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { int error; struct xentimer_softc *sc = et->et_priv; int cpu = PCPU_GET(vcpu_id); struct xentimer_pcpu_data *pcpu = DPCPU_PTR(xentimer_pcpu); struct vcpu_info *vcpu = DPCPU_GET(vcpu_info); uint64_t first_in_ns, next_time; #ifdef INVARIANTS struct thread *td = curthread; #endif KASSERT(td->td_critnest != 0, ("xentimer_et_start called without preemption disabled")); /* See sbttots() for this formula. */ first_in_ns = (((first >> 32) * NSEC_IN_SEC) + (((uint64_t)NSEC_IN_SEC * (uint32_t)first) >> 32)); next_time = xen_fetch_vcpu_time(vcpu) + first_in_ns; error = xentimer_vcpu_start_timer(cpu, next_time); if (error) panic("%s: Error %d setting singleshot timer to %"PRIu64"\n", device_get_nameunit(sc->dev), error, next_time); pcpu->timer = next_time; return (error); } /** * \brief Cancel the event timer's currently running timer, if any. */ static int xentimer_et_stop(struct eventtimer *et) { int cpu = PCPU_GET(vcpu_id); struct xentimer_pcpu_data *pcpu = DPCPU_PTR(xentimer_pcpu); pcpu->timer = 0; return (xentimer_vcpu_stop_timer(cpu)); } /** * \brief Attach a Xen PV timer driver instance. * * \param dev Bus device object to attach. * * \note * \returns EINVAL */ static int xentimer_attach(device_t dev) { struct xentimer_softc *sc = device_get_softc(dev); int error, i; sc->dev = dev; /* Bind an event channel to a VIRQ on each VCPU. */ CPU_FOREACH(i) { struct xentimer_pcpu_data *pcpu; pcpu = DPCPU_ID_PTR(i, xentimer_pcpu); error = HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, i, NULL); if (error) { device_printf(dev, "Error disabling Xen periodic timer " "on CPU %d\n", i); return (error); } error = xen_intr_bind_virq(dev, VIRQ_TIMER, i, xentimer_intr, NULL, sc, INTR_TYPE_CLK, &pcpu->irq_handle); if (error) { device_printf(dev, "Error %d binding VIRQ_TIMER " "to VCPU %d\n", error, i); return (error); } xen_intr_describe(pcpu->irq_handle, "c%d", i); } /* Register the event timer. */ sc->et.et_name = "XENTIMER"; sc->et.et_quality = XENTIMER_QUALITY; sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; sc->et.et_frequency = NSEC_IN_SEC; /* See tstosbt() for this formula */ sc->et.et_min_period = (XENTIMER_MIN_PERIOD_IN_NSEC * (((uint64_t)1 << 63) / 500000000) >> 32); sc->et.et_max_period = ((sbintime_t)4 << 32); sc->et.et_start = xentimer_et_start; sc->et.et_stop = xentimer_et_stop; sc->et.et_priv = sc; et_register(&sc->et); /* Register the timecounter. */ sc->tc.tc_name = "XENTIMER"; sc->tc.tc_quality = XENTIMER_QUALITY; /* * FIXME: due to the lack of ordering during resume, FreeBSD cannot * guarantee that the Xen PV timer is resumed before any other device * attempts to make use of it, so mark it as not safe for suspension * (ie: remove the TC_FLAGS_SUSPEND_SAFE flag). * * NB: This was not a problem in previous FreeBSD versions because the * timer was directly attached to the nexus, but it is an issue now * that the timer is attached to the xenpv bus, and thus resumed * later. * * sc->tc.tc_flags = TC_FLAGS_SUSPEND_SAFE; */ /* * The underlying resolution is in nanoseconds, since the timer info * scales TSC frequencies using a fraction that represents time in * terms of nanoseconds. */ sc->tc.tc_frequency = NSEC_IN_SEC; sc->tc.tc_counter_mask = ~0u; sc->tc.tc_get_timecount = xentimer_get_timecount; sc->tc.tc_priv = sc; tc_init(&sc->tc); /* Register the Hypervisor wall clock */ clock_register(dev, XENCLOCK_RESOLUTION); return (0); } static int xentimer_detach(device_t dev) { /* Implement Xen PV clock teardown - XXX see hpet_detach ? */ /* If possible: * 1. need to deregister timecounter * 2. need to deregister event timer * 3. need to deregister virtual IRQ event channels */ return (EBUSY); } static void xentimer_percpu_resume(void *arg) { device_t dev = (device_t) arg; struct xentimer_softc *sc = device_get_softc(dev); xentimer_et_start(&sc->et, sc->et.et_min_period, 0); } static int xentimer_resume(device_t dev) { int error; int i; /* Disable the periodic timer */ CPU_FOREACH(i) { error = HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, i, NULL); if (error != 0) { device_printf(dev, "Error disabling Xen periodic timer on CPU %d\n", i); return (error); } } /* Reset the last uptime value */ pvclock_resume(); /* Reset the RTC clock */ inittodr(time_second); /* Kick the timers on all CPUs */ smp_rendezvous(NULL, xentimer_percpu_resume, NULL, dev); if (bootverbose) device_printf(dev, "resumed operation after suspension\n"); return (0); } static int xentimer_suspend(device_t dev) { return (0); } /* * Xen early clock init */ void xen_clock_init(void) { } /* * Xen PV DELAY function * * When running on PVH mode we don't have an emulated i8524, so * make use of the Xen time info in order to code a simple DELAY * function that can be used during early boot. */ void xen_delay(int n) { struct vcpu_info *vcpu = &HYPERVISOR_shared_info->vcpu_info[0]; uint64_t end_ns; uint64_t current; end_ns = xen_fetch_vcpu_time(vcpu); end_ns += n * NSEC_IN_USEC; for (;;) { current = xen_fetch_vcpu_time(vcpu); if (current >= end_ns) break; } } static device_method_t xentimer_methods[] = { DEVMETHOD(device_identify, xentimer_identify), DEVMETHOD(device_probe, xentimer_probe), DEVMETHOD(device_attach, xentimer_attach), DEVMETHOD(device_detach, xentimer_detach), DEVMETHOD(device_suspend, xentimer_suspend), DEVMETHOD(device_resume, xentimer_resume), /* clock interface */ DEVMETHOD(clock_gettime, xentimer_gettime), DEVMETHOD(clock_settime, xentimer_settime), DEVMETHOD_END }; static driver_t xentimer_driver = { "xen_et", xentimer_methods, sizeof(struct xentimer_softc), }; DRIVER_MODULE(xentimer, xenpv, xentimer_driver, xentimer_devclass, 0, 0); MODULE_DEPEND(xentimer, xenpv, 1, 1, 1); Index: stable/11/sys/isa/rtc.h =================================================================== --- stable/11/sys/isa/rtc.h (revision 323471) +++ stable/11/sys/isa/rtc.h (revision 323472) @@ -1,124 +1,123 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)rtc.h 7.1 (Berkeley) 5/12/91 * $FreeBSD$ */ #ifndef _I386_ISA_RTC_H_ #define _I386_ISA_RTC_H_ 1 /* * MC146818 RTC Register locations */ #define RTC_SEC 0x00 /* seconds */ #define RTC_SECALRM 0x01 /* seconds alarm */ #define RTC_MIN 0x02 /* minutes */ #define RTC_MINALRM 0x03 /* minutes alarm */ #define RTC_HRS 0x04 /* hours */ #define RTC_HRSALRM 0x05 /* hours alarm */ #define RTC_WDAY 0x06 /* week day */ #define RTC_DAY 0x07 /* day of month */ #define RTC_MONTH 0x08 /* month of year */ #define RTC_YEAR 0x09 /* month of year */ #define RTC_STATUSA 0x0a /* status register A */ #define RTCSA_TUP 0x80 /* time update, don't look now */ #define RTCSA_RESET 0x70 /* reset divider */ #define RTCSA_DIVIDER 0x20 /* divider correct for 32768 Hz */ #define RTCSA_8192 0x03 /* 8192 Hz interrupt */ #define RTCSA_4096 0x04 #define RTCSA_2048 0x05 #define RTCSA_1024 0x06 /* default for profiling */ #define RTCSA_PROF RTCSA_1024 #define RTC_PROFRATE 1024 #define RTCSA_512 0x07 #define RTCSA_256 0x08 #define RTCSA_128 0x09 #define RTCSA_NOPROF RTCSA_128 #define RTC_NOPROFRATE 128 #define RTCSA_64 0x0a #define RTCSA_32 0x0b /* 32 Hz interrupt */ #define RTC_STATUSB 0x0b /* status register B */ #define RTCSB_DST 0x01 /* USA Daylight Savings Time enable */ #define RTCSB_24HR 0x02 /* 0 = 12 hours, 1 = 24 hours */ #define RTCSB_BCD 0x04 /* 0 = BCD, 1 = Binary coded time */ #define RTCSB_SQWE 0x08 /* 1 = output sqare wave at SQW pin */ #define RTCSB_UINTR 0x10 /* 1 = enable update-ended interrupt */ #define RTCSB_AINTR 0x20 /* 1 = enable alarm interrupt */ #define RTCSB_PINTR 0x40 /* 1 = enable periodic clock interrupt */ #define RTCSB_HALT 0x80 /* stop clock updates */ #define RTC_INTR 0x0c /* status register C (R) interrupt source */ #define RTCIR_UPDATE 0x10 /* update intr */ #define RTCIR_ALARM 0x20 /* alarm intr */ #define RTCIR_PERIOD 0x40 /* periodic intr */ #define RTCIR_INT 0x80 /* interrupt output signal */ #define RTC_STATUSD 0x0d /* status register D (R) Lost Power */ #define RTCSD_PWR 0x80 /* clock power OK */ #define RTC_DIAG 0x0e /* status register E - bios diagnostic */ #define RTCDG_BITS "\020\010clock_battery\007ROM_cksum\006config_unit\005memory_size\004fixed_disk\003invalid_time" #define RTC_RESET 0x0f /* status register F - reset code byte */ #define RTCRS_RST 0x00 /* normal reset */ #define RTCRS_LOAD 0x04 /* load system */ #define RTC_FDISKETTE 0x10 /* diskette drive type in upper/lower nibble */ #define RTCFDT_NONE 0 /* none present */ #define RTCFDT_360K 0x10 /* 360K */ #define RTCFDT_12M 0x20 /* 1.2M */ #define RTCFDT_720K 0x30 /* 720K */ #define RTCFDT_144M 0x40 /* 1.44M */ #define RTCFDT_288M_1 0x50 /* 2.88M, some BIOSes */ #define RTCFDT_288M 0x60 /* 2.88M */ #define RTC_BASELO 0x15 /* low byte of basemem size */ #define RTC_BASEHI 0x16 /* high byte of basemem size */ #define RTC_EXTLO 0x17 /* low byte of extended mem size */ #define RTC_EXTHI 0x18 /* low byte of extended mem size */ #define RTC_CENTURY 0x32 /* current century */ #ifdef _KERNEL extern struct mtx clock_lock; extern struct mtx atrtc_time_lock; extern int atrtcclock_disable; int rtcin(int reg); void atrtc_restore(void); void writertc(int reg, u_char val); -void atrtc_set(struct timespec *ts); #endif #endif /* _I386_ISA_RTC_H_ */ Index: stable/11/sys/x86/isa/atrtc.c =================================================================== --- stable/11/sys/x86/isa/atrtc.c (revision 323471) +++ stable/11/sys/x86/isa/atrtc.c (revision 323472) @@ -1,425 +1,425 @@ /*- * Copyright (c) 2008 Poul-Henning Kamp * Copyright (c) 2010 Alexander Motin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include "opt_isa.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEV_ISA #include #include #endif #include #include "clock_if.h" /* * clock_lock protects low-level access to individual hardware registers. * atrtc_time_lock protects the entire sequence of accessing multiple registers * to read or write the date and time. */ #define RTC_LOCK do { if (!kdb_active) mtx_lock_spin(&clock_lock); } while (0) #define RTC_UNLOCK do { if (!kdb_active) mtx_unlock_spin(&clock_lock); } while (0) struct mtx atrtc_time_lock; MTX_SYSINIT(atrtc_lock_init, &atrtc_time_lock, "atrtc", MTX_DEF); int atrtcclock_disable = 0; static int rtc_reg = -1; static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF; static u_char rtc_statusb = RTCSB_24HR; /* * RTC support routines */ int rtcin(int reg) { u_char val; RTC_LOCK; if (rtc_reg != reg) { inb(0x84); outb(IO_RTC, reg); rtc_reg = reg; inb(0x84); } val = inb(IO_RTC + 1); RTC_UNLOCK; return (val); } void writertc(int reg, u_char val) { RTC_LOCK; if (rtc_reg != reg) { inb(0x84); outb(IO_RTC, reg); rtc_reg = reg; inb(0x84); } outb(IO_RTC + 1, val); inb(0x84); RTC_UNLOCK; } static __inline int readrtc(int port) { return(bcd2bin(rtcin(port))); } static void atrtc_start(void) { writertc(RTC_STATUSA, rtc_statusa); writertc(RTC_STATUSB, RTCSB_24HR); } static void atrtc_rate(unsigned rate) { rtc_statusa = RTCSA_DIVIDER | rate; writertc(RTC_STATUSA, rtc_statusa); } static void atrtc_enable_intr(void) { rtc_statusb |= RTCSB_PINTR; writertc(RTC_STATUSB, rtc_statusb); rtcin(RTC_INTR); } static void atrtc_disable_intr(void) { rtc_statusb &= ~RTCSB_PINTR; writertc(RTC_STATUSB, rtc_statusb); rtcin(RTC_INTR); } void atrtc_restore(void) { /* Restore all of the RTC's "status" (actually, control) registers. */ rtcin(RTC_STATUSA); /* dummy to get rtc_reg set */ writertc(RTC_STATUSB, RTCSB_24HR); writertc(RTC_STATUSA, rtc_statusa); writertc(RTC_STATUSB, rtc_statusb); rtcin(RTC_INTR); } -void +static void atrtc_set(struct timespec *ts) { struct clocktime ct; clock_ts_to_ct(ts, &ct); mtx_lock(&atrtc_time_lock); /* Disable RTC updates and interrupts. */ writertc(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR); writertc(RTC_SEC, bin2bcd(ct.sec)); /* Write back Seconds */ writertc(RTC_MIN, bin2bcd(ct.min)); /* Write back Minutes */ writertc(RTC_HRS, bin2bcd(ct.hour)); /* Write back Hours */ writertc(RTC_WDAY, ct.dow + 1); /* Write back Weekday */ writertc(RTC_DAY, bin2bcd(ct.day)); /* Write back Day */ writertc(RTC_MONTH, bin2bcd(ct.mon)); /* Write back Month */ writertc(RTC_YEAR, bin2bcd(ct.year % 100)); /* Write back Year */ #ifdef USE_RTC_CENTURY writertc(RTC_CENTURY, bin2bcd(ct.year / 100)); /* ... and Century */ #endif /* Re-enable RTC updates and interrupts. */ writertc(RTC_STATUSB, rtc_statusb); rtcin(RTC_INTR); mtx_unlock(&atrtc_time_lock); } /********************************************************************** * RTC driver for subr_rtc */ struct atrtc_softc { int port_rid, intr_rid; struct resource *port_res; struct resource *intr_res; void *intr_handler; struct eventtimer et; }; static int rtc_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { atrtc_rate(max(fls(period + (period >> 1)) - 17, 1)); atrtc_enable_intr(); return (0); } static int rtc_stop(struct eventtimer *et) { atrtc_disable_intr(); return (0); } /* * This routine receives statistical clock interrupts from the RTC. * As explained above, these occur at 128 interrupts per second. * When profiling, we receive interrupts at a rate of 1024 Hz. * * This does not actually add as much overhead as it sounds, because * when the statistical clock is active, the hardclock driver no longer * needs to keep (inaccurate) statistics on its own. This decouples * statistics gathering from scheduling interrupts. * * The RTC chip requires that we read status register C (RTC_INTR) * to acknowledge an interrupt, before it will generate the next one. * Under high interrupt load, rtcintr() can be indefinitely delayed and * the clock can tick immediately after the read from RTC_INTR. In this * case, the mc146818A interrupt signal will not drop for long enough * to register with the 8259 PIC. If an interrupt is missed, the stat * clock will halt, considerably degrading system performance. This is * why we use 'while' rather than a more straightforward 'if' below. * Stat clock ticks can still be lost, causing minor loss of accuracy * in the statistics, but the stat clock will no longer stop. */ static int rtc_intr(void *arg) { struct atrtc_softc *sc = (struct atrtc_softc *)arg; int flag = 0; while (rtcin(RTC_INTR) & RTCIR_PERIOD) { flag = 1; if (sc->et.et_active) sc->et.et_event_cb(&sc->et, sc->et.et_arg); } return(flag ? FILTER_HANDLED : FILTER_STRAY); } /* * Attach to the ISA PnP descriptors for the timer and realtime clock. */ static struct isa_pnp_id atrtc_ids[] = { { 0x000bd041 /* PNP0B00 */, "AT realtime clock" }, { 0 } }; static int atrtc_probe(device_t dev) { int result; result = ISA_PNP_PROBE(device_get_parent(dev), dev, atrtc_ids); /* ENOENT means no PnP-ID, device is hinted. */ if (result == ENOENT) { device_set_desc(dev, "AT realtime clock"); return (BUS_PROBE_LOW_PRIORITY); } return (result); } static int atrtc_attach(device_t dev) { struct atrtc_softc *sc; rman_res_t s; int i; sc = device_get_softc(dev); sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->port_rid, IO_RTC, IO_RTC + 1, 2, RF_ACTIVE); if (sc->port_res == NULL) device_printf(dev, "Warning: Couldn't map I/O.\n"); atrtc_start(); clock_register(dev, 1000000); bzero(&sc->et, sizeof(struct eventtimer)); if (!atrtcclock_disable && (resource_int_value(device_get_name(dev), device_get_unit(dev), "clock", &i) != 0 || i != 0)) { sc->intr_rid = 0; while (bus_get_resource(dev, SYS_RES_IRQ, sc->intr_rid, &s, NULL) == 0 && s != 8) sc->intr_rid++; sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->intr_rid, 8, 8, 1, RF_ACTIVE); if (sc->intr_res == NULL) { device_printf(dev, "Can't map interrupt.\n"); return (0); } else if ((bus_setup_intr(dev, sc->intr_res, INTR_TYPE_CLK, rtc_intr, NULL, sc, &sc->intr_handler))) { device_printf(dev, "Can't setup interrupt.\n"); return (0); } else { /* Bind IRQ to BSP to avoid live migration. */ bus_bind_intr(dev, sc->intr_res, 0); } sc->et.et_name = "RTC"; sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_POW2DIV; sc->et.et_quality = 0; sc->et.et_frequency = 32768; sc->et.et_min_period = 0x00080000; sc->et.et_max_period = 0x80000000; sc->et.et_start = rtc_start; sc->et.et_stop = rtc_stop; sc->et.et_priv = dev; et_register(&sc->et); } return(0); } static int atrtc_resume(device_t dev) { atrtc_restore(); return(0); } static int atrtc_settime(device_t dev __unused, struct timespec *ts) { atrtc_set(ts); return (0); } static int atrtc_gettime(device_t dev, struct timespec *ts) { struct clocktime ct; /* Look if we have a RTC present and the time is valid */ if (!(rtcin(RTC_STATUSD) & RTCSD_PWR)) { device_printf(dev, "WARNING: Battery failure indication\n"); return (EINVAL); } /* * wait for time update to complete * If RTCSA_TUP is zero, we have at least 244us before next update. * This is fast enough on most hardware, but a refinement would be * to make sure that no more than 240us pass after we start reading, * and try again if so. */ mtx_lock(&atrtc_time_lock); while (rtcin(RTC_STATUSA) & RTCSA_TUP) continue; critical_enter(); ct.nsec = 0; ct.sec = readrtc(RTC_SEC); ct.min = readrtc(RTC_MIN); ct.hour = readrtc(RTC_HRS); ct.day = readrtc(RTC_DAY); ct.dow = readrtc(RTC_WDAY) - 1; ct.mon = readrtc(RTC_MONTH); ct.year = readrtc(RTC_YEAR); #ifdef USE_RTC_CENTURY ct.year += readrtc(RTC_CENTURY) * 100; #else ct.year += (ct.year < 80 ? 2000 : 1900); #endif critical_exit(); mtx_unlock(&atrtc_time_lock); /* Set dow = -1 because some clocks don't set it correctly. */ ct.dow = -1; return (clock_ct_to_ts(&ct, ts)); } static device_method_t atrtc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, atrtc_probe), DEVMETHOD(device_attach, atrtc_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX stop statclock? */ DEVMETHOD(device_resume, atrtc_resume), /* clock interface */ DEVMETHOD(clock_gettime, atrtc_gettime), DEVMETHOD(clock_settime, atrtc_settime), { 0, 0 } }; static driver_t atrtc_driver = { "atrtc", atrtc_methods, sizeof(struct atrtc_softc), }; static devclass_t atrtc_devclass; DRIVER_MODULE(atrtc, isa, atrtc_driver, atrtc_devclass, 0, 0); DRIVER_MODULE(atrtc, acpi, atrtc_driver, atrtc_devclass, 0, 0); #include "opt_ddb.h" #ifdef DDB #include DB_SHOW_COMMAND(rtc, rtc) { printf("%02x/%02x/%02x %02x:%02x:%02x, A = %02x, B = %02x, C = %02x\n", rtcin(RTC_YEAR), rtcin(RTC_MONTH), rtcin(RTC_DAY), rtcin(RTC_HRS), rtcin(RTC_MIN), rtcin(RTC_SEC), rtcin(RTC_STATUSA), rtcin(RTC_STATUSB), rtcin(RTC_INTR)); } #endif /* DDB */ Index: stable/11 =================================================================== --- stable/11 (revision 323471) +++ stable/11 (revision 323472) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r322411