ARM: 6777/1: gic: Add hooks for architecture specific extensions
authorSantosh Shilimkar <santosh.shilimkar@ti.com>
Wed, 2 Mar 2011 07:03:22 +0000 (08:03 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Wed, 9 Mar 2011 00:18:47 +0000 (00:18 +0000)
Few architectures combine the GIC with an external interrupt
controller. On such systems it may be necessary to update both
the GIC registers and the external controller's registers to control
IRQ behavior.

This can be addressed in couple of possible methods.
 1. Export common GIC routines along with 'struct irq_chip gic_chip'
    and allow architectures to have custom function by override.
 2. Provide architecture specific function pointer hooks
    within GIC library and leave platforms to add the necessary
    code as part of these hooks.

First one might be non-intrusive but have few shortcomings like arch
needs to have there own custom gic library. Locks used should be
common since it caters to same IRQs etc. Maintenance point of view
also it leads to multiple file fixes.

The second probably is cleaner and portable. It ensures that all the
common GIC infrastructure is not touched and also provides archs to
address their specific issue.

Cc: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Acked-by: Colin Cross <ccross@android.com>
Tested-by: Colin Cross <ccross@android.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/common/gic.c
arch/arm/include/asm/hardware/gic.h

index e21c1f4..cb6b041 100644 (file)
@@ -44,6 +44,19 @@ struct gic_chip_data {
        void __iomem *cpu_base;
 };
 
+/*
+ * Supported arch specific GIC irq extension.
+ * Default make them NULL.
+ */
+struct irq_chip gic_arch_extn = {
+       .irq_ack        = NULL,
+       .irq_mask       = NULL,
+       .irq_unmask     = NULL,
+       .irq_retrigger  = NULL,
+       .irq_set_type   = NULL,
+       .irq_set_wake   = NULL,
+};
+
 #ifndef MAX_GIC_NR
 #define MAX_GIC_NR     1
 #endif
@@ -74,6 +87,8 @@ static inline unsigned int gic_irq(struct irq_data *d)
 static void gic_ack_irq(struct irq_data *d)
 {
        spin_lock(&irq_controller_lock);
+       if (gic_arch_extn.irq_ack)
+               gic_arch_extn.irq_ack(d);
        writel(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI);
        spin_unlock(&irq_controller_lock);
 }
@@ -84,6 +99,8 @@ static void gic_mask_irq(struct irq_data *d)
 
        spin_lock(&irq_controller_lock);
        writel(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4);
+       if (gic_arch_extn.irq_mask)
+               gic_arch_extn.irq_mask(d);
        spin_unlock(&irq_controller_lock);
 }
 
@@ -92,6 +109,8 @@ static void gic_unmask_irq(struct irq_data *d)
        u32 mask = 1 << (d->irq % 32);
 
        spin_lock(&irq_controller_lock);
+       if (gic_arch_extn.irq_unmask)
+               gic_arch_extn.irq_unmask(d);
        writel(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4);
        spin_unlock(&irq_controller_lock);
 }
@@ -116,6 +135,9 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
 
        spin_lock(&irq_controller_lock);
 
+       if (gic_arch_extn.irq_set_type)
+               gic_arch_extn.irq_set_type(d, type);
+
        val = readl(base + GIC_DIST_CONFIG + confoff);
        if (type == IRQ_TYPE_LEVEL_HIGH)
                val &= ~confmask;
@@ -141,6 +163,14 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
        return 0;
 }
 
+static int gic_retrigger(struct irq_data *d)
+{
+       if (gic_arch_extn.irq_retrigger)
+               return gic_arch_extn.irq_retrigger(d);
+
+       return -ENXIO;
+}
+
 #ifdef CONFIG_SMP
 static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
                            bool force)
@@ -166,6 +196,21 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
 }
 #endif
 
+#ifdef CONFIG_PM
+static int gic_set_wake(struct irq_data *d, unsigned int on)
+{
+       int ret = -ENXIO;
+
+       if (gic_arch_extn.irq_set_wake)
+               ret = gic_arch_extn.irq_set_wake(d, on);
+
+       return ret;
+}
+
+#else
+#define gic_set_wake   NULL
+#endif
+
 static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
 {
        struct gic_chip_data *chip_data = get_irq_data(irq);
@@ -201,9 +246,11 @@ static struct irq_chip gic_chip = {
        .irq_mask               = gic_mask_irq,
        .irq_unmask             = gic_unmask_irq,
        .irq_set_type           = gic_set_type,
+       .irq_retrigger          = gic_retrigger,
 #ifdef CONFIG_SMP
        .irq_set_affinity       = gic_set_affinity,
 #endif
+       .irq_set_wake           = gic_set_wake,
 };
 
 void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
index 84557d3..0691f9d 100644 (file)
@@ -34,6 +34,7 @@
 
 #ifndef __ASSEMBLY__
 extern void __iomem *gic_cpu_base_addr;
+extern struct irq_chip gic_arch_extn;
 
 void gic_init(unsigned int, unsigned int, void __iomem *, void __iomem *);
 void gic_secondary_init(unsigned int);