8254: Setup interrupt properly for new I/O APIC code
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Mon, 21 Mar 2011 16:06:30 +0000 (00:06 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Mon, 21 Mar 2011 16:16:31 +0000 (00:16 +0800)
14 files changed:
sys/platform/pc32/apic/ioapic_abi.c
sys/platform/pc32/apic/ioapic_abi.h
sys/platform/pc32/apic/mpapic.c
sys/platform/pc32/icu/icu.c
sys/platform/pc32/icu/icu_var.h
sys/platform/pc32/include/smp.h
sys/platform/pc32/isa/clock.c
sys/platform/pc64/apic/ioapic_abi.c
sys/platform/pc64/apic/ioapic_abi.h
sys/platform/pc64/apic/mpapic.c
sys/platform/pc64/icu/icu.c
sys/platform/pc64/icu/icu_var.h
sys/platform/pc64/include/smp.h
sys/platform/pc64/isa/clock.c

index 8db3d45..5c678a4 100644 (file)
@@ -56,6 +56,7 @@
 
 #include <sys/thread2.h>
 
+#include <machine_base/icu/icu_var.h>
 #include <machine_base/apic/ioapic_abi.h>
 #include <machine_base/apic/ioapic_ipl.h>
 
@@ -495,6 +496,8 @@ struct machintr_abi MachIntrABI_IOAPIC = {
        .intr_config    = ioapic_intr_config
 };
 
+static int     ioapic_abi_extint_irq = -1;
+
 static int
 ioapic_setvar(int varid, const void *buf)
 {
@@ -847,4 +850,66 @@ ioapic_intr_config(int irq, enum intr_trigger trig, enum intr_polarity pola)
        imen_unlock();
 }
 
+int
+ioapic_abi_extint_irqmap(int irq)
+{
+       struct apic_intmapinfo *info;
+       struct ioapic_irqmap *map;
+       void *ioaddr;
+       int pin, error, vec;
+
+       vec = IDT_OFFSET + irq;
+
+       if (ioapic_abi_extint_irq == irq)
+               return 0;
+       else if (ioapic_abi_extint_irq >= 0)
+               return EEXIST;
+
+       error = icu_ioapic_extint(irq, vec);
+       if (error)
+               return error;
+
+       map = &ioapic_irqmaps[irq];
+
+       KKASSERT(map->im_type == IOAPIC_IMT_RESERVED ||
+                map->im_type == IOAPIC_IMT_LINE);
+       if (map->im_type == IOAPIC_IMT_LINE) {
+               if (map->im_flags & IOAPIC_IMF_CONF)
+                       return EEXIST;
+       }
+       ioapic_abi_extint_irq = irq;
+
+       map->im_type = IOAPIC_IMT_LINE;
+       map->im_trig = INTR_TRIGGER_EDGE;
+       map->im_pola = INTR_POLARITY_HIGH;
+       map->im_flags = IOAPIC_IMF_CONF;
+
+       map->im_gsi = ioapic_extpin_gsi();
+       KKASSERT(map->im_gsi >= 0);
+
+       if (bootverbose) {
+               kprintf("IOAPIC: irq %d -> extint gsi %d E\n", irq,
+                       map->im_gsi);
+       }
+
+       pin = ioapic_gsi_pin(map->im_gsi);
+       ioaddr = ioapic_gsi_ioaddr(map->im_gsi);
+
+       info = &int_to_apicintpin[irq];
+
+       imen_lock();
+
+       info->ioapic = 0; /* XXX unused */
+       info->int_pin = pin;
+       info->apic_address = ioaddr;
+       info->redirindex = IOAPIC_REDTBL + (2 * pin);
+       info->flags = IOAPIC_IM_FLAG_MASKED;
+
+       ioapic_extpin_setup(ioaddr, pin, vec);
+
+       imen_unlock();
+
+       return 0;
+}
+
 #endif /* SMP */
index 34c11a5..c87b2a4 100644 (file)
@@ -49,6 +49,7 @@
 
 extern struct machintr_abi MachIntrABI_IOAPIC;
 
+int    ioapic_abi_extint_irqmap(int);
 void   ioapic_abi_set_irqmap(int, int, enum intr_trigger, enum intr_polarity);
 void   ioapic_abi_fixup_irqmap(void);
 
index b65259b..13e8b5a 100644 (file)
@@ -1398,6 +1398,12 @@ ioapic_extpin_setup(void *addr, int pin, int vec)
            INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM, IOART_DELEXINT);
 }
 
+int
+ioapic_extpin_gsi(void)
+{
+       return 0;
+}
+
 void
 ioapic_pin_setup(void *addr, int pin, int vec,
     enum intr_trigger trig, enum intr_polarity pola)
index e302907..3cec0c0 100644 (file)
@@ -153,3 +153,30 @@ icu_irq_pending(void)
        irr2 = inb(IO_ICU2);
        return ((irr2 << 8) | irr1);
 }
+
+int
+icu_ioapic_extint(int irq, int vec)
+{
+       uint8_t mask;
+
+       /*
+        * Only first 8 interrupt is supported.
+        * Don't allow setup for the slave link.
+        */
+       if (irq >= 8 || irq == 2)
+               return EOPNOTSUPP;
+
+       mask = ~(1 << irq);
+
+       /*
+        * Re-initialize master 8259:
+        *   reset; prog 4 bytes, single ICU, edge triggered
+        */
+       outb(IO_ICU1, 0x13);
+       outb(IO_ICU1 + 1, vec);         /* start vector (unused) */
+       outb(IO_ICU1 + 1, 0x00);        /* ignore slave */
+       outb(IO_ICU1 + 1, 0x03);        /* auto EOI, 8086 */
+       outb(IO_ICU1 + 1, mask);
+
+       return 0;
+}
index 05ed83a..397f269 100644 (file)
@@ -43,4 +43,6 @@ void          icu_reinit(void);
 
 intrmask_t     icu_irq_pending(void);
 
+int            icu_ioapic_extint(int, int);
+
 #endif /* !_ARCH_ICU_ICU_VAR_H_ */
index 737dc96..4166ace 100644 (file)
@@ -173,6 +173,7 @@ int ioapic_gsi_pin(int);
 void   ioapic_pin_setup(void *, int, int,
            enum intr_trigger, enum intr_polarity);
 void   ioapic_extpin_setup(void *, int, int);
+int    ioapic_extpin_gsi(void);
 int    ioapic_gsi(int, int);
 
 extern int apic_io_enable;
index 606df1f..fcb50ee 100644 (file)
@@ -82,6 +82,9 @@
 #include <machine/smp.h>
 #include <machine/specialreg.h>
 
+#ifdef SMP
+#include <machine_base/apic/ioapic_abi.h>
+#endif
 #include <machine_base/icu/icu.h>
 #include <bus/isa/isa.h>
 #include <bus/isa/rtc.h>
@@ -1001,6 +1004,40 @@ resettodr(void)
        crit_exit();
 }
 
+#ifdef SMP
+
+static int
+i8254_ioapic_trial(int irq, struct cputimer_intr *cti)
+{
+       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(irq);
+
+       /*
+        * Force an 8254 Timer0 interrupt and wait 1/100s for
+        * it to happen, then see if we got it.
+        */
+       kprintf("IOAPIC: testing 8254 interrupt delivery\n");
+
+       i8254_intr_reload(cti, 2);
+       base = sys_cputimer->count();
+       while (sys_cputimer->count() - base < sys_cputimer->freq / 100)
+               ; /* nothing */
+
+       if (get_interrupt_counter(irq) - lastcnt == 0)
+               return ENOENT;
+       return 0;
+}
+
+#endif /* SMP */
 
 /*
  * Start both clocks running.  DragonFly note: the stat clock is no longer
@@ -1013,16 +1050,13 @@ i8254_intr_initclock(struct cputimer_intr *cti, boolean_t selected)
 #ifdef SMP /* APIC-IO */
        int apic_8254_trial = 0;
        void *clkdesc = NULL;
-       int irq = 0;
+       int irq = 0, mixed_mode = 0, error;
 #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 (!selected && i8254_intr_disable)
+               goto nointr;
 
        /*
         * The stat interrupt mask is different without the
@@ -1057,6 +1091,37 @@ if (apic_io_enable) {
                                       INTR_NOPOLL | INTR_MPSAFE | 
                                       INTR_NOENTROPY);
                machintr_intren(irq);
+       } else {
+               irq = ioapic_abi_find_irq(0, INTR_TRIGGER_EDGE,
+                       INTR_POLARITY_HIGH);
+               if (irq < 0) {
+mixed_mode_setup:
+                       error = ioapic_abi_extint_irqmap(0);
+                       if (!error) {
+                               irq = ioapic_abi_find_irq(0, INTR_TRIGGER_EDGE,
+                                       INTR_POLARITY_HIGH);
+                               if (irq < 0)
+                                       error = ENOENT;
+                       }
+
+                       if (error) {
+                               if (!selected) {
+                                       kprintf("IOAPIC: setup mixed mode for "
+                                               "irq 0 failed: %d\n", error);
+                                       goto nointr;
+                               } else {
+                                       panic("IOAPIC: setup mixed mode for "
+                                             "irq 0 failed: %d\n", error);
+                               }
+                       }
+                       mixed_mode = 1;
+               }
+               clkdesc = register_int(irq, clkintr, NULL, "clk",
+                                      NULL,
+                                      INTR_EXCL | INTR_CLOCK |
+                                      INTR_NOPOLL | INTR_MPSAFE |
+                                      INTR_NOENTROPY);
+               machintr_intren(irq);
        }
 } else {
 #endif
@@ -1074,7 +1139,8 @@ if (apic_io_enable) {
        writertc(RTC_STATUSB, RTCSB_24HR);
 
 #ifdef SMP /* APIC-IO */
-if (apic_io_enable && ioapic_use_old) {
+if (apic_io_enable) {
+if (ioapic_use_old) {
        if (apic_8254_trial) {
                sysclock_t base;
                long lastcnt;
@@ -1142,8 +1208,34 @@ if (apic_io_enable && ioapic_use_old) {
                kprintf("APIC_IO: "
                       "routing 8254 via 8259 and IOAPIC #0 intpin 0\n");
        }
+} else {       /* !ioapic_use_old */
+       error = i8254_ioapic_trial(irq, cti);
+       if (error) {
+               if (mixed_mode) {
+                       if (!selected) {
+                               kprintf("IOAPIC: mixed mode for irq %d "
+                                       "trial failed: %d\n", irq, error);
+                               goto nointr;
+                       } else {
+                               panic("IOAPIC: mixed mode for irq %d "
+                                     "trial failed: %d\n", irq, error);
+                       }
+               } else {
+                       kprintf("IOAPIC: warning 8254 is not connected "
+                               "to the correct pin, try mixed mode\n");
+                       machintr_intrdis(irq);
+                       unregister_int(clkdesc);
+                       goto mixed_mode_setup;
+               }
+       }
+}              /* ioapic_use_old */
 }
 #endif
+       return;
+
+nointr:
+       i8254_nointr = 1; /* don't try to register again */
+       cputimer_intr_deregister(cti);
 }
 
 #ifdef SMP /* APIC-IO */
index 1c3ecc3..6fbed8b 100644 (file)
@@ -56,6 +56,7 @@
 
 #include <sys/thread2.h>
 
+#include <machine_base/icu/icu_var.h>
 #include <machine_base/apic/ioapic_abi.h>
 #include <machine_base/apic/ioapic_ipl.h>
 
@@ -495,6 +496,8 @@ struct machintr_abi MachIntrABI_IOAPIC = {
        .intr_config    = ioapic_intr_config
 };
 
+static int     ioapic_abi_extint_irq = -1;
+
 static int
 ioapic_setvar(int varid, const void *buf)
 {
@@ -840,4 +843,66 @@ ioapic_intr_config(int irq, enum intr_trigger trig, enum intr_polarity pola)
        imen_unlock();
 }
 
+int
+ioapic_abi_extint_irqmap(int irq)
+{
+       struct apic_intmapinfo *info;
+       struct ioapic_irqmap *map;
+       void *ioaddr;
+       int pin, error, vec;
+
+       vec = IDT_OFFSET + irq;
+
+       if (ioapic_abi_extint_irq == irq)
+               return 0;
+       else if (ioapic_abi_extint_irq >= 0)
+               return EEXIST;
+
+       error = icu_ioapic_extint(irq, vec);
+       if (error)
+               return error;
+
+       map = &ioapic_irqmaps[irq];
+
+       KKASSERT(map->im_type == IOAPIC_IMT_RESERVED ||
+                map->im_type == IOAPIC_IMT_LINE);
+       if (map->im_type == IOAPIC_IMT_LINE) {
+               if (map->im_flags & IOAPIC_IMF_CONF)
+                       return EEXIST;
+       }
+       ioapic_abi_extint_irq = irq;
+
+       map->im_type = IOAPIC_IMT_LINE;
+       map->im_trig = INTR_TRIGGER_EDGE;
+       map->im_pola = INTR_POLARITY_HIGH;
+       map->im_flags = IOAPIC_IMF_CONF;
+
+       map->im_gsi = ioapic_extpin_gsi();
+       KKASSERT(map->im_gsi >= 0);
+
+       if (bootverbose) {
+               kprintf("IOAPIC: irq %d -> extint gsi %d E\n", irq,
+                       map->im_gsi);
+       }
+
+       pin = ioapic_gsi_pin(map->im_gsi);
+       ioaddr = ioapic_gsi_ioaddr(map->im_gsi);
+
+       info = &int_to_apicintpin[irq];
+
+       imen_lock();
+
+       info->ioapic = 0; /* XXX unused */
+       info->int_pin = pin;
+       info->apic_address = ioaddr;
+       info->redirindex = IOAPIC_REDTBL + (2 * pin);
+       info->flags = IOAPIC_IM_FLAG_MASKED;
+
+       ioapic_extpin_setup(ioaddr, pin, vec);
+
+       imen_unlock();
+
+       return 0;
+}
+
 #endif /* SMP */
index 34c11a5..c87b2a4 100644 (file)
@@ -49,6 +49,7 @@
 
 extern struct machintr_abi MachIntrABI_IOAPIC;
 
+int    ioapic_abi_extint_irqmap(int);
 void   ioapic_abi_set_irqmap(int, int, enum intr_trigger, enum intr_polarity);
 void   ioapic_abi_fixup_irqmap(void);
 
index c6abb0e..2857463 100644 (file)
@@ -1460,6 +1460,12 @@ ioapic_extpin_setup(void *addr, int pin, int vec)
            INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM, IOART_DELEXINT);
 }
 
+int
+ioapic_extpin_gsi(void)
+{
+       return 0;
+}
+
 void
 ioapic_pin_setup(void *addr, int pin, int vec,
     enum intr_trigger trig, enum intr_polarity pola)
index 584c9a6..da870aa 100644 (file)
@@ -153,3 +153,30 @@ icu_irq_pending(void)
        irr2 = inb(IO_ICU2);
        return ((irr2 << 8) | irr1);
 }
+
+int
+icu_ioapic_extint(int irq, int vec)
+{
+       uint8_t mask;
+
+       /*
+        * Only first 8 interrupt is supported.
+        * Don't allow setup for the slave link.
+        */
+       if (irq >= 8 || irq == 2)
+               return EOPNOTSUPP;
+
+       mask = ~(1 << irq);
+
+       /*
+        * Re-initialize master 8259:
+        *   reset; prog 4 bytes, single ICU, edge triggered
+        */
+       outb(IO_ICU1, 0x13);
+       outb(IO_ICU1 + 1, vec);         /* start vector (unused) */
+       outb(IO_ICU1 + 1, 0x00);        /* ignore slave */
+       outb(IO_ICU1 + 1, 0x03);        /* auto EOI, 8086 */
+       outb(IO_ICU1 + 1, mask);
+
+       return 0;
+}
index 05ed83a..397f269 100644 (file)
@@ -43,4 +43,6 @@ void          icu_reinit(void);
 
 intrmask_t     icu_irq_pending(void);
 
+int            icu_ioapic_extint(int, int);
+
 #endif /* !_ARCH_ICU_ICU_VAR_H_ */
index 8846aa2..2414ac6 100644 (file)
@@ -184,6 +184,7 @@ int ioapic_gsi_pin(int);
 void   ioapic_pin_setup(void *, int, int,
            enum intr_trigger, enum intr_polarity);
 void   ioapic_extpin_setup(void *, int, int);
+int    ioapic_extpin_gsi(void);
 int    ioapic_gsi(int, int);
 
 #if defined(READY)
index c45267a..e50fc15 100644 (file)
@@ -86,6 +86,9 @@
 #include <machine/specialreg.h>
 #include <machine/intr_machdep.h>
 
+#ifdef SMP
+#include <machine_base/apic/ioapic_abi.h>
+#endif
 #include <machine_base/icu/icu.h>
 #include <bus/isa/isa.h>
 #include <bus/isa/rtc.h>
@@ -1008,6 +1011,40 @@ resettodr(void)
        crit_exit();
 }
 
+#ifdef SMP
+
+static int
+i8254_ioapic_trial(int irq, struct cputimer_intr *cti)
+{
+       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(irq);
+
+       /*
+        * Force an 8254 Timer0 interrupt and wait 1/100s for
+        * it to happen, then see if we got it.
+        */
+       kprintf("IOAPIC: testing 8254 interrupt delivery\n");
+
+       i8254_intr_reload(cti, 2);
+       base = sys_cputimer->count();
+       while (sys_cputimer->count() - base < sys_cputimer->freq / 100)
+               ; /* nothing */
+
+       if (get_interrupt_counter(irq) - lastcnt == 0)
+               return ENOENT;
+       return 0;
+}
+
+#endif /* SMP */
 
 /*
  * Start both clocks running.  DragonFly note: the stat clock is no longer
@@ -1020,16 +1057,13 @@ i8254_intr_initclock(struct cputimer_intr *cti, boolean_t selected)
 #ifdef SMP /* APIC-IO */
        int apic_8254_trial = 0;
        void *clkdesc = NULL;
-       int irq = 0;
+       int irq = 0, mixed_mode = 0, error;
 #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 (!selected && i8254_intr_disable)
+               goto nointr;
 
        /*
         * The stat interrupt mask is different without the
@@ -1064,6 +1098,37 @@ if (apic_io_enable) {
                                       INTR_NOPOLL | INTR_MPSAFE | 
                                       INTR_NOENTROPY);
                machintr_intren(irq);
+       } else {
+               irq = ioapic_abi_find_irq(0, INTR_TRIGGER_EDGE,
+                       INTR_POLARITY_HIGH);
+               if (irq < 0) {
+mixed_mode_setup:
+                       error = ioapic_abi_extint_irqmap(0);
+                       if (!error) {
+                               irq = ioapic_abi_find_irq(0, INTR_TRIGGER_EDGE,
+                                       INTR_POLARITY_HIGH);
+                               if (irq < 0)
+                                       error = ENOENT;
+                       }
+
+                       if (error) {
+                               if (!selected) {
+                                       kprintf("IOAPIC: setup mixed mode for "
+                                               "irq 0 failed: %d\n", error);
+                                       goto nointr;
+                               } else {
+                                       panic("IOAPIC: setup mixed mode for "
+                                             "irq 0 failed: %d\n", error);
+                               }
+                       }
+                       mixed_mode = 1;
+               }
+               clkdesc = register_int(irq, clkintr, NULL, "clk",
+                                      NULL,
+                                      INTR_EXCL | INTR_CLOCK |
+                                      INTR_NOPOLL | INTR_MPSAFE |
+                                      INTR_NOENTROPY);
+               machintr_intren(irq);
        }
 } else {
 #endif
@@ -1081,7 +1146,8 @@ if (apic_io_enable) {
        writertc(RTC_STATUSB, RTCSB_24HR);
 
 #ifdef SMP /* APIC-IO */
-if (apic_io_enable && ioapic_use_old) {
+if (apic_io_enable) {
+if (ioapic_use_old) {
        if (apic_8254_trial) {
                sysclock_t base;
                long lastcnt;
@@ -1149,8 +1215,34 @@ if (apic_io_enable && ioapic_use_old) {
                kprintf("APIC_IO: "
                       "routing 8254 via 8259 and IOAPIC #0 intpin 0\n");
        }
+} else {       /* !ioapic_use_old */
+       error = i8254_ioapic_trial(irq, cti);
+       if (error) {
+               if (mixed_mode) {
+                       if (!selected) {
+                               kprintf("IOAPIC: mixed mode for irq %d "
+                                       "trial failed: %d\n", irq, error);
+                               goto nointr;
+                       } else {
+                               panic("IOAPIC: mixed mode for irq %d "
+                                     "trial failed: %d\n", irq, error);
+                       }
+               } else {
+                       kprintf("IOAPIC: warning 8254 is not connected "
+                               "to the correct pin, try mixed mode\n");
+                       machintr_intrdis(irq);
+                       unregister_int(clkdesc);
+                       goto mixed_mode_setup;
+               }
+       }
+}              /* ioapic_use_old */
 }
 #endif
+       return;
+
+nointr:
+       i8254_nointr = 1; /* don't try to register again */
+       cputimer_intr_deregister(cti);
 }
 
 #ifdef SMP /* APIC-IO */