summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/sysdev/mpic.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/sysdev/mpic.c')
-rw-r--r--arch/powerpc/sysdev/mpic.c162
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);