iosapic.c

来自「h内核」· C语言 代码 · 共 829 行 · 第 1/2 页

C
829
字号
/* * I/O SAPIC support. * * Copyright (C) 1999 Intel Corp. * Copyright (C) 1999 Asit Mallick <asit.k.mallick@intel.com> * Copyright (C) 2000-2002 J.I. Lee <jung-ik.lee@intel.com> * Copyright (C) 1999-2000, 2002-2003 Hewlett-Packard Co. *	David Mosberger-Tang <davidm@hpl.hp.com> * Copyright (C) 1999 VA Linux Systems * Copyright (C) 1999,2000 Walt Drummond <drummond@valinux.com> * * 00/04/19	D. Mosberger	Rewritten to mirror more closely the x86 I/O APIC code. *				In particular, we now have separate handlers for edge *				and level triggered interrupts. * 00/10/27	Asit Mallick, Goutham Rao <goutham.rao@intel.com> IRQ vector allocation *				PCI to vector mapping, shared PCI interrupts. * 00/10/27	D. Mosberger	Document things a bit more to make them more understandable. *				Clean up much of the old IOSAPIC cruft. * 01/07/27	J.I. Lee	PCI irq routing, Platform/Legacy interrupts and fixes for *				ACPI S5(SoftOff) support. * 02/01/23	J.I. Lee	iosapic pgm fixes for PCI irq routing from _PRT * 02/01/07     E. Focht        <efocht@ess.nec.de> Redirectable interrupt vectors in *                              iosapic_set_affinity(), initializations for *                              /proc/irq/#/smp_affinity * 02/04/02	P. Diefenbaugh	Cleaned up ACPI PCI IRQ routing. * 02/04/18	J.I. Lee	bug fix in iosapic_init_pci_irq * 02/04/30	J.I. Lee	bug fix in find_iosapic to fix ACPI PCI IRQ to IOSAPIC mapping *				error * 02/07/29	T. Kochi	Allocate interrupt vectors dynamically * 02/08/04	T. Kochi	Cleaned up terminology (irq, global system interrupt, vector, etc.) * 02/09/20	D. Mosberger	Simplified by taking advantage of ACPI's pci_irq code. * 03/02/19	B. Helgaas	Make pcat_compat system-wide, not per-IOSAPIC. *				Remove iosapic_address & gsi_base from external interfaces. *				Rationalize __init/__devinit attributes. * 04/12/04 Ashok Raj	<ashok.raj@intel.com> Intel Corporation 2004 *				Updated to work with irq migration necessary for CPU Hotplug *//* * Here is what the interrupt logic between a PCI device and the kernel looks like: * * (1) A PCI device raises one of the four interrupt pins (INTA, INTB, INTC, INTD).  The *     device is uniquely identified by its bus--, and slot-number (the function *     number does not matter here because all functions share the same interrupt *     lines). * * (2) The motherboard routes the interrupt line to a pin on a IOSAPIC controller. *     Multiple interrupt lines may have to share the same IOSAPIC pin (if they're level *     triggered and use the same polarity).  Each interrupt line has a unique Global *     System Interrupt (GSI) number which can be calculated as the sum of the controller's *     base GSI number and the IOSAPIC pin number to which the line connects. * * (3) The IOSAPIC uses an internal routing table entries (RTEs) to map the IOSAPIC pin *     into the IA-64 interrupt vector.  This interrupt vector is then sent to the CPU. * * (4) The kernel recognizes an interrupt as an IRQ.  The IRQ interface is used as *     architecture-independent interrupt handling mechanism in Linux.  As an *     IRQ is a number, we have to have IA-64 interrupt vector number <-> IRQ number *     mapping.  On smaller systems, we use one-to-one mapping between IA-64 vector and *     IRQ.  A platform can implement platform_irq_to_vector(irq) and *     platform_local_vector_to_irq(vector) APIs to differentiate the mapping. *     Please see also include/asm-ia64/hw_irq.h for those APIs. * * To sum up, there are three levels of mappings involved: * *	PCI pin -> global system interrupt (GSI) -> IA-64 vector <-> IRQ * * Note: The term "IRQ" is loosely used everywhere in Linux kernel to describe interrupts. * Now we use "IRQ" only for Linux IRQ's.  ISA IRQ (isa_irq) is the only exception in this * source code. */#include <linux/config.h>#include <linux/acpi.h>#include <linux/init.h>#include <linux/irq.h>#include <linux/kernel.h>#include <linux/list.h>#include <linux/pci.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/string.h>#include <asm/delay.h>#include <asm/hw_irq.h>#include <asm/io.h>#include <asm/iosapic.h>#include <asm/machvec.h>#include <asm/processor.h>#include <asm/ptrace.h>#include <asm/system.h>#undef DEBUG_INTERRUPT_ROUTING#undef OVERRIDE_DEBUG#ifdef DEBUG_INTERRUPT_ROUTING#define DBG(fmt...)	printk(fmt)#else#define DBG(fmt...)#endifstatic DEFINE_SPINLOCK(iosapic_lock);/* These tables map IA-64 vectors to the IOSAPIC pin that generates this vector. */static struct iosapic_intr_info {	char __iomem	*addr;		/* base address of IOSAPIC */	u32		low32;		/* current value of low word of Redirection table entry */	unsigned int	gsi_base;	/* first GSI assigned to this IOSAPIC */	char		rte_index;	/* IOSAPIC RTE index (-1 => not an IOSAPIC interrupt) */	unsigned char	dmode	: 3;	/* delivery mode (see iosapic.h) */	unsigned char 	polarity: 1;	/* interrupt polarity (see iosapic.h) */	unsigned char	trigger	: 1;	/* trigger mode (see iosapic.h) */	int		refcnt;		/* reference counter */} iosapic_intr_info[IA64_NUM_VECTORS];static struct iosapic {	char __iomem	*addr;		/* base address of IOSAPIC */	unsigned int 	gsi_base;	/* first GSI assigned to this IOSAPIC */	unsigned short 	num_rte;	/* number of RTE in this IOSAPIC */#ifdef CONFIG_NUMA	unsigned short	node;		/* numa node association via pxm */#endif} iosapic_lists[NR_IOSAPICS];static int num_iosapic;static unsigned char pcat_compat __initdata;	/* 8259 compatibility flag *//* * Find an IOSAPIC associated with a GSI */static inline intfind_iosapic (unsigned int gsi){	int i;	for (i = 0; i < num_iosapic; i++) {		if ((unsigned) (gsi - iosapic_lists[i].gsi_base) < iosapic_lists[i].num_rte)			return i;	}	return -1;}static inline int_gsi_to_vector (unsigned int gsi){	struct iosapic_intr_info *info;	for (info = iosapic_intr_info; info < iosapic_intr_info + IA64_NUM_VECTORS; ++info)		if (info->gsi_base + info->rte_index == gsi)			return info - iosapic_intr_info;	return -1;}/* * Translate GSI number to the corresponding IA-64 interrupt vector.  If no * entry exists, return -1. */inline intgsi_to_vector (unsigned int gsi){	return _gsi_to_vector(gsi);}intgsi_to_irq (unsigned int gsi){	/*	 * XXX fix me: this assumes an identity mapping vetween IA-64 vector and Linux irq	 * numbers...	 */	return _gsi_to_vector(gsi);}static voidset_rte (unsigned int vector, unsigned int dest, int mask){	unsigned long pol, trigger, dmode;	u32 low32, high32;	char __iomem *addr;	int rte_index;	char redir;	DBG(KERN_DEBUG"IOSAPIC: routing vector %d to 0x%x\n", vector, dest);	rte_index = iosapic_intr_info[vector].rte_index;	if (rte_index < 0)		return;		/* not an IOSAPIC interrupt */	addr    = iosapic_intr_info[vector].addr;	pol     = iosapic_intr_info[vector].polarity;	trigger = iosapic_intr_info[vector].trigger;	dmode   = iosapic_intr_info[vector].dmode;	vector &= (~IA64_IRQ_REDIRECTED);	redir = (dmode == IOSAPIC_LOWEST_PRIORITY) ? 1 : 0;#ifdef CONFIG_SMP	{		unsigned int irq;		for (irq = 0; irq < NR_IRQS; ++irq)			if (irq_to_vector(irq) == vector) {				set_irq_affinity_info(irq, (int)(dest & 0xffff), redir);				break;			}	}#endif	low32 = ((pol << IOSAPIC_POLARITY_SHIFT) |		 (trigger << IOSAPIC_TRIGGER_SHIFT) |		 (dmode << IOSAPIC_DELIVERY_SHIFT) |		 ((mask ? 1 : 0) << IOSAPIC_MASK_SHIFT) |		 vector);	/* dest contains both id and eid */	high32 = (dest << IOSAPIC_DEST_SHIFT);	iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32);	iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);	iosapic_intr_info[vector].low32 = low32;}static voidnop (unsigned int vector){	/* do nothing... */}static voidmask_irq (unsigned int irq){	unsigned long flags;	char __iomem *addr;	u32 low32;	int rte_index;	ia64_vector vec = irq_to_vector(irq);	addr = iosapic_intr_info[vec].addr;	rte_index = iosapic_intr_info[vec].rte_index;	if (rte_index < 0)		return;			/* not an IOSAPIC interrupt! */	spin_lock_irqsave(&iosapic_lock, flags);	{		/* set only the mask bit */		low32 = iosapic_intr_info[vec].low32 |= IOSAPIC_MASK;		iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);	}	spin_unlock_irqrestore(&iosapic_lock, flags);}static voidunmask_irq (unsigned int irq){	unsigned long flags;	char __iomem *addr;	u32 low32;	int rte_index;	ia64_vector vec = irq_to_vector(irq);	addr = iosapic_intr_info[vec].addr;	rte_index = iosapic_intr_info[vec].rte_index;	if (rte_index < 0)		return;			/* not an IOSAPIC interrupt! */	spin_lock_irqsave(&iosapic_lock, flags);	{		low32 = iosapic_intr_info[vec].low32 &= ~IOSAPIC_MASK;		iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);	}	spin_unlock_irqrestore(&iosapic_lock, flags);}static voidiosapic_set_affinity (unsigned int irq, cpumask_t mask){#ifdef CONFIG_SMP	unsigned long flags;	u32 high32, low32;	int dest, rte_index;	char __iomem *addr;	int redir = (irq & IA64_IRQ_REDIRECTED) ? 1 : 0;	ia64_vector vec;	irq &= (~IA64_IRQ_REDIRECTED);	vec = irq_to_vector(irq);	if (cpus_empty(mask))		return;	dest = cpu_physical_id(first_cpu(mask));	rte_index = iosapic_intr_info[vec].rte_index;	addr = iosapic_intr_info[vec].addr;	if (rte_index < 0)		return;			/* not an IOSAPIC interrupt */	set_irq_affinity_info(irq, dest, redir);	/* dest contains both id and eid */	high32 = dest << IOSAPIC_DEST_SHIFT;	spin_lock_irqsave(&iosapic_lock, flags);	{		low32 = iosapic_intr_info[vec].low32 & ~(7 << IOSAPIC_DELIVERY_SHIFT);		if (redir)		        /* change delivery mode to lowest priority */			low32 |= (IOSAPIC_LOWEST_PRIORITY << IOSAPIC_DELIVERY_SHIFT);		else		        /* change delivery mode to fixed */			low32 |= (IOSAPIC_FIXED << IOSAPIC_DELIVERY_SHIFT);		iosapic_intr_info[vec].low32 = low32;		iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32);		iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);	}	spin_unlock_irqrestore(&iosapic_lock, flags);#endif}/* * Handlers for level-triggered interrupts. */static unsigned intiosapic_startup_level_irq (unsigned int irq){	unmask_irq(irq);	return 0;}static voidiosapic_end_level_irq (unsigned int irq){	ia64_vector vec = irq_to_vector(irq);	move_irq(irq);	iosapic_eoi(iosapic_intr_info[vec].addr, vec);}#define iosapic_shutdown_level_irq	mask_irq#define iosapic_enable_level_irq	unmask_irq#define iosapic_disable_level_irq	mask_irq#define iosapic_ack_level_irq		nopstruct hw_interrupt_type irq_type_iosapic_level = {	.typename =	"IO-SAPIC-level",	.startup =	iosapic_startup_level_irq,	.shutdown =	iosapic_shutdown_level_irq,	.enable =	iosapic_enable_level_irq,	.disable =	iosapic_disable_level_irq,	.ack =		iosapic_ack_level_irq,	.end =		iosapic_end_level_irq,	.set_affinity =	iosapic_set_affinity};/* * Handlers for edge-triggered interrupts. */static unsigned intiosapic_startup_edge_irq (unsigned int irq){	unmask_irq(irq);	/*	 * IOSAPIC simply drops interrupts pended while the	 * corresponding pin was masked, so we can't know if an	 * interrupt is pending already.  Let's hope not...	 */	return 0;}static voidiosapic_ack_edge_irq (unsigned int irq){	irq_desc_t *idesc = irq_descp(irq);	move_irq(irq);	/*	 * Once we have recorded IRQ_PENDING already, we can mask the	 * interrupt for real. This prevents IRQ storms from unhandled	 * devices.	 */	if ((idesc->status & (IRQ_PENDING|IRQ_DISABLED)) == (IRQ_PENDING|IRQ_DISABLED))		mask_irq(irq);}#define iosapic_enable_edge_irq		unmask_irq#define iosapic_disable_edge_irq	nop#define iosapic_end_edge_irq		nopstruct hw_interrupt_type irq_type_iosapic_edge = {	.typename =	"IO-SAPIC-edge",	.startup =	iosapic_startup_edge_irq,	.shutdown =	iosapic_disable_edge_irq,	.enable =	iosapic_enable_edge_irq,	.disable =	iosapic_disable_edge_irq,	.ack =		iosapic_ack_edge_irq,	.end =		iosapic_end_edge_irq,	.set_affinity =	iosapic_set_affinity};unsigned intiosapic_version (char __iomem *addr){	/*	 * IOSAPIC Version Register return 32 bit structure like:

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?