⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 io_apic_64.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *	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 + -