Add interrupt cputimer interface.
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 17 May 2009 04:08:38 +0000 (12:08 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 17 May 2009 08:20:53 +0000 (16:20 +0800)
- Better abstraction
- Easier to extend, e.g. add another interrupt cputimer
- No lapic timer pollution in isa/clock.c

sys/dev/acpica5/acpi.c
sys/dev/acpica5/acpi_cpu.c
sys/kern/kern_cputimer.c
sys/platform/pc32/apic/mpapic.c
sys/platform/pc32/isa/clock.c
sys/platform/vkernel/platform/systimer.c
sys/sys/systimer.h

index 4e570bd..71ab4f1 100644 (file)
@@ -65,10 +65,6 @@ MALLOC_DEFINE(M_ACPIDEV, "acpidev", "ACPI devices");
 #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;
@@ -699,13 +695,7 @@ acpi_attach(device_t dev)
  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);
 }
 
index cf944d7..511ffcb 100644 (file)
@@ -1131,9 +1131,11 @@ acpi_cpu_usage_sysctl(SYSCTL_HANDLER_ARGS)
 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;
@@ -1145,7 +1147,9 @@ acpi_cpu_set_cx_lowest(struct acpi_cpu_softc *sc, int val)
             * 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) {
@@ -1154,10 +1158,24 @@ acpi_cpu_set_cx_lowest(struct acpi_cpu_softc *sc, int val)
             * 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--) {
@@ -1191,10 +1209,10 @@ acpi_cpu_cx_lowest_sysctl(SYSCTL_HANDLER_ARGS)
        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
@@ -1219,11 +1237,15 @@ acpi_cpu_global_cx_lowest_sysctl(SYSCTL_HANDLER_ARGS)
     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;
 }
 
 /*
index 7ef1184..7c2a84f 100644 (file)
@@ -274,4 +274,237 @@ SYSCTL_PROC(_kern_cputimer, OID_AUTO, clock, CTLTYPE_UINT|CTLFLAG_RD,
 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", "");
index 9e33191..598d622 100644 (file)
 
 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,
@@ -173,8 +186,10 @@ apic_initialize(boolean_t bsp)
 
        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);
        }
@@ -226,10 +241,10 @@ lapic_timer_calibrate(void)
        }
        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
@@ -256,24 +271,12 @@ lapic_timer_process_frame(struct intrframe *frame)
        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;
 
@@ -286,8 +289,8 @@ lapic_timer_intr_reload(sysclock_t reload)
        }
 }
 
-void
-lapic_timer_oneshot_intr_enable(void)
+static void
+lapic_timer_intr_enable(struct cputimer_intr *cti __unused)
 {
        uint32_t timer;
 
@@ -365,20 +368,16 @@ lapic_timer_restart_handler(void *dummy __unused)
  *   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);
 }
 
@@ -929,10 +928,10 @@ set_apic_timer(int us)
         * 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);
 }
 
index 8ba1e12..c608039 100644 (file)
@@ -124,9 +124,6 @@ enum tstate timer0_state;
 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;
@@ -135,6 +132,7 @@ static  int rtc_loaded;
 
 static int i8254_cputimer_div;
 
+static int i8254_nointr;
 static int i8254_intr_disable = 0;
 TUNABLE_INT("hw.i8254.intr_disable", &i8254_intr_disable);
 
@@ -158,6 +156,25 @@ static struct cputimer     i8254_cputimer = {
     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
@@ -308,8 +325,8 @@ i8254_cputimer_count(void)
  * 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;
@@ -317,8 +334,8 @@ cputimer_intr_config(struct cputimer *timer)
     /*
      * 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;
@@ -334,14 +351,14 @@ cputimer_intr_config(struct cputimer *timer)
  * 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;
@@ -367,67 +384,6 @@ i8254_intr_reload(sysclock_t reload)
     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,
@@ -679,6 +635,11 @@ i8254_restore(void)
        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.
@@ -826,6 +787,12 @@ startrtclock(void)
 "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)
@@ -1043,8 +1010,8 @@ resettodr(void)
  * 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
@@ -1054,10 +1021,11 @@ cpu_initclocks(void *arg __unused)
 
        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) {
                /*
@@ -1139,6 +1107,7 @@ cpu_initclocks(void *arg __unused)
                 * so make sure it is.
                 */
                KKASSERT(sys_cputimer == &i8254_cputimer);
+               KKASSERT(cti == &i8254_cputimer_intr);
 
                lastcnt = get_interrupt_counter(apic_8254_intr);
 
@@ -1147,7 +1116,7 @@ cpu_initclocks(void *arg __unused)
                 * 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 */
@@ -1185,7 +1154,6 @@ cpu_initclocks(void *arg __unused)
                                     INTR_NOENTROPY);
                        machintr_intren(apic_8254_intr);
                }
-               
        }
        if (apic_int_type(0, 0) != 3 ||
            int_to_apicintpin[apic_8254_intr].ioapic != 0 ||
@@ -1199,7 +1167,6 @@ cpu_initclocks(void *arg __unused)
        }
 #endif
 }
-SYSINIT(clocks8254, SI_BOOT2_CLOCKREG, SI_ORDER_FIRST, cpu_initclocks, NULL)
 
 #ifdef APIC_IO
 
index 9a89f00..b61f18b 100644 (file)
@@ -53,7 +53,9 @@
 #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,
@@ -70,10 +72,6 @@ static struct kqueue_info *kqueue_timer_info;
 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
  */
@@ -90,10 +88,28 @@ static struct cputimer vkernel_cputimer = {
         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.
  */
@@ -111,6 +127,10 @@ cpu_initclocks(void *arg __unused)
        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);
 }
@@ -126,16 +146,6 @@ vkernel_timer_construct(struct cputimer *timer, sysclock_t oclock)
        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.
  *
@@ -156,13 +166,14 @@ vkernel_timer_get_timecount(void)
 }
 
 /*
- * 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);
 }
 
 /*
@@ -171,7 +182,7 @@ cputimer_intr_config(struct cputimer *timer)
  * 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)
@@ -186,7 +197,7 @@ vkernel_intr_reload(sysclock_t reload)
  * 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;
index a4c4dd8..e4aeaa3 100644 (file)
@@ -125,11 +125,6 @@ extern struct cputimer *sys_cputimer;
 #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 *);
@@ -139,9 +134,101 @@ sysclock_t cputimer_default_fromus(int);
 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_ */