i386 - Get completely rid of APIC_IO
[dragonfly.git] / sys / platform / pc32 / isa / clock.c
index f02333d..99ca676 100644 (file)
@@ -35,7 +35,6 @@
  *
  *     from: @(#)clock.c       7.2 (Berkeley) 5/12/91
  * $FreeBSD: src/sys/i386/isa/clock.c,v 1.149.2.6 2002/11/02 04:41:50 iwasaki Exp $
- * $DragonFly: src/sys/platform/pc32/isa/clock.c,v 1.55 2008/08/02 01:14:43 dillon Exp $
  */
 
 /*
@@ -89,7 +88,7 @@
 
 #include <machine_base/isa/intr_machdep.h>
 
-#ifdef APIC_IO
+#ifdef SMP /* APIC-IO */
 /* The interrupt triggered by the 8254 (timer) chip */
 int apic_8254_intr;
 static void setup_8254_mixed_mode (void);
@@ -132,6 +131,10 @@ 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);
+
 static struct callout sysbeepstop_ch;
 
 static sysclock_t i8254_cputimer_count(void);
@@ -152,6 +155,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
@@ -302,8 +324,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;
@@ -311,8 +333,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;
@@ -327,15 +349,15 @@ cputimer_intr_config(struct cputimer *timer)
  *
  * We may have to convert from the system timebase to the 8254 timebase.
  */
-void
-cputimer_intr_reload(sysclock_t reload)
+static void
+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;
@@ -420,6 +442,7 @@ DODELAY(int n, int doswitch)
                ticks_left -= delta;
                if (doswitch && ticks_left > 0)
                        lwkt_switch();
+               cpu_pause();
        }
 #ifdef DELAYDEBUG
        if (state == 1)
@@ -428,20 +451,25 @@ DODELAY(int n, int doswitch)
 #endif
 }
 
+/*
+ * DELAY() never switches
+ */
 void
 DELAY(int n)
 {
        DODELAY(n, 0);
 }
 
+/*
+ * DRIVERSLEEP() does not switch if called with a spinlock held or
+ * from a hard interrupt.
+ */
 void
 DRIVERSLEEP(int usec)
 {
        globaldata_t gd = mycpu;
 
-       if (gd->gd_intr_nesting_level || 
-           gd->gd_spinlock_rd ||
-           gd->gd_spinlocks_wr) {
+       if (gd->gd_intr_nesting_level || gd->gd_spinlocks_wr) {
                DODELAY(usec, 0);
        } else {
                DODELAY(usec, 1);
@@ -612,6 +640,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.
@@ -759,6 +792,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)
@@ -841,7 +880,6 @@ void
 inittodr(time_t base)
 {
        unsigned long   sec, days;
-       int             yd;
        int             year, month;
        int             y, m;
        struct timespec ts;
@@ -882,7 +920,6 @@ inittodr(time_t base)
        if ((month > 2) && LEAPYEAR(year))
                days ++;
        days += readrtc(RTC_DAY) - 1;
-       yd = days;
        for (y = 1970; y < year; y++)
                days += DAYSPERYEAR + LEAPYEAR(y);
        sec = ((( days * 24 +
@@ -976,14 +1013,22 @@ 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
+#ifdef SMP /* APIC-IO */
        int apic_8254_trial;
        void *clkdesc;
-#endif /* APIC_IO */
+#endif
+
+       callout_init(&sysbeepstop_ch);
+
+       if (!selected && i8254_intr_disable) {
+               i8254_nointr = 1; /* don't try to register again */
+               cputimer_intr_deregister(cti);
+               return;
+       }
 
        if (statclock_disable) {
                /*
@@ -1000,8 +1045,8 @@ cpu_initclocks(void *arg __unused)
         }
 
        /* Finish initializing 8253 timer 0. */
-#ifdef APIC_IO
-
+#ifdef SMP /* APIC-IO */
+if (apic_io_enable) {
        apic_8254_intr = isa_apic_irq(0);
        apic_8254_trial = 0;
        if (apic_8254_intr >= 0 ) {
@@ -1018,20 +1063,20 @@ cpu_initclocks(void *arg __unused)
 
        clkdesc = register_int(apic_8254_intr, clkintr, NULL, "clk",
                               NULL,
-                              INTR_EXCL | INTR_FAST | 
+                              INTR_EXCL | INTR_CLOCK |
                               INTR_NOPOLL | INTR_MPSAFE | 
                               INTR_NOENTROPY);
        machintr_intren(apic_8254_intr);
-       
-#else /* APIC_IO */
-
+} else {
+#endif
        register_int(0, clkintr, NULL, "clk", NULL,
-                    INTR_EXCL | INTR_FAST | 
+                    INTR_EXCL | INTR_CLOCK |
                     INTR_NOPOLL | INTR_MPSAFE |
                     INTR_NOENTROPY);
        machintr_intren(ICU_IRQ0);
-
-#endif /* APIC_IO */
+#ifdef SMP /* APIC-IO */
+}
+#endif
 
        /* Initialize RTC. */
        writertc(RTC_STATUSA, rtc_statusa);
@@ -1042,33 +1087,42 @@ cpu_initclocks(void *arg __unused)
                if (diag != 0)
                        kprintf("RTC BIOS diagnostic error %b\n", diag, RTCDG_BITS);
 
-#ifdef APIC_IO
+#ifdef SMP /* APIC-IO */
+if (apic_io_enable) {
                if (isa_apic_irq(8) != 8)
                        panic("APIC RTC != 8");
-#endif /* APIC_IO */
+}
+#endif
 
                register_int(8, (inthand2_t *)rtcintr, NULL, "rtc", NULL,
-                            INTR_EXCL | INTR_FAST | INTR_NOPOLL |
+                            INTR_EXCL | INTR_CLOCK | INTR_NOPOLL |
                             INTR_NOENTROPY);
                machintr_intren(8);
 
                writertc(RTC_STATUSB, rtc_statusb);
        }
 
-#ifdef APIC_IO
+#ifdef SMP /* APIC-IO */
+if (apic_io_enable) {
        if (apic_8254_trial) {
                sysclock_t base;
                long lastcnt;
 
+               /*
+                * Following code assumes the 8254 is the cpu timer,
+                * so make sure it is.
+                */
+               KKASSERT(sys_cputimer == &i8254_cputimer);
+               KKASSERT(cti == &i8254_cputimer_intr);
+
                lastcnt = get_interrupt_counter(apic_8254_intr);
 
                /*
-                * XXX this assumes the 8254 is the cpu timer.  Force an
-                * 8254 Timer0 interrupt and wait 1/100s for it to happen,
-                * then see if we got it.
+                * Force an 8254 Timer0 interrupt and wait 1/100s for
+                * it to happen, then see if we got it.
                 */
                kprintf("APIC_IO: Testing 8254 interrupt delivery\n");
-               cputimer_intr_reload(2);        /* XXX assumes 8254 */
+               i8254_intr_reload(cti, 2);
                base = sys_cputimer->count();
                while (sys_cputimer->count() - base < sys_cputimer->freq / 100)
                        ;       /* nothing */
@@ -1101,12 +1155,11 @@ cpu_initclocks(void *arg __unused)
                        setup_8254_mixed_mode();
                        register_int(apic_8254_intr, clkintr, NULL, "clk",
                                     NULL,
-                                    INTR_EXCL | INTR_FAST | 
+                                    INTR_EXCL | INTR_CLOCK |
                                     INTR_NOPOLL | INTR_MPSAFE |
                                     INTR_NOENTROPY);
                        machintr_intren(apic_8254_intr);
                }
-               
        }
        if (apic_int_type(0, 0) != 3 ||
            int_to_apicintpin[apic_8254_intr].ioapic != 0 ||
@@ -1118,12 +1171,11 @@ cpu_initclocks(void *arg __unused)
                kprintf("APIC_IO: "
                       "routing 8254 via 8259 and IOAPIC #0 intpin 0\n");
        }
+}
 #endif
-       callout_init(&sysbeepstop_ch);
 }
-SYSINIT(clocks8254, SI_BOOT2_CLOCKREG, SI_ORDER_FIRST, cpu_initclocks, NULL)
 
-#ifdef APIC_IO
+#ifdef SMP /* APIC-IO */
 
 static void 
 setup_8254_mixed_mode(void)