Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754039AbdLDPLJ (ORCPT ); Mon, 4 Dec 2017 10:11:09 -0500 Received: from mail02.prevas.se ([62.95.78.10]:20658 "EHLO mail02.prevas.se" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752470AbdLDPLI (ORCPT ); Mon, 4 Dec 2017 10:11:08 -0500 X-IronPort-AV: E=Sophos;i="5.45,359,1508796000"; d="scan'208";a="2763473" To: LKML CC: Thomas Gleixner , Marc Zyngier , Shawn Guo From: Rasmus Villemoes Subject: polarity inversion on LS1021a Message-ID: <67135504-72fb-ef7e-cac4-03331550a38d@prevas.dk> Date: Mon, 4 Dec 2017 16:11:06 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.1.1 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Language: en-US Content-Transfer-Encoding: 7bit X-Originating-IP: [172.16.8.31] Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 2431 Lines: 81 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)