Messages in this thread Patch in this message | | | From | Rasmus Villemoes <> | Subject | polarity inversion on LS1021a | Date | Mon, 4 Dec 2017 16:11:06 +0100 |
| |
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)
| |