powerpc/mpic: add the mpic global timer support
authorScott Wood <scottwood@freescale.com>
Thu, 24 Mar 2011 21:43:55 +0000 (16:43 -0500)
committerKumar Gala <galak@kernel.crashing.org>
Thu, 19 May 2011 06:14:28 +0000 (01:14 -0500)
Add support for MPIC timers as requestable interrupt sources.

Based on http://patchwork.ozlabs.org/patch/20941/ by Dave Liu.

Signed-off-by: Dave Liu <daveliu@freescale.com>
Signed-off-by: Scott Wood <scottwood@freescale.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
arch/powerpc/include/asm/mpic.h
arch/powerpc/sysdev/mpic.c

index 25a0cb3a79f3e449d0971495015e36a9533958c6..664bee6622e7a9d79319e300a24f464d4d0c96ef 100644 (file)
@@ -263,6 +263,7 @@ struct mpic
 #ifdef CONFIG_SMP
        struct irq_chip         hc_ipi;
 #endif
+       struct irq_chip         hc_tm;
        const char              *name;
        /* Flags */
        unsigned int            flags;
@@ -281,7 +282,7 @@ struct mpic
 
        /* vector numbers used for internal sources (ipi/timers) */
        unsigned int            ipi_vecs[4];
-       unsigned int            timer_vecs[4];
+       unsigned int            timer_vecs[8];
 
        /* Spurious vector to program into unused sources */
        unsigned int            spurious_vec;
index 0a3c1c20115c127b1b827783042cc2385762a2d1..0e47bce5f696f877a8acbd407f7fc69ef4919abf 100644 (file)
@@ -219,6 +219,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);
@@ -269,6 +291,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))
@@ -625,6 +649,13 @@ static unsigned int mpic_is_ipi(struct mpic *mpic, unsigned int 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)
@@ -811,6 +842,25 @@ 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)
 {
@@ -941,6 +991,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,
@@ -984,6 +1040,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;
 
@@ -1140,6 +1206,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;
@@ -1150,10 +1219,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;
@@ -1356,15 +1429,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));
        }
 
@@ -1473,6 +1548,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;