polarity inversion on LS1021a

From: Rasmus Villemoes
Date: Mon Dec 04 2017 - 10:11:13 EST


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)