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

📄 iosapic.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 * describeinterrupts.  Now we use "IRQ" only for Linux IRQ's.  ISA IRQ * (isa_irq) is the only exception in this source code. */#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/string.h>#include <linux/bootmem.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#ifdef DEBUG_INTERRUPT_ROUTING#define DBG(fmt...)	printk(fmt)#else#define DBG(fmt...)#endif#define NR_PREALLOCATE_RTE_ENTRIES \	(PAGE_SIZE / sizeof(struct iosapic_rte_info))#define RTE_PREALLOCATED	(1)static DEFINE_SPINLOCK(iosapic_lock);/* * These tables map IA-64 vectors to the IOSAPIC pin that generates this * vector. */#define NO_REF_RTE	0static struct iosapic {	char __iomem	*addr;		/* base address of IOSAPIC */	unsigned int	gsi_base;	/* GSI base */	unsigned short	num_rte;	/* # of RTEs on this IOSAPIC */	int		rtes_inuse;	/* # of RTEs in use on this IOSAPIC */#ifdef CONFIG_NUMA	unsigned short	node;		/* numa node association via pxm */#endif	spinlock_t	lock;		/* lock for indirect reg access */} iosapic_lists[NR_IOSAPICS];struct iosapic_rte_info {	struct list_head rte_list;	/* RTEs sharing the same vector */	char		rte_index;	/* IOSAPIC RTE index */	int		refcnt;		/* reference counter */	unsigned int	flags;		/* flags */	struct iosapic	*iosapic;} ____cacheline_aligned;static struct iosapic_intr_info {	struct list_head rtes;		/* RTEs using this vector (empty =>					 * not an IOSAPIC interrupt) */	int		count;		/* # of registered RTEs */	u32		low32;		/* current value of low word of					 * Redirection table entry */	unsigned int	dest;		/* destination CPU physical ID */	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) */} iosapic_intr_info[NR_IRQS];static unsigned char pcat_compat __devinitdata;	/* 8259 compatibility flag */static int iosapic_kmalloc_ok;static LIST_HEAD(free_rte_list);static inline voidiosapic_write(struct iosapic *iosapic, unsigned int reg, u32 val){	unsigned long flags;	spin_lock_irqsave(&iosapic->lock, flags);	__iosapic_write(iosapic->addr, reg, val);	spin_unlock_irqrestore(&iosapic->lock, flags);}/* * Find an IOSAPIC associated with a GSI */static inline intfind_iosapic (unsigned int gsi){	int i;	for (i = 0; i < NR_IOSAPICS; i++) {		if ((unsigned) (gsi - iosapic_lists[i].gsi_base) <		    iosapic_lists[i].num_rte)			return i;	}	return -1;}static inline int __gsi_to_irq(unsigned int gsi){	int irq;	struct iosapic_intr_info *info;	struct iosapic_rte_info *rte;	for (irq = 0; irq < NR_IRQS; irq++) {		info = &iosapic_intr_info[irq];		list_for_each_entry(rte, &info->rtes, rte_list)			if (rte->iosapic->gsi_base + rte->rte_index == gsi)				return irq;	}	return -1;}intgsi_to_irq (unsigned int gsi){	unsigned long flags;	int irq;	spin_lock_irqsave(&iosapic_lock, flags);	irq = __gsi_to_irq(gsi);	spin_unlock_irqrestore(&iosapic_lock, flags);	return irq;}static struct iosapic_rte_info *find_rte(unsigned int irq, unsigned int gsi){	struct iosapic_rte_info *rte;	list_for_each_entry(rte, &iosapic_intr_info[irq].rtes, rte_list)		if (rte->iosapic->gsi_base + rte->rte_index == gsi)			return rte;	return NULL;}static voidset_rte (unsigned int gsi, unsigned int irq, unsigned int dest, int mask){	unsigned long pol, trigger, dmode;	u32 low32, high32;	int rte_index;	char redir;	struct iosapic_rte_info *rte;	ia64_vector vector = irq_to_vector(irq);	DBG(KERN_DEBUG"IOSAPIC: routing vector %d to 0x%x\n", vector, dest);	rte = find_rte(irq, gsi);	if (!rte)		return;		/* not an IOSAPIC interrupt */	rte_index = rte->rte_index;	pol     = iosapic_intr_info[irq].polarity;	trigger = iosapic_intr_info[irq].trigger;	dmode   = iosapic_intr_info[irq].dmode;	redir = (dmode == IOSAPIC_LOWEST_PRIORITY) ? 1 : 0;#ifdef CONFIG_SMP	set_irq_affinity_info(irq, (int)(dest & 0xffff), redir);#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(rte->iosapic, IOSAPIC_RTE_HIGH(rte_index), high32);	iosapic_write(rte->iosapic, IOSAPIC_RTE_LOW(rte_index), low32);	iosapic_intr_info[irq].low32 = low32;	iosapic_intr_info[irq].dest = dest;}static voidnop (unsigned int irq){	/* do nothing... */}#ifdef CONFIG_KEXECvoidkexec_disable_iosapic(void){	struct iosapic_intr_info *info;	struct iosapic_rte_info *rte;	ia64_vector vec;	int irq;	for (irq = 0; irq < NR_IRQS; irq++) {		info = &iosapic_intr_info[irq];		vec = irq_to_vector(irq);		list_for_each_entry(rte, &info->rtes,				rte_list) {			iosapic_write(rte->iosapic,					IOSAPIC_RTE_LOW(rte->rte_index),					IOSAPIC_MASK|vec);			iosapic_eoi(rte->iosapic->addr, vec);		}	}}#endifstatic voidmask_irq (unsigned int irq){	u32 low32;	int rte_index;	struct iosapic_rte_info *rte;	if (!iosapic_intr_info[irq].count)		return;			/* not an IOSAPIC interrupt! */	/* set only the mask bit */	low32 = iosapic_intr_info[irq].low32 |= IOSAPIC_MASK;	list_for_each_entry(rte, &iosapic_intr_info[irq].rtes, rte_list) {		rte_index = rte->rte_index;		iosapic_write(rte->iosapic, IOSAPIC_RTE_LOW(rte_index), low32);	}}static voidunmask_irq (unsigned int irq){	u32 low32;	int rte_index;	struct iosapic_rte_info *rte;	if (!iosapic_intr_info[irq].count)		return;			/* not an IOSAPIC interrupt! */	low32 = iosapic_intr_info[irq].low32 &= ~IOSAPIC_MASK;	list_for_each_entry(rte, &iosapic_intr_info[irq].rtes, rte_list) {		rte_index = rte->rte_index;		iosapic_write(rte->iosapic, IOSAPIC_RTE_LOW(rte_index), low32);	}}static voidiosapic_set_affinity (unsigned int irq, cpumask_t mask){#ifdef CONFIG_SMP	u32 high32, low32;	int dest, rte_index;	int redir = (irq & IA64_IRQ_REDIRECTED) ? 1 : 0;	struct iosapic_rte_info *rte;	struct iosapic *iosapic;	irq &= (~IA64_IRQ_REDIRECTED);	cpus_and(mask, mask, cpu_online_map);	if (cpus_empty(mask))		return;	if (reassign_irq_vector(irq, first_cpu(mask)))		return;	dest = cpu_physical_id(first_cpu(mask));	if (!iosapic_intr_info[irq].count)		return;			/* not an IOSAPIC interrupt */	set_irq_affinity_info(irq, dest, redir);	/* dest contains both id and eid */	high32 = dest << IOSAPIC_DEST_SHIFT;	low32 = iosapic_intr_info[irq].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);	low32 &= IOSAPIC_VECTOR_MASK;	low32 |= irq_to_vector(irq);	iosapic_intr_info[irq].low32 = low32;	iosapic_intr_info[irq].dest = dest;	list_for_each_entry(rte, &iosapic_intr_info[irq].rtes, rte_list) {		iosapic = rte->iosapic;		rte_index = rte->rte_index;		iosapic_write(iosapic, IOSAPIC_RTE_HIGH(rte_index), high32);		iosapic_write(iosapic, IOSAPIC_RTE_LOW(rte_index), low32);	}#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);	struct iosapic_rte_info *rte;	int do_unmask_irq = 0;	if (unlikely(irq_desc[irq].status & IRQ_MOVE_PENDING)) {		do_unmask_irq = 1;		mask_irq(irq);	}	list_for_each_entry(rte, &iosapic_intr_info[irq].rtes, rte_list)		iosapic_eoi(rte->iosapic->addr, vec);	if (unlikely(do_unmask_irq)) {		move_masked_irq(irq);		unmask_irq(irq);	}}#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		nopstatic struct irq_chip irq_type_iosapic_level = {	.name =		"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,	.mask =		mask_irq,	.unmask =	unmask_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_desc + irq;	move_native_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		nopstatic struct irq_chip irq_type_iosapic_edge = {	.name =		"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,	.mask =		mask_irq,	.unmask =	unmask_irq,	.set_affinity =	iosapic_set_affinity};static unsigned intiosapic_version (char __iomem *addr){	/*	 * IOSAPIC Version Register return 32 bit structure like:	 * {	 *	unsigned int version   : 8;	 *	unsigned int reserved1 : 8;	 *	unsigned int max_redir : 8;	 *	unsigned int reserved2 : 8;	 * }	 */	return __iosapic_read(addr, IOSAPIC_VERSION);}static int iosapic_find_sharable_irq(unsigned long trigger, unsigned long pol){	int i, irq = -ENOSPC, min_count = -1;	struct iosapic_intr_info *info;	/*	 * shared vectors for edge-triggered interrupts are not	 * supported yet	 */	if (trigger == IOSAPIC_EDGE)		return -EINVAL;	for (i = 0; i <= NR_IRQS; i++) {		info = &iosapic_intr_info[i];		if (info->trigger == trigger && info->polarity == pol &&		    (info->dmode == IOSAPIC_FIXED ||		     info->dmode == IOSAPIC_LOWEST_PRIORITY) &&		    can_request_irq(i, IRQF_SHARED)) {			if (min_count == -1 || info->count < min_count) {				irq = i;				min_count = info->count;			}		}	}	return irq;}/* * if the given vector is already owned by other, *  assign a new vector for the other and make the vector available */static void __initiosapic_reassign_vector (int irq){	int new_irq;	if (iosapic_intr_info[irq].count) {		new_irq = create_irq();		if (new_irq < 0)			panic("%s: out of interrupt vectors!\n", __FUNCTION__);		printk(KERN_INFO "Reassigning vector %d to %d\n",		       irq_to_vector(irq), irq_to_vector(new_irq));		memcpy(&iosapic_intr_info[new_irq], &iosapic_intr_info[irq],		       sizeof(struct iosapic_intr_info));		INIT_LIST_HEAD(&iosapic_intr_info[new_irq].rtes);		list_move(iosapic_intr_info[irq].rtes.next,			  &iosapic_intr_info[new_irq].rtes);		memset(&iosapic_intr_info[irq], 0,		       sizeof(struct iosapic_intr_info));		iosapic_intr_info[irq].low32 = IOSAPIC_MASK;		INIT_LIST_HEAD(&iosapic_intr_info[irq].rtes);	}}static struct iosapic_rte_info * __init_refok iosapic_alloc_rte (void){	int i;	struct iosapic_rte_info *rte;	int preallocated = 0;	if (!iosapic_kmalloc_ok && list_empty(&free_rte_list)) {		rte = alloc_bootmem(sizeof(struct iosapic_rte_info) *				    NR_PREALLOCATE_RTE_ENTRIES);		if (!rte)			return NULL;		for (i = 0; i < NR_PREALLOCATE_RTE_ENTRIES; i++, rte++)			list_add(&rte->rte_list, &free_rte_list);	}	if (!list_empty(&free_rte_list)) {		rte = list_entry(free_rte_list.next, struct iosapic_rte_info,				 rte_list);		list_del(&rte->rte_list);		preallocated++;	} else {		rte = kmalloc(sizeof(struct iosapic_rte_info), GFP_ATOMIC);		if (!rte)			return NULL;	}	memset(rte, 0, sizeof(struct iosapic_rte_info));

⌨️ 快捷键说明

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