summaryrefslogtreecommitdiffstats
path: root/sys/x86/isa
diff options
context:
space:
mode:
authormav <mav@FreeBSD.org>2010-06-20 21:33:29 +0000
committermav <mav@FreeBSD.org>2010-06-20 21:33:29 +0000
commitd1175426d7bcb8aeacdb7416e281e63e668da297 (patch)
tree0950027f8a4d7f49ad3b3ae792b390e08763c5c5 /sys/x86/isa
parent75f3660605fcdf678edb8498311bba6ec652af82 (diff)
downloadFreeBSD-src-d1175426d7bcb8aeacdb7416e281e63e668da297.zip
FreeBSD-src-d1175426d7bcb8aeacdb7416e281e63e668da297.tar.gz
Implement new event timers infrastructure. It provides unified APIs for
writing event timer drivers, for choosing best possible drivers by machine independent code and for operating them to supply kernel with hardclock(), statclock() and profclock() events in unified fashion on various hardware. Infrastructure provides support for both per-CPU (independent for every CPU core) and global timers in periodic and one-shot modes. MI management code at this moment uses only periodic mode, but one-shot mode use planned for later, as part of tickless kernel project. For this moment infrastructure used on i386 and amd64 architectures. Other archs are welcome to follow, while their current operation should not be affected. This patch updates existing drivers (i8254, RTC and LAPIC) for the new order, and adds event timers support into the HPET driver. These drivers have different capabilities: LAPIC - per-CPU timer, supports periodic and one-shot operation, may freeze in C3 state, calibrated on first use, so may be not exactly precise. HPET - depending on hardware can work as per-CPU or global, supports periodic and one-shot operation, usually provides several event timers. i8254 - global, limited to periodic mode, because same hardware used also as time counter. RTC - global, supports only periodic mode, set of frequencies in Hz limited by powers of 2. Depending on hardware capabilities, drivers preferred in following orders, either LAPIC, HPETs, i8254, RTC or HPETs, LAPIC, i8254, RTC. User may explicitly specify wanted timers via loader tunables or sysctls: kern.eventtimer.timer1 and kern.eventtimer.timer2. If requested driver is unavailable or unoperational, system will try to replace it. If no more timers available or "NONE" specified for second, system will operate using only one timer, multiplying it's frequency by few times and uing respective dividers to honor hz, stathz and profhz values, set during initial setup.
Diffstat (limited to 'sys/x86/isa')
-rw-r--r--sys/x86/isa/atrtc.c143
-rw-r--r--sys/x86/isa/clock.c334
2 files changed, 221 insertions, 256 deletions
diff --git a/sys/x86/isa/atrtc.c b/sys/x86/isa/atrtc.c
index 777c720..a7cd7f1 100644
--- a/sys/x86/isa/atrtc.c
+++ b/sys/x86/isa/atrtc.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2008 Poul-Henning Kamp
+ * Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -39,18 +40,21 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/kernel.h>
#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
#include <isa/rtc.h>
#ifdef DEV_ISA
#include <isa/isareg.h>
#include <isa/isavar.h>
#endif
+#include <machine/intr_machdep.h>
+#include "clock_if.h"
#define RTC_LOCK mtx_lock_spin(&clock_lock)
#define RTC_UNLOCK mtx_unlock_spin(&clock_lock)
-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;
@@ -98,7 +102,7 @@ readrtc(int port)
return(bcd2bin(rtcin(port)));
}
-void
+static void
atrtc_start(void)
{
@@ -106,7 +110,7 @@ atrtc_start(void)
writertc(RTC_STATUSB, RTCSB_24HR);
}
-void
+static void
atrtc_rate(unsigned rate)
{
@@ -114,7 +118,7 @@ atrtc_rate(unsigned rate)
writertc(RTC_STATUSA, rtc_statusa);
}
-void
+static void
atrtc_enable_intr(void)
{
@@ -123,6 +127,15 @@ atrtc_enable_intr(void)
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)
{
@@ -135,41 +148,74 @@ atrtc_restore(void)
rtcin(RTC_INTR);
}
-int
-atrtc_setup_clock(void)
-{
- int diag;
-
- if (atrtcclock_disable)
- return (0);
-
- diag = rtcin(RTC_DIAG);
- if (diag != 0) {
- printf("RTC BIOS diagnostic error %b\n",
- diag, RTCDG_BITS);
- return (0);
- }
-
- stathz = RTC_NOPROFRATE;
- profhz = RTC_PROFRATE;
-
- return (1);
-}
-
/**********************************************************************
* RTC driver for subr_rtc
*/
-#include "clock_if.h"
-
-#include <sys/rman.h>
-
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,
+ struct bintime *first, struct bintime *period)
+{
+
+ atrtc_rate(max(fls((period->frac + (period->frac >> 1)) >> 32) - 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 ? sc->et.et_arg :
+ curthread->td_intr_frame);
+ }
+ }
+ return(flag ? FILTER_HANDLED : FILTER_STRAY);
+}
+
/*
* Attach to the ISA PnP descriptors for the timer and realtime clock.
*/
@@ -196,23 +242,42 @@ static int
atrtc_attach(device_t dev)
{
struct atrtc_softc *sc;
- int i;
+ int i, diag, haveirq = 0;
- /*
- * Not that we need them or anything, but grab our resources
- * so they show up, correctly attributed, in the big picture.
- */
-
sc = device_get_softc(dev);
if (!(sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sc->port_rid, IO_RTC, IO_RTC + 1, 2, RF_ACTIVE)))
device_printf(dev,"Warning: Couldn't map I/O.\n");
if (!(sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ,
&sc->intr_rid, 8, 8, 1, RF_ACTIVE)))
- device_printf(dev,"Warning: Couldn't map Interrupt.\n");
+ device_printf(dev,"Couldn't map Interrupt.\n");
+ else if ((bus_setup_intr(dev, sc->intr_res,
+ INTR_MPSAFE | INTR_TYPE_CLK, (driver_filter_t *)rtc_intr, NULL,
+ sc, &sc->intr_handler))) {
+ device_printf(dev, "Can't setup interrupt.\n");
+ } else {
+ haveirq = 1;
+ /* Bind IRQ to BSP to avoid live migration. */
+ bus_bind_intr(dev, sc->intr_res, 0);
+ }
+ diag = rtcin(RTC_DIAG);
+ if (diag != 0)
+ printf("RTC BIOS diagnostic error %b\n", diag, RTCDG_BITS);
+ atrtc_start();
clock_register(dev, 1000000);
- if (resource_int_value("atrtc", 0, "clock", &i) == 0 && i == 0)
- atrtcclock_disable = 1;
+ bzero(&sc->et, sizeof(struct eventtimer));
+ if (haveirq &&
+ (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "clock", &i) != 0 || i != 0)) {
+ sc->et.et_name = "RTC";
+ sc->et.et_flags = ET_FLAGS_PERIODIC;
+ sc->et.et_quality = 0;
+ sc->et.et_frequency = 32768;
+ sc->et.et_start = rtc_start;
+ sc->et.et_stop = rtc_stop;
+ sc->et.et_priv = dev;
+ et_register(&sc->et);
+ }
return(0);
}
diff --git a/sys/x86/isa/clock.c b/sys/x86/isa/clock.c
index b0d49d5..5929f5b 100644
--- a/sys/x86/isa/clock.c
+++ b/sys/x86/isa/clock.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 1990 The Regents of the University of California.
+ * Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
@@ -39,11 +40,7 @@ __FBSDID("$FreeBSD$");
* Routines to handle clock hardware.
*/
-#ifndef __amd64__
-#include "opt_apic.h"
-#endif
#include "opt_clock.h"
-#include "opt_kdtrace.h"
#include "opt_isa.h"
#include "opt_mca.h"
@@ -54,21 +51,20 @@ __FBSDID("$FreeBSD$");
#include <sys/kdb.h>
#include <sys/mutex.h>
#include <sys/proc.h>
-#include <sys/timetc.h>
#include <sys/kernel.h>
#include <sys/module.h>
+#include <sys/rman.h>
#include <sys/sched.h>
#include <sys/smp.h>
#include <sys/sysctl.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
#include <machine/clock.h>
#include <machine/cpu.h>
#include <machine/intr_machdep.h>
-#include <machine/md_var.h>
-#include <machine/apicvar.h>
#include <machine/ppireg.h>
#include <machine/timerreg.h>
-#include <machine/smp.h>
#include <isa/rtc.h>
#ifdef DEV_ISA
@@ -80,12 +76,6 @@ __FBSDID("$FreeBSD$");
#include <i386/bios/mca_machdep.h>
#endif
-#ifdef KDTRACE_HOOKS
-#include <sys/dtrace_bsd.h>
-#endif
-
-#define TIMER_DIV(x) ((i8254_freq + (x) / 2) / (x))
-
int clkintr_pending;
#ifndef TIMER_FREQ
#define TIMER_FREQ 1193182
@@ -95,17 +85,23 @@ TUNABLE_INT("hw.i8254.freq", &i8254_freq);
int i8254_max_count;
static int i8254_real_max_count;
-static int lapic_allclocks = 1;
-TUNABLE_INT("machdep.lapic_allclocks", &lapic_allclocks);
-
struct mtx clock_lock;
static struct intsrc *i8254_intsrc;
-static u_int32_t i8254_lastcount;
-static u_int32_t i8254_offset;
+static uint16_t i8254_lastcount;
+static uint16_t i8254_offset;
static int (*i8254_pending)(struct intsrc *);
static int i8254_ticked;
-static int using_atrtc_timer;
-static enum lapic_clock using_lapic_timer = LAPIC_CLOCK_NONE;
+
+struct attimer_softc {
+ int intr_en;
+ int intr_rid;
+ struct resource *intr_res;
+ void *intr_handler;
+ struct timecounter tc;
+ struct eventtimer et;
+ uint32_t intr_period;
+};
+static struct attimer_softc *attimer_sc = NULL;
/* Values for timerX_state: */
#define RELEASED 0
@@ -116,39 +112,14 @@ static enum lapic_clock using_lapic_timer = LAPIC_CLOCK_NONE;
static u_char timer2_state;
static unsigned i8254_get_timecount(struct timecounter *tc);
-static unsigned i8254_simple_get_timecount(struct timecounter *tc);
-static void set_i8254_freq(u_int freq, int intr_freq);
-
-static struct timecounter i8254_timecounter = {
- i8254_get_timecount, /* get_timecount */
- 0, /* no poll_pps */
- ~0u, /* counter_mask */
- 0, /* frequency */
- "i8254", /* name */
- 0 /* quality */
-};
-
-int
-hardclockintr(struct trapframe *frame)
-{
-
- timer1clock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
- return (FILTER_HANDLED);
-}
-
-int
-statclockintr(struct trapframe *frame)
-{
-
- timer2clock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
- return (FILTER_HANDLED);
-}
+static void set_i8254_freq(u_int freq, uint32_t intr_period);
static int
-clkintr(struct trapframe *frame)
+clkintr(void *arg)
{
+ struct attimer_softc *sc = (struct attimer_softc *)arg;
- if (timecounter->tc_get_timecount == i8254_get_timecount) {
+ if (sc->intr_period != 0) {
mtx_lock_spin(&clock_lock);
if (i8254_ticked)
i8254_ticked = 0;
@@ -159,25 +130,11 @@ clkintr(struct trapframe *frame)
clkintr_pending = 0;
mtx_unlock_spin(&clock_lock);
}
- KASSERT(using_lapic_timer == LAPIC_CLOCK_NONE,
- ("clk interrupt enabled with lapic timer"));
-#ifdef KDTRACE_HOOKS
- /*
- * If the DTrace hooks are configured and a callback function
- * has been registered, then call it to process the high speed
- * timers.
- */
- int cpu = PCPU_GET(cpuid);
- if (cyclic_clock_func[cpu] != NULL)
- (*cyclic_clock_func[cpu])(frame);
-#endif
-
-#ifdef SMP
- if (smp_started)
- ipi_all_but_self(IPI_HARDCLOCK);
-#endif
- hardclockintr(frame);
+ if (sc && sc->et.et_active) {
+ sc->et.et_event_cb(&sc->et,
+ sc->et.et_arg ? sc->et.et_arg : curthread->td_intr_frame);
+ }
#ifdef DEV_MCA
/* Reset clock interrupt by asserting bit 7 of port 0x61 */
@@ -233,43 +190,6 @@ timer_spkr_setfreq(int freq)
mtx_unlock_spin(&clock_lock);
}
-/*
- * 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
-rtcintr(struct trapframe *frame)
-{
- int flag = 0;
-
- while (rtcin(RTC_INTR) & RTCIR_PERIOD) {
- flag = 1;
-#ifdef SMP
- if (smp_started)
- ipi_all_but_self(IPI_STATCLOCK);
-#endif
- statclockintr(frame);
- }
- return(flag ? FILTER_HANDLED : FILTER_STRAY);
-}
-
static int
getit(void)
{
@@ -405,17 +325,18 @@ DELAY(int n)
}
static void
-set_i8254_freq(u_int freq, int intr_freq)
+set_i8254_freq(u_int freq, uint32_t intr_period)
{
int new_i8254_real_max_count;
- i8254_timecounter.tc_frequency = freq;
mtx_lock_spin(&clock_lock);
i8254_freq = freq;
- if (using_lapic_timer != LAPIC_CLOCK_NONE)
+ if (intr_period == 0)
new_i8254_real_max_count = 0x10000;
- else
- new_i8254_real_max_count = TIMER_DIV(intr_freq);
+ else {
+ new_i8254_real_max_count =
+ min(((uint64_t)i8254_freq * intr_period) >> 32, 0x10000);
+ }
if (new_i8254_real_max_count != i8254_real_max_count) {
i8254_real_max_count = new_i8254_real_max_count;
if (i8254_real_max_count == 0x10000)
@@ -465,113 +386,22 @@ i8254_init(void)
{
mtx_init(&clock_lock, "clk", NULL, MTX_SPIN | MTX_NOPROFILE);
- set_i8254_freq(i8254_freq, hz);
+ set_i8254_freq(i8254_freq, 0);
}
void
startrtclock()
{
- atrtc_start();
-
- set_i8254_freq(i8254_freq, hz);
- tc_init(&i8254_timecounter);
-
init_TSC();
}
-/*
- * Start both clocks running.
- */
void
-cpu_initclocks()
+cpu_initclocks(void)
{
-#if defined(__amd64__) || defined(DEV_APIC)
- enum lapic_clock tlsca;
-#endif
- int tasc;
-
- /* Initialize RTC. */
- atrtc_start();
- tasc = atrtc_setup_clock();
-
- /*
- * If the atrtc successfully initialized and the users didn't force
- * otherwise use the LAPIC in order to cater hardclock only, otherwise
- * take in charge all the clock sources.
- */
-#if defined(__amd64__) || defined(DEV_APIC)
- tlsca = (lapic_allclocks == 0 && tasc != 0) ? LAPIC_CLOCK_HARDCLOCK :
- LAPIC_CLOCK_ALL;
- using_lapic_timer = lapic_setup_clock(tlsca);
-#endif
- /*
- * If we aren't using the local APIC timer to drive the kernel
- * clocks, setup the interrupt handler for the 8254 timer 0 so
- * that it can drive hardclock(). Otherwise, change the 8254
- * timecounter to user a simpler algorithm.
- */
- if (using_lapic_timer == LAPIC_CLOCK_NONE) {
- timer1hz = hz;
- intr_add_handler("clk", 0, (driver_filter_t *)clkintr, NULL,
- NULL, INTR_TYPE_CLK, NULL);
- i8254_intsrc = intr_lookup_source(0);
- if (i8254_intsrc != NULL)
- i8254_pending =
- i8254_intsrc->is_pic->pic_source_pending;
- } else {
- i8254_timecounter.tc_get_timecount =
- i8254_simple_get_timecount;
- i8254_timecounter.tc_counter_mask = 0xffff;
- set_i8254_freq(i8254_freq, hz);
- }
-
- /*
- * If the separate statistics clock hasn't been explicility disabled
- * and we aren't already using the local APIC timer to drive the
- * kernel clocks, then setup the RTC to periodically interrupt to
- * drive statclock() and profclock().
- */
- if (using_lapic_timer != LAPIC_CLOCK_ALL) {
- using_atrtc_timer = tasc;
- if (using_atrtc_timer) {
- timer2hz = RTC_NOPROFRATE;
- /* Enable periodic interrupts from the RTC. */
- intr_add_handler("rtc", 8,
- (driver_filter_t *)rtcintr, NULL, NULL,
- INTR_TYPE_CLK, NULL);
- atrtc_enable_intr();
- } else {
- profhz = hz;
- if (hz < 128)
- stathz = hz;
- else
- stathz = hz / (hz / 128);
- timer2hz = 0;
- }
- }
init_TSC_tc();
-}
-
-void
-cpu_startprofclock(void)
-{
-
- if (using_lapic_timer == LAPIC_CLOCK_ALL || !using_atrtc_timer)
- return;
- atrtc_rate(RTCSA_PROF);
- timer2hz = RTC_PROFRATE;
-}
-
-void
-cpu_stopprofclock(void)
-{
-
- if (using_lapic_timer == LAPIC_CLOCK_ALL || !using_atrtc_timer)
- return;
- atrtc_rate(RTCSA_NOPROF);
- timer2hz = RTC_NOPROFRATE;
+ cpu_initclocks_bsp();
}
static int
@@ -586,8 +416,14 @@ sysctl_machdep_i8254_freq(SYSCTL_HANDLER_ARGS)
*/
freq = i8254_freq;
error = sysctl_handle_int(oidp, &freq, 0, req);
- if (error == 0 && req->newptr != NULL)
- set_i8254_freq(freq, hz);
+ if (error == 0 && req->newptr != NULL) {
+ if (attimer_sc) {
+ set_i8254_freq(freq, attimer_sc->intr_period);
+ attimer_sc->tc.tc_frequency = freq;
+ } else {
+ set_i8254_freq(freq, 0);
+ }
+ }
return (error);
}
@@ -595,19 +431,17 @@ SYSCTL_PROC(_machdep, OID_AUTO, i8254_freq, CTLTYPE_INT | CTLFLAG_RW,
0, sizeof(u_int), sysctl_machdep_i8254_freq, "IU", "");
static unsigned
-i8254_simple_get_timecount(struct timecounter *tc)
-{
-
- return (i8254_max_count - getit());
-}
-
-static unsigned
i8254_get_timecount(struct timecounter *tc)
{
+ device_t dev = (device_t)tc->tc_priv;
+ struct attimer_softc *sc = device_get_softc(dev);
register_t flags;
- u_int count;
+ uint16_t count;
u_int high, low;
+ if (sc->intr_period == 0)
+ return (i8254_max_count - getit());
+
#ifdef __amd64__
flags = read_rflags();
#else
@@ -635,6 +469,33 @@ i8254_get_timecount(struct timecounter *tc)
return (count);
}
+static int
+attimer_start(struct eventtimer *et,
+ struct bintime *first, struct bintime *period)
+{
+ device_t dev = (device_t)et->et_priv;
+ struct attimer_softc *sc = device_get_softc(dev);
+
+ sc->intr_period = period->frac >> 32;
+ set_i8254_freq(i8254_freq, sc->intr_period);
+ if (!sc->intr_en) {
+ i8254_intsrc->is_pic->pic_enable_source(i8254_intsrc);
+ sc->intr_en = 1;
+ }
+ return (0);
+}
+
+static int
+attimer_stop(struct eventtimer *et)
+{
+ device_t dev = (device_t)et->et_priv;
+ struct attimer_softc *sc = device_get_softc(dev);
+
+ sc->intr_period = 0;
+ set_i8254_freq(i8254_freq, sc->intr_period);
+ return (0);
+}
+
#ifdef DEV_ISA
/*
* Attach to the ISA PnP descriptors for the timer
@@ -650,14 +511,53 @@ attimer_probe(device_t dev)
int result;
result = ISA_PNP_PROBE(device_get_parent(dev), dev, attimer_ids);
- if (result <= 0)
- device_quiet(dev);
return(result);
}
static int
attimer_attach(device_t dev)
{
+ struct attimer_softc *sc;
+ int i;
+
+ attimer_sc = sc = device_get_softc(dev);
+ bzero(sc, sizeof(struct attimer_softc));
+ if (!(sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ,
+ &sc->intr_rid, 0, 0, 1, RF_ACTIVE)))
+ device_printf(dev,"Warning: Couldn't map Interrupt.\n");
+ i8254_intsrc = intr_lookup_source(0);
+ if (i8254_intsrc != NULL)
+ i8254_pending = i8254_intsrc->is_pic->pic_source_pending;
+ set_i8254_freq(i8254_freq, 0);
+ sc->tc.tc_get_timecount = i8254_get_timecount;
+ sc->tc.tc_counter_mask = 0xffff;
+ sc->tc.tc_frequency = i8254_freq;
+ sc->tc.tc_name = "i8254";
+ sc->tc.tc_quality = 0;
+ sc->tc.tc_priv = dev;
+ tc_init(&sc->tc);
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "clock", &i) != 0 || i != 0) {
+ /* Dirty hack, to make bus_setup_intr to not enable source. */
+ i8254_intsrc->is_handlers++;
+ if ((bus_setup_intr(dev, sc->intr_res,
+ INTR_MPSAFE | INTR_TYPE_CLK,
+ (driver_filter_t *)clkintr, NULL,
+ sc, &sc->intr_handler))) {
+ device_printf(dev, "Can't setup interrupt.\n");
+ } else {
+ i8254_intsrc->is_pic->pic_enable_intr(i8254_intsrc);
+ sc->et.et_name = "i8254";
+ sc->et.et_flags = ET_FLAGS_PERIODIC;
+ sc->et.et_quality = 100;
+ sc->et.et_frequency = i8254_freq;
+ sc->et.et_start = attimer_start;
+ sc->et.et_stop = attimer_stop;
+ sc->et.et_priv = dev;
+ et_register(&sc->et);
+ }
+ i8254_intsrc->is_handlers--;
+ }
return(0);
}
@@ -683,7 +583,7 @@ static device_method_t attimer_methods[] = {
static driver_t attimer_driver = {
"attimer",
attimer_methods,
- 1, /* no softc */
+ sizeof(struct attimer_softc),
};
static devclass_t attimer_devclass;
OpenPOWER on IntegriCloud