Skip to content
Snippets Groups Projects
io_apic.c 99.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    			break;
    		}
    
    		default: /* invalid */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		{
    			printk(KERN_WARNING "broken BIOS!!\n");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			break;
    		}
    	}
    	return trigger;
    }
    
    static inline int irq_polarity(int idx)
    {
    	return MPBIOS_polarity(idx);
    }
    
    static inline int irq_trigger(int idx)
    {
    	return MPBIOS_trigger(idx);
    }
    
    
    int (*ioapic_renumber_irq)(int ioapic, int irq);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    static int pin_2_irq(int idx, int apic, int pin)
    {
    	int irq, i;
    
    	int bus = mp_irqs[idx].srcbus;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/*
    	 * Debugging check, we are in big trouble if this message pops up!
    	 */
    
    	if (mp_irqs[idx].dstirq != pin)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		printk(KERN_ERR "broken BIOS or MPTABLE parser, ayiee!!\n");
    
    
    	if (test_bit(bus, mp_bus_not_pci)) {
    
    		irq = mp_irqs[idx].srcbusirq;
    
    		/*
    		 * PCI IRQs are mapped in order
    		 */
    		i = irq = 0;
    		while (i < apic)
    			irq += nr_ioapic_registers[i++];
    		irq += pin;
    
                     * For MPS mode, so far only needed by ES7000 platform
                     */
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    		if (ioapic_renumber_irq)
    			irq = ioapic_renumber_irq(apic, irq);
    
    #ifdef CONFIG_X86_32
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	/*
    	 * PCI IRQ command line redirection. Yes, limits are hardcoded.
    	 */
    	if ((pin >= 16) && (pin <= 23)) {
    		if (pirq_entries[pin-16] != -1) {
    			if (!pirq_entries[pin-16]) {
    				apic_printk(APIC_VERBOSE, KERN_DEBUG
    						"disabling PIRQ%d\n", pin-16);
    			} else {
    				irq = pirq_entries[pin-16];
    				apic_printk(APIC_VERBOSE, KERN_DEBUG
    						"using PIRQ%d -> IRQ %d\n",
    						pin-16, irq);
    			}
    		}
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return irq;
    }
    
    
    /*
     * Find a specific PCI IRQ entry.
     * Not an __init, possibly needed by modules
     */
    int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin,
    
    				struct io_apic_irq_attr *irq_attr)
    
    {
    	int apic, i, best_guess = -1;
    
    	apic_printk(APIC_DEBUG,
    		    "querying PCI -> IRQ mapping bus:%d, slot:%d, pin:%d.\n",
    		    bus, slot, pin);
    	if (test_bit(bus, mp_bus_not_pci)) {
    		apic_printk(APIC_VERBOSE,
    			    "PCI BIOS passed nonexistent PCI bus %d!\n", bus);
    		return -1;
    	}
    	for (i = 0; i < mp_irq_entries; i++) {
    		int lbus = mp_irqs[i].srcbus;
    
    		for (apic = 0; apic < nr_ioapics; apic++)
    			if (mp_ioapics[apic].apicid == mp_irqs[i].dstapic ||
    			    mp_irqs[i].dstapic == MP_APIC_ALL)
    				break;
    
    		if (!test_bit(lbus, mp_bus_not_pci) &&
    		    !mp_irqs[i].irqtype &&
    		    (bus == lbus) &&
    		    (slot == ((mp_irqs[i].srcbusirq >> 2) & 0x1f))) {
    			int irq = pin_2_irq(i, apic, mp_irqs[i].dstirq);
    
    			if (!(apic || IO_APIC_IRQ(irq)))
    				continue;
    
    			if (pin == (mp_irqs[i].srcbusirq & 3)) {
    
    				set_io_apic_irq_attr(irq_attr, apic,
    						     mp_irqs[i].dstirq,
    						     irq_trigger(i),
    						     irq_polarity(i));
    
    				return irq;
    			}
    			/*
    			 * Use the first all-but-pin matching entry as a
    			 * best-guess fuzzy result for broken mptables.
    			 */
    			if (best_guess < 0) {
    
    				set_io_apic_irq_attr(irq_attr, apic,
    						     mp_irqs[i].dstirq,
    						     irq_trigger(i),
    						     irq_polarity(i));
    
    				best_guess = irq;
    			}
    		}
    	}
    	return best_guess;
    }
    EXPORT_SYMBOL(IO_APIC_get_PCI_irq_vector);
    
    
    void lock_vector_lock(void)
    {
    	/* Used to the online set of cpus does not change
    	 * during assign_irq_vector.
    	 */
    	spin_lock(&vector_lock);
    }
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    void unlock_vector_lock(void)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	spin_unlock(&vector_lock);
    }
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    static int
    __assign_irq_vector(int irq, struct irq_cfg *cfg, const struct cpumask *mask)
    
    	/*
    	 * NOTE! The local APIC isn't very good at handling
    	 * multiple interrupts at the same interrupt level.
    	 * As the interrupt level is determined by taking the
    	 * vector number and shifting that right by 4, we
    	 * want to spread these out a bit so that they don't
    	 * all fall in the same interrupt level.
    	 *
    	 * Also, we've got to be careful not to trash gate
    	 * 0x80, because int 0x80 is hm, kind of importantish. ;)
    	 */
    
    	static int current_vector = FIRST_DEVICE_VECTOR, current_offset = 0;
    	unsigned int old_vector;
    
    	int cpu, err;
    	cpumask_var_t tmp_mask;
    
    	if (cfg->move_in_progress)
    
    	if (!alloc_cpumask_var(&tmp_mask, GFP_ATOMIC))
    		return -ENOMEM;
    
    	old_vector = cfg->vector;
    	if (old_vector) {
    
    		cpumask_and(tmp_mask, mask, cpu_online_mask);
    		cpumask_and(tmp_mask, cfg->domain, tmp_mask);
    		if (!cpumask_empty(tmp_mask)) {
    			free_cpumask_var(tmp_mask);
    
    	/* Only try and allocate irqs on cpus that are present */
    
    	err = -ENOSPC;
    	for_each_cpu_and(cpu, mask, cpu_online_mask) {
    
    		int new_cpu;
    		int vector, offset;
    
    		apic->vector_allocation_domain(cpu, tmp_mask);
    
    		vector = current_vector;
    		offset = current_offset;
    
    		vector += 8;
    		if (vector >= first_system_vector) {
    
    			/* If out of vectors on large boxen, must share them. */
    
    			offset = (offset + 1) % 8;
    			vector = FIRST_DEVICE_VECTOR + offset;
    		}
    		if (unlikely(current_vector == vector))
    			continue;
    
    
    		if (test_bit(vector, used_vectors))
    
    		for_each_cpu_and(new_cpu, tmp_mask, cpu_online_mask)
    
    			if (per_cpu(vector_irq, new_cpu)[vector] != -1)
    				goto next;
    		/* Found one! */
    		current_vector = vector;
    		current_offset = offset;
    		if (old_vector) {
    			cfg->move_in_progress = 1;
    
    			cpumask_copy(cfg->old_domain, cfg->domain);
    
    		for_each_cpu_and(new_cpu, tmp_mask, cpu_online_mask)
    
    			per_cpu(vector_irq, new_cpu)[vector] = irq;
    		cfg->vector = vector;
    
    		cpumask_copy(cfg->domain, tmp_mask);
    		err = 0;
    		break;
    
    	free_cpumask_var(tmp_mask);
    	return err;
    
    int assign_irq_vector(int irq, struct irq_cfg *cfg, const struct cpumask *mask)
    
    	unsigned long flags;
    
    	spin_lock_irqsave(&vector_lock, flags);
    
    	err = __assign_irq_vector(irq, cfg, mask);
    
    	spin_unlock_irqrestore(&vector_lock, flags);
    
    static void __clear_irq_vector(int irq, struct irq_cfg *cfg)
    
    {
    	int cpu, vector;
    
    	BUG_ON(!cfg->vector);
    
    	vector = cfg->vector;
    
    	for_each_cpu_and(cpu, cfg->domain, cpu_online_mask)
    
    		per_cpu(vector_irq, cpu)[vector] = -1;
    
    	cfg->vector = 0;
    
    	cpumask_clear(cfg->domain);
    
    
    	if (likely(!cfg->move_in_progress))
    		return;
    
    	for_each_cpu_and(cpu, cfg->old_domain, cpu_online_mask) {
    
    		for (vector = FIRST_EXTERNAL_VECTOR; vector < NR_VECTORS;
    								vector++) {
    			if (per_cpu(vector_irq, cpu)[vector] != irq)
    				continue;
    			per_cpu(vector_irq, cpu)[vector] = -1;
    			break;
    		}
    	}
    	cfg->move_in_progress = 0;
    
    }
    
    void __setup_vector_irq(int cpu)
    {
    	/* Initialize vector_irq on a new cpu */
    	/* This function must be called with vector_lock held */
    	int irq, vector;
    	struct irq_cfg *cfg;
    
    	struct irq_desc *desc;
    
    
    	/* Mark the inuse vectors */
    
    	for_each_irq_desc(irq, desc) {
    		cfg = desc->chip_data;
    
    		if (!cpumask_test_cpu(cpu, cfg->domain))
    
    			continue;
    		vector = cfg->vector;
    		per_cpu(vector_irq, cpu)[vector] = irq;
    	}
    	/* Mark the free vectors */
    	for (vector = 0; vector < NR_VECTORS; ++vector) {
    		irq = per_cpu(vector_irq, cpu)[vector];
    		if (irq < 0)
    			continue;
    
    		cfg = irq_cfg(irq);
    
    		if (!cpumask_test_cpu(cpu, cfg->domain))
    
    			per_cpu(vector_irq, cpu)[vector] = -1;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static struct irq_chip ioapic_chip;
    
    static struct irq_chip ir_ioapic_chip;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    #define IOAPIC_AUTO     -1
    #define IOAPIC_EDGE     0
    #define IOAPIC_LEVEL    1
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    #ifdef CONFIG_X86_32
    
    static inline int IO_APIC_irq_trigger(int irq)
    {
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    	int apic, idx, pin;
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    	for (apic = 0; apic < nr_ioapics; apic++) {
    		for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) {
    			idx = find_irq_entry(apic, pin, mp_INT);
    			if ((idx != -1) && (irq == pin_2_irq(idx, apic, pin)))
    				return irq_trigger(idx);
    		}
    	}
    	/*
    
             * nonexistent IRQs are edge default
             */
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    	return 0;
    
    #else
    static inline int IO_APIC_irq_trigger(int irq)
    {
    
    static void ioapic_register_intr(int irq, struct irq_desc *desc, unsigned long trigger)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	if ((trigger == IOAPIC_AUTO && IO_APIC_irq_trigger(irq)) ||
    
    	    trigger == IOAPIC_LEVEL)
    
    	else
    		desc->status &= ~IRQ_LEVEL;
    
    
    	if (irq_remapped(irq)) {
    		desc->status |= IRQ_MOVE_PCNTXT;
    		if (trigger)
    			set_irq_chip_and_handler_name(irq, &ir_ioapic_chip,
    						      handle_fasteoi_irq,
    						     "fasteoi");
    		else
    			set_irq_chip_and_handler_name(irq, &ir_ioapic_chip,
    						      handle_edge_irq, "edge");
    		return;
    	}
    
    	if ((trigger == IOAPIC_AUTO && IO_APIC_irq_trigger(irq)) ||
    	    trigger == IOAPIC_LEVEL)
    
    		set_irq_chip_and_handler_name(irq, &ioapic_chip,
    
    					      handle_fasteoi_irq,
    					      "fasteoi");
    
    		set_irq_chip_and_handler_name(irq, &ioapic_chip,
    
    					      handle_edge_irq, "edge");
    
    int setup_ioapic_entry(int apic_id, int irq,
    		       struct IO_APIC_route_entry *entry,
    		       unsigned int destination, int trigger,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	/*
    	 * add it to the IO-APIC irq-routing table:
    	 */
    	memset(entry,0,sizeof(*entry));
    
    
    	if (intr_remapping_enabled) {
    
    		struct intel_iommu *iommu = map_ioapic_to_ir(apic_id);
    
    		struct irte irte;
    		struct IR_IO_APIC_route_entry *ir_entry =
    			(struct IR_IO_APIC_route_entry *) entry;
    		int index;
    
    		if (!iommu)
    
    			panic("No mapping iommu for ioapic %d\n", apic_id);
    
    
    		index = alloc_irte(iommu, irq, 1);
    		if (index < 0)
    
    			panic("Failed to allocate IRTE for ioapic %d\n", apic_id);
    
    
    		memset(&irte, 0, sizeof(irte));
    
    		irte.present = 1;
    
    		irte.dst_mode = apic->irq_dest_mode;
    
    		/*
    		 * Trigger mode in the IRTE will always be edge, and the
    		 * actual level or edge trigger will be setup in the IO-APIC
    		 * RTE. This will help simplify level triggered irq migration.
    		 * For more details, see the comments above explainig IO-APIC
    		 * irq migration in the presence of interrupt-remapping.
    		 */
    		irte.trigger_mode = 0;
    
    		irte.dlvry_mode = apic->irq_delivery_mode;
    
    		irte.vector = vector;
    		irte.dest_id = IRTE_DEST(destination);
    
    
    		/* Set source-id of interrupt request */
    		set_ioapic_sid(&irte, apic_id);
    
    
    		modify_irte(irq, &irte);
    
    		ir_entry->index2 = (index >> 15) & 0x1;
    		ir_entry->zero = 0;
    		ir_entry->format = 1;
    		ir_entry->index = (index & 0x7fff);
    
    		/*
    		 * IO-APIC RTE will be configured with virtual vector.
    		 * irq handler will do the explicit EOI to the io-apic.
    		 */
    		ir_entry->vector = pin;
    
    		entry->delivery_mode = apic->irq_delivery_mode;
    		entry->dest_mode = apic->irq_dest_mode;
    
    		entry->dest = destination;
    
    	entry->mask = 0;				/* enable IRQ */
    
    	entry->trigger = trigger;
    	entry->polarity = polarity;
    
    	/* Mask level triggered irqs.
    	 * Use IRQ_DELAYED_DISABLE for edge triggered irqs.
    	 */
    	if (trigger)
    		entry->mask = 1;
    	return 0;
    }
    
    
    static void setup_IO_APIC_irq(int apic_id, int pin, unsigned int irq, struct irq_desc *desc,
    
    			      int trigger, int polarity)
    
    {
    	struct irq_cfg *cfg;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	struct IO_APIC_route_entry entry;
    
    	unsigned int dest;
    
    
    	if (!IO_APIC_IRQ(irq))
    		return;
    
    
    	cfg = desc->chip_data;
    
    	if (assign_irq_vector(irq, cfg, apic->target_cpus()))
    
    	dest = apic->cpu_mask_to_apicid_and(cfg->domain, apic->target_cpus());
    
    
    	apic_printk(APIC_VERBOSE,KERN_DEBUG
    		    "IOAPIC[%d]: Set routing entry (%d-%d -> 0x%x -> "
    		    "IRQ %d Mode:%i Active:%i)\n",
    
    		    apic_id, mp_ioapics[apic_id].apicid, pin, cfg->vector,
    
    		    irq, trigger, polarity);
    
    
    
    	if (setup_ioapic_entry(mp_ioapics[apic_id].apicid, irq, &entry,
    
    			       dest, trigger, polarity, cfg->vector, pin)) {
    
    		printk("Failed to setup ioapic entry for ioapic  %d, pin %d\n",
    
    		       mp_ioapics[apic_id].apicid, pin);
    
    		__clear_irq_vector(irq, cfg);
    
    	ioapic_register_intr(irq, desc, trigger);
    
    	if (irq < nr_legacy_irqs)
    
    		disable_8259A_irq(irq);
    
    
    	ioapic_write_entry(apic_id, pin, entry);
    
    static struct {
    	DECLARE_BITMAP(pin_programmed, MP_MAX_IOAPIC_PIN + 1);
    } mp_ioapic_routing[MAX_IO_APICS];
    
    
    static void __init setup_IO_APIC_irqs(void)
    {
    
    	int apic_id = 0, pin, idx, irq;
    
    	struct irq_desc *desc;
    
    	struct irq_cfg *cfg;
    
    	int node = cpu_to_node(boot_cpu_id);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	apic_printk(APIC_VERBOSE, KERN_DEBUG "init IO_APIC IRQs\n");
    
    
    #ifdef CONFIG_ACPI
    	if (!acpi_disabled && acpi_ioapic) {
    		apic_id = mp_find_ioapic(0);
    		if (apic_id < 0)
    			apic_id = 0;
    	}
    #endif
    
    	for (pin = 0; pin < nr_ioapic_registers[apic_id]; pin++) {
    		idx = find_irq_entry(apic_id, pin, mp_INT);
    		if (idx == -1) {
    			if (!notcon) {
    				notcon = 1;
    				apic_printk(APIC_VERBOSE,
    					KERN_DEBUG " %d-%d",
    					mp_ioapics[apic_id].apicid, pin);
    			} else
    				apic_printk(APIC_VERBOSE, " %d-%d",
    					mp_ioapics[apic_id].apicid, pin);
    			continue;
    		}
    		if (notcon) {
    			apic_printk(APIC_VERBOSE,
    				" (apicid-pin) not connected\n");
    			notcon = 0;
    		}
    
    		irq = pin_2_irq(idx, apic_id, pin);
    
    		/*
    		 * Skip the timer IRQ if there's a quirk handler
    		 * installed and if it returns 1:
    		 */
    		if (apic->multi_timer_check &&
    				apic->multi_timer_check(apic_id, irq))
    			continue;
    
    		desc = irq_to_desc_alloc_node(irq, node);
    		if (!desc) {
    			printk(KERN_INFO "can not get irq_desc for %d\n", irq);
    			continue;
    
    		cfg = desc->chip_data;
    		add_pin_to_irq_node(cfg, node, apic_id, pin);
    
    		/*
    		 * don't mark it in pin_programmed, so later acpi could
    		 * set it correctly when irq < 16
    		 */
    
    		setup_IO_APIC_irq(apic_id, pin, irq, desc,
    				irq_trigger(idx), irq_polarity(idx));
    
    	if (notcon)
    		apic_printk(APIC_VERBOSE,
    
    			" (apicid-pin) not connected\n");
    
     * Set up the timer pin, possibly with the 8259A-master behind.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    
    static void __init setup_timer_IRQ0_pin(unsigned int apic_id, unsigned int pin,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct IO_APIC_route_entry entry;
    
    
    	if (intr_remapping_enabled)
    		return;
    
    
    	memset(&entry, 0, sizeof(entry));
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/*
    	 * We use logical delivery to get the timer IRQ
    	 * to the first CPU.
    	 */
    
    	entry.dest_mode = apic->irq_dest_mode;
    
    Yinghai Lu's avatar
    Yinghai Lu committed
    	entry.mask = 0;			/* don't mask IRQ for edge */
    
    	entry.dest = apic->cpu_mask_to_apicid(apic->target_cpus());
    
    	entry.delivery_mode = apic->irq_delivery_mode;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	entry.polarity = 0;
    	entry.trigger = 0;
    	entry.vector = vector;
    
    	/*
    	 * The timer IRQ doesn't have to know that behind the
    
    	 * scene we may have a 8259A-master in AEOI mode ...
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	 */
    
    	set_irq_chip_and_handler_name(0, &ioapic_chip, handle_edge_irq, "edge");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/*
    	 * Add it to the IO-APIC irq-routing table:
    	 */
    
    	ioapic_write_entry(apic_id, pin, entry);
    
    
    __apicdebuginit(void) print_IO_APIC(void)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	int apic, i;
    	union IO_APIC_reg_00 reg_00;
    	union IO_APIC_reg_01 reg_01;
    	union IO_APIC_reg_02 reg_02;
    	union IO_APIC_reg_03 reg_03;
    	unsigned long flags;
    
    	struct irq_cfg *cfg;
    
    	struct irq_desc *desc;
    
    	unsigned int irq;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	printk(KERN_DEBUG "number of MP IRQ sources: %d.\n", mp_irq_entries);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	for (i = 0; i < nr_ioapics; i++)
    		printk(KERN_DEBUG "number of IO-APIC #%d registers: %d.\n",
    
    		       mp_ioapics[i].apicid, nr_ioapic_registers[i]);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/*
    	 * We are a bit conservative about what we expect.  We have to
    	 * know about every hardware change ASAP.
    	 */
    	printk(KERN_INFO "testing the IO APIC.......................\n");
    
    	for (apic = 0; apic < nr_ioapics; apic++) {
    
    	spin_lock_irqsave(&ioapic_lock, flags);
    	reg_00.raw = io_apic_read(apic, 0);
    	reg_01.raw = io_apic_read(apic, 1);
    	if (reg_01.bits.version >= 0x10)
    		reg_02.raw = io_apic_read(apic, 2);
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    	if (reg_01.bits.version >= 0x20)
    		reg_03.raw = io_apic_read(apic, 3);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	spin_unlock_irqrestore(&ioapic_lock, flags);
    
    
    	printk(KERN_DEBUG "IO APIC #%d......\n", mp_ioapics[apic].apicid);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	printk(KERN_DEBUG ".... register #00: %08X\n", reg_00.raw);
    	printk(KERN_DEBUG ".......    : physical APIC id: %02X\n", reg_00.bits.ID);
    	printk(KERN_DEBUG ".......    : Delivery Type: %X\n", reg_00.bits.delivery_type);
    	printk(KERN_DEBUG ".......    : LTS          : %X\n", reg_00.bits.LTS);
    
    
    	printk(KERN_DEBUG ".... register #01: %08X\n", *(int *)&reg_01);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	printk(KERN_DEBUG ".......     : max redirection entries: %04X\n", reg_01.bits.entries);
    
    	printk(KERN_DEBUG ".......     : PRQ implemented: %X\n", reg_01.bits.PRQ);
    	printk(KERN_DEBUG ".......     : IO APIC version: %04X\n", reg_01.bits.version);
    
    	/*
    	 * Some Intel chipsets with IO APIC VERSION of 0x1? don't have reg_02,
    	 * but the value of reg_02 is read as the previous read register
    	 * value, so ignore it if reg_02 == reg_01.
    	 */
    	if (reg_01.bits.version >= 0x10 && reg_02.raw != reg_01.raw) {
    		printk(KERN_DEBUG ".... register #02: %08X\n", reg_02.raw);
    		printk(KERN_DEBUG ".......     : arbitration: %02X\n", reg_02.bits.arbitration);
    	}
    
    	/*
    	 * Some Intel chipsets with IO APIC VERSION of 0x2? don't have reg_02
    	 * or reg_03, but the value of reg_0[23] is read as the previous read
    	 * register value, so ignore it if reg_03 == reg_0[12].
    	 */
    	if (reg_01.bits.version >= 0x20 && reg_03.raw != reg_02.raw &&
    	    reg_03.raw != reg_01.raw) {
    		printk(KERN_DEBUG ".... register #03: %08X\n", reg_03.raw);
    		printk(KERN_DEBUG ".......     : Boot DT    : %X\n", reg_03.bits.boot_DT);
    	}
    
    	printk(KERN_DEBUG ".... IRQ redirection table:\n");
    
    
    	printk(KERN_DEBUG " NR Dst Mask Trig IRR Pol"
    			  " Stat Dmod Deli Vect:   \n");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	for (i = 0; i <= reg_01.bits.entries; i++) {
    		struct IO_APIC_route_entry entry;
    
    
    		entry = ioapic_read_entry(apic, i);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    		printk(KERN_DEBUG " %02x %03X ",
    			i,
    			entry.dest
    		);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    		printk("%1d    %1d    %1d   %1d   %1d    %1d    %1d    %02X\n",
    			entry.mask,
    			entry.trigger,
    			entry.irr,
    			entry.polarity,
    			entry.delivery_status,
    			entry.dest_mode,
    			entry.delivery_mode,
    			entry.vector
    		);
    	}
    	}
    	printk(KERN_DEBUG "IRQ to pin mappings:\n");
    
    	for_each_irq_desc(irq, desc) {
    		struct irq_pin_list *entry;
    
    		cfg = desc->chip_data;
    		entry = cfg->irq_2_pin;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			continue;
    
    		printk(KERN_DEBUG "IRQ%d ", irq);
    
    		for_each_irq_pin(entry, cfg->irq_2_pin)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			printk("-> %d:%d", entry->apic, entry->pin);
    		printk("\n");
    	}
    
    	printk(KERN_INFO ".................................... done.\n");
    
    	return;
    }
    
    
    __apicdebuginit(void) print_APIC_field(int base)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	printk(KERN_DEBUG);
    
    	for (i = 0; i < 8; i++)
    		printk(KERN_CONT "%08x", apic_read(base + i*0x10));
    
    	printk(KERN_CONT "\n");
    
    __apicdebuginit(void) print_local_APIC(void *dummy)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	printk(KERN_DEBUG "printing local APIC contents on CPU#%d/%d:\n",
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		smp_processor_id(), hard_smp_processor_id());
    
    	v = apic_read(APIC_ID);
    
    	printk(KERN_INFO "... APIC ID:      %08x (%01x)\n", v, read_apic_id());
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	v = apic_read(APIC_LVR);
    	printk(KERN_INFO "... APIC VERSION: %08x\n", v);
    	ver = GET_APIC_VERSION(v);
    
    	maxlvt = lapic_get_maxlvt();
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	v = apic_read(APIC_TASKPRI);
    	printk(KERN_DEBUG "... APIC TASKPRI: %08x (%02x)\n", v, v & APIC_TPRI_MASK);
    
    
    	if (APIC_INTEGRATED(ver)) {                     /* !82489DX */
    
    		if (!APIC_XAPIC(ver)) {
    			v = apic_read(APIC_ARBPRI);
    			printk(KERN_DEBUG "... APIC ARBPRI: %08x (%02x)\n", v,
    			       v & APIC_ARBPRI_MASK);
    		}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		v = apic_read(APIC_PROCPRI);
    		printk(KERN_DEBUG "... APIC PROCPRI: %08x\n", v);
    	}
    
    
    	/*
    	 * Remote read supported only in the 82489DX and local APIC for
    	 * Pentium processors.
    	 */
    	if (!APIC_INTEGRATED(ver) || maxlvt == 3) {
    		v = apic_read(APIC_RRR);
    		printk(KERN_DEBUG "... APIC RRR: %08x\n", v);
    	}
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	v = apic_read(APIC_LDR);
    	printk(KERN_DEBUG "... APIC LDR: %08x\n", v);
    
    	if (!x2apic_enabled()) {
    		v = apic_read(APIC_DFR);
    		printk(KERN_DEBUG "... APIC DFR: %08x\n", v);
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	v = apic_read(APIC_SPIV);
    	printk(KERN_DEBUG "... APIC SPIV: %08x\n", v);
    
    	printk(KERN_DEBUG "... APIC ISR field:\n");
    
    	print_APIC_field(APIC_ISR);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	printk(KERN_DEBUG "... APIC TMR field:\n");
    
    	print_APIC_field(APIC_TMR);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	printk(KERN_DEBUG "... APIC IRR field:\n");
    
    	print_APIC_field(APIC_IRR);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	if (APIC_INTEGRATED(ver)) {             /* !82489DX */
    		if (maxlvt > 3)         /* Due to the Pentium erratum 3AP. */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			apic_write(APIC_ESR, 0);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		v = apic_read(APIC_ESR);
    		printk(KERN_DEBUG "... APIC ESR: %08x\n", v);
    	}
    
    
    	icr = apic_icr_read();
    
    	printk(KERN_DEBUG "... APIC ICR: %08x\n", (u32)icr);
    	printk(KERN_DEBUG "... APIC ICR2: %08x\n", (u32)(icr >> 32));
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	v = apic_read(APIC_LVTT);
    	printk(KERN_DEBUG "... APIC LVTT: %08x\n", v);
    
    	if (maxlvt > 3) {                       /* PC is LVT#4. */
    		v = apic_read(APIC_LVTPC);
    		printk(KERN_DEBUG "... APIC LVTPC: %08x\n", v);
    	}
    	v = apic_read(APIC_LVT0);
    	printk(KERN_DEBUG "... APIC LVT0: %08x\n", v);
    	v = apic_read(APIC_LVT1);
    	printk(KERN_DEBUG "... APIC LVT1: %08x\n", v);
    
    	if (maxlvt > 2) {			/* ERR is LVT#3. */
    		v = apic_read(APIC_LVTERR);
    		printk(KERN_DEBUG "... APIC LVTERR: %08x\n", v);
    	}
    
    	v = apic_read(APIC_TMICT);
    	printk(KERN_DEBUG "... APIC TMICT: %08x\n", v);
    	v = apic_read(APIC_TMCCT);
    	printk(KERN_DEBUG "... APIC TMCCT: %08x\n", v);
    	v = apic_read(APIC_TDCR);
    	printk(KERN_DEBUG "... APIC TDCR: %08x\n", v);
    
    
    	if (boot_cpu_has(X86_FEATURE_EXTAPIC)) {
    		v = apic_read(APIC_EFEAT);
    		maxlvt = (v >> 16) & 0xff;
    		printk(KERN_DEBUG "... APIC EFEAT: %08x\n", v);
    		v = apic_read(APIC_ECTRL);
    		printk(KERN_DEBUG "... APIC ECTRL: %08x\n", v);
    		for (i = 0; i < maxlvt; i++) {
    			v = apic_read(APIC_EILVTn(i));
    			printk(KERN_DEBUG "... APIC EILVT%d: %08x\n", i, v);
    		}
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	printk("\n");
    }
    
    
    __apicdebuginit(void) print_local_APICs(int maxcpu)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	preempt_disable();
    
    	for_each_online_cpu(cpu) {
    		if (cpu >= maxcpu)
    			break;
    
    		smp_call_function_single(cpu, print_local_APIC, NULL, 1);
    
    	preempt_enable();
    
    __apicdebuginit(void) print_PIC(void)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	unsigned int v;
    	unsigned long flags;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return;
    
    	printk(KERN_DEBUG "\nprinting PIC contents\n");
    
    	spin_lock_irqsave(&i8259A_lock, flags);
    
    	v = inb(0xa1) << 8 | inb(0x21);
    	printk(KERN_DEBUG "... PIC  IMR: %04x\n", v);
    
    	v = inb(0xa0) << 8 | inb(0x20);
    	printk(KERN_DEBUG "... PIC  IRR: %04x\n", v);
    
    
    	outb(0x0b,0xa0);
    	outb(0x0b,0x20);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	v = inb(0xa0) << 8 | inb(0x20);
    
    	outb(0x0a,0xa0);
    	outb(0x0a,0x20);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	spin_unlock_irqrestore(&i8259A_lock, flags);
    
    	printk(KERN_DEBUG "... PIC  ISR: %04x\n", v);
    
    	v = inb(0x4d1) << 8 | inb(0x4d0);
    	printk(KERN_DEBUG "... PIC ELCR: %04x\n", v);
    }
    
    
    static int __initdata show_lapic = 1;
    static __init int setup_show_lapic(char *arg)
    {
    	int num = -1;
    
    	if (strcmp(arg, "all") == 0) {
    		show_lapic = CONFIG_NR_CPUS;
    	} else {
    		get_option(&arg, &num);
    		if (num >= 0)
    			show_lapic = num;
    	}
    
    	return 1;
    }
    __setup("show_lapic=", setup_show_lapic);
    
    __apicdebuginit(int) print_ICs(void)
    
    
    	/* don't print out if apic is not there */
    
    	if (!cpu_has_apic && !apic_from_smp_config())
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /* Where if anywhere is the i8259 connect in external int mode */
    static struct { int pin, apic; } ioapic_i8259 = { -1, -1 };
    
    
    void __init enable_IO_APIC(void)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	union IO_APIC_reg_01 reg_01;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	unsigned long flags;
    
    	/*
    	 * The number of IO-APIC IRQ registers (== #pins):
    	 */
    
    	for (apic = 0; apic < nr_ioapics; apic++) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		spin_lock_irqsave(&ioapic_lock, flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		spin_unlock_irqrestore(&ioapic_lock, flags);
    
    		nr_ioapic_registers[apic] = reg_01.bits.entries+1;
    	}
    
    
    	if (!nr_legacy_irqs)
    		return;
    
    
    	for(apic = 0; apic < nr_ioapics; apic++) {
    
    		int pin;
    		/* See if any of the pins is in ExtINT mode */
    
    		for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) {
    
    			entry = ioapic_read_entry(apic, pin);
    
    
    			/* If the interrupt line is enabled and in ExtInt mode
    			 * I have found the pin where the i8259 is connected.
    			 */
    			if ((entry.mask == 0) && (entry.delivery_mode == dest_ExtINT)) {
    				ioapic_i8259.apic = apic;
    				ioapic_i8259.pin  = pin;
    				goto found_i8259;
    			}
    		}
    	}
     found_i8259:
    	/* Look to see what if the MP table has reported the ExtINT */
    	/* If we could not find the appropriate pin by looking at the ioapic
    	 * the i8259 probably is not connected the ioapic but give the
    	 * mptable a chance anyway.
    	 */
    	i8259_pin  = find_isa_irq_pin(0, mp_ExtINT);
    	i8259_apic = find_isa_irq_apic(0, mp_ExtINT);
    	/* Trust the MP table if nothing is setup in the hardware */
    	if ((ioapic_i8259.pin == -1) && (i8259_pin >= 0)) {
    		printk(KERN_WARNING "ExtINT not setup in hardware but reported by MP table\n");
    		ioapic_i8259.pin  = i8259_pin;
    		ioapic_i8259.apic = i8259_apic;
    	}
    	/* Complain if the MP table and the hardware disagree */
    	if (((ioapic_i8259.apic != i8259_apic) || (ioapic_i8259.pin != i8259_pin)) &&
    		(i8259_pin >= 0) && (ioapic_i8259.pin >= 0))
    	{
    		printk(KERN_WARNING "ExtINT in hardware and MP table differ\n");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	/*
    	 * Do not trust the IO-APIC being empty at bootup
    	 */
    	clear_IO_APIC();
    }
    
    /*
     * Not an __init, needed by the reboot code
     */
    void disable_IO_APIC(void)
    {
    	/*
    	 * Clear the IO-APIC before rebooting:
    	 */
    	clear_IO_APIC();
    
    
    	if (!nr_legacy_irqs)
    		return;
    
    
    	 * If the i8259 is routed through an IOAPIC
    
    	 * Put that IOAPIC in virtual wire mode
    
    	 * so legacy interrupts can be delivered.
    
    	 *
    	 * With interrupt-remapping, for now we will use virtual wire A mode,
    	 * as virtual wire B is little complex (need to configure both
    	 * IOAPIC RTE aswell as interrupt-remapping table entry).
    	 * As this gets called during crash dump, keep this simple for now.
    
    	if (ioapic_i8259.pin != -1 && !intr_remapping_enabled) {
    
    		struct IO_APIC_route_entry entry;
    
    		memset(&entry, 0, sizeof(entry));
    		entry.mask            = 0; /* Enabled */
    		entry.trigger         = 0; /* Edge */
    		entry.irr             = 0;
    		entry.polarity        = 0; /* High */
    		entry.delivery_status = 0;
    		entry.dest_mode       = 0; /* Physical */
    
    		entry.delivery_mode   = dest_ExtINT; /* ExtInt */
    
    		entry.dest            = read_apic_id();
    
    
    		/*
    		 * Add it to the IO-APIC irq-routing table:
    		 */
    
    		ioapic_write_entry(ioapic_i8259.apic, ioapic_i8259.pin, entry);
    
    	/*
    	 * Use virtual wire A mode when interrupt remapping is enabled.
    	 */
    
    	if (cpu_has_apic || apic_from_smp_config())
    
    		disconnect_bsp_APIC(!intr_remapping_enabled &&