[PATCH][RFC] APIC meta LVT API

From: Zwane Mwaikambo (zwane@linux.realnet.co.sz)
Date: Sat May 04 2002 - 04:46:42 EST


This patch is an attempt at providing an interface to use when setting up
a local APIC LVT.

Although this is really 2.5 stuff i tested and developed on:
2.4.19-pre2-ac3-SMP 4-way i686
2.4.19-pre2-ac3-SMP 4-way w/ noapic i686
2.4.19-pre5-ac3-UP w/ Local-APIC/IO-APIC i686
2.4.19-pre2-ac3-UP w/ Local-APIC i686

The API provides the following interfaces:
void enable_apic_lvt(unsigned int lvt, unsigned int value);
void disable_apic_lvt(unsigned int lvt);
which also manipulate the vector:lvt mappings

void apic_mask_lvt(unsigned int lvt);
void apic_unmask_lvt(unsigned int lvt);

all comments (constructive, flames) welcome

Thanks,
        Zwane Mwaikambo

arch/i386/kernel/apic.c | 194 ++++++++++++++++++++++++++++++++++++++-------
arch/i386/kernel/io_apic.c | 70 +---------------
include/asm-i386/apic.h | 11 ++
include/asm-i386/hw_irq.h | 3
4 files changed, 186 insertions(+), 92 deletions(-)

diff -ur linux-bochs-2.4-ac/arch/i386/kernel/apic.c linux-2.4-apic/arch/i386/kernel/apic.c
--- linux-bochs-2.4-ac/arch/i386/kernel/apic.c Thu Mar 28 05:56:03 2002
+++ linux-2.4-apic/arch/i386/kernel/apic.c Sat May 4 10:09:22 2002
@@ -10,6 +10,7 @@
  * for testing these extensively.
  * Maciej W. Rozycki : Various updates and fixes.
  * Mikael Pettersson : Power Management for UP-APIC.
+ * Zwane Mwaikambo : APIC LVT/vector meta-API.
  */
 
 #include <linux/config.h>
@@ -37,6 +38,33 @@
 int prof_old_multiplier[NR_CPUS] = { 1, };
 int prof_counter[NR_CPUS] = { 1, };
 
+spinlock_t lapic_lock = SPIN_LOCK_UNLOCKED;
+int vector_lvt[LAST_APIC_VECTOR];
+int irq_vector[NR_IRQS] = { FIRST_DEVICE_VECTOR , 0 };
+
+/* used by IO-APIC too */
+int assign_irq_vector(int irq)
+{
+ static int current_vector = FIRST_DEVICE_VECTOR, offset = 0;
+ if (APIC_VECTOR(irq) > 0)
+ return APIC_VECTOR(irq);
+next:
+ current_vector += 8;
+ if (current_vector == SYSCALL_VECTOR)
+ goto next;
+
+ if (current_vector > FIRST_SYSTEM_VECTOR) {
+ offset++;
+ current_vector = FIRST_DEVICE_VECTOR + offset;
+ }
+
+ if (current_vector == FIRST_SYSTEM_VECTOR)
+ panic("ran out of interrupt sources!");
+
+ APIC_VECTOR(irq) = current_vector;
+ return current_vector;
+}
+
 int get_maxlvt(void)
 {
         unsigned int v, ver, maxlvt;
@@ -48,6 +76,123 @@
         return maxlvt;
 }
 
+void inline unmask_apic_lvt(unsigned int lvt)
+{
+ unsigned long reg;
+ reg = apic_read(lvt);
+ apic_write_around(lvt, reg & ~APIC_LVT_MASKED);
+}
+
+void inline mask_apic_lvt(unsigned int lvt)
+{
+ unsigned long reg;
+ reg = apic_read(lvt);
+ apic_write_around(lvt, reg | APIC_LVT_MASKED);
+}
+
+/* you can enable, but we don't enforce unmasking */
+void enable_apic_lvt(unsigned int lvt, unsigned int val)
+{
+ unsigned int vector;
+ unsigned long reg, flags;
+
+ spin_lock_irqsave(&lapic_lock, flags);
+ reg = apic_read(lvt);
+ vector = val & APIC_VECTOR_MASK;
+ if (vector)
+ Dprintk("...vector:%x -> LVT:%#x\n", vector, lvt);
+ Dprintk("LVT %#x before enable:%#x after:%#x\n", lvt, reg, val);
+
+ if (vector) {
+ if (vector < FIRST_DEVICE_VECTOR)
+ BUG();
+
+ vector_lvt[vector] = lvt;
+ }
+
+ apic_write_around(lvt, val);
+ spin_unlock_irqrestore(&lapic_lock, flags);
+}
+
+/* forces masking, and zeroes vector */
+void disable_apic_lvt(unsigned int lvt)
+{
+ unsigned long reg, flags;
+ unsigned int vector;
+
+ spin_lock_irqsave(&lapic_lock, flags);
+ mask_apic_lvt(lvt);
+ reg = apic_read(lvt);
+ vector = reg & APIC_VECTOR_MASK;
+ if (lvt != vector_lvt[vector])
+ Dprintk(KERN_ERR "APIC: Unmatched LVT:%#x vector:%#x", lvt, vector);
+
+ apic_write_around(lvt, reg & ~APIC_VECTOR_MASK);
+ vector_lvt[vector] = 0;
+ spin_unlock_irqrestore(&lapic_lock, flags);
+}
+
+void enable_lapic_irq(unsigned int irq)
+{
+ int vector;
+
+ vector = irq_vector[irq];
+ if (vector < FIRST_DEVICE_VECTOR)
+ BUG();
+
+ unmask_apic_lvt(vector_lvt[vector]);
+}
+
+void disable_lapic_irq(unsigned int irq)
+{
+ int vector;
+
+ vector = irq_vector[irq];
+ if (vector < FIRST_DEVICE_VECTOR)
+ BUG();
+
+ mask_apic_lvt(vector_lvt[vector]);
+}
+
+unsigned int startup_apic_irq(unsigned int irq)
+{
+ enable_lapic_irq(irq);
+ return 0;
+}
+
+void shutdown_apic_irq(unsigned int irq)
+{
+ disable_lapic_irq(irq);
+}
+
+
+void ack_lapic_irq(unsigned int irq)
+{
+ ack_APIC_irq();
+}
+
+void end_lapic_irq(unsigned int i) { /* nothing */ }
+
+struct hw_interrupt_type edge_lapic_irq_type = {
+ typename: "local-APIC-edge",
+ /*startup: startup_apic_irq,
+ shutdown: shutdown_apic_irq,*/
+ enable: enable_lapic_irq,
+ disable: disable_lapic_irq,
+ ack: ack_lapic_irq,
+ end: end_lapic_irq
+};
+
+struct hw_interrupt_type level_lapic_irq_type = {
+ typename: "local-APIC-level",
+ /* startup: startup_apic_irq,
+ shutdown: shutdown_apic_irq,*/
+ enable: enable_lapic_irq,
+ disable: disable_lapic_irq,
+ ack: ack_lapic_irq,
+ end: end_lapic_irq
+};
+
 void clear_local_APIC(void)
 {
         int maxlvt;
@@ -67,16 +212,11 @@
          * Careful: we have to set masks only first to deassert
          * any level-triggered sources.
          */
- v = apic_read(APIC_LVTT);
- apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED);
- v = apic_read(APIC_LVT0);
- apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED);
- v = apic_read(APIC_LVT1);
- apic_write_around(APIC_LVT1, v | APIC_LVT_MASKED);
- if (maxlvt >= 4) {
- v = apic_read(APIC_LVTPC);
- apic_write_around(APIC_LVTPC, v | APIC_LVT_MASKED);
- }
+ mask_apic_lvt(APIC_LVTT);
+ mask_apic_lvt(APIC_LVT0);
+ mask_apic_lvt(APIC_LVT1);
+ if (maxlvt >= 4)
+ mask_apic_lvt(APIC_LVTPC);
 
         /*
          * Clean APIC state for other OSs:
@@ -85,9 +225,9 @@
         apic_write_around(APIC_LVT0, APIC_LVT_MASKED);
         apic_write_around(APIC_LVT1, APIC_LVT_MASKED);
         if (maxlvt >= 3)
- apic_write_around(APIC_LVTERR, APIC_LVT_MASKED);
+ mask_apic_lvt(APIC_LVTERR);
         if (maxlvt >= 4)
- apic_write_around(APIC_LVTPC, APIC_LVT_MASKED);
+ mask_apic_lvt(APIC_LVTPC);
         v = GET_APIC_VERSION(apic_read(APIC_LVR));
         if (APIC_INTEGRATED(v)) { /* !82489DX */
                 if (maxlvt > 3)
@@ -253,11 +393,11 @@
         /*
          * Set up the virtual wire mode.
          */
- apic_write_around(APIC_LVT0, APIC_DM_EXTINT);
+ enable_apic_lvt(APIC_LVT0, APIC_DM_EXTINT);
         value = APIC_DM_NMI;
         if (!APIC_INTEGRATED(ver)) /* 82489DX */
                 value |= APIC_LVT_LEVEL_TRIGGER;
- apic_write_around(APIC_LVT1, value);
+ enable_apic_lvt(APIC_LVT1, value);
 }
 
 void __init setup_local_APIC (void)
@@ -377,7 +517,7 @@
                 value = APIC_DM_EXTINT | APIC_LVT_MASKED;
                 printk("masked ExtINT on CPU#%d\n", smp_processor_id());
         }
- apic_write_around(APIC_LVT0, value);
+ enable_apic_lvt(APIC_LVT0, value);
 
         /*
          * only the BP should see the LINT1 NMI signal, obviously.
@@ -388,7 +528,7 @@
                 value = APIC_DM_NMI | APIC_LVT_MASKED;
         if (!APIC_INTEGRATED(ver)) /* 82489DX */
                 value |= APIC_LVT_LEVEL_TRIGGER;
- apic_write_around(APIC_LVT1, value);
+ enable_apic_lvt(APIC_LVT1, value);
 
         if (APIC_INTEGRATED(ver) && !esr_disable) { /* !82489DX */
                 maxlvt = get_maxlvt();
@@ -398,7 +538,7 @@
                 printk("ESR value before enabling vector: %08lx\n", value);
 
                 value = ERROR_APIC_VECTOR; // enables sending errors
- apic_write_around(APIC_LVTERR, value);
+ enable_apic_lvt(APIC_LVTERR, value);
                 /*
                  * spec says clear errors after enabling vector.
                  */
@@ -764,7 +904,9 @@
 
         lvtt1_value = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) |
                         APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR;
- apic_write_around(APIC_LVTT, lvtt1_value);
+
+ irq_vector[0] = LOCAL_TIMER_VECTOR;
+ enable_apic_lvt(APIC_LVTT, lvtt1_value);
 
         /*
          * Divide PICLK by 16
@@ -924,22 +1066,14 @@
 
 void __init disable_APIC_timer(void)
 {
- if (using_apic_timer) {
- unsigned long v;
-
- v = apic_read(APIC_LVTT);
- apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED);
- }
+ if (using_apic_timer)
+ mask_apic_lvt(APIC_LVTT);
 }
 
 void enable_APIC_timer(void)
 {
- if (using_apic_timer) {
- unsigned long v;
-
- v = apic_read(APIC_LVTT);
- apic_write_around(APIC_LVTT, v & ~APIC_LVT_MASKED);
- }
+ if (using_apic_timer)
+ unmask_apic_lvt(APIC_LVTT);
 }
 
 /*
diff -ur linux-bochs-2.4-ac/arch/i386/kernel/io_apic.c linux-2.4-apic/arch/i386/kernel/io_apic.c
--- linux-bochs-2.4-ac/arch/i386/kernel/io_apic.c Wed Nov 14 03:28:41 2001
+++ linux-2.4-apic/arch/i386/kernel/io_apic.c Sat May 4 09:58:45 2002
@@ -17,6 +17,7 @@
  * thanks to Eric Gilmore
  * and Rolf G. Tews
  * for testing these extensively
+ * Zwane Mwaikambo : Updated to use APIC API
  */
 
 #include <linux/mm.h>
@@ -553,30 +554,6 @@
         return 0;
 }
 
-int irq_vector[NR_IRQS] = { FIRST_DEVICE_VECTOR , 0 };
-
-static int __init assign_irq_vector(int irq)
-{
- static int current_vector = FIRST_DEVICE_VECTOR, offset = 0;
- if (IO_APIC_VECTOR(irq) > 0)
- return IO_APIC_VECTOR(irq);
-next:
- current_vector += 8;
- if (current_vector == SYSCALL_VECTOR)
- goto next;
-
- if (current_vector > FIRST_SYSTEM_VECTOR) {
- offset++;
- current_vector = FIRST_DEVICE_VECTOR + offset;
- }
-
- if (current_vector == FIRST_SYSTEM_VECTOR)
- panic("ran out of interrupt sources!");
-
- IO_APIC_VECTOR(irq) = current_vector;
- return current_vector;
-}
-
 extern void (*interrupt[NR_IRQS])(void);
 static struct hw_interrupt_type ioapic_level_irq_type;
 static struct hw_interrupt_type ioapic_edge_irq_type;
@@ -1344,39 +1321,6 @@
         }
 }
 
-static void enable_lapic_irq (unsigned int irq)
-{
- unsigned long v;
-
- v = apic_read(APIC_LVT0);
- apic_write_around(APIC_LVT0, v & ~APIC_LVT_MASKED);
-}
-
-static void disable_lapic_irq (unsigned int irq)
-{
- unsigned long v;
-
- v = apic_read(APIC_LVT0);
- apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED);
-}
-
-static void ack_lapic_irq (unsigned int irq)
-{
- ack_APIC_irq();
-}
-
-static void end_lapic_irq (unsigned int i) { /* nothing */ }
-
-static struct hw_interrupt_type lapic_irq_type = {
- "local-APIC-edge",
- NULL, /* startup_irq() not used for IRQ0 */
- NULL, /* shutdown_irq() not used for IRQ0 */
- enable_lapic_irq,
- disable_lapic_irq,
- ack_lapic_irq,
- end_lapic_irq
-};
-
 static void enable_NMI_through_LVT0 (void * dummy)
 {
         unsigned int v, ver;
@@ -1386,7 +1330,8 @@
         v = APIC_DM_NMI; /* unmask and set to NMI */
         if (!APIC_INTEGRATED(ver)) /* 82489DX */
                 v |= APIC_LVT_LEVEL_TRIGGER;
- apic_write_around(APIC_LVT0, v);
+
+ enable_apic_lvt(APIC_LVT0, v);
 }
 
 static void setup_nmi (void)
@@ -1554,22 +1499,23 @@
         printk(KERN_INFO "...trying to set up timer as Virtual Wire IRQ...");
 
         disable_8259A_irq(0);
- irq_desc[0].handler = &lapic_irq_type;
- apic_write_around(APIC_LVT0, APIC_DM_FIXED | vector); /* Fixed mode */
+ irq_desc[0].handler = &edge_lapic_irq_type;
+ enable_apic_lvt(APIC_LVT0, APIC_DM_FIXED | vector); /* Fixed mode */
         enable_8259A_irq(0);
 
         if (timer_irq_works()) {
                 printk(" works.\n");
                 return;
         }
- apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | vector);
+
+ disable_apic_lvt(APIC_LVT0);
         printk(" failed.\n");
 
         printk(KERN_INFO "...trying to set up timer as ExtINT IRQ...");
 
         init_8259A(0);
         make_8259A_irq(0);
- apic_write_around(APIC_LVT0, APIC_DM_EXTINT);
+ enable_apic_lvt(APIC_LVT0, APIC_DM_EXTINT);
 
         unlock_ExtINT_logic();
 
diff -ur linux-bochs-2.4-ac/include/asm-i386/apic.h linux-2.4-apic/include/asm-i386/apic.h
--- linux-bochs-2.4-ac/include/asm-i386/apic.h Mon Apr 29 13:48:01 2002
+++ linux-2.4-apic/include/asm-i386/apic.h Sat May 4 09:48:47 2002
@@ -81,10 +81,21 @@
 extern int APIC_init_uniprocessor (void);
 extern void disable_APIC_timer(void);
 extern void enable_APIC_timer(void);
+extern void enable_apic_lvt(unsigned int lvt, unsigned int value);
+extern void disable_apic_lvt(unsigned int lvt);
+extern void apic_mask_lvt(unsigned int lvt);
+extern void apic_unmask_lvt(unsigned int lvt);
+extern void disable_lapic_irq(unsigned int irq);
+extern void enable_lapic_irq(unsigned int irq);
+extern unsigned int startup_apic_irq(unsigned int irq);
+extern void shutdown_apic_irq(unsigned int irq);
 
+extern int assign_irq_vector(int irq);
 extern struct pm_dev *apic_pm_register(pm_dev_t, unsigned long, pm_callback);
 extern void apic_pm_unregister(struct pm_dev*);
 
+extern struct hw_interrupt_type edge_lapic_irq_type, level_lapic_irq_type;
+
 extern unsigned int apic_timer_irqs [NR_CPUS];
 extern int check_nmi_watchdog (void);
 
diff -ur linux-bochs-2.4-ac/include/asm-i386/hw_irq.h linux-2.4-apic/include/asm-i386/hw_irq.h
--- linux-bochs-2.4-ac/include/asm-i386/hw_irq.h Sun Apr 28 10:01:23 2002
+++ linux-2.4-apic/include/asm-i386/hw_irq.h Sat May 4 10:14:02 2002
@@ -58,9 +58,12 @@
  */
 #define FIRST_DEVICE_VECTOR 0x31
 #define FIRST_SYSTEM_VECTOR 0xef
+#define LAST_APIC_VECTOR 0xff
 
 extern int irq_vector[NR_IRQS];
+extern int vector_lvt[LAST_APIC_VECTOR];
 #define IO_APIC_VECTOR(irq) irq_vector[irq]
+#define APIC_VECTOR(irq) irq_vector[irq]
 
 /*
  * Various low-level irq details needed by irq.c, process.c,
                

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Tue May 07 2002 - 22:00:22 EST