📄 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. *//* XXX Xen hacks ... *//* make this generic */#define le32_to_cpu(x) \ ({ \ __u32 __x = (x); \ ((__u32)( \ (((__u32)(__x) & (__u32)0x000000ffUL) << 24) | \ (((__u32)(__x) & (__u32)0x0000ff00UL) << 8) | \ (((__u32)(__x) & (__u32)0x00ff0000UL) >> 8) | \ (((__u32)(__x) & (__u32)0xff000000UL) >> 24) )); \ })#define alloc_bootmem(x) xmalloc_bytes(x)#define IRQ_NONE (0)#define IRQ_HANDLED (1)#define IRQ_RETVAL(x) ((x) != 0)#define IRQ_SENSE_MASK 0x1#define IRQ_SENSE_LEVEL 0x1 /* interrupt on active level */#define IRQ_SENSE_EDGE 0x0 /* interrupt triggered by edge */#define IRQ_POLARITY_MASK 0x2#define IRQ_POLARITY_POSITIVE 0x2 /* high level or low->high edge */#define IRQ_POLARITY_NEGATIVE 0x0 /* low level or high->low edge */#define CONFIG_IRQ_ALL_CPUS 0#define distribute_irqs CONFIG_IRQ_ALL_CPUS#define CONFIG_MPIC_BROKEN_U3#define PCI_DEVFN(slot,func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))#define PCI_FUNC(devfn) ((devfn) & 0x07)#define PCI_HEADER_TYPE 0x0e /* 8 bits */#define PCI_VENDOR_ID 0x00 /* 16 bits */#define PCI_VENDOR_ID_AMD 0x1022#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */#define PCI_CAP_LIST_ID 0 /* Capability ID */#define PCI_CAP_ID_HT_IRQCONF 0x08 /* HyperTransport IRQ Configuration */#define PCI_STATUS 0x06 /* 16 bits */#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */#define MSG_ALL 0x8001#define MSG_ALL_BUT_SELF 0x8000/* keeps file even closer to the original */#define pt_regs cpu_user_regs/* XXX ... Xen hacks */#undef DEBUG#undef DEBUG_IPI#undef DEBUG_IRQ#undef DEBUG_LOW#include <xen/config.h>#include <xen/types.h>#include <xen/kernel.h>#include <xen/init.h>#include <xen/irq.h>#include <xen/smp.h>#ifndef __XEN__ #include <linux/interrupt.h>#include <linux/bootmem.h>#endif#include <xen/spinlock.h>#ifndef __XEN__#include <asm/pci.h>#include <asm/ptrace.h>#include <asm/signal.h>#endif#include <asm/io.h>#ifndef __XEN__#include <asm/pgtable.h>#include <asm/irq.h>#include <asm/machdep.h>#endif#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 */#ifdef CONFIG_IRQ_ALL_CPUS#define distribute_irqs (1)#else#define distribute_irqs (0)#endif#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){ if (source >= 128 || !mpic->fixups) return 0; return mpic->fixups[source].base != NULL;}static inline void mpic_ht_end_irq(struct mpic *mpic, unsigned int source){ struct mpic_irq_fixup *fixup = &mpic->fixups[source]; if (fixup->applebase) { unsigned int soff = (fixup->index >> 3) & ~3; unsigned int mask = 1U << (fixup->index & 0x1f); writel(mask, fixup->applebase + soff); } else { spin_lock(&mpic->fixup_lock); writeb(0x11 + 2 * fixup->index, fixup->base + 2); writel(fixup->data, fixup->base + 4); spin_unlock(&mpic->fixup_lock); }}static void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source, unsigned int irqflags){ struct mpic_irq_fixup *fixup = &mpic->fixups[source]; unsigned long flags; u32 tmp; if (fixup->base == NULL) return; DBG("startup_ht_interrupt(%u, %u) index: %d\n", source, irqflags, fixup->index); spin_lock_irqsave(&mpic->fixup_lock, flags); /* Enable and configure */ writeb(0x10 + 2 * fixup->index, fixup->base + 2); tmp = readl(fixup->base + 4); tmp &= ~(0x23U); if (irqflags & IRQ_LEVEL) tmp |= 0x22; writel(tmp, fixup->base + 4); spin_unlock_irqrestore(&mpic->fixup_lock, flags);}static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source, unsigned int irqflags){ struct mpic_irq_fixup *fixup = &mpic->fixups[source]; unsigned long flags; u32 tmp; if (fixup->base == NULL) return; DBG("shutdown_ht_interrupt(%u, %u)\n", source, irqflags); /* Disable */ spin_lock_irqsave(&mpic->fixup_lock, flags); writeb(0x10 + 2 * fixup->index, fixup->base + 2); tmp = readl(fixup->base + 4); tmp |= 1; writel(tmp, fixup->base + 4); spin_unlock_irqrestore(&mpic->fixup_lock, flags);}static void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase, unsigned int devfn, u32 vdid){ int i, irq, n; u8 __iomem *base; u32 tmp; u8 pos; for (pos = readb(devbase + PCI_CAPABILITY_LIST); pos != 0; pos = readb(devbase + pos + PCI_CAP_LIST_NEXT)) { u8 id = readb(devbase + pos + PCI_CAP_LIST_ID); if (id == PCI_CAP_ID_HT_IRQCONF) { id = readb(devbase + pos + 3); if (id == 0x80) break; } } if (pos == 0) return; base = devbase + pos; writeb(0x01, base + 2); n = (readl(base + 4) >> 16) & 0xff; printk(KERN_INFO "mpic: - HT:%02x.%x [0x%02x] vendor %04x device %04x" " has %d irqs\n", devfn >> 3, devfn & 0x7, pos, vdid & 0xffff, vdid >> 16, n + 1); for (i = 0; i <= n; i++) { writeb(0x10 + 2 * i, base + 2); tmp = readl(base + 4); irq = (tmp >> 16) & 0xff; DBG("HT PIC index 0x%x, irq 0x%x, tmp: %08x\n", i, irq, tmp); /* mask it , will be unmasked later */ tmp |= 0x1; writel(tmp, base + 4); mpic->fixups[irq].index = i; mpic->fixups[irq].base = base; /* Apple HT PIC has a non-standard way of doing EOIs */ if ((vdid & 0xffff) == 0x106b) mpic->fixups[irq].applebase = devbase + 0x60; else mpic->fixups[irq].applebase = NULL; writeb(0x11 + 2 * i, base + 2); mpic->fixups[irq].data = readl(base + 4) | 0x80000000; }} static void __init mpic_scan_ht_pics(struct mpic *mpic){ unsigned int devfn; u8 __iomem *cfgspace; printk(KERN_INFO "mpic: Setting up HT PICs workarounds for U3/U4\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 * so we only need to map 64kB. */ cfgspace = ioremap(0xf2000000, 0x10000); 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 < 0x100; devfn++) { u8 __iomem *devbase = cfgspace + (devfn << 8); u8 hdr_type = readb(devbase + PCI_HEADER_TYPE); u32 l = readl(devbase + PCI_VENDOR_ID); u16 s; 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 is supports capability lists */ s = readw(devbase + PCI_STATUS); if (!(s & PCI_STATUS_CAP_LIST)) goto next; mpic_scan_ht_pic(mpic, devbase, devfn, l); next: /* next device, if function 0 */ if (PCI_FUNC(devfn) == 0 && (hdr_type & 0x80) == 0) 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); #ifdef CONFIG_MPIC_BROKEN_U3 if (mpic->flags & MPIC_BROKEN_U3) { unsigned int bsrc = irq - mpic->irq_offset; if (mpic_is_ht_interrupt(mpic, bsrc) && (irq_desc[irq].status & IRQ_LEVEL)) mpic_ht_end_irq(mpic, bsrc); }#endif /* CONFIG_MPIC_BROKEN_U3 */}static unsigned int mpic_startup_irq(unsigned int irq){#ifdef CONFIG_MPIC_BROKEN_U3 struct mpic *mpic = mpic_from_irq(irq); unsigned int src = irq - mpic->irq_offset;#endif /* CONFIG_MPIC_BROKEN_U3 */ mpic_enable_irq(irq);#ifdef CONFIG_MPIC_BROKEN_U3 if (mpic_is_ht_interrupt(mpic, src)) mpic_startup_ht_interrupt(mpic, src, irq_desc[irq].status);#endif /* CONFIG_MPIC_BROKEN_U3 */ return 0;}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_shutdown_irq(unsigned int irq){#ifdef CONFIG_MPIC_BROKEN_U3 struct mpic *mpic = mpic_from_irq(irq); unsigned int src = irq - mpic->irq_offset; if (mpic_is_ht_interrupt(mpic, src)) mpic_shutdown_ht_interrupt(mpic, src, irq_desc[irq].status);#endif /* CONFIG_MPIC_BROKEN_U3 */ mpic_disable_irq(irq);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -