diff options
Diffstat (limited to 'arch/powerpc/sysdev/mpic.c')
-rw-r--r-- | arch/powerpc/sysdev/mpic.c | 162 |
1 files changed, 155 insertions, 7 deletions
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 0b84b7c775d..4fd2bec8991 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -36,6 +36,8 @@ #include <asm/mpic.h> #include <asm/smp.h> +#include "mpic.h" + #ifdef DEBUG #define DBG(fmt...) printk(fmt) #else @@ -354,6 +356,12 @@ static void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source, tmp |= 0x22; writel(tmp, fixup->base + 4); spin_unlock_irqrestore(&mpic->fixup_lock, flags); + +#ifdef CONFIG_PM + /* use the lowest bit inverted to the actual HW, + * set if this fixup was enabled, clear otherwise */ + mpic->save_data[source].fixup_data = tmp | 1; +#endif } static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source, @@ -375,8 +383,58 @@ static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source, tmp |= 1; writel(tmp, fixup->base + 4); spin_unlock_irqrestore(&mpic->fixup_lock, flags); + +#ifdef CONFIG_PM + /* use the lowest bit inverted to the actual HW, + * set if this fixup was enabled, clear otherwise */ + mpic->save_data[source].fixup_data = tmp & ~1; +#endif } +#ifdef CONFIG_PCI_MSI +static void __init mpic_scan_ht_msi(struct mpic *mpic, u8 __iomem *devbase, + unsigned int devfn) +{ + u8 __iomem *base; + u8 pos, flags; + u64 addr = 0; + + 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) { + id = readb(devbase + pos + 3); + if ((id & HT_5BIT_CAP_MASK) == HT_CAPTYPE_MSI_MAPPING) + break; + } + } + + if (pos == 0) + return; + + base = devbase + pos; + + flags = readb(base + HT_MSI_FLAGS); + if (!(flags & HT_MSI_FLAGS_FIXED)) { + addr = readl(base + HT_MSI_ADDR_LO) & HT_MSI_ADDR_LO_MASK; + addr = addr | ((u64)readl(base + HT_MSI_ADDR_HI) << 32); + } + + printk(KERN_DEBUG "mpic: - HT:%02x.%x %s MSI mapping found @ 0x%lx\n", + PCI_SLOT(devfn), PCI_FUNC(devfn), + flags & HT_MSI_FLAGS_ENABLE ? "enabled" : "disabled", addr); + + if (!(flags & HT_MSI_FLAGS_ENABLE)) + writeb(flags | HT_MSI_FLAGS_ENABLE, base + HT_MSI_FLAGS); +} +#else +static void __init mpic_scan_ht_msi(struct mpic *mpic, u8 __iomem *devbase, + unsigned int devfn) +{ + return; +} +#endif + static void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase, unsigned int devfn, u32 vdid) { @@ -468,6 +526,7 @@ static void __init mpic_scan_ht_pics(struct mpic *mpic) goto next; mpic_scan_ht_pic(mpic, devbase, devfn, l); + mpic_scan_ht_msi(mpic, devbase, devfn); next: /* next device, if function 0 */ @@ -559,7 +618,7 @@ static irqreturn_t mpic_ipi_action(int irq, void *dev_id) */ -static void mpic_unmask_irq(unsigned int irq) +void mpic_unmask_irq(unsigned int irq) { unsigned int loops = 100000; struct mpic *mpic = mpic_from_irq(irq); @@ -579,7 +638,7 @@ static void mpic_unmask_irq(unsigned int irq) } while(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK); } -static void mpic_mask_irq(unsigned int irq) +void mpic_mask_irq(unsigned int irq) { unsigned int loops = 100000; struct mpic *mpic = mpic_from_irq(irq); @@ -600,7 +659,7 @@ static void mpic_mask_irq(unsigned int irq) } while(!(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK)); } -static void mpic_end_irq(unsigned int irq) +void mpic_end_irq(unsigned int irq) { struct mpic *mpic = mpic_from_irq(irq); @@ -733,7 +792,7 @@ static unsigned int mpic_type_to_vecpri(struct mpic *mpic, unsigned int type) } } -static int mpic_set_irq_type(unsigned int virq, unsigned int flow_type) +int mpic_set_irq_type(unsigned int virq, unsigned int flow_type) { struct mpic *mpic = mpic_from_irq(virq); unsigned int src = mpic_irq_to_hw(virq); @@ -834,6 +893,8 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq, if (hw >= mpic->irq_count) return -EINVAL; + mpic_msi_reserve_hwirq(mpic, hw); + /* Default chip */ chip = &mpic->hc_irq; @@ -1142,8 +1203,10 @@ void __init mpic_init(struct mpic *mpic) /* Do the HT PIC fixups on U3 broken mpic */ DBG("MPIC flags: %x\n", mpic->flags); - if ((mpic->flags & MPIC_U3_HT_IRQS) && (mpic->flags & MPIC_PRIMARY)) - mpic_scan_ht_pics(mpic); + if ((mpic->flags & MPIC_U3_HT_IRQS) && (mpic->flags & MPIC_PRIMARY)) { + mpic_scan_ht_pics(mpic); + mpic_u3msi_init(mpic); + } for (i = 0; i < mpic->num_sources; i++) { /* start with vector = source number, and masked */ @@ -1167,6 +1230,12 @@ void __init mpic_init(struct mpic *mpic) /* Set current processor priority to 0 */ mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0); + +#ifdef CONFIG_PM + /* allocate memory to save mpic state */ + mpic->save_data = alloc_bootmem(mpic->num_sources * sizeof(struct mpic_irq_save)); + BUG_ON(mpic->save_data == NULL); +#endif } void __init mpic_set_clk_ratio(struct mpic *mpic, u32 clock_ratio) @@ -1333,8 +1402,11 @@ unsigned int mpic_get_one_irq(struct mpic *mpic) #ifdef DEBUG_LOW DBG("%s: get_one_irq(): %d\n", mpic->name, src); #endif - if (unlikely(src == mpic->spurious_vec)) + if (unlikely(src == mpic->spurious_vec)) { + if (mpic->flags & MPIC_SPV_EOI) + mpic_eoi(mpic); return NO_IRQ; + } return irq_linear_revmap(mpic->irqhost, src); } @@ -1417,3 +1489,79 @@ void __devinit smp_mpic_setup_cpu(int cpu) mpic_setup_this_cpu(); } #endif /* CONFIG_SMP */ + +#ifdef CONFIG_PM +static int mpic_suspend(struct sys_device *dev, pm_message_t state) +{ + struct mpic *mpic = container_of(dev, struct mpic, sysdev); + int i; + + for (i = 0; i < mpic->num_sources; i++) { + mpic->save_data[i].vecprio = + mpic_irq_read(i, MPIC_INFO(IRQ_VECTOR_PRI)); + mpic->save_data[i].dest = + mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)); + } + + return 0; +} + +static int mpic_resume(struct sys_device *dev) +{ + struct mpic *mpic = container_of(dev, struct mpic, sysdev); + int i; + + for (i = 0; i < mpic->num_sources; i++) { + mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), + mpic->save_data[i].vecprio); + mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), + mpic->save_data[i].dest); + +#ifdef CONFIG_MPIC_U3_HT_IRQS + { + struct mpic_irq_fixup *fixup = &mpic->fixups[i]; + + if (fixup->base) { + /* we use the lowest bit in an inverted meaning */ + if ((mpic->save_data[i].fixup_data & 1) == 0) + continue; + + /* Enable and configure */ + writeb(0x10 + 2 * fixup->index, fixup->base + 2); + + writel(mpic->save_data[i].fixup_data & ~1, + fixup->base + 4); + } + } +#endif + } /* end for loop */ + + return 0; +} +#endif + +static struct sysdev_class mpic_sysclass = { +#ifdef CONFIG_PM + .resume = mpic_resume, + .suspend = mpic_suspend, +#endif + set_kset_name("mpic"), +}; + +static int mpic_init_sys(void) +{ + struct mpic *mpic = mpics; + int error, id = 0; + + error = sysdev_class_register(&mpic_sysclass); + + while (mpic && !error) { + mpic->sysdev.cls = &mpic_sysclass; + mpic->sysdev.id = id++; + error = sysdev_register(&mpic->sysdev); + mpic = mpic->next; + } + return error; +} + +device_initcall(mpic_init_sys); |