#include <asm/smp.h>
#include <asm/desc.h>
#include <asm/proto.h>
-#include <asm/mach_apic.h>
#define __apicdebuginit __init
int sis_apic_bug; /* not actually supported, dummy for compile */
+#undef APIC_LOCKUP_DEBUG
+
+#define APIC_LOCKUP_DEBUG
+
static spinlock_t ioapic_lock = SPIN_LOCK_UNLOCKED;
/*
* shared ISA-space IRQs, so we have to support them. We are super
* fast in the common case, and fast for shared ISA-space IRQs.
*/
-static void add_pin_to_irq(unsigned int irq, int apic, int pin)
+static void __init add_pin_to_irq(unsigned int irq, int apic, int pin)
{
static int first_free_entry = NR_IRQS;
struct irq_pin_list *entry = irq_2_pin + irq;
/* mask = 1 */
DO_ACTION( __unmask, 0, &= 0xfffeffff, )
/* mask = 0 */
+DO_ACTION( __mask_and_edge, 0, = (reg & 0xffff7fff) | 0x00010000, )
+ /* mask = 1, trigger = 0 */
+DO_ACTION( __unmask_and_level, 0, = (reg & 0xfffeffff) | 0x00008000, )
+ /* mask = 0, trigger = 1 */
static void mask_IO_APIC_irq (unsigned int irq)
{
/*
* Find the IRQ entry number of a certain pin.
*/
-static int find_irq_entry(int apic, int pin, int type)
+static int __init find_irq_entry(int apic, int pin, int type)
{
int i;
/*
* EISA Edge/Level control register, ELCR
*/
-static int EISA_ELCR(unsigned int irq)
+static int __init EISA_ELCR(unsigned int irq)
{
if (irq < 16) {
unsigned int port = 0x4d0 + (irq >> 3);
return polarity;
}
-static int MPBIOS_trigger(int idx)
+static int __init MPBIOS_trigger(int idx)
{
int bus = mp_irqs[idx].mpc_srcbus;
int trigger;
/* irq_vectors is indexed by the sum of all RTEs in all I/O APICs. */
u8 irq_vector[NR_IRQ_VECTORS] = { FIRST_DEVICE_VECTOR , 0 };
+#ifdef CONFIG_PCI_MSI
int assign_irq_vector(int irq)
+#else
+int __init assign_irq_vector(int irq)
+#endif
{
static int current_vector = FIRST_DEVICE_VECTOR, offset = 0;
*/
memset(&entry,0,sizeof(entry));
- entry.delivery_mode = INT_DELIVERY_MODE;
- entry.dest_mode = INT_DEST_MODE;
+ entry.delivery_mode = dest_LowestPrio;
+ entry.dest_mode = INT_DELIVERY_MODE;
entry.mask = 0; /* enable IRQ */
entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS);
* We use logical delivery to get the timer IRQ
* to the first CPU.
*/
- entry.dest_mode = INT_DEST_MODE;
+ entry.dest_mode = INT_DELIVERY_MODE;
entry.mask = 0; /* unmask IRQ now */
entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS);
- entry.delivery_mode = INT_DELIVERY_MODE;
+ entry.delivery_mode = dest_LowestPrio;
entry.polarity = 0;
entry.trigger = 0;
entry.vector = vector;
static void __init setup_ioapic_ids_from_mpc (void)
{
union IO_APIC_reg_00 reg_00;
+ physid_mask_t phys_id_present_map = phys_cpu_present_map;
int apic;
int i;
unsigned char old_id;
mp_ioapics[apic].mpc_apicid = reg_00.bits.ID;
}
- printk(KERN_INFO "Using IO-APIC %d\n", mp_ioapics[apic].mpc_apicid);
+ /*
+ * Sanity check, is the ID really free? Every APIC in a
+ * system must have a unique ID or we get lots of nice
+ * 'stuck on smp_invalidate_needed IPI wait' messages.
+ */
+ if (physid_isset(mp_ioapics[apic].mpc_apicid, phys_id_present_map)) {
+ printk(KERN_ERR "BIOS bug, IO-APIC#%d ID %d is already used!...\n",
+ apic, mp_ioapics[apic].mpc_apicid);
+ for (i = 0; i < 0xf; i++)
+ if (!physid_isset(i, phys_id_present_map))
+ break;
+ if (i >= 0xf)
+ panic("Max APIC ID exceeded!\n");
+ printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n",
+ i);
+ physid_set(i, phys_id_present_map);
+ mp_ioapics[apic].mpc_apicid = i;
+ } else {
+ printk(KERN_INFO
+ "Using IO-APIC %d\n", mp_ioapics[apic].mpc_apicid);
+ physid_set(mp_ioapics[apic].mpc_apicid, phys_id_present_map);
+ }
/*
static void end_level_ioapic_irq (unsigned int irq)
{
+ unsigned long v;
+ int i;
+
+/*
+ * It appears there is an erratum which affects at least version 0x11
+ * of I/O APIC (that's the 82093AA and cores integrated into various
+ * chipsets). Under certain conditions a level-triggered interrupt is
+ * erroneously delivered as edge-triggered one but the respective IRR
+ * bit gets set nevertheless. As a result the I/O unit expects an EOI
+ * message but it will never arrive and further interrupts are blocked
+ * from the source. The exact reason is so far unknown, but the
+ * phenomenon was observed when two consecutive interrupt requests
+ * from a given source get delivered to the same CPU and the source is
+ * temporarily disabled in between.
+ *
+ * A workaround is to simulate an EOI message manually. We achieve it
+ * by setting the trigger mode to edge and then to level when the edge
+ * trigger mode gets detected in the TMR of a local APIC for a
+ * level-triggered interrupt. We mask the source for the time of the
+ * operation to prevent an edge-triggered interrupt escaping meanwhile.
+ * The idea is from Manfred Spraul. --macro
+ */
+ i = IO_APIC_VECTOR(irq);
+ v = apic_read(APIC_TMR + ((i & ~0x1f) >> 1));
+
ack_APIC_irq();
+
+ if (!(v & (1 << (i & 0x1f)))) {
+#ifdef APIC_LOCKUP_DEBUG
+ struct irq_pin_list *entry;
+#endif
+
+#ifdef APIC_MISMATCH_DEBUG
+ atomic_inc(&irq_mis_count);
+#endif
+ spin_lock(&ioapic_lock);
+ __mask_and_edge_IO_APIC_irq(irq);
+#ifdef APIC_LOCKUP_DEBUG
+ for (entry = irq_2_pin + irq;;) {
+ unsigned int reg;
+
+ if (entry->pin == -1)
+ break;
+ reg = io_apic_read(entry->apic, 0x10 + entry->pin * 2);
+ if (reg & 0x00004000)
+ printk(KERN_CRIT "Aieee!!! Remote IRR"
+ " still set after unlock!\n");
+ if (!entry->next)
+ break;
+ entry = irq_2_pin + entry->next;
+ }
+#endif
+ __unmask_and_level_IO_APIC_irq(irq);
+ spin_unlock(&ioapic_lock);
+ }
}
static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask)
dest = cpu_mask_to_apicid(mask);
/*
- * Only the high 8 bits are valid.
+ * Only the first 8 bits are valid.
*/
- dest = SET_APIC_LOGICAL_ID(dest);
+ dest = dest << 24;
spin_lock_irqsave(&ioapic_lock, flags);
__DO_ACTION(1, = dest, )
#ifdef CONFIG_ACPI_BOOT
-#define IO_APIC_MAX_ID 0xFE
+#define IO_APIC_MAX_ID 15
int __init io_apic_get_unique_id (int ioapic, int apic_id)
{
memset(&entry,0,sizeof(entry));
- entry.delivery_mode = INT_DELIVERY_MODE;
- entry.dest_mode = INT_DEST_MODE;
+ entry.delivery_mode = dest_LowestPrio;
+ entry.dest_mode = INT_DELIVERY_MODE;
entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS);
entry.trigger = edge_level;
entry.polarity = active_high_low;
#endif /*CONFIG_ACPI_BOOT*/
+#ifndef CONFIG_SMP
+void send_IPI_self(int vector)
+{
+ unsigned int cfg;
+
+ /*
+ * Wait for idle.
+ */
+ apic_wait_icr_idle();
+ cfg = APIC_DM_FIXED | APIC_DEST_SELF | vector | APIC_DEST_LOGICAL;
+
+ /*
+ * Send the IPI. The write to APIC_ICR fires this off.
+ */
+ apic_write_around(APIC_ICR, cfg);
+}
+#endif
+
/*
* This function currently is only a helper for the i386 smp boot process where