ioapic: Properly setup I/O APIC pin according to the saved information
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 18 Mar 2011 07:17:39 +0000 (15:17 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 18 Mar 2011 07:38:45 +0000 (15:38 +0800)
sys/platform/pc32/apic/ioapic_abi.c
sys/platform/pc32/apic/mpapic.c
sys/platform/pc32/include/smp.h
sys/platform/pc64/apic/ioapic_abi.c
sys/platform/pc64/apic/mpapic.c
sys/platform/pc64/include/smp.h

index ef6f42a..a8c3240 100644 (file)
@@ -771,7 +771,8 @@ ioapic_abi_set_irqmap(int irq, int gsi, enum intr_trigger trig,
        if (trig == INTR_TRIGGER_LEVEL)
                info->flags |= IOAPIC_IM_FLAG_LEVEL;
 
-       /* TODO setup pin */
+       ioapic_pin_setup(ioaddr, pin, IDT_OFFSET + irq,
+           map->im_trig, map->im_pola);
 }
 
 #endif /* SMP */
index c4e9e40..61bbc7b 100644 (file)
@@ -85,6 +85,8 @@ static void   ioapic_set_apic_id(const struct ioapic_info *);
 static void    ioapic_gsi_setup(int);
 static const struct ioapic_info *
                ioapic_gsi_search(int);
+static void    ioapic_pin_prog(void *, int, int,
+                   enum intr_trigger, enum intr_polarity, uint32_t);
 
 static struct cputimer_intr lapic_cputimer_intr = {
        .freq = 0,
@@ -1242,6 +1244,13 @@ ioapic_gsi_setup(int gsi)
        enum intr_polarity pola;
        int irq;
 
+       if (gsi == 0) {
+               /* ExtINT */
+               ioapic_extpin_setup(ioapic_gsi_ioaddr(gsi),
+                   ioapic_gsi_pin(gsi), 0);
+               return;
+       }
+
        for (irq = 0; irq < 16; ++irq) {
                if (gsi == ioapic_conf.ioc_intsrc[irq]) {
                        trig = INTR_TRIGGER_EDGE;
@@ -1251,10 +1260,7 @@ ioapic_gsi_setup(int gsi)
        }
 
        if (irq == 16) {
-               if (gsi == 0) {
-                       /* TODO Program EXTINT */
-                       return;
-               } else if (gsi < 16) {
+               if (gsi < 16) {
                        trig = INTR_TRIGGER_EDGE;
                        pola = INTR_POLARITY_HIGH;
                } else {
@@ -1298,6 +1304,94 @@ ioapic_gsi_search(int gsi)
        panic("ioapic_gsi_search: no I/O APIC\n");
 }
 
+void
+ioapic_extpin_setup(void *addr, int pin, int vec)
+{
+       imen_lock();
+       ioapic_pin_prog(addr, pin, vec,
+           INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM, IOART_DELEXINT);
+       imen_unlock();
+}
+
+void
+ioapic_pin_setup(void *addr, int pin, int vec,
+    enum intr_trigger trig, enum intr_polarity pola)
+{
+       /*
+        * Always clear an I/O APIC pin before [re]programming it.  This is
+        * particularly important if the pin is set up for a level interrupt
+        * as the IOART_REM_IRR bit might be set.   When we reprogram the
+        * vector any EOI from pending ints on this pin could be lost and
+        * IRR might never get reset.
+        *
+        * To fix this problem, clear the vector and make sure it is 
+        * programmed as an edge interrupt.  This should theoretically
+        * clear IRR so we can later, safely program it as a level 
+        * interrupt.
+        */
+       imen_lock();
+
+       ioapic_pin_prog(addr, pin, vec, INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH,
+           IOART_DELFIXED);
+       ioapic_pin_prog(addr, pin, vec, trig, pola, IOART_DELFIXED);
+
+       imen_unlock();
+}
+
+static void
+ioapic_pin_prog(void *addr, int pin, int vec,
+    enum intr_trigger trig, enum intr_polarity pola, uint32_t del_mode)
+{
+       uint32_t flags, target;
+       int select;
+
+       KKASSERT(del_mode == IOART_DELEXINT || del_mode == IOART_DELFIXED);
+
+       select = IOAPIC_REDTBL0 + (2 * pin);
+
+       flags = ioapic_read(addr, select) & IOART_RESV;
+       flags |= IOART_INTMSET | IOART_DESTPHY | del_mode;
+
+       if (del_mode == IOART_DELEXINT) {
+               KKASSERT(trig == INTR_TRIGGER_CONFORM &&
+                        pola == INTR_POLARITY_CONFORM);
+               flags |= IOART_TRGREDG | IOART_INTAHI;
+       } else {
+               switch (trig) {
+               case INTR_TRIGGER_EDGE:
+                       flags |= IOART_TRGREDG;
+                       break;
+
+               case INTR_TRIGGER_LEVEL:
+                       flags |= IOART_TRGRLVL;
+                       break;
+
+               case INTR_TRIGGER_CONFORM:
+                       panic("ioapic_pin_prog: trig conform is not "
+                             "supported\n");
+               }
+               switch (pola) {
+               case INTR_POLARITY_HIGH:
+                       flags |= IOART_INTAHI;
+                       break;
+
+               case INTR_POLARITY_LOW:
+                       flags |= IOART_INTALO;
+                       break;
+
+               case INTR_POLARITY_CONFORM:
+                       panic("ioapic_pin_prog: pola conform is not "
+                             "supported\n");
+               }
+       }
+
+       target = ioapic_read(addr, select + 1) & IOART_HI_DEST_RESV;
+       target |= 0;
+
+       ioapic_write(addr, select, flags | vec);
+       ioapic_write(addr, select + 1, target);
+}
+
 static void
 ioapic_setup(const struct ioapic_info *info)
 {
index 609c998..9a09b10 100644 (file)
@@ -142,6 +142,10 @@ extern volatile lapic_t            lapic;
 extern volatile ioapic_t       **ioapic;
 extern int                     lapic_id_max;
 
+#ifndef _SYS_BUS_H_
+#include <sys/bus.h>
+#endif
+
 /* functions in mpapic.c */
 void   apic_dump               (char*);
 void   apic_initialize         (boolean_t);
@@ -163,6 +167,9 @@ void        ioapic_add(void *, int, int);
 void   ioapic_intsrc(int, int);
 void   *ioapic_gsi_ioaddr(int);
 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);
 
 extern int apic_io_enable;
 extern int ioapic_use_old;
index 532a4ac..a2db183 100644 (file)
@@ -764,7 +764,8 @@ ioapic_abi_set_irqmap(int irq, int gsi, enum intr_trigger trig,
        if (trig == INTR_TRIGGER_LEVEL)
                info->flags |= IOAPIC_IM_FLAG_LEVEL;
 
-       /* TODO setup pin */
+       ioapic_pin_setup(ioaddr, pin, IDT_OFFSET + irq,
+           map->im_trig, map->im_pola);
 }
 
 #endif /* SMP */
index c2033e0..fdeef85 100644 (file)
@@ -86,6 +86,8 @@ static void   ioapic_set_apic_id(const struct ioapic_info *);
 static void    ioapic_gsi_setup(int);
 static const struct ioapic_info *
                ioapic_gsi_search(int);
+static void    ioapic_pin_prog(void *, int, int,
+                   enum intr_trigger, enum intr_polarity, uint32_t);
 
 static struct cputimer_intr lapic_cputimer_intr = {
        .freq = 0,
@@ -1304,6 +1306,13 @@ ioapic_gsi_setup(int gsi)
        enum intr_polarity pola;
        int irq;
 
+       if (gsi == 0) {
+               /* ExtINT */
+               ioapic_extpin_setup(ioapic_gsi_ioaddr(gsi),
+                   ioapic_gsi_pin(gsi), 0);
+               return;
+       }
+
        for (irq = 0; irq < 16; ++irq) {
                if (gsi == ioapic_conf.ioc_intsrc[irq]) {
                        trig = INTR_TRIGGER_EDGE;
@@ -1313,10 +1322,7 @@ ioapic_gsi_setup(int gsi)
        }
 
        if (irq == 16) {
-               if (gsi == 0) {
-                       /* TODO Program EXTINT */
-                       return;
-               } else if (gsi < 16) {
+               if (gsi < 16) {
                        trig = INTR_TRIGGER_EDGE;
                        pola = INTR_POLARITY_HIGH;
                } else {
@@ -1360,6 +1366,94 @@ ioapic_gsi_search(int gsi)
        panic("ioapic_gsi_search: no I/O APIC\n");
 }
 
+void
+ioapic_extpin_setup(void *addr, int pin, int vec)
+{
+       imen_lock();
+       ioapic_pin_prog(addr, pin, vec,
+           INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM, IOART_DELEXINT);
+       imen_unlock();
+}
+
+void
+ioapic_pin_setup(void *addr, int pin, int vec,
+    enum intr_trigger trig, enum intr_polarity pola)
+{
+       /*
+        * Always clear an I/O APIC pin before [re]programming it.  This is
+        * particularly important if the pin is set up for a level interrupt
+        * as the IOART_REM_IRR bit might be set.   When we reprogram the
+        * vector any EOI from pending ints on this pin could be lost and
+        * IRR might never get reset.
+        *
+        * To fix this problem, clear the vector and make sure it is 
+        * programmed as an edge interrupt.  This should theoretically
+        * clear IRR so we can later, safely program it as a level 
+        * interrupt.
+        */
+       imen_lock();
+
+       ioapic_pin_prog(addr, pin, vec, INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH,
+           IOART_DELFIXED);
+       ioapic_pin_prog(addr, pin, vec, trig, pola, IOART_DELFIXED);
+
+       imen_unlock();
+}
+
+static void
+ioapic_pin_prog(void *addr, int pin, int vec,
+    enum intr_trigger trig, enum intr_polarity pola, uint32_t del_mode)
+{
+       uint32_t flags, target;
+       int select;
+
+       KKASSERT(del_mode == IOART_DELEXINT || del_mode == IOART_DELFIXED);
+
+       select = IOAPIC_REDTBL0 + (2 * pin);
+
+       flags = ioapic_read(addr, select) & IOART_RESV;
+       flags |= IOART_INTMSET | IOART_DESTPHY | del_mode;
+
+       if (del_mode == IOART_DELEXINT) {
+               KKASSERT(trig == INTR_TRIGGER_CONFORM &&
+                        pola == INTR_POLARITY_CONFORM);
+               flags |= IOART_TRGREDG | IOART_INTAHI;
+       } else {
+               switch (trig) {
+               case INTR_TRIGGER_EDGE:
+                       flags |= IOART_TRGREDG;
+                       break;
+
+               case INTR_TRIGGER_LEVEL:
+                       flags |= IOART_TRGRLVL;
+                       break;
+
+               case INTR_TRIGGER_CONFORM:
+                       panic("ioapic_pin_prog: trig conform is not "
+                             "supported\n");
+               }
+               switch (pola) {
+               case INTR_POLARITY_HIGH:
+                       flags |= IOART_INTAHI;
+                       break;
+
+               case INTR_POLARITY_LOW:
+                       flags |= IOART_INTALO;
+                       break;
+
+               case INTR_POLARITY_CONFORM:
+                       panic("ioapic_pin_prog: pola conform is not "
+                             "supported\n");
+               }
+       }
+
+       target = ioapic_read(addr, select + 1) & IOART_HI_DEST_RESV;
+       target |= 0;
+
+       ioapic_write(addr, select, flags | vec);
+       ioapic_write(addr, select + 1, target);
+}
+
 static void
 ioapic_setup(const struct ioapic_info *info)
 {
index 623bd32..45e8b9d 100644 (file)
@@ -153,6 +153,10 @@ extern volatile lapic_t            *lapic;
 extern volatile ioapic_t       **ioapic;
 extern int                     lapic_id_max;
 
+#ifndef _SYS_BUS_H_
+#include <sys/bus.h>
+#endif
+
 /* functions in mpapic.c */
 void   apic_dump               (char*);
 void   apic_initialize         (boolean_t);
@@ -174,6 +178,9 @@ void        ioapic_add(void *, int, int);
 void   ioapic_intsrc(int, int);
 void   *ioapic_gsi_ioaddr(int);
 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);
 
 #if defined(READY)
 void   clr_io_apic_mask24      (int, u_int32_t);