From patchwork Tue Oct 24 19:54:50 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Berger X-Patchwork-Id: 10025457 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id B00B260375 for ; Tue, 24 Oct 2017 19:58:50 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9E86628A68 for ; Tue, 24 Oct 2017 19:58:50 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9263928A6C; Tue, 24 Oct 2017 19:58:50 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, DKIM_VALID, FREEMAIL_FROM, RCVD_IN_DNSWL_MED autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id B8B1128A68 for ; Tue, 24 Oct 2017 19:58:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=Di0n+mJJ8IuAYh8qvC0nKJQ3nzyl/zFzEMq6QmsCyvM=; b=SazErXoCBQd114k8H1Dnz+j6nU AHbrt70p0692h5kOBrfh3siBD5yzuCYyNZ4WsTb/yZ7w/NhQYmfI0gDJQU/VvoFBFShDYbXr0aTJu CXAaPQFO8JjFa9EFQVyMC33eNe/uKUweszAO0gjjWI+J9cRUUygAnT8NCMs2vwK59AL92D8d6DiTH 3zOm+JbueGryS89aLUlo6MqiOSK/bQrIbH8k/KFmCVZ8n+HzGm+x4ycrDhaimAIza583dINawyUwY zUvETFBntFp4qd3FAvdI49xynJR5zTwCW/t+0n179jOVSEM3wdNuN7yvlHRap1W7k8VJsMWrhF/7P qCnYK6/A==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1e75Ld-0001xV-7d; Tue, 24 Oct 2017 19:58:49 +0000 Received: from mail-qk0-x244.google.com ([2607:f8b0:400d:c09::244]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1e75J1-0008Nd-0D for linux-arm-kernel@lists.infradead.org; Tue, 24 Oct 2017 19:56:23 +0000 Received: by mail-qk0-x244.google.com with SMTP id o187so27812906qke.7 for ; Tue, 24 Oct 2017 12:55:48 -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=wHmv+bnxLam91M/d6kMu6gfUViIeuQKP804l27wByr4=; b=HmbxsEaZIWdl9hOG6eGJ12rNlGw8qgFIE/n+zXUE/hAWPOyFpOAi329kweEXhj7duc hUwMpJyHY+MFaPzeEjejwjUP2svDwAyAEgvpFpSjJJW7q5VsuoX6c6jWcZgfKUejzkJQ +f0F9pBn6P3mviav5veidf8WUFB9mhghjgDzFAdMbzg6BIl9MzVYx8tt1gtHQowuxevd SqcSewMTcmLaH0DKyA3TQX4ei08nb4PsM3N2ncmj5HbLarHN1AfEVnvLapHtTDAVkGg3 ZperdKIP5CI44jv5x/rh3VQcGT1N972fGb8ENmGwXIbOnt5i3SCGjJ9gP5iLe7xdrlJB bJ8A== 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=wHmv+bnxLam91M/d6kMu6gfUViIeuQKP804l27wByr4=; b=qr8a2+AVJVskSzpGfcCkIBRO/gVmSI3TKju0eUsJIP2Jy7b45ogpnfrxuTi1/rT7ZV 4QOboYKmsMFWHTxmTCzNubviOrJs95fixcDucWKKATzJPrNelMNd+flz7Ch+fBt2/oRB xjkuDKgBEZA3eNHBpJCoFFQf5zq9gt0Ea07Kh6s0eQiZJjbJ1+FUfmCBHAgL0G0E61Ma cP7KIHHLXRhrdtcwox1M5aXpJbjlFmpfzs5D9EvMtKXECqsH2wTd1tlOKLGbNOxe3MUx fU/09Sy+bOZ73MZOIFasLS9lpNKI6PGfXCMynGKfrAgW1uHNky9ADLx8A7dnaWX5eIhJ s4JQ== X-Gm-Message-State: AMCzsaWY5ZYl5PJK9DyVVwtLJQdyO7QlCf4/PK8bsynuz+hphjAm+h75 SJqUxeR2X/xiJqtIHwFYpQM= X-Google-Smtp-Source: ABhQp+S+JzRypMrMMXEBPoztDGkGMcxcMd4wQm4yjMm+uFH1fB4zA+t85WbJPAH+xa4Dj92LC7h6qw== X-Received: by 10.55.221.198 with SMTP id u67mr25702384qku.174.1508874947218; Tue, 24 Oct 2017 12:55:47 -0700 (PDT) Received: from stb-bld-02.irv.broadcom.com ([192.19.255.250]) by smtp.gmail.com with ESMTPSA id s27sm794249qtj.3.2017.10.24.12.55.45 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 24 Oct 2017 12:55:46 -0700 (PDT) From: Doug Berger To: Gregory Fong Subject: [PATCH v2 6/7] gpio: brcmstb: consolidate interrupt domains Date: Tue, 24 Oct 2017 12:54:50 -0700 Message-Id: <20171024195451.30535-7-opendmb@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20171024195451.30535-1-opendmb@gmail.com> References: <20171024195451.30535-1-opendmb@gmail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20171024_125607_881022_DADBB92C X-CRM114-Status: GOOD ( 24.75 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Florian Fainelli , Linus Walleij , linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, Doug Berger , bcm-kernel-feedback-list@broadcom.com, Brian Norris , linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP The GPIOLIB IRQ chip helpers were very appealing, but badly broke the 1:1 mapping between a GPIO controller's device_node and its interrupt domain. When another device-tree node references a GPIO device as its interrupt parent, the irq_create_of_mapping() function looks for the irq domain of the GPIO device and since all bank irq domains reference the same GPIO device node it always resolves to the irq domain of the first bank regardless of which bank the number of the GPIO should resolve. This domain can only map hwirq numbers 0-31 so interrupts on GPIO above that can't be mapped by the device-tree. This commit effectively reverts the patch from Gregory Fong [1] that was accepted upstream and replaces it with a consolidated irq domain implementation with one larger interrupt domain per GPIO controller instance spanning multiple GPIO banks based on an earlier patch [2] also submitted by Gregory Fong. [1] https://patchwork.kernel.org/patch/6921561/ [2] https://patchwork.kernel.org/patch/6347811/ Fixes: 19a7b6940b78 ("gpio: brcmstb: Add interrupt and wakeup source support") Signed-off-by: Doug Berger Reviewed-by: Gregory Fong Reviewed-by: Florian Fainelli --- drivers/gpio/Kconfig | 2 +- drivers/gpio/gpio-brcmstb.c | 188 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 146 insertions(+), 44 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3f80f167ed56..373adab79e4c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -139,7 +139,7 @@ config GPIO_BRCMSTB default y if (ARCH_BRCMSTB || BMIPS_GENERIC) depends on OF_GPIO && (ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST) select GPIO_GENERIC - select GPIOLIB_IRQCHIP + select IRQ_DOMAIN help Say yes here to enable GPIO support for Broadcom STB (BCM7XXX) SoCs. diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index 183863902f7f..9a8c603625a3 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -38,20 +38,22 @@ struct brcmstb_gpio_bank { struct gpio_chip gc; struct brcmstb_gpio_priv *parent_priv; u32 width; - struct irq_chip irq_chip; }; struct brcmstb_gpio_priv { struct list_head bank_list; void __iomem *reg_base; struct platform_device *pdev; + struct irq_domain *irq_domain; + struct irq_chip irq_chip; int parent_irq; int gpio_base; + int num_gpios; int parent_wake_irq; struct notifier_block reboot_notifier; }; -#define MAX_GPIO_PER_BANK 32 +#define MAX_GPIO_PER_BANK 32 #define GPIO_BANK(gpio) ((gpio) >> 5) /* assumes MAX_GPIO_PER_BANK is a multiple of 2 */ #define GPIO_BIT(gpio) ((gpio) & (MAX_GPIO_PER_BANK - 1)) @@ -78,24 +80,42 @@ brcmstb_gpio_get_active_irqs(struct brcmstb_gpio_bank *bank) return status; } +static int brcmstb_gpio_hwirq_to_offset(irq_hw_number_t hwirq, + struct brcmstb_gpio_bank *bank) +{ + return hwirq - (bank->gc.base - bank->parent_priv->gpio_base); +} + static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank, - unsigned int offset, bool enable) + unsigned int hwirq, bool enable) { struct gpio_chip *gc = &bank->gc; struct brcmstb_gpio_priv *priv = bank->parent_priv; + u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(hwirq, bank)); u32 imask; unsigned long flags; spin_lock_irqsave(&gc->bgpio_lock, flags); imask = gc->read_reg(priv->reg_base + GIO_MASK(bank->id)); if (enable) - imask |= BIT(offset); + imask |= mask; else - imask &= ~BIT(offset); + imask &= ~mask; gc->write_reg(priv->reg_base + GIO_MASK(bank->id), imask); spin_unlock_irqrestore(&gc->bgpio_lock, flags); } +static int brcmstb_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc); + /* gc_offset is relative to this gpio_chip; want real offset */ + int hwirq = offset + (gc->base - priv->gpio_base); + + if (hwirq >= priv->num_gpios) + return -ENXIO; + return irq_create_mapping(priv->irq_domain, hwirq); +} + /* -------------------- IRQ chip functions -------------------- */ static void brcmstb_gpio_irq_mask(struct irq_data *d) @@ -119,7 +139,7 @@ static void brcmstb_gpio_irq_ack(struct irq_data *d) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); struct brcmstb_gpio_priv *priv = bank->parent_priv; - u32 mask = BIT(d->hwirq); + u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank)); gc->write_reg(priv->reg_base + GIO_STAT(bank->id), mask); } @@ -129,7 +149,7 @@ static int brcmstb_gpio_irq_set_type(struct irq_data *d, unsigned int type) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); struct brcmstb_gpio_priv *priv = bank->parent_priv; - u32 mask = BIT(d->hwirq); + u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank)); u32 edge_insensitive, iedge_insensitive; u32 edge_config, iedge_config; u32 level, ilevel; @@ -226,18 +246,20 @@ static irqreturn_t brcmstb_gpio_wake_irq_handler(int irq, void *data) static void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank) { struct brcmstb_gpio_priv *priv = bank->parent_priv; - struct irq_domain *irq_domain = bank->gc.irqdomain; + struct irq_domain *domain = priv->irq_domain; + int hwbase = bank->gc.base - priv->gpio_base; unsigned long status; while ((status = brcmstb_gpio_get_active_irqs(bank))) { - int bit; + unsigned int irq, offset; - for_each_set_bit(bit, &status, 32) { - if (bit >= bank->width) + for_each_set_bit(offset, &status, 32) { + if (offset >= bank->width) dev_warn(&priv->pdev->dev, "IRQ for invalid GPIO (bank=%d, offset=%d)\n", - bank->id, bit); - generic_handle_irq(irq_find_mapping(irq_domain, bit)); + bank->id, offset); + irq = irq_linear_revmap(domain, hwbase + offset); + generic_handle_irq(irq); } } } @@ -245,8 +267,7 @@ static void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank) /* Each UPG GIO block has one IRQ for all banks */ static void brcmstb_gpio_irq_handler(struct irq_desc *desc) { - struct gpio_chip *gc = irq_desc_get_handler_data(desc); - struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc); + struct brcmstb_gpio_priv *priv = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); struct brcmstb_gpio_bank *bank; @@ -272,6 +293,63 @@ static int brcmstb_gpio_reboot(struct notifier_block *nb, return NOTIFY_DONE; } +static struct brcmstb_gpio_bank *brcmstb_gpio_hwirq_to_bank( + struct brcmstb_gpio_priv *priv, irq_hw_number_t hwirq) +{ + struct brcmstb_gpio_bank *bank; + int i = 0; + + /* banks are in descending order */ + list_for_each_entry_reverse(bank, &priv->bank_list, node) { + i += bank->gc.ngpio; + if (hwirq < i) + return bank; + } + return NULL; +} + +/* + * 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 brcmstb_gpio_irq_lock_class; + + +static int brcmstb_gpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct brcmstb_gpio_priv *priv = d->host_data; + struct brcmstb_gpio_bank *bank = + brcmstb_gpio_hwirq_to_bank(priv, hwirq); + struct platform_device *pdev = priv->pdev; + int ret; + + if (!bank) + return -EINVAL; + + dev_dbg(&pdev->dev, "Mapping irq %d for gpio line %d (bank %d)\n", + irq, (int)hwirq, bank->id); + ret = irq_set_chip_data(irq, &bank->gc); + if (ret < 0) + return ret; + irq_set_lockdep_class(irq, &brcmstb_gpio_irq_lock_class); + irq_set_chip_and_handler(irq, &priv->irq_chip, handle_level_irq); + irq_set_noprobe(irq); + return 0; +} + +static void brcmstb_gpio_irq_unmap(struct irq_domain *d, unsigned int irq) +{ + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); +} + +static const struct irq_domain_ops brcmstb_gpio_irq_domain_ops = { + .map = brcmstb_gpio_irq_map, + .unmap = brcmstb_gpio_irq_unmap, + .xlate = irq_domain_xlate_twocell, +}; + /* Make sure that the number of banks matches up between properties */ static int brcmstb_gpio_sanity_check_banks(struct device *dev, struct device_node *np, struct resource *res) @@ -293,13 +371,25 @@ static int brcmstb_gpio_remove(struct platform_device *pdev) { struct brcmstb_gpio_priv *priv = platform_get_drvdata(pdev); struct brcmstb_gpio_bank *bank; - int ret = 0; + int offset, ret = 0, virq; if (!priv) { dev_err(&pdev->dev, "called %s without drvdata!\n", __func__); return -EFAULT; } + if (priv->parent_irq > 0) + irq_set_chained_handler_and_data(priv->parent_irq, NULL, NULL); + + /* Remove all IRQ mappings and delete the domain */ + if (priv->irq_domain) { + for (offset = 0; offset < priv->num_gpios; offset++) { + virq = irq_find_mapping(priv->irq_domain, offset); + irq_dispose_mapping(virq); + } + irq_domain_remove(priv->irq_domain); + } + /* * You can lose return values below, but we report all errors, and it's * more important to actually perform all of the steps. @@ -347,26 +437,24 @@ static int brcmstb_gpio_of_xlate(struct gpio_chip *gc, return offset; } -/* Before calling, must have bank->parent_irq set and gpiochip registered */ +/* priv->parent_irq and priv->num_gpios must be set before calling */ static int brcmstb_gpio_irq_setup(struct platform_device *pdev, - struct brcmstb_gpio_bank *bank) + struct brcmstb_gpio_priv *priv) { - struct brcmstb_gpio_priv *priv = bank->parent_priv; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; int err; - bank->irq_chip.name = dev_name(dev); - bank->irq_chip.irq_mask = brcmstb_gpio_irq_mask; - bank->irq_chip.irq_unmask = brcmstb_gpio_irq_unmask; - bank->irq_chip.irq_ack = brcmstb_gpio_irq_ack; - bank->irq_chip.irq_set_type = brcmstb_gpio_irq_set_type; - - /* Ensures that all non-wakeup IRQs are disabled at suspend */ - bank->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND; + priv->irq_domain = + irq_domain_add_linear(np, priv->num_gpios, + &brcmstb_gpio_irq_domain_ops, + priv); + if (!priv->irq_domain) { + dev_err(dev, "Couldn't allocate IRQ domain\n"); + return -ENXIO; + } - if (IS_ENABLED(CONFIG_PM_SLEEP) && !priv->parent_wake_irq && - of_property_read_bool(np, "wakeup-source")) { + if (of_property_read_bool(np, "wakeup-source")) { priv->parent_wake_irq = platform_get_irq(pdev, 1); if (priv->parent_wake_irq < 0) { priv->parent_wake_irq = 0; @@ -387,7 +475,7 @@ static int brcmstb_gpio_irq_setup(struct platform_device *pdev, if (err < 0) { dev_err(dev, "Couldn't request wake IRQ"); - return err; + goto out_free_domain; } priv->reboot_notifier.notifier_call = @@ -396,17 +484,28 @@ static int brcmstb_gpio_irq_setup(struct platform_device *pdev, } } + priv->irq_chip.name = dev_name(dev); + priv->irq_chip.irq_disable = brcmstb_gpio_irq_mask; + priv->irq_chip.irq_mask = brcmstb_gpio_irq_mask; + priv->irq_chip.irq_unmask = brcmstb_gpio_irq_unmask; + priv->irq_chip.irq_ack = brcmstb_gpio_irq_ack; + priv->irq_chip.irq_set_type = brcmstb_gpio_irq_set_type; + + /* Ensures that all non-wakeup IRQs are disabled at suspend */ + priv->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND; + if (priv->parent_wake_irq) - bank->irq_chip.irq_set_wake = brcmstb_gpio_irq_set_wake; + priv->irq_chip.irq_set_wake = brcmstb_gpio_irq_set_wake; - err = gpiochip_irqchip_add(&bank->gc, &bank->irq_chip, 0, - handle_level_irq, IRQ_TYPE_NONE); - if (err) - return err; - gpiochip_set_chained_irqchip(&bank->gc, &bank->irq_chip, - priv->parent_irq, brcmstb_gpio_irq_handler); + irq_set_chained_handler_and_data(priv->parent_irq, + brcmstb_gpio_irq_handler, priv); return 0; + +out_free_domain: + irq_domain_remove(priv->irq_domain); + + return err; } static int brcmstb_gpio_probe(struct platform_device *pdev) @@ -511,6 +610,8 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) gc->of_xlate = brcmstb_gpio_of_xlate; /* not all ngpio lines are valid, will use bank width later */ gc->ngpio = MAX_GPIO_PER_BANK; + if (priv->parent_irq > 0) + gc->to_irq = brcmstb_gpio_to_irq; /* * Mask all interrupts by default, since wakeup interrupts may @@ -526,12 +627,6 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) } gpio_base += gc->ngpio; - if (priv->parent_irq > 0) { - err = brcmstb_gpio_irq_setup(pdev, bank); - if (err) - goto fail; - } - dev_dbg(dev, "bank=%d, base=%d, ngpio=%d, width=%d\n", bank->id, gc->base, gc->ngpio, bank->width); @@ -541,6 +636,13 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) num_banks++; } + priv->num_gpios = gpio_base - priv->gpio_base; + if (priv->parent_irq > 0) { + err = brcmstb_gpio_irq_setup(pdev, priv); + if (err) + goto fail; + } + dev_info(dev, "Registered %d banks (GPIO(s): %d-%d)\n", num_banks, priv->gpio_base, gpio_base - 1);