📄 io_apic_64.c
字号:
/* * Intel IO-APIC support for multi-Pentium hosts. * * Copyright (C) 1997, 1998, 1999, 2000 Ingo Molnar, Hajnalka Szabo * * 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>#include <linux/mc146818rtc.h>#include <linux/acpi.h>#include <linux/sysdev.h>#include <linux/msi.h>#include <linux/htirq.h>#include <linux/dmar.h>#ifdef CONFIG_ACPI#include <acpi/acpi_bus.h>#endif#include <asm/idle.h>#include <asm/io.h>#include <asm/smp.h>#include <asm/desc.h>#include <asm/proto.h>#include <asm/mach_apic.h>#include <asm/acpi.h>#include <asm/dma.h>#include <asm/nmi.h>#include <asm/msidef.h>#include <asm/hypertransport.h>struct irq_cfg { cpumask_t domain; cpumask_t old_domain; unsigned move_cleanup_count; u8 vector; u8 move_in_progress : 1;};/* irq_cfg is indexed by the sum of all RTEs in all I/O APICs. */struct irq_cfg irq_cfg[NR_IRQS] __read_mostly = { [0] = { .domain = CPU_MASK_ALL, .vector = IRQ0_VECTOR, }, [1] = { .domain = CPU_MASK_ALL, .vector = IRQ1_VECTOR, }, [2] = { .domain = CPU_MASK_ALL, .vector = IRQ2_VECTOR, }, [3] = { .domain = CPU_MASK_ALL, .vector = IRQ3_VECTOR, }, [4] = { .domain = CPU_MASK_ALL, .vector = IRQ4_VECTOR, }, [5] = { .domain = CPU_MASK_ALL, .vector = IRQ5_VECTOR, }, [6] = { .domain = CPU_MASK_ALL, .vector = IRQ6_VECTOR, }, [7] = { .domain = CPU_MASK_ALL, .vector = IRQ7_VECTOR, }, [8] = { .domain = CPU_MASK_ALL, .vector = IRQ8_VECTOR, }, [9] = { .domain = CPU_MASK_ALL, .vector = IRQ9_VECTOR, }, [10] = { .domain = CPU_MASK_ALL, .vector = IRQ10_VECTOR, }, [11] = { .domain = CPU_MASK_ALL, .vector = IRQ11_VECTOR, }, [12] = { .domain = CPU_MASK_ALL, .vector = IRQ12_VECTOR, }, [13] = { .domain = CPU_MASK_ALL, .vector = IRQ13_VECTOR, }, [14] = { .domain = CPU_MASK_ALL, .vector = IRQ14_VECTOR, }, [15] = { .domain = CPU_MASK_ALL, .vector = IRQ15_VECTOR, },};static int assign_irq_vector(int irq, cpumask_t mask);#define __apicdebuginit __initint sis_apic_bug; /* not actually supported, dummy for compile */static int no_timer_check;static int disable_timer_pin_1 __initdata;int timer_over_8254 __initdata = 1;/* Where if anywhere is the i8259 connect in external int mode */static struct { int pin, apic; } ioapic_i8259 = { -1, -1 };static DEFINE_SPINLOCK(ioapic_lock);DEFINE_SPINLOCK(vector_lock);/* * # of IRQ routing registers */int nr_ioapic_registers[MAX_IO_APICS];/* * Rough estimation of how many shared IRQs there are, can * be changed anytime. */#define MAX_PLUS_SHARED_IRQS NR_IRQS#define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + NR_IRQS)/* * This is performance-critical, we want to do it O(1) * * the indexing order of this array favors 1:1 mappings * between pins and IRQs. */static struct irq_pin_list { short apic, pin, next;} irq_2_pin[PIN_MAP_SIZE];struct io_apic { unsigned int index; unsigned int unused[3]; unsigned int data;};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].mpc_apicaddr & ~PAGE_MASK);}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. */static inline void io_apic_modify(unsigned int apic, unsigned int value){ struct io_apic __iomem *io_apic = io_apic_base(apic); writel(value, &io_apic->data);}static int io_apic_level_ack_pending(unsigned int irq){ struct irq_pin_list *entry; unsigned long flags; int pending = 0; spin_lock_irqsave(&ioapic_lock, flags); entry = irq_2_pin + irq; for (;;) { unsigned int reg; int pin; pin = entry->pin; if (pin == -1) break; reg = io_apic_read(entry->apic, 0x10 + pin*2); /* Is the remote IRR bit set? */ pending |= (reg >> 14) & 1; if (!entry->next) break; entry = irq_2_pin + entry->next; } spin_unlock_irqrestore(&ioapic_lock, flags); return pending;}/* * Synchronize the IO-APIC and the CPU by doing * a dummy read from the IO-APIC */static inline void io_apic_sync(unsigned int apic){ struct io_apic __iomem *io_apic = io_apic_base(apic); readl(&io_apic->data);}#define __DO_ACTION(R, ACTION, FINAL) \ \{ \ int pin; \ struct irq_pin_list *entry = irq_2_pin + irq; \ \ BUG_ON(irq >= NR_IRQS); \ for (;;) { \ unsigned int reg; \ pin = entry->pin; \ if (pin == -1) \ break; \ reg = io_apic_read(entry->apic, 0x10 + R + pin*2); \ reg ACTION; \ io_apic_modify(entry->apic, reg); \ FINAL; \ if (!entry->next) \ break; \ entry = irq_2_pin + entry->next; \ } \}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; spin_lock_irqsave(&ioapic_lock, flags); eu.w1 = io_apic_read(apic, 0x10 + 2 * pin); eu.w2 = io_apic_read(apic, 0x11 + 2 * pin); spin_unlock_irqrestore(&ioapic_lock, flags); return eu.entry;}/* * 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; eu.entry = e; 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){ unsigned long flags; spin_lock_irqsave(&ioapic_lock, flags); __ioapic_write_entry(apic, pin, e); 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 }; spin_lock_irqsave(&ioapic_lock, flags); io_apic_write(apic, 0x10 + 2*pin, eu.w1); io_apic_write(apic, 0x11 + 2*pin, eu.w2); spin_unlock_irqrestore(&ioapic_lock, flags);}#ifdef CONFIG_SMPstatic void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, u8 vector){ int apic, pin; struct irq_pin_list *entry = irq_2_pin + irq; BUG_ON(irq >= NR_IRQS); for (;;) { unsigned int reg; apic = entry->apic; pin = entry->pin; if (pin == -1) break; io_apic_write(apic, 0x11 + pin*2, dest); reg = io_apic_read(apic, 0x10 + pin*2); reg &= ~0x000000ff; reg |= vector; io_apic_modify(apic, reg); if (!entry->next) break; entry = irq_2_pin + entry->next; }}static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask){ struct irq_cfg *cfg = irq_cfg + irq; unsigned long flags; unsigned int dest; cpumask_t tmp; cpus_and(tmp, mask, cpu_online_map); if (cpus_empty(tmp)) return; if (assign_irq_vector(irq, mask)) return; cpus_and(tmp, cfg->domain, mask); dest = cpu_mask_to_apicid(tmp); /* * Only the high 8 bits are valid. */ dest = SET_APIC_LOGICAL_ID(dest); spin_lock_irqsave(&ioapic_lock, flags); __target_IO_APIC_irq(irq, dest, cfg->vector); irq_desc[irq].affinity = mask; spin_unlock_irqrestore(&ioapic_lock, flags);}#endif/* * 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 void add_pin_to_irq(unsigned int irq, int apic, int pin){ static int first_free_entry = NR_IRQS; struct irq_pin_list *entry = irq_2_pin + irq; BUG_ON(irq >= NR_IRQS); while (entry->next) entry = irq_2_pin + entry->next; if (entry->pin != -1) { entry->next = first_free_entry; entry = irq_2_pin + entry->next; if (++first_free_entry >= PIN_MAP_SIZE) panic("io_apic.c: ran out of irq_2_pin entries!"); } entry->apic = apic; entry->pin = pin;}#define DO_ACTION(name,R,ACTION, FINAL) \ \ static void name##_IO_APIC_irq (unsigned int irq) \ __DO_ACTION(R, ACTION, FINAL)DO_ACTION( __mask, 0, |= 0x00010000, io_apic_sync(entry->apic) ) /* mask = 1 */DO_ACTION( __unmask, 0, &= 0xfffeffff, ) /* mask = 0 */static void mask_IO_APIC_irq (unsigned int irq){ unsigned long flags; spin_lock_irqsave(&ioapic_lock, flags); __mask_IO_APIC_irq(irq); spin_unlock_irqrestore(&ioapic_lock, flags);}static void unmask_IO_APIC_irq (unsigned int irq){ unsigned long flags; spin_lock_irqsave(&ioapic_lock, flags); __unmask_IO_APIC_irq(irq); spin_unlock_irqrestore(&ioapic_lock, flags);}static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin){ struct IO_APIC_route_entry entry; /* Check delivery_mode to be sure we're not clearing an SMI pin */ entry = ioapic_read_entry(apic, pin); 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){ 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);}int skip_ioapic_setup;int ioapic_force;static int __init parse_noapic(char *str){ disable_ioapic_setup(); return 0;}early_param("noapic", parse_noapic);/* Actually the next is obsolete, but keep it for paranoid reasons -AK */static int __init disable_timer_pin_setup(char *arg){ disable_timer_pin_1 = 1; return 1;}__setup("disable_timer_pin_1", disable_timer_pin_setup);static int __init setup_disable_8254_timer(char *s){ timer_over_8254 = -1; return 1;}static int __init setup_enable_8254_timer(char *s){ timer_over_8254 = 2; return 1;}__setup("disable_8254_timer", setup_disable_8254_timer);__setup("enable_8254_timer", setup_enable_8254_timer);/* * 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].mpc_irqtype == type && (mp_irqs[i].mpc_dstapic == mp_ioapics[apic].mpc_apicid || mp_irqs[i].mpc_dstapic == MP_APIC_ALL) && mp_irqs[i].mpc_dstirq == pin) 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){ int i; for (i = 0; i < mp_irq_entries; i++) { int lbus = mp_irqs[i].mpc_srcbus; if (test_bit(lbus, mp_bus_not_pci) && (mp_irqs[i].mpc_irqtype == type) && (mp_irqs[i].mpc_srcbusirq == irq)) return mp_irqs[i].mpc_dstirq; } 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].mpc_srcbus; if (test_bit(lbus, mp_bus_not_pci) && (mp_irqs[i].mpc_irqtype == type) && (mp_irqs[i].mpc_srcbusirq == irq)) break; } if (i < mp_irq_entries) { int apic; for(apic = 0; apic < nr_ioapics; apic++) { if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic) return apic; } } return -1;}/* * Find a specific PCI IRQ entry. * Not an __init, possibly needed by modules */static int pin_2_irq(int idx, int apic, int pin);int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin){ 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 (mp_bus_id_to_pci_bus[bus] == -1) { 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].mpc_srcbus; for (apic = 0; apic < nr_ioapics; apic++) if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic || mp_irqs[i].mpc_dstapic == MP_APIC_ALL) break; if (!test_bit(lbus, mp_bus_not_pci) && !mp_irqs[i].mpc_irqtype && (bus == lbus) && (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f))) { int irq = pin_2_irq(i,apic,mp_irqs[i].mpc_dstirq); if (!(apic || IO_APIC_IRQ(irq))) continue; if (pin == (mp_irqs[i].mpc_srcbusirq & 3)) return irq; /* * Use the first all-but-pin matching entry as a * best-guess fuzzy result for broken mptables. */ if (best_guess < 0) best_guess = irq; } } BUG_ON(best_guess >= NR_IRQS); return best_guess;}/* 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)/* 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 MPBIOS_polarity(int idx){ int bus = mp_irqs[idx].mpc_srcbus; int polarity; /* * Determine IRQ line polarity (high active or low active): */ switch (mp_irqs[idx].mpc_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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -