Skip to content
Snippets Groups Projects
io_apic.c 103 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>
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #include <linux/sysdev.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/nmi.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)
    
    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);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     * # of IRQ routing registers
     */
    int nr_ioapic_registers[MAX_IO_APICS];
    
    
    /* I/O APIC entries */
    
    struct mpc_ioapic mp_ioapics[MAX_IO_APICS];
    
    /* IO APIC gsi routing info */
    struct mp_ioapic_gsi  mp_gsi_routing[MAX_IO_APICS];
    
    
    /* 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;
    
    
    #if defined (CONFIG_MCA) || defined (CONFIG_EISA)
    int mp_bus_id_to_type[MAX_MP_BUSSES];
    #endif
    
    DECLARE_BITMAP(mp_bus_not_pci, MAX_MP_BUSSES);
    
    
    int skip_ioapic_setup;
    
    
    void arch_disable_smp_support(void)
    {
    #ifdef CONFIG_PCI
    	noioapicquirk = 1;
    	noioapicreroute = -1;
    #endif
    	skip_ioapic_setup = 1;
    }
    
    
    static int __init parse_noapic(char *str)
    
    {
    	/* disable IO-APIC */
    
    	return 0;
    }
    early_param("noapic", parse_noapic);
    
    struct irq_pin_list {
    	int apic, pin;
    	struct irq_pin_list *next;
    };
    
    
    static struct irq_pin_list *get_one_free_irq_2_pin(int node)
    
    {
    	struct irq_pin_list *pin;
    
    	pin = kzalloc_node(sizeof(*pin), GFP_ATOMIC, node);
    
    	return pin;
    }
    
    
    Yinghai Lu's avatar
    Yinghai Lu committed
    /* irq_cfg is indexed by the sum of all RTEs in all I/O APICs. */
    
    #ifdef CONFIG_SPARSE_IRQ
    
    static struct irq_cfg irq_cfgx[NR_IRQS_LEGACY];
    
    static struct irq_cfg irq_cfgx[NR_IRQS];
    
    int __init arch_early_irq_init(void)
    
    	struct irq_cfg *cfg;
    	struct irq_desc *desc;
    	int count;
    
    	if (!legacy_pic->nr_legacy_irqs) {
    		nr_irqs_gsi = 0;
    		io_apic_irqs = ~0UL;
    	}
    
    
    	cfg = irq_cfgx;
    	count = ARRAY_SIZE(irq_cfgx);
    
    	node = cpu_to_node(0);
    
    	for (i = 0; i < count; i++) {
    		desc = irq_to_desc(i);
    		desc->chip_data = &cfg[i];
    
    		zalloc_cpumask_var_node(&cfg[i].domain, GFP_NOWAIT, node);
    		zalloc_cpumask_var_node(&cfg[i].old_domain, GFP_NOWAIT, 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);
    		}
    
    #ifdef CONFIG_SPARSE_IRQ
    
    struct irq_cfg *irq_cfg(unsigned int irq)
    
    	struct irq_cfg *cfg = NULL;
    	struct irq_desc *desc;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	desc = irq_to_desc(irq);
    	if (desc)
    
    		cfg = get_irq_desc_chip_data(desc);
    
    static struct irq_cfg *get_one_free_irq_cfg(int node)
    
    	struct irq_cfg *cfg;
    
    	cfg = kzalloc_node(sizeof(*cfg), GFP_ATOMIC, node);
    
    		if (!zalloc_cpumask_var_node(&cfg->domain, GFP_ATOMIC, node)) {
    
    			kfree(cfg);
    			cfg = NULL;
    
    		} else if (!zalloc_cpumask_var_node(&cfg->old_domain,
    
    			free_cpumask_var(cfg->domain);
    			kfree(cfg);
    			cfg = NULL;
    		}
    	}
    
    int arch_init_chip_data(struct irq_desc *desc, int node)
    
    	struct irq_cfg *cfg;
    
    	cfg = get_irq_desc_chip_data(desc);
    
    		cfg = get_one_free_irq_cfg(node);
    		desc->chip_data = cfg;
    		if (!cfg) {
    
    			printk(KERN_ERR "can not alloc irq_cfg\n");
    			BUG_ON(1);
    		}
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /* for move_irq_desc */
    
    init_copy_irq_2_pin(struct irq_cfg *old_cfg, struct irq_cfg *cfg, int node)
    
    	struct irq_pin_list *old_entry, *head, *tail, *entry;
    
    	cfg->irq_2_pin = NULL;
    	old_entry = old_cfg->irq_2_pin;
    	if (!old_entry)
    		return;
    
    	entry = get_one_free_irq_2_pin(node);
    
    	entry->apic	= old_entry->apic;
    	entry->pin	= old_entry->pin;
    	head		= entry;
    	tail		= entry;
    	old_entry	= old_entry->next;
    	while (old_entry) {
    
    		entry = get_one_free_irq_2_pin(node);
    
    		if (!entry) {
    			entry = head;
    			while (entry) {
    				head = entry->next;
    				kfree(entry);
    				entry = head;
    			}
    			/* still use the old one */
    			return;
    		}
    		entry->apic	= old_entry->apic;
    		entry->pin	= old_entry->pin;
    		tail->next	= entry;
    		tail		= entry;
    		old_entry	= old_entry->next;
    	}
    
    	tail->next = NULL;
    	cfg->irq_2_pin = head;
    
    static void free_irq_2_pin(struct irq_cfg *old_cfg, struct irq_cfg *cfg)
    
    	struct irq_pin_list *entry, *next;
    
    	if (old_cfg->irq_2_pin == cfg->irq_2_pin)
    		return;
    
    	entry = old_cfg->irq_2_pin;
    
    	while (entry) {
    		next = entry->next;
    		kfree(entry);
    		entry = next;
    	}
    	old_cfg->irq_2_pin = NULL;
    
    void arch_init_copy_chip_data(struct irq_desc *old_desc,
    
    				 struct irq_desc *desc, int node)
    
    	struct irq_cfg *cfg;
    	struct irq_cfg *old_cfg;
    
    	cfg = get_one_free_irq_cfg(node);
    
    	if (!cfg)
    		return;
    
    	desc->chip_data = cfg;
    
    	old_cfg = old_desc->chip_data;
    
    
    	cfg->vector = old_cfg->vector;
    	cfg->move_in_progress = old_cfg->move_in_progress;
    	cpumask_copy(cfg->domain, old_cfg->domain);
    	cpumask_copy(cfg->old_domain, old_cfg->old_domain);
    
    	init_copy_irq_2_pin(old_cfg, cfg, node);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    static void free_irq_cfg(struct irq_cfg *cfg)
    
    	free_cpumask_var(cfg->domain);
    	free_cpumask_var(cfg->old_domain);
    	kfree(cfg);
    
    }
    
    void arch_free_chip_data(struct irq_desc *old_desc, struct irq_desc *desc)
    {
    	struct irq_cfg *old_cfg, *cfg;
    
    
    	old_cfg = get_irq_desc_chip_data(old_desc);
    	cfg = get_irq_desc_chip_data(desc);
    
    
    	if (old_cfg == cfg)
    		return;
    
    	if (old_cfg) {
    		free_irq_2_pin(old_cfg, cfg);
    		free_irq_cfg(old_cfg);
    		old_desc->chip_data = NULL;
    	}
    }
    
    /* end for move_irq_desc */
    
    struct irq_cfg *irq_cfg(unsigned int irq)
    
    {
    	return irq < nr_irqs ? irq_cfgx + irq : NULL;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    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)
    
    		+ (mp_ioapics[idx].apicaddr & ~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);
    }
    
    
    static inline unsigned int 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);
    }
    
    static inline void 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
     */
    static inline void 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);
    }
    
    
    static bool io_apic_level_ack_pending(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) {
    
    		unsigned int reg;
    		int pin;
    
    		pin = entry->pin;
    		reg = io_apic_read(entry->apic, 0x10 + pin*2);
    		/* Is the remote IRR bit set? */
    		if (reg & IO_APIC_REDIR_REMOTE_IRR) {
    
    			raw_spin_unlock_irqrestore(&ioapic_lock, flags);
    
    	raw_spin_unlock_irqrestore(&ioapic_lock, flags);
    
    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;
    	unsigned long flags;
    
    	raw_spin_lock_irqsave(&ioapic_lock, flags);
    
    	eu.w1 = io_apic_read(apic, 0x10 + 2 * pin);
    	eu.w2 = io_apic_read(apic, 0x11 + 2 * 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);
    
    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_nopanic(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 = get_one_free_irq_2_pin(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_nopanic(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 __mask_and_edge_IO_APIC_irq(struct irq_pin_list *entry)
    {
    	__io_apic_modify_irq(entry, ~IO_APIC_REDIR_LEVEL_TRIGGER,
    			     IO_APIC_REDIR_MASKED, NULL);
    }
    
    static void __unmask_and_level_IO_APIC_irq(struct irq_pin_list *entry)
    {
    	__io_apic_modify_irq(entry, ~IO_APIC_REDIR_MASKED,
    			     IO_APIC_REDIR_LEVEL_TRIGGER, NULL);
    
    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);
    
    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;
    	/*
    	 * Disable it in the IO-APIC irq-routing table:
    	 */
    
    	ioapic_mask_entry(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 < nr_ioapic_registers[apic]; pin++)
    			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 */
    
    
    struct IO_APIC_route_entry **alloc_ioapic_entries(void)
    {
    	int apic;
    	struct IO_APIC_route_entry **ioapic_entries;
    
    	ioapic_entries = kzalloc(sizeof(*ioapic_entries) * nr_ioapics,
    				GFP_ATOMIC);
    	if (!ioapic_entries)
    		return 0;
    
    	for (apic = 0; apic < nr_ioapics; apic++) {
    		ioapic_entries[apic] =
    			kzalloc(sizeof(struct IO_APIC_route_entry) *
    				nr_ioapic_registers[apic], GFP_ATOMIC);
    		if (!ioapic_entries[apic])
    			goto nomem;
    	}
    
    	return ioapic_entries;
    
    nomem:
    	while (--apic >= 0)
    		kfree(ioapic_entries[apic]);
    	kfree(ioapic_entries);
    
    	return 0;
    }
    
     * Saves all the IO-APIC RTE's
    
    int save_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries)
    
    	if (!ioapic_entries)
    		return -ENOMEM;
    
    
    	for (apic = 0; apic < nr_ioapics; apic++) {
    
    		if (!ioapic_entries[apic])
    			return -ENOMEM;
    
    		for (pin = 0; pin < nr_ioapic_registers[apic]; pin++)
    
    			ioapic_entries[apic][pin] =
    
    				ioapic_read_entry(apic, pin);
    
    /*
     * Mask all IO APIC entries.
     */
    void mask_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries)
    
    	for (apic = 0; apic < nr_ioapics; apic++) {
    
    		if (!ioapic_entries[apic])
    
    		for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) {
    			struct IO_APIC_route_entry entry;
    
    
    			entry = ioapic_entries[apic][pin];
    
    			if (!entry.mask) {
    				entry.mask = 1;
    				ioapic_write_entry(apic, pin, entry);
    			}
    		}
    	}
    }
    
    
    /*
     * Restore IO APIC entries which was saved in ioapic_entries.
     */
    int restore_IO_APIC_setup(struct IO_APIC_route_entry **ioapic_entries)
    
    	if (!ioapic_entries)
    		return -ENOMEM;
    
    
    	for (apic = 0; apic < nr_ioapics; apic++) {
    
    		if (!ioapic_entries[apic])
    			return -ENOMEM;
    
    
    		for (pin = 0; pin < nr_ioapic_registers[apic]; pin++)
    			ioapic_write_entry(apic, pin,
    
    					ioapic_entries[apic][pin]);
    
    void free_ioapic_entries(struct IO_APIC_route_entry **ioapic_entries)
    {
    	int apic;
    
    	for (apic = 0; apic < nr_ioapics; apic++)
    		kfree(ioapic_entries[apic]);
    
    	kfree(ioapic_entries);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /*
     * Find the IRQ entry number of a certain pin.
     */
    static int find_irq_entry(int apic, int pin, int type)
    {
    	int i;
    
    	for (i = 0; i < mp_irq_entries; i++)
    
    		if (mp_irqs[i].irqtype == type &&
    		    (mp_irqs[i].dstapic == mp_ioapics[apic].apicid ||
    		     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))
    
    		for(apic = 0; apic < nr_ioapics; apic++) {
    
    			if (mp_ioapics[apic].apicid == mp_irqs[i].dstapic)
    
    #if defined(CONFIG_EISA) || defined(CONFIG_MCA)
    
    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)
    
    /* MCA interrupts are always polarity zero level triggered,
     * when listed as conforming in the MP table. */
    
    #define default_MCA_trigger(idx)	(1)
    
    #define default_MCA_polarity(idx)	default_ISA_polarity(idx)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    static int MPBIOS_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 MPBIOS_trigger(int idx)
    {
    
    	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);
    
    #if defined(CONFIG_EISA) || defined(CONFIG_MCA)
    
    			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;
    				}
    				case MP_BUS_MCA: /* MCA pin */
    				{
    					trigger = default_MCA_trigger(idx);
    					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;