From patchwork Fri Sep 1 18:57:35 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 808954 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-tegra-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="jshVj8d3"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3xkT6x59xQz9s7c for ; Sat, 2 Sep 2017 04:59:05 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752292AbdIAS6b (ORCPT ); Fri, 1 Sep 2017 14:58:31 -0400 Received: from mail-wm0-f66.google.com ([74.125.82.66]:34847 "EHLO mail-wm0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752569AbdIAS6H (ORCPT ); Fri, 1 Sep 2017 14:58:07 -0400 Received: by mail-wm0-f66.google.com with SMTP id e204so964328wma.2; Fri, 01 Sep 2017 11:58:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=YG7Ugx3dX0q5S01R7qpc3WHptGIboJ/nl2v2wFURo+0=; b=jshVj8d3o7a1g0gxwJGW/ctHKBG4lBdgLFA5hEToL/oitA4oUz2ec40Iz7nS0oYnq+ 1QqKMrDcFXAaWDMerOUtHmFcfvOYROoCuShvq6dKzVzcdq6ezOzXqX4x38jELUu6pWfl i8l8DXdttwmTGBZuxS8oUPLFhOET3Bd/OoiYCyRVtEGb5ZJwj8h5Rm/p3zl8EEAYCKdF Y1lIQTSeTdFBfweF6oUdfks4ifZdWITe30yXqs/IVl7wa2FQkwF+rdN2874QxyofiYfe nM17EByU5kFOedg23vcpgcJKVPtdHpovlEFaHviufjoBWI3m8HCtgbMVNNmBEDq3PUtc 5x3g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=YG7Ugx3dX0q5S01R7qpc3WHptGIboJ/nl2v2wFURo+0=; b=d2gbhgrBRh7gA9j/TV76sqV4C1H+90wOjQ1DEuuPatMDNFb71nSuDHZoGzhCCqKAKW TzaGx0dyIgZJTk6Y8PT/s9fzXv18KHg7AUNAu8UB7e57DB8ztjQOQfYSsOXIK4NUHd+D JOMOQAcPl2kKECgPrE+pt1JalmjnfbjsUbWS3obWvLEMB6QKLdFtKXn/LP54ABtGhJt6 vGxPrCMMyVm280yw/pVHYx3WtDq+SPMhA4hwCItZVXQpOzYoo83NOxYIBmUPpYWlmpXG 0vPfBm6F3Dd/ueGCkQTt2DwAo4sju6WzjzpfEvYtgiDMGLW7yuVZJSaJt0oAwloA9D44 wgXw== X-Gm-Message-State: AHPjjUiSVIicY09srBtuIP6k1fTWtezPMa2MNvp6xGG9PmFaYqhq0HZD VD4CjEjkLNYG8g== X-Google-Smtp-Source: ADKCNb7+p8kPzonGbrWSYUva0mvM/h8dG5NQT1nmKnL9KFwAfXd5/L70whJePAFIwKrlrpIg0M/hLA== X-Received: by 10.28.87.1 with SMTP id l1mr899216wmb.99.1504292285558; Fri, 01 Sep 2017 11:58:05 -0700 (PDT) Received: from localhost (p200300E41BD6D60076D02BFFFE273F51.dip0.t-ipconnect.de. [2003:e4:1bd6:d600:76d0:2bff:fe27:3f51]) by smtp.gmail.com with ESMTPSA id l4sm653323wrb.70.2017.09.01.11.58.04 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 01 Sep 2017 11:58:04 -0700 (PDT) From: Thierry Reding To: Linus Walleij Cc: Jonathan Hunter , linux-gpio@vger.kernel.org, linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 15/16] gpio: tegra: Use banked GPIO infrastructure Date: Fri, 1 Sep 2017 20:57:35 +0200 Message-Id: <20170901185736.28051-16-thierry.reding@gmail.com> X-Mailer: git-send-email 2.13.3 In-Reply-To: <20170901185736.28051-1-thierry.reding@gmail.com> References: <20170901185736.28051-1-thierry.reding@gmail.com> Sender: linux-tegra-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-tegra@vger.kernel.org From: Thierry Reding Convert the Tegra GPIO driver to use the banked GPIO infrastructure, which simplifies some parts of the driver. Signed-off-by: Thierry Reding --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-tegra.c | 203 ++++++++++++++++++++++------------------------ 2 files changed, 98 insertions(+), 106 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f9e22c6fdd02..9364f037fe86 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -439,6 +439,7 @@ config GPIO_TEGRA default ARCH_TEGRA depends on ARCH_TEGRA || COMPILE_TEST depends on OF_GPIO + select GPIOLIB_IRQCHIP help Say yes here to support GPIO pins on NVIDIA Tegra SoCs. diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index fbaf974277df..250cebc3ab60 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -67,8 +67,8 @@ struct tegra_gpio_info; struct tegra_gpio_bank { - unsigned int bank; - unsigned int irq; + struct gpio_bank bank; + unsigned int index; spinlock_t lvl_lock[4]; spinlock_t dbc_lock[4]; /* Lock for updating debounce count register */ #ifdef CONFIG_PM_SLEEP @@ -84,6 +84,11 @@ struct tegra_gpio_bank { struct tegra_gpio_info *tgi; }; +static struct tegra_gpio_bank *to_tegra_gpio_bank(struct gpio_bank *bank) +{ + return container_of(bank, struct tegra_gpio_bank, bank); +} + struct tegra_gpio_soc_config { bool debounce_supported; u32 bank_stride; @@ -98,9 +103,14 @@ struct tegra_gpio_info { const struct tegra_gpio_soc_config *soc; struct gpio_chip gc; struct irq_chip ic; - u32 bank_count; }; +static inline struct tegra_gpio_info * +to_tegra_gpio_info(struct gpio_chip *chip) +{ + return container_of(chip, struct tegra_gpio_info, gc); +} + static inline void tegra_gpio_writel(struct tegra_gpio_info *tgi, u32 val, u32 reg) { @@ -264,8 +274,8 @@ static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) static void tegra_gpio_irq_ack(struct irq_data *d) { - struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); - struct tegra_gpio_info *tgi = bank->tgi; + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + struct tegra_gpio_info *tgi = to_tegra_gpio_info(chip); unsigned int gpio = d->hwirq; tegra_gpio_writel(tgi, 1 << GPIO_BIT(gpio), GPIO_INT_CLR(tgi, gpio)); @@ -273,8 +283,8 @@ static void tegra_gpio_irq_ack(struct irq_data *d) static void tegra_gpio_irq_mask(struct irq_data *d) { - struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); - struct tegra_gpio_info *tgi = bank->tgi; + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + struct tegra_gpio_info *tgi = to_tegra_gpio_info(chip); unsigned int gpio = d->hwirq; tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 0); @@ -282,8 +292,8 @@ static void tegra_gpio_irq_mask(struct irq_data *d) static void tegra_gpio_irq_unmask(struct irq_data *d) { - struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); - struct tegra_gpio_info *tgi = bank->tgi; + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + struct tegra_gpio_info *tgi = to_tegra_gpio_info(chip); unsigned int gpio = d->hwirq; tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 1); @@ -292,12 +302,15 @@ static void tegra_gpio_irq_unmask(struct irq_data *d) static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) { unsigned int gpio = d->hwirq, port = GPIO_PORT(gpio), lvl_type; - struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); - struct tegra_gpio_info *tgi = bank->tgi; + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + struct tegra_gpio_info *tgi = to_tegra_gpio_info(chip); + struct tegra_gpio_bank *bank; unsigned long flags; u32 val; int ret; + bank = &tgi->bank_info[GPIO_BANK(gpio)]; + switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_RISING: lvl_type = GPIO_INT_LVL_EDGE_RISING; @@ -352,52 +365,27 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) static void tegra_gpio_irq_shutdown(struct irq_data *d) { - struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); - struct tegra_gpio_info *tgi = bank->tgi; - unsigned int gpio = d->hwirq; + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); - gpiochip_unlock_as_irq(&tgi->gc, gpio); + gpiochip_unlock_as_irq(chip, d->hwirq); } -static void tegra_gpio_irq_handler(struct irq_desc *desc) +static void tegra_gpio_update_bank(struct gpio_bank *bank) { - unsigned int port, pin, gpio; - bool unmasked = false; - u32 lvl; - unsigned long sta; - struct irq_chip *chip = irq_desc_get_chip(desc); - struct tegra_gpio_bank *bank = irq_desc_get_handler_data(desc); - struct tegra_gpio_info *tgi = bank->tgi; - - chained_irq_enter(chip, desc); + struct tegra_gpio_info *tgi = to_tegra_gpio_info(bank->chip); + struct tegra_gpio_bank *b = to_tegra_gpio_bank(bank); + unsigned int port; for (port = 0; port < 4; port++) { - gpio = tegra_gpio_compose(bank->bank, port, 0); - sta = tegra_gpio_readl(tgi, GPIO_INT_STA(tgi, gpio)) & - tegra_gpio_readl(tgi, GPIO_INT_ENB(tgi, gpio)); - lvl = tegra_gpio_readl(tgi, GPIO_INT_LVL(tgi, gpio)); - - for_each_set_bit(pin, &sta, 8) { - tegra_gpio_writel(tgi, 1 << pin, - GPIO_INT_CLR(tgi, gpio)); - - /* if gpio is edge triggered, clear condition - * before executing the handler so that we don't - * miss edges - */ - if (!unmasked && lvl & (0x100 << pin)) { - unmasked = true; - chained_irq_exit(chip, desc); - } + unsigned int gpio = tegra_gpio_compose(b->index, port, 0); + u8 *pending = (u8 *)bank->pending; + u32 status, enable; - generic_handle_irq(irq_find_mapping(tgi->irq_domain, - gpio + pin)); - } - } - - if (!unmasked) - chained_irq_exit(chip, desc); + status = tegra_gpio_readl(tgi, GPIO_INT_STA(tgi, gpio)); + enable = tegra_gpio_readl(tgi, GPIO_INT_ENB(tgi, gpio)); + pending[port] = status & enable; + } } #ifdef CONFIG_PM_SLEEP @@ -410,7 +398,7 @@ static int tegra_gpio_resume(struct device *dev) local_irq_save(flags); - for (b = 0; b < tgi->bank_count; b++) { + for (b = 0; b < tgi->gc.num_banks; b++) { struct tegra_gpio_bank *bank = &tgi->bank_info[b]; for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { @@ -449,7 +437,7 @@ static int tegra_gpio_suspend(struct device *dev) unsigned int b, p; local_irq_save(flags); - for (b = 0; b < tgi->bank_count; b++) { + for (b = 0; b < tgi->gc.num_banks; b++) { struct tegra_gpio_bank *bank = &tgi->bank_info[b]; for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { @@ -484,20 +472,26 @@ static int tegra_gpio_suspend(struct device *dev) static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) { - struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); - unsigned int gpio = d->hwirq; + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + unsigned int gpio = d->hwirq, parent_irq; + struct tegra_gpio_bank *b; + struct gpio_bank *bank; u32 port, bit, mask; + bank = chip->banks[GPIO_BANK(gpio)]; + b = to_tegra_gpio_bank(bank); port = GPIO_PORT(gpio); bit = GPIO_BIT(gpio); mask = BIT(bit); if (enable) - bank->wake_enb[port] |= mask; + b->wake_enb[port] |= mask; else - bank->wake_enb[port] &= ~mask; + b->wake_enb[port] &= ~mask; + + parent_irq = chip->irq.parents[bank->parent_irq]; - return irq_set_irq_wake(bank->irq, enable); + return irq_set_irq_wake(parent_irq, enable); } #endif @@ -511,7 +505,7 @@ static int dbg_gpio_show(struct seq_file *s, void *unused) struct tegra_gpio_info *tgi = s->private; unsigned int i, j; - for (i = 0; i < tgi->bank_count; i++) { + for (i = 0; i < tgi->gc.num_banks; i++) { for (j = 0; j < 4; j++) { unsigned int gpio = tegra_gpio_compose(i, j, 0); @@ -561,17 +555,18 @@ static const struct dev_pm_ops tegra_gpio_pm_ops = { }; /* - * This lock class tells lockdep that GPIO irqs are in a different category + * This lock class tells lockdep that GPIO IRQs are in a different category * than their parents, so it won't report false recursion. */ -static struct lock_class_key gpio_lock_class; +static struct lock_class_key tegra_gpio_lock_class; static int tegra_gpio_probe(struct platform_device *pdev) { struct tegra_gpio_info *tgi; struct resource *res; struct tegra_gpio_bank *bank; - unsigned int gpio, i, j; + struct gpio_irq_chip *irq; + unsigned int i, j; int ret; tgi = devm_kzalloc(&pdev->dev, sizeof(*tgi), GFP_KERNEL); @@ -580,14 +575,20 @@ static int tegra_gpio_probe(struct platform_device *pdev) tgi->soc = of_device_get_match_data(&pdev->dev); tgi->dev = &pdev->dev; + irq = &tgi->gc.irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tgi->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tgi->regs)) + return PTR_ERR(tgi->regs); ret = platform_irq_count(pdev); if (ret < 0) return ret; - tgi->bank_count = ret; + tgi->gc.num_banks = ret; - if (!tgi->bank_count) { + if (!tgi->gc.num_banks) { dev_err(&pdev->dev, "Missing IRQ resource\n"); return -ENODEV; } @@ -602,7 +603,7 @@ static int tegra_gpio_probe(struct platform_device *pdev) tgi->gc.get_direction = tegra_gpio_get_direction; tgi->gc.to_irq = tegra_gpio_to_irq; tgi->gc.base = 0; - tgi->gc.ngpio = tgi->bank_count * 32; + tgi->gc.ngpio = tgi->gc.num_banks * 32; tgi->gc.parent = &pdev->dev; tgi->gc.of_node = pdev->dev.of_node; @@ -616,76 +617,66 @@ static int tegra_gpio_probe(struct platform_device *pdev) tgi->ic.irq_set_wake = tegra_gpio_irq_set_wake; #endif + irq = &tgi->gc.irq; + irq->chip = &tgi->ic; + irq->handler = handle_simple_irq; + irq->lock_key = &tegra_gpio_lock_class; + irq->default_type = IRQ_TYPE_NONE; + irq->parent_handler = gpio_irq_chip_banked_handler; + irq->update_bank = tegra_gpio_update_bank; + + irq->parents = devm_kcalloc(&pdev->dev, tgi->gc.num_banks, + sizeof(unsigned int), GFP_KERNEL); + if (!irq->parents) + return -ENOMEM; + + irq->num_parents = tgi->gc.num_banks; + platform_set_drvdata(pdev, tgi); if (tgi->soc->debounce_supported) tgi->gc.set_config = tegra_gpio_set_config; - tgi->bank_info = devm_kcalloc(&pdev->dev, tgi->bank_count, + tgi->bank_info = devm_kcalloc(&pdev->dev, tgi->gc.num_banks, sizeof(*tgi->bank_info), GFP_KERNEL); if (!tgi->bank_info) return -ENOMEM; - tgi->irq_domain = irq_domain_add_linear(pdev->dev.of_node, - tgi->gc.ngpio, - &irq_domain_simple_ops, NULL); - if (!tgi->irq_domain) - return -ENODEV; + tgi->gc.banks = devm_kcalloc(&pdev->dev, tgi->gc.num_banks, + sizeof(struct gpio_bank *), + GFP_KERNEL); + if (!tgi->gc.banks) + return -ENOMEM; - for (i = 0; i < tgi->bank_count; i++) { + for (i = 0; i < tgi->gc.num_banks; i++) { ret = platform_get_irq(pdev, i); if (ret < 0) { dev_err(&pdev->dev, "Missing IRQ resource: %d\n", ret); return ret; } + irq->parents[i] = ret; + bank = &tgi->bank_info[i]; - bank->bank = i; - bank->irq = ret; - bank->tgi = tgi; - } + bank->bank.chip = &tgi->gc; + bank->bank.parent_irq = i; + bank->bank.num_pins = 32; + bank->index = i; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - tgi->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(tgi->regs)) - return PTR_ERR(tgi->regs); + tgi->gc.banks[i] = &bank->bank; - for (i = 0; i < tgi->bank_count; i++) { for (j = 0; j < 4; j++) { - int gpio = tegra_gpio_compose(i, j, 0); + unsigned int gpio = tegra_gpio_compose(i, j, 0); tegra_gpio_writel(tgi, 0x00, GPIO_INT_ENB(tgi, gpio)); + spin_lock_init(&bank->lvl_lock[j]); + spin_lock_init(&bank->dbc_lock[j]); } } ret = devm_gpiochip_add_data(&pdev->dev, &tgi->gc, tgi); - if (ret < 0) { - irq_domain_remove(tgi->irq_domain); + if (ret < 0) return ret; - } - - for (gpio = 0; gpio < tgi->gc.ngpio; gpio++) { - int irq = irq_create_mapping(tgi->irq_domain, gpio); - /* No validity check; all Tegra GPIOs are valid IRQs */ - - bank = &tgi->bank_info[GPIO_BANK(gpio)]; - - irq_set_lockdep_class(irq, &gpio_lock_class); - irq_set_chip_data(irq, bank); - irq_set_chip_and_handler(irq, &tgi->ic, handle_simple_irq); - } - - for (i = 0; i < tgi->bank_count; i++) { - bank = &tgi->bank_info[i]; - - irq_set_chained_handler_and_data(bank->irq, - tegra_gpio_irq_handler, bank); - - for (j = 0; j < 4; j++) { - spin_lock_init(&bank->lvl_lock[j]); - spin_lock_init(&bank->dbc_lock[j]); - } - } tegra_gpio_debuginit(tgi);