The LS1021A has a standard GIC-400, but allows inverting the polarity of
six external interrupt lines via a certain register, effectively
supporting IRQ_TYPE_LEVEL_LOW and IRQ_TYPE_EDGE_FALLING for those.

I'm trying to figure out how one would add support for this. The patch
below works but is obviously just meant to help show what I mean, so
please don't comment on all the things that are wrong with it.

It feels wrong to create a whole new irqchip driver copy-pasting the
entire irg-gic.c, but I can't figure out how and where one could hook
into the existing one. Any pointers on how to do this properly will be
greatly appreciated.

Thanks,
Rasmus


diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 651d726e8b12..299710b7dd09 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -290,6 +290,48 @@ static int gic_irq_get_irqchip_state(struct
irq_data *d,
        return 0;
 }

+static int gic_set_type_ls1_ext_irq_polarity(unsigned int gicirq,
+                                            unsigned int *type)
+{
+       struct device_node *np;
+       void __iomem *scfg = NULL;
+       u32 polarity_mask = 0;
+       u32 intpcr;
+
+       np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-scfg");
+       if (!np)
+               return 0;
+
+       scfg = of_iomap(np, 0);
+       if (!scfg)
+               return -EINVAL;
+
+       switch (gicirq) {
+       case 195: polarity_mask = 0x80000000; break;
+       case 196: polarity_mask = 0x40000000; break;
+       case 197: polarity_mask = 0x20000000; break;
+       case 199: polarity_mask = 0x10000000; break;
+       case 200: polarity_mask = 0x08000000; break;
+       case 201: polarity_mask = 0x04000000; break;
+       }
+       if (!polarity_mask)
+               return 0;
+
+       intpcr = ioread32be(scfg + 0x1ac);
+
+       if (*type == IRQ_TYPE_LEVEL_LOW || *type == IRQ_TYPE_EDGE_FALLING)
+               iowrite32be(intpcr | polarity_mask, scfg + 0x1ac);
+       else
+               iowrite32be(intpcr & ~polarity_mask, scfg + 0x1ac);
+
+       if (*type == IRQ_TYPE_LEVEL_LOW)
+               *type = IRQ_TYPE_LEVEL_HIGH;
+       else if (*type == IRQ_TYPE_EDGE_FALLING)
+               *type = IRQ_TYPE_EDGE_RISING;
+
+       return 0;
+}
+
 static int gic_set_type(struct irq_data *d, unsigned int type)
 {
        void __iomem *base = gic_dist_base(d);
@@ -299,6 +341,8 @@ static int gic_set_type(struct irq_data *d, unsigned
int type)
        if (gicirq < 16)
                return -EINVAL;

+       gic_set_type_ls1_ext_irq_polarity(gicirq, &type);
+
        /* SPIs have restrictions on the supported types */
        if (gicirq >= 32 && type != IRQ_TYPE_LEVEL_HIGH &&
                            type != IRQ_TYPE_EDGE_RISING)

Reply via email to