📄 iosapic.c
字号:
/* * 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 + -