From patchwork Mon Jan 14 19:14:49 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?J=2E_Neusch=C3=A4fer?= X-Patchwork-Id: 1024746 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-gpio-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=gmx.net Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 43djpy3GH5z9sD9 for ; Tue, 15 Jan 2019 06:15:22 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726895AbfANTPL (ORCPT ); Mon, 14 Jan 2019 14:15:11 -0500 Received: from mout.gmx.net ([212.227.17.20]:45889 "EHLO mout.gmx.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726760AbfANTPK (ORCPT ); Mon, 14 Jan 2019 14:15:10 -0500 Received: from longitude ([109.90.232.48]) by mail.gmx.com (mrgmx102 [212.227.17.168]) with ESMTPSA (Nemesis) id 0MCOdh-1gadEw0soj-009AoY; Mon, 14 Jan 2019 20:15:07 +0100 From: =?utf-8?q?Jonathan_Neusch=C3=A4fer?= To: linux-gpio@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Linus Walleij , Bartosz Golaszewski , =?utf-8?q?Jonathan_Neusch=C3=A4fer?= Subject: [PATCH v2 1/2] gpio: hlwd: Add basic IRQ support Date: Mon, 14 Jan 2019 20:14:49 +0100 Message-Id: <20190114191450.11597-2-j.neuschaefer@gmx.net> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190114191450.11597-1-j.neuschaefer@gmx.net> References: <20190114191450.11597-1-j.neuschaefer@gmx.net> MIME-Version: 1.0 X-Provags-ID: V03:K1:QLAld9XrVtTSMmrDEexrOGNWxlcLD84M8PRPhwutG2bXLJDy2eh Pj4WvSnvw4b4WmoXT1wk2vxRfSPdlw129tqOnmJJEHKY4kQqtqYBDpLFiHmaXdH3t8/QG2u GS3JhJvhISO8L2DYhZ47I4xauAJJM2O1o6KBuN/Dgtpw2x+vMNl7K4neErVGxupEr47Le8f QH6TdPwAkcQRxSQphWp/A== X-Spam-Flag: NO X-UI-Out-Filterresults: notjunk:1; V03:K0:emt6X8MUkio=:e0SfuFjHn+UZGjuNf9zek6 SmUbr8G+PwFeVj70ctiS7WYX0zGpDCq3/Kxh3Qe7X1yAyvMvmjftd2f/Ph3/BoH73E6Hs+aJp ZG57YjreyTDClQfi5G50LivfGZQHjHf56PpWF0SxlTwfMYJSHEqvwmi9vbbUsEd1vSXO/z/uC KeRlxudCfKdb9aHFrlDzcIfLdGSBzwxfTPVpP11DgI98WSGLz5OMxHW8yh7OANVbQwWu2nnIx DOD+O+9Hw10cX+hyJA2eV/VhBIMZmPO5zfFS9t8l1SV7ERadZgO6KtgwdDf4CJ+rvOuSm3r5j uSrVBWBQnjm+4xfss9YgjbBLpP4iV8wEgsOOF18ZjntSwyoLz342q3bslAVy0eQSTFDZJu2ds LWbHa5mQKaUHz3i9jK7r9++zvyEBORTavhbFGA6kd73vfgiXeONWIWAtAxksEQ28If0uhLnee MxuV2PFaJzc/lPU4Oc0OCl4SCAWVs+bmt+4KiplRdKeymTau+vTbzq/z/mj08ijYniTju5M/f gck4E0JnZ6aRiljAijuhNNtLgWtmOFXFuJX1m4D3wSUT7OgANJSNsMfTx0vr/5x36oO2lRi7F 3fVUWruolpNOuLm9DFxKnLifLmMxVwYUwbTym/Su5QRl+4My/pEzZFn27xzz1D9y92sCTo0VQ c1Il4xvQ6e9fp+HZg6hsICF+PSh5znNXYZYf1R6/4jY5wgaxKzLrNdNuNnMGThnN0pOVEyZNw Eakv2jx2E0hen1ShkVZtXBvf59MJ2XW5gznGgNzo9DirgizmBiGQk0h2K47MNTmtidg4x8YNo 5XsIC44pveJVtIbdER8g2L0HazxpYqRWQ/CsADMqMY4gPPlLkpqoiQI0melSvIFjsRCPXHaSZ bmbTEcDVWgc+pBbQoJYQqcx5zEo0/+OKzMr8Qy9JkwE/qZwMoLDqjgNjRJ7Pko/ZvtvZvMspH mvDLBdf9bnA== Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This patch implements level-triggered IRQs in the Hollywood GPIO driver. Edge triggered interrupts are not supported in this GPIO controller, so I moved their emulation into a separate patch. Signed-off-by: Jonathan Neuschäfer --- v2: - Changed register addresses to io{read,write}32be() instead of hlwd->gpioc.{read,write}_reg, as suggested by Linus Walleij. - Changed the IRQ chip's name from "GPIO" to dev_name(&pdev->dev), which is "d8000c0.gpio" for the one GPIO controller in the Wii, but might help tell the two GPIO controllers in the Wii U apart. (also suggested by Linus Walleij) v1: https://lore.kernel.org/lkml/20190113135844.13109-2-j.neuschaefer@gmx.net/ --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-hlwd.c | 136 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b5a2845347ec..f77180dd87bd 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -258,6 +258,7 @@ config GPIO_HLWD tristate "Nintendo Wii (Hollywood) GPIO" depends on OF_GPIO select GPIO_GENERIC + select GPIOLIB_IRQCHIP help Select this to support the GPIO controller of the Nintendo Wii. diff --git a/drivers/gpio/gpio-hlwd.c b/drivers/gpio/gpio-hlwd.c index a63136a68ba3..61983c115856 100644 --- a/drivers/gpio/gpio-hlwd.c +++ b/drivers/gpio/gpio-hlwd.c @@ -48,9 +48,107 @@ struct hlwd_gpio { struct gpio_chip gpioc; + struct irq_chip irqc; void __iomem *regs; + int irq; }; +static void hlwd_gpio_irqhandler(struct irq_desc *desc) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_desc_get_handler_data(desc)); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long flags; + unsigned long pending; + int hwirq; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG); + pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK); + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); + + chained_irq_enter(chip, desc); + + for_each_set_bit(hwirq, &pending, 32) { + int irq = irq_find_mapping(hlwd->gpioc.irq.domain, hwirq); + + generic_handle_irq(irq); + } + + chained_irq_exit(chip, desc); +} + +static void hlwd_gpio_irq_ack(struct irq_data *data) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + + iowrite32be(BIT(data->hwirq), hlwd->regs + HW_GPIOB_INTFLAG); +} + +static void hlwd_gpio_irq_mask(struct irq_data *data) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + unsigned long flags; + u32 mask; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK); + mask &= ~BIT(data->hwirq); + iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK); + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); +} + +static void hlwd_gpio_irq_unmask(struct irq_data *data) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + unsigned long flags; + u32 mask; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK); + mask |= BIT(data->hwirq); + iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK); + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); +} + +static void hlwd_gpio_irq_enable(struct irq_data *data) +{ + hlwd_gpio_irq_ack(data); + hlwd_gpio_irq_unmask(data); +} + +static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + unsigned long flags; + u32 level; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + + switch (flow_type) { + case IRQ_TYPE_LEVEL_HIGH: + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + level |= BIT(data->hwirq); + iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); + break; + case IRQ_TYPE_LEVEL_LOW: + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + level &= ~BIT(data->hwirq); + iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); + break; + default: + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); + return -EINVAL; + } + + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); + return 0; +} + static int hlwd_gpio_probe(struct platform_device *pdev) { struct hlwd_gpio *hlwd; @@ -92,7 +190,43 @@ static int hlwd_gpio_probe(struct platform_device *pdev) ngpios = 32; hlwd->gpioc.ngpio = ngpios; - return devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd); + res = devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd); + if (res) + return res; + + /* Mask and ack all interrupts */ + iowrite32be(0, hlwd->regs + HW_GPIOB_INTMASK); + iowrite32be(0xffffffff, hlwd->regs + HW_GPIOB_INTFLAG); + + /* + * If this GPIO controller is not marked as an interrupt controller in + * the DT, return. + */ + if (!of_property_read_bool(pdev->dev.of_node, "interrupt-controller")) + return 0; + + hlwd->irq = platform_get_irq(pdev, 0); + if (hlwd->irq < 0) { + dev_info(&pdev->dev, "platform_get_irq returned %d\n", + hlwd->irq); + return hlwd->irq; + } + + hlwd->irqc.name = dev_name(&pdev->dev); + hlwd->irqc.irq_mask = hlwd_gpio_irq_mask; + hlwd->irqc.irq_unmask = hlwd_gpio_irq_unmask; + hlwd->irqc.irq_enable = hlwd_gpio_irq_enable; + hlwd->irqc.irq_set_type = hlwd_gpio_irq_set_type; + + res = gpiochip_irqchip_add(&hlwd->gpioc, &hlwd->irqc, 0, + handle_level_irq, IRQ_TYPE_NONE); + if (res) + return res; + + gpiochip_set_chained_irqchip(&hlwd->gpioc, &hlwd->irqc, + hlwd->irq, hlwd_gpio_irqhandler); + + return 0; } static const struct of_device_id hlwd_gpio_match[] = { From patchwork Mon Jan 14 19:14:50 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?J=2E_Neusch=C3=A4fer?= X-Patchwork-Id: 1024747 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-gpio-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=gmx.net Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 43djpz26P8z9sDL for ; Tue, 15 Jan 2019 06:15:23 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726892AbfANTPK (ORCPT ); Mon, 14 Jan 2019 14:15:10 -0500 Received: from mout.gmx.net ([212.227.17.21]:38791 "EHLO mout.gmx.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726728AbfANTPK (ORCPT ); Mon, 14 Jan 2019 14:15:10 -0500 Received: from longitude ([109.90.232.48]) by mail.gmx.com (mrgmx101 [212.227.17.168]) with ESMTPSA (Nemesis) id 0LeiJ8-1h2EgU1QMA-00qTQn; Mon, 14 Jan 2019 20:15:08 +0100 From: =?utf-8?q?Jonathan_Neusch=C3=A4fer?= To: linux-gpio@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Linus Walleij , Bartosz Golaszewski , =?utf-8?q?Jonathan_Neusch=C3=A4fer?= Subject: [PATCH v2 2/2] gpio: hlwd: Implement edge trigger emulation Date: Mon, 14 Jan 2019 20:14:50 +0100 Message-Id: <20190114191450.11597-3-j.neuschaefer@gmx.net> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190114191450.11597-1-j.neuschaefer@gmx.net> References: <20190114191450.11597-1-j.neuschaefer@gmx.net> MIME-Version: 1.0 X-Provags-ID: V03:K1:mP8bCeC010qffF5qftc4yIN0iStt1NP0xDOUSSNG2lPoC9EV22X 58Aj6+5Rl3GGZ0zd6jftZ2VKkgAWoesdzyDKzNG/uyXZgf1eRlMLfjfQqCZHYXpjSpLXUkl F0+eCgBgZwJ4UfODytQIrQ7lnZkokvkfJc8Bl5u66mDiQIzalvnRJ9CkKZ3iVtpip8DBJI9 Xzf9U2ETvvKPu23NAPg2g== X-Spam-Flag: NO X-UI-Out-Filterresults: notjunk:1; V03:K0:ojxmK40wKwk=:N9afsRF1zy4LDGPowC26Wo Pa2xWt4KkLLmJHuDoa06QV/OqgA/GBWkO3GfpHSVxI1T+B+3rqxQj9Xs3v2tzvX8tr6txcMqk 5aMaIjCv91zdD6ylx2NAM/8hxA2tyxrsfRcXcmxGK51GVsQGKzWvwPF8vu8lqZZtoomSvLSan qyv9+/E52JxYG7ko06psteWn87haDYOjCGNIE8gxbngx3OXElPiYOTTbDjwoozhM7gLRXU2dU JR93wxOkPWF55D5nkpQqonaO0JKAM9EndVdINYaJMx+F+m3uyzBznHVskXofkVrl2s91D1AUk VRjOcaqkZFE6PGyuFQJHtt1dneAr5bIxJF2594F6BvG924CSUiFV9X6/OgWqIitLAx52cfZBm VKclQAfXyGAXGhBSKRJkfPTwCJz6ZKX6DNF3mhxERCrcMVvaVX/4ZzpMeVxmzGQNmSaLXv3GJ uIIlF6hcivQkaGUKBaiDhiE+AkziQ6EFGgdFRVmSuTHb1SzHFED9nAwmFtRWYeqwX5m+V7jTK e+3zT7rXtB2yT4WOoTV9RtG1ATE3VxLzgcONGpQDZUENzB68HnEqAR5xeRlaxvYC9NQSehu9b G6byhWEJcBrIbyoZot/DnFlnSmy8qsd4E2rXfZUImqeYaHLgu39Qynd7fxqslRR580xYS253L 5dYiAUQuTCh3vjIdK9c7rGQc9VBeCp9b8/ezEUYKNlLHbhVHtI5hOull9ZReTsu3hHhuN9io8 XWT1rzGjZVAuFMKHwS4DvrKlyMpO3SZ2yv0TUNKRt+fWymJQpQl2d6Ojb7zLjG8FFZu7z/ZW2 9eUXNdVIIEeckkYHOD/6cYX1zdEDc3bmaZvbKAo4SGeSxD+xRNN4rVaSY6SqtHQlX63N9XrnA owJj/oFF5Fk4dFyOKX0ko/Z0epgKH+zpeprAo8QkblTlyMqZGHuVzpqx5hByE7etww86AfukN dtboGKO28nA== Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Like the Spreadtrum EIC driver[1], this driver needs to emulate edge triggered interrupts to support the generic gpio-keys driver. [1]: https://www.spinics.net/lists/kernel/msg2764576.html Signed-off-by: Jonathan Neuschäfer --- v2: - Changed register addresses to io{read,write}32be() instead of hlwd->gpioc.{read,write}_reg, as suggested by Linus Walleij. v1: https://lore.kernel.org/lkml/20190113135844.13109-3-j.neuschaefer@gmx.net/ --- drivers/gpio/gpio-hlwd.c | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/drivers/gpio/gpio-hlwd.c b/drivers/gpio/gpio-hlwd.c index 61983c115856..a7b17897356e 100644 --- a/drivers/gpio/gpio-hlwd.c +++ b/drivers/gpio/gpio-hlwd.c @@ -51,6 +51,8 @@ struct hlwd_gpio { struct irq_chip irqc; void __iomem *regs; int irq; + u32 edge_emulation; + u32 rising_edge, falling_edge; }; static void hlwd_gpio_irqhandler(struct irq_desc *desc) @@ -61,10 +63,36 @@ static void hlwd_gpio_irqhandler(struct irq_desc *desc) unsigned long flags; unsigned long pending; int hwirq; + u32 emulated_pending; spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG); pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK); + + /* Treat interrupts due to edge trigger emulation separately */ + emulated_pending = hlwd->edge_emulation & pending; + pending &= ~emulated_pending; + if (emulated_pending) { + u32 level, rising, falling; + + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + rising = level & emulated_pending; + falling = ~level & emulated_pending; + + /* Invert the levels */ + iowrite32be(level ^ emulated_pending, + hlwd->regs + HW_GPIOB_INTLVL); + + /* Ack all emulated-edge interrupts */ + iowrite32be(emulated_pending, hlwd->regs + HW_GPIOB_INTFLAG); + + /* Signal interrupts only on the correct edge */ + rising &= hlwd->rising_edge; + falling &= hlwd->falling_edge; + + /* Mark emulated interrupts as pending */ + pending |= rising | falling; + } spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); chained_irq_enter(chip, desc); @@ -120,6 +148,27 @@ static void hlwd_gpio_irq_enable(struct irq_data *data) hlwd_gpio_irq_unmask(data); } +static void hlwd_gpio_irq_setup_emulation(struct hlwd_gpio *hlwd, int hwirq, + unsigned int flow_type) +{ + u32 level, state; + + /* Set the trigger level to the inactive level */ + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + state = ioread32be(hlwd->regs + HW_GPIOB_IN) & BIT(hwirq); + level &= ~BIT(hwirq); + level |= state ^ BIT(hwirq); + iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); + + hlwd->edge_emulation |= BIT(hwirq); + hlwd->rising_edge &= ~BIT(hwirq); + hlwd->falling_edge &= ~BIT(hwirq); + if (flow_type & IRQ_TYPE_EDGE_RISING) + hlwd->rising_edge |= BIT(hwirq); + if (flow_type & IRQ_TYPE_EDGE_FALLING) + hlwd->falling_edge |= BIT(hwirq); +} + static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) { struct hlwd_gpio *hlwd = @@ -129,6 +178,8 @@ static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + hlwd->edge_emulation &= ~BIT(data->hwirq); + switch (flow_type) { case IRQ_TYPE_LEVEL_HIGH: level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); @@ -140,6 +191,11 @@ static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) level &= ~BIT(data->hwirq); iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); break; + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + hlwd_gpio_irq_setup_emulation(hlwd, data->hwirq, flow_type); + break; default: spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); return -EINVAL;