📄 mpic.c
字号:
/* * arch/powerpc/kernel/mpic.c * * Driver for interrupt controllers following the OpenPIC standard, the * common implementation beeing IBM's MPIC. This driver also can deal * with various broken implementations of this HW. * * Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. */#undef DEBUG#include <linux/config.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/irq.h>#include <linux/smp.h>#include <linux/interrupt.h>#include <linux/bootmem.h>#include <linux/spinlock.h>#include <linux/pci.h>#include <asm/ptrace.h>#include <asm/signal.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/irq.h>#include <asm/machdep.h>#include <asm/mpic.h>#include <asm/smp.h>#ifdef DEBUG#define DBG(fmt...) printk(fmt)#else#define DBG(fmt...)#endifstatic struct mpic *mpics;static struct mpic *mpic_primary;static DEFINE_SPINLOCK(mpic_lock);#ifdef CONFIG_PPC32 /* XXX for now */#define distribute_irqs CONFIG_IRQ_ALL_CPUS#endif/* * Register accessor functions */static inline u32 _mpic_read(unsigned int be, volatile u32 __iomem *base, unsigned int reg){ if (be) return in_be32(base + (reg >> 2)); else return in_le32(base + (reg >> 2));}static inline void _mpic_write(unsigned int be, volatile u32 __iomem *base, unsigned int reg, u32 value){ if (be) out_be32(base + (reg >> 2), value); else out_le32(base + (reg >> 2), value);}static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi){ unsigned int be = (mpic->flags & MPIC_BIG_ENDIAN) != 0; unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10); if (mpic->flags & MPIC_BROKEN_IPI) be = !be; return _mpic_read(be, mpic->gregs, offset);}static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 value){ unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10); _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset, value);}static inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg){ unsigned int cpu = 0; if (mpic->flags & MPIC_PRIMARY) cpu = hard_smp_processor_id(); return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN, mpic->cpuregs[cpu], reg);}static inline void _mpic_cpu_write(struct mpic *mpic, unsigned int reg, u32 value){ unsigned int cpu = 0; if (mpic->flags & MPIC_PRIMARY) cpu = hard_smp_processor_id(); _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->cpuregs[cpu], reg, value);}static inline u32 _mpic_irq_read(struct mpic *mpic, unsigned int src_no, unsigned int reg){ unsigned int isu = src_no >> mpic->isu_shift; unsigned int idx = src_no & mpic->isu_mask; return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu], reg + (idx * MPIC_IRQ_STRIDE));}static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no, unsigned int reg, u32 value){ unsigned int isu = src_no >> mpic->isu_shift; unsigned int idx = src_no & mpic->isu_mask; _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu], reg + (idx * MPIC_IRQ_STRIDE), value);}#define mpic_read(b,r) _mpic_read(mpic->flags & MPIC_BIG_ENDIAN,(b),(r))#define mpic_write(b,r,v) _mpic_write(mpic->flags & MPIC_BIG_ENDIAN,(b),(r),(v))#define mpic_ipi_read(i) _mpic_ipi_read(mpic,(i))#define mpic_ipi_write(i,v) _mpic_ipi_write(mpic,(i),(v))#define mpic_cpu_read(i) _mpic_cpu_read(mpic,(i))#define mpic_cpu_write(i,v) _mpic_cpu_write(mpic,(i),(v))#define mpic_irq_read(s,r) _mpic_irq_read(mpic,(s),(r))#define mpic_irq_write(s,r,v) _mpic_irq_write(mpic,(s),(r),(v))/* * Low level utility functions *//* Check if we have one of those nice broken MPICs with a flipped endian on * reads from IPI registers */static void __init mpic_test_broken_ipi(struct mpic *mpic){ u32 r; mpic_write(mpic->gregs, MPIC_GREG_IPI_VECTOR_PRI_0, MPIC_VECPRI_MASK); r = mpic_read(mpic->gregs, MPIC_GREG_IPI_VECTOR_PRI_0); if (r == le32_to_cpu(MPIC_VECPRI_MASK)) { printk(KERN_INFO "mpic: Detected reversed IPI registers\n"); mpic->flags |= MPIC_BROKEN_IPI; }}#ifdef CONFIG_MPIC_BROKEN_U3/* Test if an interrupt is sourced from HyperTransport (used on broken U3s) * to force the edge setting on the MPIC and do the ack workaround. */static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source_no){ if (source_no >= 128 || !mpic->fixups) return 0; return mpic->fixups[source_no].base != NULL;}static inline void mpic_apic_end_irq(struct mpic *mpic, unsigned int source_no){ struct mpic_irq_fixup *fixup = &mpic->fixups[source_no]; u32 tmp; spin_lock(&mpic->fixup_lock); writeb(0x11 + 2 * fixup->irq, fixup->base); tmp = readl(fixup->base + 2); writel(tmp | 0x80000000ul, fixup->base + 2); /* config writes shouldn't be posted but let's be safe ... */ (void)readl(fixup->base + 2); spin_unlock(&mpic->fixup_lock);}static void __init mpic_amd8111_read_irq(struct mpic *mpic, u8 __iomem *devbase){ int i, irq; u32 tmp; printk(KERN_INFO "mpic: - Workarounds on AMD 8111 @ %p\n", devbase); for (i=0; i < 24; i++) { writeb(0x10 + 2*i, devbase + 0xf2); tmp = readl(devbase + 0xf4); if ((tmp & 0x1) || !(tmp & 0x20)) continue; irq = (tmp >> 16) & 0xff; mpic->fixups[irq].irq = i; mpic->fixups[irq].base = devbase + 0xf2; }} static void __init mpic_amd8131_read_irq(struct mpic *mpic, u8 __iomem *devbase){ int i, irq; u32 tmp; printk(KERN_INFO "mpic: - Workarounds on AMD 8131 @ %p\n", devbase); for (i=0; i < 4; i++) { writeb(0x10 + 2*i, devbase + 0xba); tmp = readl(devbase + 0xbc); if ((tmp & 0x1) || !(tmp & 0x20)) continue; irq = (tmp >> 16) & 0xff; mpic->fixups[irq].irq = i; mpic->fixups[irq].base = devbase + 0xba; }} static void __init mpic_scan_ioapics(struct mpic *mpic){ unsigned int devfn; u8 __iomem *cfgspace; printk(KERN_INFO "mpic: Setting up IO-APICs workarounds for U3\n"); /* Allocate fixups array */ mpic->fixups = alloc_bootmem(128 * sizeof(struct mpic_irq_fixup)); BUG_ON(mpic->fixups == NULL); memset(mpic->fixups, 0, 128 * sizeof(struct mpic_irq_fixup)); /* Init spinlock */ spin_lock_init(&mpic->fixup_lock); /* Map u3 config space. We assume all IO-APICs are on the primary bus * and slot will never be above "0xf" so we only need to map 32k */ cfgspace = (unsigned char __iomem *)ioremap(0xf2000000, 0x8000); BUG_ON(cfgspace == NULL); /* Now we scan all slots. We do a very quick scan, we read the header type, * vendor ID and device ID only, that's plenty enough */ for (devfn = 0; devfn < PCI_DEVFN(0x10,0); devfn ++) { u8 __iomem *devbase = cfgspace + (devfn << 8); u8 hdr_type = readb(devbase + PCI_HEADER_TYPE); u32 l = readl(devbase + PCI_VENDOR_ID); u16 vendor_id, device_id; int multifunc = 0; DBG("devfn %x, l: %x\n", devfn, l); /* If no device, skip */ if (l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) goto next; /* Check if it's a multifunction device (only really used * to function 0 though */ multifunc = !!(hdr_type & 0x80); vendor_id = l & 0xffff; device_id = (l >> 16) & 0xffff; /* If a known device, go to fixup setup code */ if (vendor_id == PCI_VENDOR_ID_AMD && device_id == 0x7460) mpic_amd8111_read_irq(mpic, devbase); if (vendor_id == PCI_VENDOR_ID_AMD && device_id == 0x7450) mpic_amd8131_read_irq(mpic, devbase); next: /* next device, if function 0 */ if ((PCI_FUNC(devfn) == 0) && !multifunc) devfn += 7; }}#endif /* CONFIG_MPIC_BROKEN_U3 *//* Find an mpic associated with a given linux interrupt */static struct mpic *mpic_find(unsigned int irq, unsigned int *is_ipi){ struct mpic *mpic = mpics; while(mpic) { /* search IPIs first since they may override the main interrupts */ if (irq >= mpic->ipi_offset && irq < (mpic->ipi_offset + 4)) { if (is_ipi) *is_ipi = 1; return mpic; } if (irq >= mpic->irq_offset && irq < (mpic->irq_offset + mpic->irq_count)) { if (is_ipi) *is_ipi = 0; return mpic; } mpic = mpic -> next; } return NULL;}/* Convert a cpu mask from logical to physical cpu numbers. */static inline u32 mpic_physmask(u32 cpumask){ int i; u32 mask = 0; for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1) mask |= (cpumask & 1) << get_hard_smp_processor_id(i); return mask;}#ifdef CONFIG_SMP/* Get the mpic structure from the IPI number */static inline struct mpic * mpic_from_ipi(unsigned int ipi){ return container_of(irq_desc[ipi].handler, struct mpic, hc_ipi);}#endif/* Get the mpic structure from the irq number */static inline struct mpic * mpic_from_irq(unsigned int irq){ return container_of(irq_desc[irq].handler, struct mpic, hc_irq);}/* Send an EOI */static inline void mpic_eoi(struct mpic *mpic){ mpic_cpu_write(MPIC_CPU_EOI, 0); (void)mpic_cpu_read(MPIC_CPU_WHOAMI);}#ifdef CONFIG_SMPstatic irqreturn_t mpic_ipi_action(int irq, void *dev_id, struct pt_regs *regs){ struct mpic *mpic = dev_id; smp_message_recv(irq - mpic->ipi_offset, regs); return IRQ_HANDLED;}#endif /* CONFIG_SMP *//* * Linux descriptor level callbacks */static void mpic_enable_irq(unsigned int irq){ unsigned int loops = 100000; struct mpic *mpic = mpic_from_irq(irq); unsigned int src = irq - mpic->irq_offset; DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, irq, src); mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI, mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & ~MPIC_VECPRI_MASK); /* make sure mask gets to controller before we return to user */ do { if (!loops--) { printk(KERN_ERR "mpic_enable_irq timeout\n"); break; } } while(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK); }static void mpic_disable_irq(unsigned int irq){ unsigned int loops = 100000; struct mpic *mpic = mpic_from_irq(irq); unsigned int src = irq - mpic->irq_offset; DBG("%s: disable_irq: %d (src %d)\n", mpic->name, irq, src); mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI, mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) | MPIC_VECPRI_MASK); /* make sure mask gets to controller before we return to user */ do { if (!loops--) { printk(KERN_ERR "mpic_enable_irq timeout\n"); break; } } while(!(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK));}static void mpic_end_irq(unsigned int irq){ struct mpic *mpic = mpic_from_irq(irq); DBG("%s: end_irq: %d\n", mpic->name, irq); /* We always EOI on end_irq() even for edge interrupts since that * should only lower the priority, the MPIC should have properly * latched another edge interrupt coming in anyway */#ifdef CONFIG_MPIC_BROKEN_U3 if (mpic->flags & MPIC_BROKEN_U3) { unsigned int src = irq - mpic->irq_offset; if (mpic_is_ht_interrupt(mpic, src)) mpic_apic_end_irq(mpic, src); }#endif /* CONFIG_MPIC_BROKEN_U3 */ mpic_eoi(mpic);}#ifdef CONFIG_SMPstatic void mpic_enable_ipi(unsigned int irq){ struct mpic *mpic = mpic_from_ipi(irq); unsigned int src = irq - mpic->ipi_offset; DBG("%s: enable_ipi: %d (ipi %d)\n", mpic->name, irq, src); mpic_ipi_write(src, mpic_ipi_read(src) & ~MPIC_VECPRI_MASK);}static void mpic_disable_ipi(unsigned int irq){ /* NEVER disable an IPI... that's just plain wrong! */}static void mpic_end_ipi(unsigned int irq){ struct mpic *mpic = mpic_from_ipi(irq); /* * IPIs are marked IRQ_PER_CPU. This has the side effect of * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from * applying to them. We EOI them late to avoid re-entering. * We mark IPI's with SA_INTERRUPT as they must run with * irqs disabled. */ mpic_eoi(mpic);}#endif /* CONFIG_SMP */static void mpic_set_affinity(unsigned int irq, cpumask_t cpumask){ struct mpic *mpic = mpic_from_irq(irq); cpumask_t tmp; cpus_and(tmp, cpumask, cpu_online_map); mpic_irq_write(irq - mpic->irq_offset, MPIC_IRQ_DESTINATION, mpic_physmask(cpus_addr(tmp)[0])); }/* * Exported functions */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -