Skip to content
Snippets Groups Projects
io_apic.c 94.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     *	Intel IO-APIC support for multi-Pentium hosts.
     *
    
    Ingo Molnar's avatar
    Ingo Molnar committed
     *	Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     *
     *	Many thanks to Stig Venaas for trying out countless experimental
     *	patches and reporting/debugging problems patiently!
     *
     *	(c) 1999, Multiple IO-APIC support, developed by
     *	Ken-ichi Yaku <yaku@css1.kbnes.nec.co.jp> and
     *      Hidemi Kishimoto <kisimoto@css1.kbnes.nec.co.jp>,
     *	further tested and cleaned up by Zach Brown <zab@redhat.com>
     *	and Ingo Molnar <mingo@redhat.com>
     *
     *	Fixes
     *	Maciej W. Rozycki	:	Bits for genuine 82489DX APICs;
     *					thanks to Eric Gilmore
     *					and Rolf G. Tews
     *					for testing these extensively
     *	Paul Diefenbaugh	:	Added full ACPI support
     */
    
    #include <linux/mm.h>
    #include <linux/interrupt.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <linux/sched.h>
    
    #include <linux/pci.h>
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #include <linux/mc146818rtc.h>
    #include <linux/compiler.h>
    #include <linux/acpi.h>
    
    #include <linux/module.h>
    
    #include <linux/syscore_ops.h>
    
    #include <linux/htirq.h>
    
    #include <linux/kthread.h>
    
    #include <linux/jiffies.h>	/* time_after() */
    
    #ifdef CONFIG_ACPI
    #include <acpi/acpi_bus.h>
    #endif
    #include <linux/bootmem.h>
    #include <linux/dmar.h>
    
    #include <linux/hpet.h>
    
    #include <asm/idle.h>
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #include <asm/io.h>
    #include <asm/smp.h>
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #include <asm/desc.h>
    
    #include <asm/proto.h>
    #include <asm/acpi.h>
    #include <asm/dma.h>
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #include <asm/timer.h>
    
    #include <asm/i8259.h>
    
    #include <asm/hypertransport.h>
    
    #include <asm/setup.h>
    
    #include <asm/irq_remapping.h>
    
    #include <asm/hw_irq.h>
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    Ingo Molnar's avatar
    Ingo Molnar committed
    #include <asm/apic.h>
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    #define __apicdebuginit(type) static type __init
    
    #define for_each_irq_pin(entry, head) \
    	for (entry = head; entry; entry = entry->next)
    
    #ifdef CONFIG_IRQ_REMAP
    static void irq_remap_modify_chip_defaults(struct irq_chip *chip);
    static inline bool irq_remapped(struct irq_cfg *cfg)
    {
    	return cfg->irq_2_iommu.iommu != NULL;
    }
    #else
    static inline bool irq_remapped(struct irq_cfg *cfg)
    {
    	return false;
    }
    static inline void irq_remap_modify_chip_defaults(struct irq_chip *chip)
    {
    }
    #endif
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /*
    
     *      Is the SiS APIC rmw bug present ?
     *      -1 = don't know, 0 = no, 1 = yes
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    int sis_apic_bug = -1;
    
    
    static DEFINE_RAW_SPINLOCK(ioapic_lock);
    static DEFINE_RAW_SPINLOCK(vector_lock);
    
    static struct ioapic {
    	/*
    	 * # of IRQ routing registers
    	 */
    	int nr_registers;
    
    	/*
    	 * Saved state during suspend/resume, or while enabling intr-remap.
    	 */
    	struct IO_APIC_route_entry *saved_registers;
    
    	/* I/O APIC config */
    	struct mpc_ioapic mp_config;
    
    	/* IO APIC gsi routing info */
    	struct mp_ioapic_gsi  gsi_config;
    
    	DECLARE_BITMAP(pin_programmed, MP_MAX_IOAPIC_PIN + 1);
    
    } ioapics[MAX_IO_APICS];
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    #define mpc_ioapic_ver(ioapic_idx)	ioapics[ioapic_idx].mp_config.apicver
    
    int mpc_ioapic_id(int ioapic_idx)
    
    	return ioapics[ioapic_idx].mp_config.apicid;
    
    unsigned int mpc_ioapic_addr(int ioapic_idx)
    
    	return ioapics[ioapic_idx].mp_config.apicaddr;
    
    struct mp_ioapic_gsi *mp_ioapic_gsi_routing(int ioapic_idx)
    
    	return &ioapics[ioapic_idx].gsi_config;
    
    /* The one past the highest gsi number used */
    u32 gsi_top;
    
    /* MP IRQ source entries */
    
    struct mpc_intsrc mp_irqs[MAX_IRQ_SOURCES];
    
    
    /* # of MP IRQ source entries */
    int mp_irq_entries;
    
    
    /* GSI interrupts */
    static int nr_irqs_gsi = NR_IRQS_LEGACY;
    
    
    int mp_bus_id_to_type[MAX_MP_BUSSES];
    #endif
    
    DECLARE_BITMAP(mp_bus_not_pci, MAX_MP_BUSSES);
    
    
    int skip_ioapic_setup;
    
    
    /**
     * disable_ioapic_support() - disables ioapic support at runtime
     */
    void disable_ioapic_support(void)
    
    {
    #ifdef CONFIG_PCI
    	noioapicquirk = 1;
    	noioapicreroute = -1;
    #endif
    	skip_ioapic_setup = 1;
    }
    
    
    static int __init parse_noapic(char *str)
    
    {
    	/* disable IO-APIC */
    
    	disable_ioapic_support();
    
    	return 0;
    }
    early_param("noapic", parse_noapic);
    
    static int io_apic_setup_irq_pin(unsigned int irq, int node,
    				 struct io_apic_irq_attr *attr);
    
    /* Will be called in mpparse/acpi/sfi codes for saving IRQ info */
    void mp_save_irq(struct mpc_intsrc *m)
    {
    	int i;
    
    	apic_printk(APIC_VERBOSE, "Int: type %d, pol %d, trig %d, bus %02x,"
    		" IRQ %02x, APIC ID %x, APIC INT %02x\n",
    		m->irqtype, m->irqflag & 3, (m->irqflag >> 2) & 3, m->srcbus,
    		m->srcbusirq, m->dstapic, m->dstirq);
    
    	for (i = 0; i < mp_irq_entries; i++) {
    
    		if (!memcmp(&mp_irqs[i], m, sizeof(*m)))
    
    	memcpy(&mp_irqs[mp_irq_entries], m, sizeof(*m));
    
    	if (++mp_irq_entries == MAX_IRQ_SOURCES)
    		panic("Max # of irq sources exceeded!!\n");
    }
    
    
    struct irq_pin_list {
    	int apic, pin;
    	struct irq_pin_list *next;
    };
    
    
    static struct irq_pin_list *alloc_irq_pin_list(int node)
    
    	return kzalloc_node(sizeof(struct irq_pin_list), GFP_KERNEL, node);
    
    Yinghai Lu's avatar
    Yinghai Lu committed
    /* irq_cfg is indexed by the sum of all RTEs in all I/O APICs. */
    
    static struct irq_cfg irq_cfgx[NR_IRQS_LEGACY];
    
    int __init arch_early_irq_init(void)
    
    	struct irq_cfg *cfg;
    
    	if (!legacy_pic->nr_legacy_irqs)
    
    		io_apic_irqs = ~0UL;
    
    
    	for (i = 0; i < nr_ioapics; i++) {
    
    			kzalloc(sizeof(struct IO_APIC_route_entry) *
    
    				ioapics[i].nr_registers, GFP_KERNEL);
    
    		if (!ioapics[i].saved_registers)
    
    			pr_err("IOAPIC %d: suspend/resume impossible!\n", i);
    	}
    
    
    	cfg = irq_cfgx;
    	count = ARRAY_SIZE(irq_cfgx);
    
    	node = cpu_to_node(0);
    
    	/* Make sure the legacy interrupts are marked in the bitmap */
    	irq_reserve_irqs(0, legacy_pic->nr_legacy_irqs);
    
    
    	for (i = 0; i < count; i++) {
    
    		irq_set_chip_data(i, &cfg[i]);
    
    		zalloc_cpumask_var_node(&cfg[i].domain, GFP_KERNEL, node);
    		zalloc_cpumask_var_node(&cfg[i].old_domain, GFP_KERNEL, node);
    
    		/*
    		 * For legacy IRQ's, start with assigning irq0 to irq15 to
    		 * IRQ0_VECTOR to IRQ15_VECTOR on cpu 0.
    		 */
    
    		if (i < legacy_pic->nr_legacy_irqs) {
    
    			cfg[i].vector = IRQ0_VECTOR + i;
    			cpumask_set_cpu(0, cfg[i].domain);
    		}
    
    static struct irq_cfg *irq_cfg(unsigned int irq)
    
    	return irq_get_chip_data(irq);
    
    static struct irq_cfg *alloc_irq_cfg(unsigned int irq, int node)
    
    	struct irq_cfg *cfg;
    
    	cfg = kzalloc_node(sizeof(*cfg), GFP_KERNEL, node);
    
    	if (!cfg)
    		return NULL;
    
    	if (!zalloc_cpumask_var_node(&cfg->domain, GFP_KERNEL, node))
    
    	if (!zalloc_cpumask_var_node(&cfg->old_domain, GFP_KERNEL, node))
    
    out_domain:
    	free_cpumask_var(cfg->domain);
    out_cfg:
    	kfree(cfg);
    	return NULL;
    
    static void free_irq_cfg(unsigned int at, struct irq_cfg *cfg)
    
    	if (!cfg)
    		return;
    
    	irq_set_chip_data(at, NULL);
    
    	free_cpumask_var(cfg->domain);
    	free_cpumask_var(cfg->old_domain);
    	kfree(cfg);
    }
    
    static struct irq_cfg *alloc_irq_and_cfg_at(unsigned int at, int node)
    {
    	int res = irq_alloc_desc_at(at, node);
    	struct irq_cfg *cfg;
    
    	if (res < 0) {
    		if (res != -EEXIST)
    			return NULL;
    
    		cfg = irq_get_chip_data(at);
    
    	cfg = alloc_irq_cfg(at, node);
    
    		irq_set_chip_data(at, cfg);
    
    	else
    		irq_free_desc(at);
    	return cfg;
    }
    
    static int alloc_irq_from(unsigned int from, int node)
    {
    	return irq_alloc_desc_from(from, node);
    }
    
    static void free_irq_at(unsigned int at, struct irq_cfg *cfg)
    {
    
    	free_irq_cfg(at, cfg);
    
    struct io_apic {
    	unsigned int index;
    	unsigned int unused[3];
    	unsigned int data;
    
    	unsigned int unused2[11];
    	unsigned int eoi;
    
    };
    
    static __attribute_const__ struct io_apic __iomem *io_apic_base(int idx)
    {
    	return (void __iomem *) __fix_to_virt(FIX_IO_APIC_BASE_0 + idx)
    
    		+ (mpc_ioapic_addr(idx) & ~PAGE_MASK);
    
    static inline void io_apic_eoi(unsigned int apic, unsigned int vector)
    {
    	struct io_apic __iomem *io_apic = io_apic_base(apic);
    	writel(vector, &io_apic->eoi);
    }
    
    
    unsigned int native_io_apic_read(unsigned int apic, unsigned int reg)
    
    {
    	struct io_apic __iomem *io_apic = io_apic_base(apic);
    	writel(reg, &io_apic->index);
    	return readl(&io_apic->data);
    }
    
    
    void native_io_apic_write(unsigned int apic, unsigned int reg, unsigned int value)
    
    {
    	struct io_apic __iomem *io_apic = io_apic_base(apic);
    
    	writel(reg, &io_apic->index);
    	writel(value, &io_apic->data);
    }
    
    /*
     * Re-write a value: to be used for read-modify-write
     * cycles where the read already set up the index register.
     *
     * Older SiS APIC requires we rewrite the index register
     */
    
    void native_io_apic_modify(unsigned int apic, unsigned int reg, unsigned int value)
    
    	struct io_apic __iomem *io_apic = io_apic_base(apic);
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    
    	if (sis_apic_bug)
    		writel(reg, &io_apic->index);
    
    	writel(value, &io_apic->data);
    }
    
    
    union entry_union {
    	struct { u32 w1, w2; };
    	struct IO_APIC_route_entry entry;
    };
    
    
    static struct IO_APIC_route_entry __ioapic_read_entry(int apic, int pin)
    {
    	union entry_union eu;
    
    	eu.w1 = io_apic_read(apic, 0x10 + 2 * pin);
    	eu.w2 = io_apic_read(apic, 0x11 + 2 * pin);
    
    static struct IO_APIC_route_entry ioapic_read_entry(int apic, int pin)
    {
    	union entry_union eu;
    	unsigned long flags;
    
    	raw_spin_lock_irqsave(&ioapic_lock, flags);
    
    	eu.entry = __ioapic_read_entry(apic, pin);
    
    	raw_spin_unlock_irqrestore(&ioapic_lock, flags);
    
    /*
     * When we write a new IO APIC routing entry, we need to write the high
     * word first! If the mask bit in the low word is clear, we will enable
     * the interrupt, and we need to make sure the entry is fully populated
     * before that happens.
     */
    
    static void __ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e)
    
    	union entry_union eu = {{0, 0}};
    
    
    	io_apic_write(apic, 0x11 + 2*pin, eu.w2);
    	io_apic_write(apic, 0x10 + 2*pin, eu.w1);
    
    static void ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e)
    
    	raw_spin_lock_irqsave(&ioapic_lock, flags);
    
    	__ioapic_write_entry(apic, pin, e);
    
    	raw_spin_unlock_irqrestore(&ioapic_lock, flags);
    
    }
    
    /*
     * When we mask an IO APIC routing entry, we need to write the low
     * word first, in order to set the mask bit before we change the
     * high bits!
     */
    static void ioapic_mask_entry(int apic, int pin)
    {
    	unsigned long flags;
    	union entry_union eu = { .entry.mask = 1 };
    
    
    	raw_spin_lock_irqsave(&ioapic_lock, flags);
    
    	io_apic_write(apic, 0x10 + 2*pin, eu.w1);
    	io_apic_write(apic, 0x11 + 2*pin, eu.w2);
    
    	raw_spin_unlock_irqrestore(&ioapic_lock, flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     * The common case is 1:1 IRQ<->pin mappings. Sometimes there are
     * 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 int __add_pin_to_irq_node(struct irq_cfg *cfg, int node, int apic, int pin)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	struct irq_pin_list **last, *entry;
    
    	/* don't allow duplicates */
    	last = &cfg->irq_2_pin;
    	for_each_irq_pin(entry, cfg->irq_2_pin) {
    
    		if (entry->apic == apic && entry->pin == pin)
    
    		last = &entry->next;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	entry = alloc_irq_pin_list(node);
    
    		printk(KERN_ERR "can not alloc irq_pin_list (%d,%d,%d)\n",
    				node, apic, pin);
    		return -ENOMEM;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	entry->apic = apic;
    	entry->pin = pin;
    
    	return 0;
    }
    
    static void add_pin_to_irq_node(struct irq_cfg *cfg, int node, int apic, int pin)
    {
    
    	if (__add_pin_to_irq_node(cfg, node, apic, pin))
    
    		panic("IO-APIC: failed to add irq-pin. Can not proceed\n");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     * Reroute an IRQ to a different pin.
     */
    
    static void __init replace_pin_at_irq_node(struct irq_cfg *cfg, int node,
    
    					   int oldapic, int oldpin,
    					   int newapic, int newpin)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	for_each_irq_pin(entry, cfg->irq_2_pin) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		if (entry->apic == oldapic && entry->pin == oldpin) {
    			entry->apic = newapic;
    			entry->pin = newpin;
    
    			/* every one is different, right? */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	/* old apic/pin didn't exist, so just add new ones */
    	add_pin_to_irq_node(cfg, node, newapic, newpin);
    
    static void __io_apic_modify_irq(struct irq_pin_list *entry,
    				 int mask_and, int mask_or,
    				 void (*final)(struct irq_pin_list *entry))
    {
    	unsigned int reg, pin;
    
    	pin = entry->pin;
    	reg = io_apic_read(entry->apic, 0x10 + pin * 2);
    	reg &= mask_and;
    	reg |= mask_or;
    	io_apic_modify(entry->apic, 0x10 + pin * 2, reg);
    	if (final)
    		final(entry);
    }
    
    
    static void io_apic_modify_irq(struct irq_cfg *cfg,
    			       int mask_and, int mask_or,
    			       void (*final)(struct irq_pin_list *entry))
    
    {
    	struct irq_pin_list *entry;
    
    	for_each_irq_pin(entry, cfg->irq_2_pin)
    		__io_apic_modify_irq(entry, mask_and, mask_or, final);
    }
    
    
    static void io_apic_sync(struct irq_pin_list *entry)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	/*
    	 * Synchronize the IO-APIC and the CPU by doing
    	 * a dummy read from the IO-APIC
    	 */
    	struct io_apic __iomem *io_apic;
    
    	io_apic = io_apic_base(entry->apic);
    
    Yinghai Lu's avatar
    Yinghai Lu committed
    	readl(&io_apic->data);
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    static void mask_ioapic(struct irq_cfg *cfg)
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    	unsigned long flags;
    
    	raw_spin_lock_irqsave(&ioapic_lock, flags);
    
    	io_apic_modify_irq(cfg, ~0, IO_APIC_REDIR_MASKED, &io_apic_sync);
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    	raw_spin_unlock_irqrestore(&ioapic_lock, flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    static void mask_ioapic_irq(struct irq_data *data)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	mask_ioapic(data->chip_data);
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    }
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    static void __unmask_ioapic(struct irq_cfg *cfg)
    {
    	io_apic_modify_irq(cfg, ~IO_APIC_REDIR_MASKED, 0, NULL);
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    static void unmask_ioapic(struct irq_cfg *cfg)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	unsigned long flags;
    
    
    	raw_spin_lock_irqsave(&ioapic_lock, flags);
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    	__unmask_ioapic(cfg);
    
    	raw_spin_unlock_irqrestore(&ioapic_lock, flags);
    
    static void unmask_ioapic_irq(struct irq_data *data)
    
    	unmask_ioapic(data->chip_data);
    
    /*
     * IO-APIC versions below 0x20 don't support EOI register.
     * For the record, here is the information about various versions:
     *     0Xh     82489DX
     *     1Xh     I/OAPIC or I/O(x)APIC which are not PCI 2.2 Compliant
     *     2Xh     I/O(x)APIC which is PCI 2.2 Compliant
     *     30h-FFh Reserved
     *
     * Some of the Intel ICH Specs (ICH2 to ICH5) documents the io-apic
     * version as 0x2. This is an error with documentation and these ICH chips
     * use io-apic's of version 0x20.
     *
     * For IO-APIC's with EOI register, we use that to do an explicit EOI.
     * Otherwise, we simulate the EOI message manually by changing the trigger
     * mode to edge and then back to level, with RTE being masked during this.
     */
    static void __eoi_ioapic_pin(int apic, int pin, int vector, struct irq_cfg *cfg)
    {
    	if (mpc_ioapic_ver(apic) >= 0x20) {
    		/*
    		 * Intr-remapping uses pin number as the virtual vector
    		 * in the RTE. Actual vector is programmed in
    		 * intr-remapping table entry. Hence for the io-apic
    		 * EOI we use the pin number.
    		 */
    		if (cfg && irq_remapped(cfg))
    			io_apic_eoi(apic, pin);
    		else
    			io_apic_eoi(apic, vector);
    	} else {
    		struct IO_APIC_route_entry entry, entry1;
    
    		entry = entry1 = __ioapic_read_entry(apic, pin);
    
    		/*
    		 * Mask the entry and change the trigger mode to edge.
    		 */
    		entry1.mask = 1;
    		entry1.trigger = IOAPIC_EDGE;
    
    		__ioapic_write_entry(apic, pin, entry1);
    
    		/*
    		 * Restore the previous level triggered entry.
    		 */
    		__ioapic_write_entry(apic, pin, entry);
    	}
    }
    
    static void eoi_ioapic_irq(unsigned int irq, struct irq_cfg *cfg)
    {
    	struct irq_pin_list *entry;
    	unsigned long flags;
    
    	raw_spin_lock_irqsave(&ioapic_lock, flags);
    	for_each_irq_pin(entry, cfg->irq_2_pin)
    		__eoi_ioapic_pin(entry->apic, entry->pin, cfg->vector, cfg);
    	raw_spin_unlock_irqrestore(&ioapic_lock, flags);
    }
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin)
    {
    	struct IO_APIC_route_entry entry;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	/* Check delivery_mode to be sure we're not clearing an SMI pin */
    
    	entry = ioapic_read_entry(apic, pin);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (entry.delivery_mode == dest_SMI)
    		return;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	/*
    
    	 * Make sure the entry is masked and re-read the contents to check
    	 * if it is a level triggered pin and if the remote-IRR is set.
    	 */
    	if (!entry.mask) {
    		entry.mask = 1;
    		ioapic_write_entry(apic, pin, entry);
    		entry = ioapic_read_entry(apic, pin);
    	}
    
    	if (entry.irr) {
    
    		/*
    		 * Make sure the trigger mode is set to level. Explicit EOI
    		 * doesn't clear the remote-IRR if the trigger mode is not
    		 * set to level.
    		 */
    		if (!entry.trigger) {
    			entry.trigger = IOAPIC_LEVEL;
    			ioapic_write_entry(apic, pin, entry);
    		}
    
    
    		raw_spin_lock_irqsave(&ioapic_lock, flags);
    		__eoi_ioapic_pin(apic, pin, entry.vector, NULL);
    		raw_spin_unlock_irqrestore(&ioapic_lock, flags);
    
    	}
    
    	/*
    	 * Clear the rest of the bits in the IO-APIC RTE except for the mask
    	 * bit.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	 */
    
    	ioapic_mask_entry(apic, pin);
    
    	entry = ioapic_read_entry(apic, pin);
    	if (entry.irr)
    		printk(KERN_ERR "Unable to reset IRR for apic: %d, pin :%d\n",
    		       mpc_ioapic_id(apic), pin);
    
    static void clear_IO_APIC (void)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	int apic, pin;
    
    	for (apic = 0; apic < nr_ioapics; apic++)
    
    		for (pin = 0; pin < ioapics[apic].nr_registers; pin++)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			clear_IO_APIC_pin(apic, pin);
    }
    
    
    #ifdef CONFIG_X86_32
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     * support for broken MP BIOSs, enables hand-redirection of PIRQ0-7 to
     * specific CPU-side IRQs.
     */
    
    #define MAX_PIRQS 8
    
    Yinghai Lu's avatar
    Yinghai Lu committed
    static int pirq_entries[MAX_PIRQS] = {
    	[0 ... MAX_PIRQS - 1] = -1
    };
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    static int __init ioapic_pirq_setup(char *str)
    {
    	int i, max;
    	int ints[MAX_PIRQS+1];
    
    	get_options(str, ARRAY_SIZE(ints), ints);
    
    	apic_printk(APIC_VERBOSE, KERN_INFO
    			"PIRQ redirection, working around broken MP-BIOS.\n");
    	max = MAX_PIRQS;
    	if (ints[0] < MAX_PIRQS)
    		max = ints[0];
    
    	for (i = 0; i < max; i++) {
    		apic_printk(APIC_VERBOSE, KERN_DEBUG
    				"... PIRQ%d -> IRQ %d\n", i, ints[i+1]);
    		/*
    		 * PIRQs are mapped upside down, usually.
    		 */
    		pirq_entries[MAX_PIRQS-i-1] = ints[i+1];
    	}
    	return 1;
    }
    
    __setup("pirq=", ioapic_pirq_setup);
    
    #endif /* CONFIG_X86_32 */
    
    /*
    
     * Saves all the IO-APIC RTE's
    
    int save_ioapic_entries(void)
    
    
    	for (apic = 0; apic < nr_ioapics; apic++) {
    
    		if (!ioapics[apic].saved_registers) {
    
    		for (pin = 0; pin < ioapics[apic].nr_registers; pin++)
    
    			ioapics[apic].saved_registers[pin] =
    
    				ioapic_read_entry(apic, pin);
    
    /*
     * Mask all IO APIC entries.
     */
    
    void mask_ioapic_entries(void)
    
    {
    	int apic, pin;
    
    	for (apic = 0; apic < nr_ioapics; apic++) {
    
    		if (!ioapics[apic].saved_registers)
    
    		for (pin = 0; pin < ioapics[apic].nr_registers; pin++) {
    
    			struct IO_APIC_route_entry entry;
    
    
    			entry = ioapics[apic].saved_registers[pin];
    
    			if (!entry.mask) {
    				entry.mask = 1;
    				ioapic_write_entry(apic, pin, entry);
    			}
    		}
    	}
    }
    
    
     * Restore IO APIC entries which was saved in the ioapic structure.
    
    int restore_ioapic_entries(void)
    
    	for (apic = 0; apic < nr_ioapics; apic++) {
    
    		if (!ioapics[apic].saved_registers)
    
    		for (pin = 0; pin < ioapics[apic].nr_registers; pin++)
    
    			ioapic_write_entry(apic, pin,
    
    					   ioapics[apic].saved_registers[pin]);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     * Find the IRQ entry number of a certain pin.
     */
    
    static int find_irq_entry(int ioapic_idx, int pin, int type)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	int i;
    
    	for (i = 0; i < mp_irq_entries; i++)
    
    		if (mp_irqs[i].irqtype == type &&
    
    		    (mp_irqs[i].dstapic == mpc_ioapic_id(ioapic_idx) ||
    
    		     mp_irqs[i].dstapic == MP_APIC_ALL) &&
    		    mp_irqs[i].dstirq == pin)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			return i;
    
    	return -1;
    }
    
    /*
     * Find the pin to which IRQ[irq] (ISA) is connected
     */
    
    static int __init find_isa_irq_pin(int irq, int type)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	int i;
    
    	for (i = 0; i < mp_irq_entries; i++) {
    
    		int lbus = mp_irqs[i].srcbus;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    		if (test_bit(lbus, mp_bus_not_pci) &&
    
    		    (mp_irqs[i].irqtype == type) &&
    		    (mp_irqs[i].srcbusirq == irq))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    			return mp_irqs[i].dstirq;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    	return -1;
    }
    
    
    static int __init find_isa_irq_apic(int irq, int type)
    {
    	int i;
    
    	for (i = 0; i < mp_irq_entries; i++) {
    
    		int lbus = mp_irqs[i].srcbus;
    
    		if (test_bit(lbus, mp_bus_not_pci) &&
    
    		    (mp_irqs[i].irqtype == type) &&
    		    (mp_irqs[i].srcbusirq == irq))
    
    		int ioapic_idx;
    
    		for (ioapic_idx = 0; ioapic_idx < nr_ioapics; ioapic_idx++)
    			if (mpc_ioapic_id(ioapic_idx) == mp_irqs[i].dstapic)
    				return ioapic_idx;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     * EISA Edge/Level control register, ELCR
     */
    static int EISA_ELCR(unsigned int irq)
    {
    
    	if (irq < legacy_pic->nr_legacy_irqs) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		unsigned int port = 0x4d0 + (irq >> 3);
    		return (inb(port) >> (irq & 7)) & 1;
    	}
    	apic_printk(APIC_VERBOSE, KERN_INFO
    			"Broken MPtable reports ISA irq %d\n", irq);
    	return 0;
    }
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /* ISA interrupts are always polarity zero edge triggered,
     * when listed as conforming in the MP table. */
    
    #define default_ISA_trigger(idx)	(0)
    #define default_ISA_polarity(idx)	(0)
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /* EISA interrupts are always polarity zero and can be edge or level
     * trigger depending on the ELCR value.  If an interrupt is listed as
     * EISA conforming in the MP table, that means its trigger type must
     * be read in from the ELCR */
    
    
    #define default_EISA_trigger(idx)	(EISA_ELCR(mp_irqs[idx].srcbusirq))
    
    #define default_EISA_polarity(idx)	default_ISA_polarity(idx)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /* PCI interrupts are always polarity one level triggered,
     * when listed as conforming in the MP table. */
    
    #define default_PCI_trigger(idx)	(1)
    #define default_PCI_polarity(idx)	(1)
    
    
    static int irq_polarity(int idx)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	int bus = mp_irqs[idx].srcbus;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int polarity;
    
    	/*
    	 * Determine IRQ line polarity (high active or low active):
    	 */
    
    	switch (mp_irqs[idx].irqflag & 3)
    
    		case 0: /* conforms, ie. bus-type dependent polarity */
    			if (test_bit(bus, mp_bus_not_pci))
    				polarity = default_ISA_polarity(idx);
    			else
    				polarity = default_PCI_polarity(idx);
    			break;
    		case 1: /* high active */
    		{
    			polarity = 0;
    			break;
    		}
    		case 2: /* reserved */
    		{
    			printk(KERN_WARNING "broken BIOS!!\n");
    			polarity = 1;
    			break;
    		}
    		case 3: /* low active */
    		{
    			polarity = 1;
    			break;
    		}
    		default: /* invalid */
    		{
    			printk(KERN_WARNING "broken BIOS!!\n");
    			polarity = 1;
    			break;
    		}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    	return polarity;
    }
    
    
    static int irq_trigger(int idx)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	int bus = mp_irqs[idx].srcbus;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int trigger;
    
    	/*
    	 * Determine IRQ trigger mode (edge or level sensitive):
    	 */
    
    	switch ((mp_irqs[idx].irqflag>>2) & 3)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	{
    
    		case 0: /* conforms, ie. bus-type dependent */
    			if (test_bit(bus, mp_bus_not_pci))
    				trigger = default_ISA_trigger(idx);
    			else
    				trigger = default_PCI_trigger(idx);
    
    			switch (mp_bus_id_to_type[bus]) {
    				case MP_BUS_ISA: /* ISA pin */
    				{
    					/* set before the switch */
    					break;
    				}
    				case MP_BUS_EISA: /* EISA pin */
    				{
    					trigger = default_EISA_trigger(idx);
    					break;
    				}
    				case MP_BUS_PCI: /* PCI pin */
    				{
    					/* set before the switch */
    					break;
    				}
    				default:
    				{
    					printk(KERN_WARNING "broken BIOS!!\n");
    					trigger = 1;
    					break;
    				}
    			}
    #endif
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			break;
    
    		case 1: /* edge */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		{
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			break;
    		}
    
    		case 2: /* reserved */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		{
    
    			printk(KERN_WARNING "broken BIOS!!\n");
    			trigger = 1;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			break;
    		}
    
    		case 3: /* level */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		{
    
    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 int pin_2_irq(int idx, int apic, int pin)
    {
    
    	int bus = mp_irqs[idx].srcbus;
    
    	struct mp_ioapic_gsi *gsi_cfg = mp_ioapic_gsi_routing(apic);
    
    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;
    
    		u32 gsi = gsi_cfg->gsi_base + pin;