Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/egtvedt...
[pandora-kernel.git] / arch / powerpc / sysdev / mpic.c
index f91c065..58d7a53 100644 (file)
@@ -6,6 +6,7 @@
  *  with various broken implementations of this HW.
  *
  *  Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp.
+ *  Copyright 2010-2011 Freescale Semiconductor, Inc.
  *
  *  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
@@ -27,6 +28,8 @@
 #include <linux/spinlock.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
+#include <linux/syscore_ops.h>
+#include <linux/ratelimit.h>
 
 #include <asm/ptrace.h>
 #include <asm/signal.h>
@@ -218,6 +221,28 @@ static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 valu
        _mpic_write(mpic->reg_type, &mpic->gregs, offset, value);
 }
 
+static inline u32 _mpic_tm_read(struct mpic *mpic, unsigned int tm)
+{
+       unsigned int offset = MPIC_INFO(TIMER_VECTOR_PRI) +
+                             ((tm & 3) * MPIC_INFO(TIMER_STRIDE));
+
+       if (tm >= 4)
+               offset += 0x1000 / 4;
+
+       return _mpic_read(mpic->reg_type, &mpic->tmregs, offset);
+}
+
+static inline void _mpic_tm_write(struct mpic *mpic, unsigned int tm, u32 value)
+{
+       unsigned int offset = MPIC_INFO(TIMER_VECTOR_PRI) +
+                             ((tm & 3) * MPIC_INFO(TIMER_STRIDE));
+
+       if (tm >= 4)
+               offset += 0x1000 / 4;
+
+       _mpic_write(mpic->reg_type, &mpic->tmregs, offset, value);
+}
+
 static inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg)
 {
        unsigned int cpu = mpic_processor_id(mpic);
@@ -268,6 +293,8 @@ static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no,
 #define mpic_write(b,r,v)      _mpic_write(mpic->reg_type,&(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_tm_read(i)                _mpic_tm_read(mpic,(i))
+#define mpic_tm_write(i,v)     _mpic_tm_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))
@@ -607,8 +634,6 @@ static int irq_choose_cpu(const struct cpumask *mask)
 }
 #endif
 
-#define mpic_irq_to_hw(virq)   ((unsigned int)irq_map[virq].hwirq)
-
 /* Find an mpic associated with a given linux interrupt */
 static struct mpic *mpic_find(unsigned int irq)
 {
@@ -621,11 +646,18 @@ static struct mpic *mpic_find(unsigned int irq)
 /* Determine if the linux irq is an IPI */
 static unsigned int mpic_is_ipi(struct mpic *mpic, unsigned int irq)
 {
-       unsigned int src = mpic_irq_to_hw(irq);
+       unsigned int src = virq_to_hw(irq);
 
        return (src >= mpic->ipi_vecs[0] && src <= mpic->ipi_vecs[3]);
 }
 
+/* Determine if the linux irq is a timer */
+static unsigned int mpic_is_tm(struct mpic *mpic, unsigned int irq)
+{
+       unsigned int src = virq_to_hw(irq);
+
+       return (src >= mpic->timer_vecs[0] && src <= mpic->timer_vecs[7]);
+}
 
 /* Convert a cpu mask from logical to physical cpu numbers. */
 static inline u32 mpic_physmask(u32 cpumask)
@@ -633,7 +665,7 @@ static inline u32 mpic_physmask(u32 cpumask)
        int i;
        u32 mask = 0;
 
-       for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1)
+       for (i = 0; i < min(32, NR_CPUS); ++i, cpumask >>= 1)
                mask |= (cpumask & 1) << get_hard_smp_processor_id(i);
        return mask;
 }
@@ -674,7 +706,7 @@ void mpic_unmask_irq(struct irq_data *d)
 {
        unsigned int loops = 100000;
        struct mpic *mpic = mpic_from_irq_data(d);
-       unsigned int src = mpic_irq_to_hw(d->irq);
+       unsigned int src = irqd_to_hwirq(d);
 
        DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, d->irq, src);
 
@@ -695,7 +727,7 @@ void mpic_mask_irq(struct irq_data *d)
 {
        unsigned int loops = 100000;
        struct mpic *mpic = mpic_from_irq_data(d);
-       unsigned int src = mpic_irq_to_hw(d->irq);
+       unsigned int src = irqd_to_hwirq(d);
 
        DBG("%s: disable_irq: %d (src %d)\n", mpic->name, d->irq, src);
 
@@ -733,7 +765,7 @@ void mpic_end_irq(struct irq_data *d)
 static void mpic_unmask_ht_irq(struct irq_data *d)
 {
        struct mpic *mpic = mpic_from_irq_data(d);
-       unsigned int src = mpic_irq_to_hw(d->irq);
+       unsigned int src = irqd_to_hwirq(d);
 
        mpic_unmask_irq(d);
 
@@ -744,7 +776,7 @@ static void mpic_unmask_ht_irq(struct irq_data *d)
 static unsigned int mpic_startup_ht_irq(struct irq_data *d)
 {
        struct mpic *mpic = mpic_from_irq_data(d);
-       unsigned int src = mpic_irq_to_hw(d->irq);
+       unsigned int src = irqd_to_hwirq(d);
 
        mpic_unmask_irq(d);
        mpic_startup_ht_interrupt(mpic, src, irqd_is_level_type(d));
@@ -755,7 +787,7 @@ static unsigned int mpic_startup_ht_irq(struct irq_data *d)
 static void mpic_shutdown_ht_irq(struct irq_data *d)
 {
        struct mpic *mpic = mpic_from_irq_data(d);
-       unsigned int src = mpic_irq_to_hw(d->irq);
+       unsigned int src = irqd_to_hwirq(d);
 
        mpic_shutdown_ht_interrupt(mpic, src);
        mpic_mask_irq(d);
@@ -764,7 +796,7 @@ static void mpic_shutdown_ht_irq(struct irq_data *d)
 static void mpic_end_ht_irq(struct irq_data *d)
 {
        struct mpic *mpic = mpic_from_irq_data(d);
-       unsigned int src = mpic_irq_to_hw(d->irq);
+       unsigned int src = irqd_to_hwirq(d);
 
 #ifdef DEBUG_IRQ
        DBG("%s: end_irq: %d\n", mpic->name, d->irq);
@@ -785,7 +817,7 @@ static void mpic_end_ht_irq(struct irq_data *d)
 static void mpic_unmask_ipi(struct irq_data *d)
 {
        struct mpic *mpic = mpic_from_ipi(d);
-       unsigned int src = mpic_irq_to_hw(d->irq) - mpic->ipi_vecs[0];
+       unsigned int src = virq_to_hw(d->irq) - mpic->ipi_vecs[0];
 
        DBG("%s: enable_ipi: %d (ipi %d)\n", mpic->name, d->irq, src);
        mpic_ipi_write(src, mpic_ipi_read(src) & ~MPIC_VECPRI_MASK);
@@ -812,27 +844,42 @@ static void mpic_end_ipi(struct irq_data *d)
 
 #endif /* CONFIG_SMP */
 
+static void mpic_unmask_tm(struct irq_data *d)
+{
+       struct mpic *mpic = mpic_from_irq_data(d);
+       unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0];
+
+       DBG("%s: enable_tm: %d (tm %d)\n", mpic->name, irq, src);
+       mpic_tm_write(src, mpic_tm_read(src) & ~MPIC_VECPRI_MASK);
+       mpic_tm_read(src);
+}
+
+static void mpic_mask_tm(struct irq_data *d)
+{
+       struct mpic *mpic = mpic_from_irq_data(d);
+       unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0];
+
+       mpic_tm_write(src, mpic_tm_read(src) | MPIC_VECPRI_MASK);
+       mpic_tm_read(src);
+}
+
 int mpic_set_affinity(struct irq_data *d, const struct cpumask *cpumask,
                      bool force)
 {
        struct mpic *mpic = mpic_from_irq_data(d);
-       unsigned int src = mpic_irq_to_hw(d->irq);
+       unsigned int src = irqd_to_hwirq(d);
 
        if (mpic->flags & MPIC_SINGLE_DEST_CPU) {
                int cpuid = irq_choose_cpu(cpumask);
 
                mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid);
        } else {
-               cpumask_var_t tmp;
-
-               alloc_cpumask_var(&tmp, GFP_KERNEL);
+               u32 mask = cpumask_bits(cpumask)[0];
 
-               cpumask_and(tmp, cpumask, cpu_online_mask);
+               mask &= cpumask_bits(cpu_online_mask)[0];
 
                mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION),
-                              mpic_physmask(cpumask_bits(tmp)[0]));
-
-               free_cpumask_var(tmp);
+                              mpic_physmask(mask));
        }
 
        return 0;
@@ -862,7 +909,7 @@ static unsigned int mpic_type_to_vecpri(struct mpic *mpic, unsigned int type)
 int mpic_set_irq_type(struct irq_data *d, unsigned int flow_type)
 {
        struct mpic *mpic = mpic_from_irq_data(d);
-       unsigned int src = mpic_irq_to_hw(d->irq);
+       unsigned int src = irqd_to_hwirq(d);
        unsigned int vecpri, vold, vnew;
 
        DBG("mpic: set_irq_type(mpic:@%p,virq:%d,src:0x%x,type:0x%x)\n",
@@ -898,7 +945,7 @@ int mpic_set_irq_type(struct irq_data *d, unsigned int flow_type)
 void mpic_set_vector(unsigned int virq, unsigned int vector)
 {
        struct mpic *mpic = mpic_from_irq(virq);
-       unsigned int src = mpic_irq_to_hw(virq);
+       unsigned int src = virq_to_hw(virq);
        unsigned int vecpri;
 
        DBG("mpic: set_vector(mpic:@%p,virq:%d,src:%d,vector:0x%x)\n",
@@ -916,7 +963,7 @@ void mpic_set_vector(unsigned int virq, unsigned int vector)
 void mpic_set_destination(unsigned int virq, unsigned int cpuid)
 {
        struct mpic *mpic = mpic_from_irq(virq);
-       unsigned int src = mpic_irq_to_hw(virq);
+       unsigned int src = virq_to_hw(virq);
 
        DBG("mpic: set_destination(mpic:@%p,virq:%d,src:%d,cpuid:0x%x)\n",
            mpic, virq, src, cpuid);
@@ -942,6 +989,12 @@ static struct irq_chip mpic_ipi_chip = {
 };
 #endif /* CONFIG_SMP */
 
+static struct irq_chip mpic_tm_chip = {
+       .irq_mask       = mpic_mask_tm,
+       .irq_unmask     = mpic_unmask_tm,
+       .irq_eoi        = mpic_end_irq,
+};
+
 #ifdef CONFIG_MPIC_U3_HT_IRQS
 static struct irq_chip mpic_irq_ht_chip = {
        .irq_startup    = mpic_startup_ht_irq,
@@ -985,6 +1038,16 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq,
        }
 #endif /* CONFIG_SMP */
 
+       if (hw >= mpic->timer_vecs[0] && hw <= mpic->timer_vecs[7]) {
+               WARN_ON(!(mpic->flags & MPIC_PRIMARY));
+
+               DBG("mpic: mapping as timer\n");
+               irq_set_chip_data(virq, mpic);
+               irq_set_chip_and_handler(virq, &mpic->hc_tm,
+                                        handle_fasteoi_irq);
+               return 0;
+       }
+
        if (hw >= mpic->irq_count)
                return -EINVAL;
 
@@ -1025,6 +1088,7 @@ static int mpic_host_xlate(struct irq_host *h, struct device_node *ct,
                           irq_hw_number_t *out_hwirq, unsigned int *out_flags)
 
 {
+       struct mpic *mpic = h->host_data;
        static unsigned char map_mpic_senses[4] = {
                IRQ_TYPE_EDGE_RISING,
                IRQ_TYPE_LEVEL_LOW,
@@ -1033,7 +1097,38 @@ static int mpic_host_xlate(struct irq_host *h, struct device_node *ct,
        };
 
        *out_hwirq = intspec[0];
-       if (intsize > 1) {
+       if (intsize >= 4 && (mpic->flags & MPIC_FSL)) {
+               /*
+                * Freescale MPIC with extended intspec:
+                * First two cells are as usual.  Third specifies
+                * an "interrupt type".  Fourth is type-specific data.
+                *
+                * See Documentation/devicetree/bindings/powerpc/fsl/mpic.txt
+                */
+               switch (intspec[2]) {
+               case 0:
+               case 1: /* no EISR/EIMR support for now, treat as shared IRQ */
+                       break;
+               case 2:
+                       if (intspec[0] >= ARRAY_SIZE(mpic->ipi_vecs))
+                               return -EINVAL;
+
+                       *out_hwirq = mpic->ipi_vecs[intspec[0]];
+                       break;
+               case 3:
+                       if (intspec[0] >= ARRAY_SIZE(mpic->timer_vecs))
+                               return -EINVAL;
+
+                       *out_hwirq = mpic->timer_vecs[intspec[0]];
+                       break;
+               default:
+                       pr_debug("%s: unknown irq type %u\n",
+                                __func__, intspec[2]);
+                       return -EINVAL;
+               }
+
+               *out_flags = map_mpic_senses[intspec[1] & 3];
+       } else if (intsize > 1) {
                u32 mask = 0x3;
 
                /* Apple invented a new race of encoding on machines with
@@ -1109,6 +1204,9 @@ struct mpic * __init mpic_alloc(struct device_node *node,
        mpic->hc_ipi.name = name;
 #endif /* CONFIG_SMP */
 
+       mpic->hc_tm = mpic_tm_chip;
+       mpic->hc_tm.name = name;
+
        mpic->flags = flags;
        mpic->isu_size = isu_size;
        mpic->irq_count = irq_count;
@@ -1119,10 +1217,14 @@ struct mpic * __init mpic_alloc(struct device_node *node,
        else
                intvec_top = 255;
 
-       mpic->timer_vecs[0] = intvec_top - 8;
-       mpic->timer_vecs[1] = intvec_top - 7;
-       mpic->timer_vecs[2] = intvec_top - 6;
-       mpic->timer_vecs[3] = intvec_top - 5;
+       mpic->timer_vecs[0] = intvec_top - 12;
+       mpic->timer_vecs[1] = intvec_top - 11;
+       mpic->timer_vecs[2] = intvec_top - 10;
+       mpic->timer_vecs[3] = intvec_top - 9;
+       mpic->timer_vecs[4] = intvec_top - 8;
+       mpic->timer_vecs[5] = intvec_top - 7;
+       mpic->timer_vecs[6] = intvec_top - 6;
+       mpic->timer_vecs[7] = intvec_top - 5;
        mpic->ipi_vecs[0]   = intvec_top - 4;
        mpic->ipi_vecs[1]   = intvec_top - 3;
        mpic->ipi_vecs[2]   = intvec_top - 2;
@@ -1132,6 +1234,8 @@ struct mpic * __init mpic_alloc(struct device_node *node,
        /* Check for "big-endian" in device-tree */
        if (node && of_get_property(node, "big-endian", NULL) != NULL)
                mpic->flags |= MPIC_BIG_ENDIAN;
+       if (node && of_device_is_compatible(node, "fsl,mpic"))
+               mpic->flags |= MPIC_FSL;
 
        /* Look for protected sources */
        if (node) {
@@ -1323,15 +1427,17 @@ void __init mpic_init(struct mpic *mpic)
        /* Set current processor priority to max */
        mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf);
 
-       /* Initialize timers: just disable them all */
+       /* Initialize timers to our reserved vectors and mask them for now */
        for (i = 0; i < 4; i++) {
                mpic_write(mpic->tmregs,
                           i * MPIC_INFO(TIMER_STRIDE) +
-                          MPIC_INFO(TIMER_DESTINATION), 0);
+                          MPIC_INFO(TIMER_DESTINATION),
+                          1 << hard_smp_processor_id());
                mpic_write(mpic->tmregs,
                           i * MPIC_INFO(TIMER_STRIDE) +
                           MPIC_INFO(TIMER_VECTOR_PRI),
                           MPIC_VECPRI_MASK |
+                          (9 << MPIC_VECPRI_PRIORITY_SHIFT) |
                           (mpic->timer_vecs[0] + i));
        }
 
@@ -1427,7 +1533,7 @@ void __init mpic_set_serial_int(struct mpic *mpic, int enable)
 void mpic_irq_set_priority(unsigned int irq, unsigned int pri)
 {
        struct mpic *mpic = mpic_find(irq);
-       unsigned int src = mpic_irq_to_hw(irq);
+       unsigned int src = virq_to_hw(irq);
        unsigned long flags;
        u32 reg;
 
@@ -1440,6 +1546,11 @@ void mpic_irq_set_priority(unsigned int irq, unsigned int pri)
                        ~MPIC_VECPRI_PRIORITY_MASK;
                mpic_ipi_write(src - mpic->ipi_vecs[0],
                               reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
+       } else if (mpic_is_tm(mpic, irq)) {
+               reg = mpic_tm_read(src - mpic->timer_vecs[0]) &
+                       ~MPIC_VECPRI_PRIORITY_MASK;
+               mpic_tm_write(src - mpic->timer_vecs[0],
+                             reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
        } else {
                reg = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI))
                        & ~MPIC_VECPRI_PRIORITY_MASK;
@@ -1538,9 +1649,8 @@ static unsigned int _mpic_get_one_irq(struct mpic *mpic, int reg)
                return NO_IRQ;
        }
        if (unlikely(mpic->protected && test_bit(src, mpic->protected))) {
-               if (printk_ratelimit())
-                       printk(KERN_WARNING "%s: Got protected source %d !\n",
-                              mpic->name, (int)src);
+               printk_ratelimited(KERN_WARNING "%s: Got protected source %d !\n",
+                                  mpic->name, (int)src);
                mpic_eoi(mpic);
                return NO_IRQ;
        }
@@ -1578,9 +1688,8 @@ unsigned int mpic_get_coreint_irq(void)
                return NO_IRQ;
        }
        if (unlikely(mpic->protected && test_bit(src, mpic->protected))) {
-               if (printk_ratelimit())
-                       printk(KERN_WARNING "%s: Got protected source %d !\n",
-                              mpic->name, (int)src);
+               printk_ratelimited(KERN_WARNING "%s: Got protected source %d !\n",
+                                  mpic->name, (int)src);
                return NO_IRQ;
        }
 
@@ -1619,46 +1728,28 @@ void mpic_request_ipis(void)
        }
 }
 
-static void mpic_send_ipi(unsigned int ipi_no, const struct cpumask *cpu_mask)
+void smp_mpic_message_pass(int cpu, int msg)
 {
        struct mpic *mpic = mpic_primary;
+       u32 physmask;
 
        BUG_ON(mpic == NULL);
 
-#ifdef DEBUG_IPI
-       DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no);
-#endif
-
-       mpic_cpu_write(MPIC_INFO(CPU_IPI_DISPATCH_0) +
-                      ipi_no * MPIC_INFO(CPU_IPI_DISPATCH_STRIDE),
-                      mpic_physmask(cpumask_bits(cpu_mask)[0]));
-}
-
-void smp_mpic_message_pass(int target, int msg)
-{
-       cpumask_var_t tmp;
-
        /* make sure we're sending something that translates to an IPI */
        if ((unsigned int)msg > 3) {
                printk("SMP %d: smp_message_pass: unknown msg %d\n",
                       smp_processor_id(), msg);
                return;
        }
-       switch (target) {
-       case MSG_ALL:
-               mpic_send_ipi(msg, cpu_online_mask);
-               break;
-       case MSG_ALL_BUT_SELF:
-               alloc_cpumask_var(&tmp, GFP_NOWAIT);
-               cpumask_andnot(tmp, cpu_online_mask,
-                              cpumask_of(smp_processor_id()));
-               mpic_send_ipi(msg, tmp);
-               free_cpumask_var(tmp);
-               break;
-       default:
-               mpic_send_ipi(msg, cpumask_of(target));
-               break;
-       }
+
+#ifdef DEBUG_IPI
+       DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, msg);
+#endif
+
+       physmask = 1 << get_hard_smp_processor_id(cpu);
+
+       mpic_cpu_write(MPIC_INFO(CPU_IPI_DISPATCH_0) +
+                      msg * MPIC_INFO(CPU_IPI_DISPATCH_STRIDE), physmask);
 }
 
 int __init smp_mpic_probe(void)
@@ -1702,9 +1793,8 @@ void mpic_reset_core(int cpu)
 #endif /* CONFIG_SMP */
 
 #ifdef CONFIG_PM
-static int mpic_suspend(struct sys_device *dev, pm_message_t state)
+static void mpic_suspend_one(struct mpic *mpic)
 {
-       struct mpic *mpic = container_of(dev, struct mpic, sysdev);
        int i;
 
        for (i = 0; i < mpic->num_sources; i++) {
@@ -1713,13 +1803,22 @@ static int mpic_suspend(struct sys_device *dev, pm_message_t state)
                mpic->save_data[i].dest =
                        mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION));
        }
+}
+
+static int mpic_suspend(void)
+{
+       struct mpic *mpic = mpics;
+
+       while (mpic) {
+               mpic_suspend_one(mpic);
+               mpic = mpic->next;
+       }
 
        return 0;
 }
 
-static int mpic_resume(struct sys_device *dev)
+static void mpic_resume_one(struct mpic *mpic)
 {
-       struct mpic *mpic = container_of(dev, struct mpic, sysdev);
        int i;
 
        for (i = 0; i < mpic->num_sources; i++) {
@@ -1746,33 +1845,28 @@ static int mpic_resume(struct sys_device *dev)
        }
 #endif
        } /* end for loop */
+}
 
-       return 0;
+static void mpic_resume(void)
+{
+       struct mpic *mpic = mpics;
+
+       while (mpic) {
+               mpic_resume_one(mpic);
+               mpic = mpic->next;
+       }
 }
-#endif
 
-static struct sysdev_class mpic_sysclass = {
-#ifdef CONFIG_PM
+static struct syscore_ops mpic_syscore_ops = {
        .resume = mpic_resume,
        .suspend = mpic_suspend,
-#endif
-       .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;
+       register_syscore_ops(&mpic_syscore_ops);
+       return 0;
 }
 
 device_initcall(mpic_init_sys);
+#endif