#define _COMPONENT ACPI_BUS
ACPI_MODULE_NAME("ACPI")
-#ifdef SMP
-extern void lapic_timer_fixup(void);
-#endif
-
static d_open_t acpiopen;
static d_close_t acpiclose;
static d_ioctl_t acpiioctl;
out:
ACPI_UNLOCK;
-#ifdef SMP
- /*
- * See the comment near lapic_timer_fixup() in
- * platform/pc32/apic/mpapic.c
- */
- lapic_timer_fixup();
-#endif
+ cputimer_intr_pmfixup();
return_VALUE (error);
}
static int
acpi_cpu_set_cx_lowest(struct acpi_cpu_softc *sc, int val)
{
- int i, old_lowest;
+ int i, old_lowest, error = 0;
uint32_t old_type, type;
+ get_mplock();
+
old_lowest = atomic_swap_int(&sc->cpu_cx_lowest, val);
old_type = sc->cpu_cx_states[old_lowest].type;
* All of the CPUs exit C3 state, use a better
* one shot timer.
*/
- cputimer_intr_switch(CPUTIMER_INTRT_FAST);
+ error = cputimer_intr_select_caps(CPUTIMER_INTR_CAP_NONE);
+ KKASSERT(!error);
+ cputimer_intr_restart();
}
} else if (type == ACPI_STATE_C3 && old_type != ACPI_STATE_C3) {
if (atomic_fetchadd_int(&cpu_c3_ncpus, 1) == 0) {
* to an one shot timer, which could handle
* C3 state, i.e. the timer will not hang.
*/
- cputimer_intr_switch(CPUTIMER_INTRT_C3);
+ error = cputimer_intr_select_caps(CPUTIMER_INTR_CAP_PS);
+ if (!error) {
+ cputimer_intr_restart();
+ } else {
+ kprintf("no suitable intr cuptimer found\n");
+
+ /* Restore */
+ sc->cpu_cx_lowest = old_lowest;
+ atomic_fetchadd_int(&cpu_c3_ncpus, -1);
+ }
}
}
+ rel_mplock();
+
+ if (error)
+ return error;
+
/* If not disabling, cache the new lowest non-C3 state. */
sc->cpu_non_c3 = 0;
for (i = sc->cpu_cx_lowest; i >= 0; i--) {
return (EINVAL);
crit_enter();
- acpi_cpu_set_cx_lowest(sc, val);
+ error = acpi_cpu_set_cx_lowest(sc, val);
crit_exit();
- return (0);
+ return error;
}
static int
crit_enter();
for (i = 0; i < cpu_ndevices; i++) {
sc = device_get_softc(cpu_devices[i]);
- acpi_cpu_set_cx_lowest(sc, val);
+ error = acpi_cpu_set_cx_lowest(sc, val);
+ if (error) {
+ KKASSERT(i == 0);
+ break;
+ }
}
crit_exit();
- return (0);
+ return error;
}
/*
SYSCTL_PROC(_kern_cputimer, OID_AUTO, freq, CTLTYPE_INT|CTLFLAG_RD,
NULL, 0, sysctl_cputimer_freq, "I", "");
+static struct cputimer_intr *sys_cputimer_intr;
+static uint32_t cputimer_intr_caps;
+SLIST_HEAD(, cputimer_intr) cputimer_intr_head =
+ SLIST_HEAD_INITIALIZER(&cputimer_intr_head);
+void
+cputimer_intr_register(struct cputimer_intr *cti)
+{
+ struct cputimer_intr *scan;
+
+ SLIST_FOREACH(scan, &cputimer_intr_head, next) {
+ if (scan == cti)
+ return;
+ }
+ cti->config(cti, sys_cputimer);
+ SLIST_INSERT_HEAD(&cputimer_intr_head, cti, next);
+}
+
+void
+cputimer_intr_deregister(struct cputimer_intr *cti)
+{
+ KKASSERT(cti != sys_cputimer_intr);
+ SLIST_REMOVE(&cputimer_intr_head, cti, cputimer_intr, next);
+}
+
+int
+cputimer_intr_select(struct cputimer_intr *cti, int prio)
+{
+ KKASSERT(cti != NULL);
+
+ if (prio == 0)
+ prio = cti->prio;
+
+ if (sys_cputimer_intr == NULL) {
+ KKASSERT(cputimer_intr_caps == 0);
+ sys_cputimer_intr = cti;
+ return 0;
+ }
+
+ if ((cti->caps & cputimer_intr_caps) == cputimer_intr_caps) {
+ if (prio > sys_cputimer_intr->prio) {
+ sys_cputimer_intr = cti;
+ return 0;
+ } else {
+ return EBUSY;
+ }
+ } else {
+ return EOPNOTSUPP;
+ }
+}
+
+void
+cputimer_intr_default_enable(struct cputimer_intr *cti __unused)
+{
+}
+
+void
+cputimer_intr_default_restart(struct cputimer_intr *cti)
+{
+ cti->reload(cti, 0);
+}
+
+void
+cputimer_intr_default_config(struct cputimer_intr *cti __unused,
+ const struct cputimer *timer __unused)
+{
+}
+
+void
+cputimer_intr_default_pmfixup(struct cputimer_intr *cti __unused)
+{
+}
+
+void
+cputimer_intr_default_initclock(struct cputimer_intr *cti __unused,
+ boolean_t selected __unused)
+{
+}
+
+void
+cputimer_intr_enable(void)
+{
+ struct cputimer_intr *cti;
+
+ SLIST_FOREACH(cti, &cputimer_intr_head, next)
+ cti->enable(cti);
+}
+
+void
+cputimer_intr_config(const struct cputimer *timer)
+{
+ struct cputimer_intr *cti;
+
+ SLIST_FOREACH(cti, &cputimer_intr_head, next)
+ cti->config(cti, timer);
+}
+
+void
+cputimer_intr_pmfixup(void)
+{
+ struct cputimer_intr *cti;
+
+ SLIST_FOREACH(cti, &cputimer_intr_head, next)
+ cti->pmfixup(cti);
+}
+
+void
+cputimer_intr_reload(sysclock_t reload)
+{
+ struct cputimer_intr *cti = sys_cputimer_intr;
+
+ cti->reload(cti, reload);
+}
+
+void
+cputimer_intr_restart(void)
+{
+ struct cputimer_intr *cti = sys_cputimer_intr;
+
+ cti->restart(cti);
+}
+
+int
+cputimer_intr_select_caps(uint32_t caps)
+{
+ struct cputimer_intr *cti, *maybe;
+ int error;
+
+ maybe = NULL;
+ SLIST_FOREACH(cti, &cputimer_intr_head, next) {
+ if ((cti->caps & caps) == caps) {
+ if (maybe == NULL)
+ maybe = cti;
+ else if (cti->prio > maybe->prio)
+ maybe = cti;
+ }
+ }
+ if (maybe == NULL)
+ return ENOENT;
+
+ cputimer_intr_caps = caps;
+ error = cputimer_intr_select(maybe, CPUTIMER_INTR_PRIO_MAX);
+ KKASSERT(!error);
+
+ return 0;
+}
+
+static void
+cputimer_intr_initclocks(void)
+{
+ struct cputimer_intr *cti, *ncti;
+
+ /*
+ * An interrupt cputimer may deregister itself,
+ * so use SLIST_FOREACH_MUTABLE here.
+ */
+ SLIST_FOREACH_MUTABLE(cti, &cputimer_intr_head, next, ncti) {
+ boolean_t selected = FALSE;
+
+ if (cti == sys_cputimer_intr)
+ selected = TRUE;
+ cti->initclock(cti, selected);
+ }
+}
+/* NOTE: Must be SECOND to allow platform initialization to go first */
+SYSINIT(cputimer_intr, SI_BOOT2_CLOCKREG, SI_ORDER_SECOND,
+ cputimer_intr_initclocks, NULL)
+
+static int
+sysctl_cputimer_intr_reglist(SYSCTL_HANDLER_ARGS)
+{
+ struct cputimer_intr *scan;
+ int error = 0;
+ int loop = 0;
+
+ /*
+ * Build a list of available interrupt cputimers
+ */
+ SLIST_FOREACH(scan, &cputimer_intr_head, next) {
+ if (error == 0 && loop)
+ error = SYSCTL_OUT(req, " ", 1);
+ if (error == 0)
+ error = SYSCTL_OUT(req, scan->name, strlen(scan->name));
+ ++loop;
+ }
+ return (error);
+}
+
+static int
+sysctl_cputimer_intr_freq(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+
+ error = SYSCTL_OUT(req, &sys_cputimer_intr->freq,
+ sizeof(sys_cputimer_intr->freq));
+ return (error);
+}
+
+static int
+sysctl_cputimer_intr_select(SYSCTL_HANDLER_ARGS)
+{
+ struct cputimer_intr *cti;
+ char name[32];
+ int error;
+
+ ksnprintf(name, sizeof(name), "%s", sys_cputimer_intr->name);
+ error = sysctl_handle_string(oidp, name, sizeof(name), req);
+ if (error != 0 || req->newptr == NULL)
+ return error;
+
+ SLIST_FOREACH(cti, &cputimer_intr_head, next) {
+ if (strcmp(cti->name, name) == 0)
+ break;
+ }
+ if (cti == NULL)
+ return ENOENT;
+ if (cti == sys_cputimer_intr)
+ return 0;
+
+ error = cputimer_intr_select(cti, CPUTIMER_INTR_PRIO_MAX);
+ if (!error)
+ cputimer_intr_restart();
+ return error;
+}
+
+SYSCTL_NODE(_kern_cputimer, OID_AUTO, intr, CTLFLAG_RW, NULL,
+ "interrupt cputimer");
+
+SYSCTL_PROC(_kern_cputimer_intr, OID_AUTO, reglist, CTLTYPE_STRING|CTLFLAG_RD,
+ NULL, 0, sysctl_cputimer_intr_reglist, "A", "");
+SYSCTL_PROC(_kern_cputimer_intr, OID_AUTO, freq, CTLTYPE_INT|CTLFLAG_RD,
+ NULL, 0, sysctl_cputimer_intr_freq, "I", "");
+SYSCTL_PROC(_kern_cputimer_intr, OID_AUTO, select, CTLTYPE_STRING|CTLFLAG_RW,
+ NULL, 0, sysctl_cputimer_intr_select, "A", "");
static void lapic_timer_calibrate(void);
static void lapic_timer_set_divisor(int);
-static void lapic_timer_intr_reload(sysclock_t);
static void lapic_timer_fixup_handler(void *);
static void lapic_timer_restart_handler(void *);
-void lapic_timer_fixup(void);
void lapic_timer_process(void);
void lapic_timer_process_frame(struct intrframe *);
-void lapic_timer_intr_test(void);
-void lapic_timer_oneshot_intr_enable(void);
-void lapic_timer_restart(void);
-
-int lapic_timer_enable = 1;
+static int lapic_timer_enable = 1;
TUNABLE_INT("hw.lapic_timer_enable", &lapic_timer_enable);
+static void lapic_timer_intr_reload(struct cputimer_intr *, sysclock_t);
+static void lapic_timer_intr_enable(struct cputimer_intr *);
+static void lapic_timer_intr_restart(struct cputimer_intr *);
+static void lapic_timer_intr_pmfixup(struct cputimer_intr *);
+
+static struct cputimer_intr lapic_cputimer_intr = {
+ .freq = 0,
+ .reload = lapic_timer_intr_reload,
+ .enable = lapic_timer_intr_enable,
+ .config = cputimer_intr_default_config,
+ .restart = lapic_timer_intr_restart,
+ .pmfixup = lapic_timer_intr_pmfixup,
+ .initclock = cputimer_intr_default_initclock,
+ .next = SLIST_ENTRY_INITIALIZER,
+ .name = "lapic",
+ .type = CPUTIMER_INTR_LAPIC,
+ .prio = CPUTIMER_INTR_PRIO_LAPIC,
+ .caps = CPUTIMER_INTR_CAP_NONE
+};
+
/*
* pointers to pmapped apic hardware.
*/
volatile ioapic_t **ioapic;
-static sysclock_t lapic_timer_freq;
static int lapic_timer_divisor_idx = -1;
static const uint32_t lapic_timer_divisors[] = {
APIC_TDCR_2, APIC_TDCR_4, APIC_TDCR_8, APIC_TDCR_16,
if (bsp) {
lapic_timer_calibrate();
- if (lapic_timer_enable)
- cputimer_intr_reload = lapic_timer_intr_reload;
+ if (lapic_timer_enable) {
+ cputimer_intr_register(&lapic_cputimer_intr);
+ cputimer_intr_select(&lapic_cputimer_intr, 0);
+ }
} else {
lapic_timer_set_divisor(lapic_timer_divisor_idx);
}
}
if (lapic_timer_divisor_idx >= APIC_TIMER_NDIVISORS)
panic("lapic: no proper timer divisor?!\n");
- lapic_timer_freq = value / 2;
+ lapic_cputimer_intr.freq = value / 2;
kprintf("lapic: divisor index %d, frequency %u Hz\n",
- lapic_timer_divisor_idx, lapic_timer_freq);
+ lapic_timer_divisor_idx, lapic_cputimer_intr.freq);
}
static void
lapic_timer_process_oncpu(mycpu, frame);
}
-void
-lapic_timer_intr_test(void)
-{
- struct globaldata *gd = mycpu;
-
- if (!gd->gd_timer_running) {
- gd->gd_timer_running = 1;
- KKASSERT(lapic_timer_freq != 0);
- lapic_timer_oneshot_quick(lapic_timer_freq);
- }
-}
-
static void
-lapic_timer_intr_reload(sysclock_t reload)
+lapic_timer_intr_reload(struct cputimer_intr *cti, sysclock_t reload)
{
struct globaldata *gd = mycpu;
- reload = (int64_t)reload * lapic_timer_freq / sys_cputimer->freq;
+ reload = (int64_t)reload * cti->freq / sys_cputimer->freq;
if (reload < 2)
reload = 2;
}
}
-void
-lapic_timer_oneshot_intr_enable(void)
+static void
+lapic_timer_intr_enable(struct cputimer_intr *cti __unused)
{
uint32_t timer;
* module controls PM. So once ACPI-CA is attached, we try
* to apply the fixup to prevent LAPIC timer from hanging.
*/
-void
-lapic_timer_fixup(void)
+static void
+lapic_timer_intr_pmfixup(struct cputimer_intr *cti __unused)
{
- if (lapic_timer_enable) {
- lwkt_send_ipiq_mask(smp_active_mask,
- lapic_timer_fixup_handler, NULL);
- }
+ lwkt_send_ipiq_mask(smp_active_mask,
+ lapic_timer_fixup_handler, NULL);
}
-void
-lapic_timer_restart(void)
+static void
+lapic_timer_intr_restart(struct cputimer_intr *cti __unused)
{
- KKASSERT(lapic_timer_enable);
- cputimer_intr_reload = lapic_timer_intr_reload;
lwkt_send_ipiq_mask(smp_active_mask, lapic_timer_restart_handler, NULL);
}
* divisor (lapic.dcr_timer is setup during the
* divisor calculation).
*/
- KKASSERT(lapic_timer_freq != 0 &&
+ KKASSERT(lapic_cputimer_intr.freq != 0 &&
lapic_timer_divisor_idx >= 0);
- count = ((us * (int64_t)lapic_timer_freq) + 999999) / 1000000;
+ count = ((us * (int64_t)lapic_cputimer_intr.freq) + 999999) / 1000000;
lapic_timer_oneshot(count);
}
enum tstate timer1_state;
enum tstate timer2_state;
-static void i8254_intr_reload(sysclock_t);
-void (*cputimer_intr_reload)(sysclock_t) = i8254_intr_reload;
-
static int beeping = 0;
static const u_char daysinmonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
static int i8254_cputimer_div;
+static int i8254_nointr;
static int i8254_intr_disable = 0;
TUNABLE_INT("hw.i8254.intr_disable", &i8254_intr_disable);
0, 0, 0
};
+static void i8254_intr_reload(struct cputimer_intr *, sysclock_t);
+static void i8254_intr_config(struct cputimer_intr *, const struct cputimer *);
+static void i8254_intr_initclock(struct cputimer_intr *, boolean_t);
+
+static struct cputimer_intr i8254_cputimer_intr = {
+ .freq = TIMER_FREQ,
+ .reload = i8254_intr_reload,
+ .enable = cputimer_intr_default_enable,
+ .config = i8254_intr_config,
+ .restart = cputimer_intr_default_restart,
+ .pmfixup = cputimer_intr_default_pmfixup,
+ .initclock = i8254_intr_initclock,
+ .next = SLIST_ENTRY_INITIALIZER,
+ .name = "i8254",
+ .type = CPUTIMER_INTR_8254,
+ .prio = CPUTIMER_INTR_PRIO_8254,
+ .caps = CPUTIMER_INTR_CAP_PS
+};
+
/*
* timer0 clock interrupt. Timer0 is in one-shot mode and has stopped
* counting as of this interrupt. We use timer1 in free-running mode (not
* simple shift, multiplication, or division, we do so. Otherwise 64
* bit arithmatic is required every time the interrupt timer is reloaded.
*/
-void
-cputimer_intr_config(struct cputimer *timer)
+static void
+i8254_intr_config(struct cputimer_intr *cti, const struct cputimer *timer)
{
int freq;
int div;
/*
* Will a simple divide do the trick?
*/
- div = (timer->freq + (i8254_cputimer.freq / 2)) / i8254_cputimer.freq;
- freq = i8254_cputimer.freq * div;
+ div = (timer->freq + (cti->freq / 2)) / cti->freq;
+ freq = cti->freq * div;
if (freq >= timer->freq - 1 && freq <= timer->freq + 1)
i8254_cputimer_div = div;
* We may have to convert from the system timebase to the 8254 timebase.
*/
static void
-i8254_intr_reload(sysclock_t reload)
+i8254_intr_reload(struct cputimer_intr *cti, sysclock_t reload)
{
__uint16_t count;
if (i8254_cputimer_div)
reload /= i8254_cputimer_div;
else
- reload = (int64_t)reload * i8254_cputimer.freq / sys_cputimer->freq;
+ reload = (int64_t)reload * cti->freq / sys_cputimer->freq;
if ((int)reload < 2)
reload = 2;
clock_unlock();
}
-#ifdef SMP
-
-extern int lapic_timer_enable;
-extern void lapic_timer_oneshot_intr_enable(void);
-extern void lapic_timer_intr_test(void);
-extern void lapic_timer_restart(void);
-
-#endif /* SMP */
-
-void
-cputimer_intr_enable(void)
-{
-#ifdef SMP
- if (lapic_timer_enable)
- lapic_timer_oneshot_intr_enable();
-#endif
-}
-
-void
-cputimer_intr_switch(enum cputimer_intr_type type)
-{
-#ifdef SMP
- if (!i8254_intr_disable && lapic_timer_enable) {
- switch (type) {
- case CPUTIMER_INTRT_C3:
- cputimer_intr_reload = i8254_intr_reload;
- /* Force a quick reload */
- i8254_intr_reload(0);
- break;
-
- case CPUTIMER_INTRT_FAST:
- lapic_timer_restart();
- break;
- }
- }
-#endif
-}
-
-static int
-sysctl_cputimer_intr_switch(SYSCTL_HANDLER_ARGS)
-{
- enum cputimer_intr_type type = CPUTIMER_INTRT_FAST;
- int error;
-
- error = sysctl_handle_int(oidp, &type, 0, req);
- if (error || req->newptr == NULL)
- return error;
- switch (type) {
- case CPUTIMER_INTRT_C3:
- case CPUTIMER_INTRT_FAST:
- break;
- default:
- return EINVAL;
- }
- cputimer_intr_switch(type);
- return 0;
-}
-SYSCTL_PROC(_hw, OID_AUTO, cputimer_intr_type, CTLTYPE_INT | CTLFLAG_RW,
- 0, 0, sysctl_cputimer_intr_switch, "I",
- "cputimer_intr switch [0|1]");
-
/*
* DELAY(usec) - Spin for the specified number of microseconds.
* DRIVERSLEEP(usec) - Spin for the specified number of microseconds,
outb(TIMER_CNTR0, 0); /* msb */
clock_unlock();
+ if (!i8254_nointr) {
+ cputimer_intr_register(&i8254_cputimer_intr);
+ cputimer_intr_select(&i8254_cputimer_intr, 0);
+ }
+
/*
* Timer1 or timer2 is our free-running clock, but only if another
* has not been selected.
"CLK_USE_I8254_CALIBRATION not specified - using default frequency\n");
freq = i8254_cputimer.freq;
#endif
+ /*
+ * NOTE:
+ * Interrupt timer's freq must be adjusted
+ * before we change the cuptimer's frequency.
+ */
+ i8254_cputimer_intr.freq = freq;
cputimer_set_frequency(&i8254_cputimer, freq);
} else {
if (bootverbose)
* used. Instead, 8254 based systimers are used for all major clock
* interrupts. statclock_disable is set by default.
*/
-void
-cpu_initclocks(void *arg __unused)
+static void
+i8254_intr_initclock(struct cputimer_intr *cti, boolean_t selected)
{
int diag;
#ifdef APIC_IO
callout_init(&sysbeepstop_ch);
-#ifdef SMP
- if (lapic_timer_enable && i8254_intr_disable)
+ if (!selected && i8254_intr_disable) {
+ i8254_nointr = 1; /* don't try to register again */
+ cputimer_intr_deregister(cti);
return;
-#endif
+ }
if (statclock_disable) {
/*
* so make sure it is.
*/
KKASSERT(sys_cputimer == &i8254_cputimer);
+ KKASSERT(cti == &i8254_cputimer_intr);
lastcnt = get_interrupt_counter(apic_8254_intr);
* it to happen, then see if we got it.
*/
kprintf("APIC_IO: Testing 8254 interrupt delivery\n");
- i8254_intr_reload(2);
+ i8254_intr_reload(cti, 2);
base = sys_cputimer->count();
while (sys_cputimer->count() - base < sys_cputimer->freq / 100)
; /* nothing */
INTR_NOENTROPY);
machintr_intren(apic_8254_intr);
}
-
}
if (apic_int_type(0, 0) != 3 ||
int_to_apicintpin[apic_8254_intr].ioapic != 0 ||
}
#endif
}
-SYSINIT(clocks8254, SI_BOOT2_CLOCKREG, SI_ORDER_FIRST, cpu_initclocks, NULL)
#ifdef APIC_IO
#include <unistd.h>
#include <signal.h>
-static void cputimer_intr(void *dummy, struct intrframe *frame);
+#define VKTIMER_FREQ 1000000 /* 1us granularity */
+
+static void vktimer_intr(void *dummy, struct intrframe *frame);
int disable_rtc_set;
SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set,
static int cputimer_mib[16];
static int cputimer_miblen;
-static void vkernel_intr_reload(sysclock_t);
-void (*cputimer_intr_reload)(sysclock_t) = vkernel_intr_reload;
-
-
/*
* SYSTIMER IMPLEMENTATION
*/
cputimer_default_fromus,
vkernel_timer_construct,
cputimer_default_destruct,
- 1000000, /* 1us granularity */
+ VKTIMER_FREQ,
0, 0, 0
};
+static void vktimer_intr_reload(struct cputimer_intr *, sysclock_t);
+static void vktimer_intr_initclock(struct cputimer_intr *);
+
+static struct cputimer_intr vkernel_cputimer_intr = {
+ .freq = VKTIMER_FREQ,
+ .reload = vktimer_intr_reload,
+ .enable = cputimer_intr_default_enable,
+ .config = cputimer_intr_default_config,
+ .restart = cputimer_intr_default_restart,
+ .pmfixup = cputimer_intr_default_pmfixup,
+ .initclock = vktimer_intr_initclock,
+ .next = SLIST_ENTRY_INITIALIZER,
+ .name = "vkernel",
+ .type = CPUTIMER_INTR_VKERNEL,
+ .prio = CPUTIMER_INTR_PRIO_VKERNEL,
+ .caps = CPUTIMER_INTR_CAP_NONE
+};
+
/*
* Initialize the systimer subsystem, called from MI code in early boot.
*/
if (sysctlnametomib("kern.cputimer.clock", cputimer_mib, &len) < 0)
panic("cpu_initclocks: can't get kern.cputimer.clock!");
cputimer_miblen = len;
+
+ cputimer_intr_register(&vkernel_cputimer_intr);
+ cputimer_intr_select(&vkernel_cputimer_intr, 0);
+
cputimer_register(&vkernel_cputimer);
cputimer_select(&vkernel_cputimer, 0);
}
timer->base = oclock - vkernel_timer_get_timecount();
}
-void
-cputimer_intr_enable(void)
-{
-}
-
-void
-cputimer_intr_switch(enum cputimer_intr_type type)
-{
-}
-
/*
* Get the current counter, with 2's complement rollover.
*
}
/*
- * Configure the interrupt for our core systimer. Use the kqueue timer
+ * Initialize the interrupt for our core systimer. Use the kqueue timer
* support functions.
*/
-void
-cputimer_intr_config(struct cputimer *timer)
+static void
+vktimer_intr_initclock(struct cputimer_intr *cti __unused)
{
- kqueue_timer_info = kqueue_add_timer(cputimer_intr, NULL);
+ KKASSERT(kqueue_timer_info == NULL);
+ kqueue_timer_info = kqueue_add_timer(vktimer_intr, NULL);
}
/*
* check to ensure that a reasonable reload value is selected.
*/
static void
-vkernel_intr_reload(sysclock_t reload)
+vktimer_intr_reload(struct cputimer_intr *cti __unused, sysclock_t reload)
{
if (kqueue_timer_info) {
if ((int)reload < 1)
* NOTE: frame is a struct intrframe pointer.
*/
static void
-cputimer_intr(void *dummy, struct intrframe *frame)
+vktimer_intr(void *dummy, struct intrframe *frame)
{
static sysclock_t sysclock_count;
struct globaldata *gd = mycpu;
#define CPUTIMER_PRI_HPET 15
#define CPUTIMER_PRI_VKERNEL 20
-enum cputimer_intr_type {
- CPUTIMER_INTRT_C3 = 0,
- CPUTIMER_INTRT_FAST
-};
-
void cputimer_select(struct cputimer *, int);
void cputimer_register(struct cputimer *);
void cputimer_deregister(struct cputimer *);
void cputimer_default_construct(struct cputimer *, sysclock_t);
void cputimer_default_destruct(struct cputimer *);
+/*
+ * Interrupt cputimer interface.
+ *
+ * Interrupt cputimers are normally one shot timers which will
+ * generate interrupt upon expiration.
+ *
+ * initclock -- Called at SI_BOOT2_CLOCKREG, SI_ORDER_SECOND. The
+ * interrupt timer could deregister itself here, if it
+ * is not the selected system interrupt cputimer. Before
+ * this function is called, 'enable' and 'reload' will
+ * not be called.
+ * enable -- Enable interrupt. It is called by each CPU. It is
+ * only called once during boot. Before this function
+ * is called, 'reload' will not be called.
+ * reload -- Called by each CPU when it wants to to reprogram the
+ * one shot timer expiration time. The reload value is
+ * measured in sys_cputimer->freq.
+ * config -- Setup the interrupt cputimer according to the passed
+ * in non-interrupt cputimer. It will be called when
+ * sys_cputimer's frequency is changed or when sys_cputimer
+ * itself is changed. It is also called when this interrupt
+ * cputimer gets registered.
+ * restart -- Start the possibly stalled interrupt cputimer immediately.
+ * Do fixup if necessary.
+ * pmfixup -- Called after ACPI power management is enabled.
+ */
+struct cputimer_intr {
+ sysclock_t freq;
+ void (*reload)
+ (struct cputimer_intr *, sysclock_t);
+ void (*enable)
+ (struct cputimer_intr *);
+ void (*config)
+ (struct cputimer_intr *, const struct cputimer *);
+ void (*restart)
+ (struct cputimer_intr *);
+ void (*pmfixup)
+ (struct cputimer_intr *);
+ void (*initclock)
+ (struct cputimer_intr *, boolean_t);
+ SLIST_ENTRY(cputimer_intr) next;
+ const char *name;
+ int type; /* CPUTIMER_INTR_ */
+ int prio; /* CPUTIMER_INTR_PRIO_ */
+ uint32_t caps; /* CPUTIMER_INTR_CAP_ */
+};
+
+#define CPUTIMER_INTR_8254 0
+#define CPUTIMER_INTR_LAPIC 1
+#define CPUTIMER_INTR_VKERNEL 2
+
+/* NOTE: Keep the new values less than CPUTIMER_INTR_PRIO_MAX */
+#define CPUTIMER_INTR_PRIO_8254 0
+#define CPUTIMER_INTR_PRIO_LAPIC 10
+#define CPUTIMER_INTR_PRIO_VKERNEL 20
+#define CPUTIMER_INTR_PRIO_MAX 1000
+
+#define CPUTIMER_INTR_CAP_NONE 0
+#define CPUTIMER_INTR_CAP_PS 0x1 /* works during powersaving */
+
+/*
+ * Interrupt cputimer implementation interfaces
+ *
+ * NOTE:
+ * cputimer_intr_deregister() is _not_ allowed to be called
+ * with the currently selected interrupt cputimer.
+ */
+void cputimer_intr_register(struct cputimer_intr *);
+void cputimer_intr_deregister(struct cputimer_intr *);
+int cputimer_intr_select(struct cputimer_intr *, int);
+
+/*
+ * Interrupt cputimer implementation helper functions
+ *
+ * default_enable -- NOP
+ * default_restart -- reload(0)
+ * default_config -- NOP
+ * default_pmfixup -- NOP
+ * default_initclock -- NOP
+ */
+void cputimer_intr_default_enable(struct cputimer_intr *);
+void cputimer_intr_default_restart(struct cputimer_intr *);
+void cputimer_intr_default_config(struct cputimer_intr *,
+ const struct cputimer *);
+void cputimer_intr_default_pmfixup(struct cputimer_intr *);
+void cputimer_intr_default_initclock(struct cputimer_intr *, boolean_t);
+
+/*
+ * Interrupt cputimer external interfaces
+ */
void cputimer_intr_enable(void);
-void cputimer_intr_config(struct cputimer *);
-extern void (*cputimer_intr_reload)(sysclock_t);
-void cputimer_intr_switch(enum cputimer_intr_type);
+void cputimer_intr_pmfixup(void);
+void cputimer_intr_config(const struct cputimer *);
+void cputimer_intr_reload(sysclock_t);
+void cputimer_intr_restart(void);
+int cputimer_intr_select_caps(uint32_t);
#endif /* !_SYS_SYSTIMER_H_ */