From patchwork Mon Jun 24 13:25:28 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 167568 Delivered-To: patch@linaro.org Received: by 2002:a92:4782:0:0:0:0:0 with SMTP id e2csp4228512ilk; Mon, 24 Jun 2019 06:25:44 -0700 (PDT) X-Google-Smtp-Source: APXvYqzn2isTL0ZP+hnghAyjefX8rUvMhMMkM/FZ8apRazO51f4GUOA0BPY0xIlnp/g/IJd4hByw X-Received: by 2002:a17:90a:e38f:: with SMTP id b15mr25365929pjz.85.1561382744511; Mon, 24 Jun 2019 06:25:44 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1561382744; cv=none; d=google.com; s=arc-20160816; b=cFzRqdlWrFKIWYCcG5IsH90GBgWceB+O1oz3MdBj8cPv7UaO9H2CIpVq3MXZ/f9tMo 77TyvfaSHiMiJO+9/efmIqe3OcwNVf91qGrcu0Y4X0W+dyE3vCJT0B7x9LaP5sMxJN/8 QPmEzCorEu9wLKET66axq1UuJuYXhXBnX8h0myD5vZVC4sMqMsM9O5XpMIOzPR9uBgar 0VijvPaTBRAk9Jtds1ysqBy5W1SQlnwBnFJyLTQczuYPZRNM95EmDBCrpNgFUBgWLsie JyeL3dmPi7kHp23HBx7KHkcdu/e/BeE0mByljGD0va4K9gtc9PKtqp2zb60xUf9m8qLM momQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=dW+rkh+4s9B0h3XCfcFqX5dh1eXr11qqPi5yD44s2ro=; b=SUIjuJXGo9fywPKjL+0jl1AzgqNHAr/TUactfpKMjxLqgIFL3vP/I1pDjp0dYz7G6h IS3Ado3mi/MkR0yLR6VJV34mgdX/SpzzFTJYA18X2jRJ34QkS2St4sRZ6GPF6XO72FjF v8Dmel4a1OtrxZ4E3Lt2x7WboDE3vqsvOsvUl3ZBu46e702/xMcdfrcMY+1wZWcmpa9a Ao1jXq5URCumCO23D9EkFMQzlyLszVW5v2q/T/SYwGASyTeywJlWwQGsix4/dyDD2v0Z UfTKfotLbGHmSrtHi45fNpES3bnp046tbYtAeVSSYSz/2AK2dHTj7ry9Jn9fCdwkMOE9 mVVg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=PF6tD135; spf=pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-gpio-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id l1si10373860plb.302.2019.06.24.06.25.44; Mon, 24 Jun 2019 06:25:44 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=PF6tD135; spf=pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-gpio-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728523AbfFXNZn (ORCPT + 5 others); Mon, 24 Jun 2019 09:25:43 -0400 Received: from mail-lf1-f67.google.com ([209.85.167.67]:45616 "EHLO mail-lf1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728635AbfFXNZm (ORCPT ); Mon, 24 Jun 2019 09:25:42 -0400 Received: by mail-lf1-f67.google.com with SMTP id u10so9996664lfm.12 for ; Mon, 24 Jun 2019 06:25:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=dW+rkh+4s9B0h3XCfcFqX5dh1eXr11qqPi5yD44s2ro=; b=PF6tD135eg4bKLtnyu095U/Bsa/VhSoIYrWJdzttfLloeo5vOMpCyaduyA9iW/2nxw uH4aVDnWHtUENUJG7vi0/u0sgQFLgyp/TGb/JeDFVTUy6mxCTlpXPKpD6fgsoQc3li0Z 0bFq3WxYOGa+AScYLFXZKx/gfQHgJPdnMd3AFMMZBrQw+RIAWkCScRtTk4dOqNo6JB2I lF1MreWGzLax59SVOeIHvAZLeey4V+m182nFjVqiHyywqULV7OITAchldb+qyuN+FN7P +rN5u6FORwrmLmB5xfeq30fpQMayYhplhGFcI4YYTMadTAXsoiuinM4IIOy7NcLXr52e yjRg== 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:mime-version :content-transfer-encoding; bh=dW+rkh+4s9B0h3XCfcFqX5dh1eXr11qqPi5yD44s2ro=; b=mskznoQ5c4eUJYhRH1RDfYMq0meSyiBGA+mEFm0vdlr+qHuG7X0U5Vnp8m2FwyblXc wIPiFKclbxiy/iq/EEWuhDYzrWxLatM1gikRr3D9wzmihD6rzOuT+eJx3ovhBeEEsnF5 nVXMYDtjC2MfZKIlsqrUP5XMlKZl/iYLENbpakAtekc9G7qhyfCoEboGhsEleP2KCV9q jlOj8HsoAMNJkPbCLDAAqAie5gyhtqyzfBnvMsXmOTI9Rk2x3gTDZhVGWamVpo8HVHzu Bk2v3VYqne01HDBrFt3A45fD6QnHWpSW+A0o8akOkOXCCAcu4MoiGqdHDDvGeJqhQQR/ h0TQ== X-Gm-Message-State: APjAAAU2fciSSMvZ/fsK/2nZw7Xv3IGa4QEl6CcViyKyqX1ehXg0Sfq9 mgxyhTXkLJWXFQ1jPBZl8bqrNymd8bg= X-Received: by 2002:ac2:4c84:: with SMTP id d4mr74993029lfl.1.1561382739417; Mon, 24 Jun 2019 06:25:39 -0700 (PDT) Received: from localhost.bredbandsbolaget (c-22cd225c.014-348-6c756e10.bbcust.telenor.se. [92.34.205.34]) by smtp.gmail.com with ESMTPSA id t4sm416102ljh.9.2019.06.24.06.25.37 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Mon, 24 Jun 2019 06:25:38 -0700 (PDT) From: Linus Walleij To: linux-gpio@vger.kernel.org Cc: Bartosz Golaszewski , Linus Walleij , Thomas Gleixner , Marc Zyngier , Lina Iyer , Jon Hunter , Sowjanya Komatineni , Bitan Biswas , linux-tegra@vger.kernel.org, David Daney , Masahiro Yamada , Brian Masney , Thierry Reding Subject: [PATCH 1/4 v1] gpio: Add support for hierarchical IRQ domains Date: Mon, 24 Jun 2019 15:25:28 +0200 Message-Id: <20190624132531.6184-1-linus.walleij@linaro.org> X-Mailer: git-send-email 2.21.0 MIME-Version: 1.0 Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Hierarchical IRQ domains can be used to stack different IRQ controllers on top of each other. Bring hierarchical IRQ domains into the GPIOLIB core with the following basic idea: Drivers that need their interrupts handled hierarchically specify a callback to translate the child hardware IRQ and IRQ type for each GPIO offset to a parent hardware IRQ and parent hardware IRQ type. Users have to pass the callback, fwnode, and parent irqdomain before calling gpiochip_irqchip_add(). We use the new method of just filling in the struct gpio_irq_chip before adding the gpiochip for all hierarchical irqchips of this type. The code path for device tree is pretty straight-forward, while the code path for old boardfiles or anything else will be more convoluted requireing upfront allocation of the interrupts when adding the chip. One specific use-case where this can be useful is if a power management controller has top-level controls for wakeup interrupts. In such cases, the power management controller can be a parent to other interrupt controllers and program additional registers when an IRQ has its wake capability enabled or disabled. The hierarchical irqchip helper code will only be available when IRQ_DOMAIN_HIERARCHY is selected to GPIO chips using this should select or depend on that symbol. When using hierarchical IRQs, the parent interrupt controller must also be hierarchical all the way up to the top interrupt controller wireing directly into the CPU, so on systems that do not have this we can get rid of all the extra code for supporting hierarchical irqs. Cc: Thomas Gleixner Cc: Marc Zyngier Cc: Lina Iyer Cc: Jon Hunter Cc: Sowjanya Komatineni Cc: Bitan Biswas Cc: linux-tegra@vger.kernel.org Cc: David Daney Cc: Masahiro Yamada Cc: Brian Masney Signed-off-by: Thierry Reding Signed-off-by: Linus Walleij --- ChangeLog RFC->v1: - Tested on real hardware - Incorporate Thierry's idea to have a translation callback. He was right about this approach, I was wrong in insisting on IRQ maps. --- Documentation/driver-api/gpio/driver.rst | 120 ++++++++-- drivers/gpio/gpiolib.c | 285 ++++++++++++++++++++++- include/linux/gpio/driver.h | 46 ++++ 3 files changed, 426 insertions(+), 25 deletions(-) -- 2.21.0 diff --git a/Documentation/driver-api/gpio/driver.rst b/Documentation/driver-api/gpio/driver.rst index 1ce7fcd0f989..3099c7fbefdb 100644 --- a/Documentation/driver-api/gpio/driver.rst +++ b/Documentation/driver-api/gpio/driver.rst @@ -259,7 +259,7 @@ most often cascaded off a parent interrupt controller, and in some special cases the GPIO logic is melded with a SoC's primary interrupt controller. The IRQ portions of the GPIO block are implemented using an irq_chip, using -the header . So basically such a driver is utilizing two sub- +the header . So this combined driver is utilizing two sub- systems simultaneously: gpio and irq. It is legal for any IRQ consumer to request an IRQ from any irqchip even if it @@ -391,14 +391,108 @@ Infrastructure helpers for GPIO irqchips ---------------------------------------- To help out in handling the set-up and management of GPIO irqchips and the -associated irqdomain and resource allocation callbacks, the gpiolib has -some helpers that can be enabled by selecting the GPIOLIB_IRQCHIP Kconfig -symbol: - -- gpiochip_irqchip_add(): adds a chained cascaded irqchip to a gpiochip. It - will pass the struct gpio_chip* for the chip to all IRQ callbacks, so the - callbacks need to embed the gpio_chip in its state container and obtain a - pointer to the container using container_of(). +associated irqdomain and resource allocation callbacks. These are activated +by selecting the Kconfig symbol GPIOLIB_IRQCHIP. If the symbol +IRQ_DOMAIN_HIERARCHY is also selected, hierarchical helpers will also be +provided. A big portion of overhead code will be managed by gpiolib, +under the assumption that your interrupts are 1-to-1-mapped to the +GPIO line index: + + GPIO line offset Hardware IRQ + 0 0 + 1 1 + 2 2 + ... ... + ngpio-1 ngpio-1 + +If some GPIO lines do not have corresponding IRQs, the bitmask valid_mask +and the flag need_valid_mask in gpio_irq_chip can be used to mask off some +lines as invalid for associating with IRQs. + +The preferred way to set up the helpers is to fill in the +struct gpio_irq_chip inside struct gpio_chip before adding the gpio_chip. +If you do this, the additional irq_chip will be set up by gpiolib at the +same time as setting up the rest of the GPIO functionality. The following +is a typical example of a cascaded interrupt handler using gpio_irq_chip: + + /* Typical state container with dynamic irqchip */ + struct my_gpio { + struct gpio_chip gc; + struct irq_chip irq; + }; + + int irq; /* from platform etc */ + struct my_gpio *g; + struct gpio_irq_chip *girq + + /* Set up the irqchip dynamically */ + g->irq.name = "my_gpio_irq"; + g->irq.irq_ack = my_gpio_ack_irq; + g->irq.irq_mask = my_gpio_mask_irq; + g->irq.irq_unmask = my_gpio_unmask_irq; + g->irq.irq_set_type = my_gpio_set_irq_type; + + /* Get a pointer to the gpio_irq_chip */ + girq = &g->gc.irq; + girq->chip = &g->irq; + girq->parent_handler = ftgpio_gpio_irq_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_bad_irq; + girq->parents[0] = irq; + + return devm_gpiochip_add_data(dev, &g->gc, g); + +The helper support using hierarchical interrupt controllers as well. +In this case the typical set-up will look like this: + + /* Typical state container with dynamic irqchip */ + struct my_gpio { + struct gpio_chip gc; + struct irq_chip irq; + struct fwnode_handle *fwnode; + }; + + int irq; /* from platform etc */ + struct my_gpio *g; + struct gpio_irq_chip *girq + + /* Set up the irqchip dynamically */ + g->irq.name = "my_gpio_irq"; + g->irq.irq_ack = my_gpio_ack_irq; + g->irq.irq_mask = my_gpio_mask_irq; + g->irq.irq_unmask = my_gpio_unmask_irq; + g->irq.irq_set_type = my_gpio_set_irq_type; + + /* Get a pointer to the gpio_irq_chip */ + girq = &g->gc.irq; + girq->chip = &g->irq; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_bad_irq; + girq->fwnode = g->fwnode; + girq->parent_domain = parent; + girq->child_to_parent_hwirq = my_gpio_child_to_parent_hwirq; + + return devm_gpiochip_add_data(dev, &g->gc, g); + +As you can see pretty similar, but you do not supply a parent handler for +the IRQ, instead a parent irqdomain, an fwnode for the hardware and +a funcion .child_to_parent_hwirq() that has the purpose of looking up +the parent hardware irq from a child (i.e. this gpio chip) hardware irq. +As always it is good to look at examples in the kernel tree for advice +on how to find the required pieces. + +The old way of adding irqchips to gpiochips after registration is also still +available but we try to move away from this: + +- DEPRECATED: gpiochip_irqchip_add(): adds a chained cascaded irqchip to a + gpiochip. It will pass the struct gpio_chip* for the chip to all IRQ + callbacks, so the callbacks need to embed the gpio_chip in its state + container and obtain a pointer to the container using container_of(). (See Documentation/driver-model/design-patterns.txt) - gpiochip_irqchip_add_nested(): adds a nested cascaded irqchip to a gpiochip, @@ -406,10 +500,10 @@ symbol: cascaded irq has to be handled by a threaded interrupt handler. Apart from that it works exactly like the chained irqchip. -- gpiochip_set_chained_irqchip(): sets up a chained cascaded irq handler for a - gpio_chip from a parent IRQ and passes the struct gpio_chip* as handler - data. Notice that we pass is as the handler data, since the irqchip data is - likely used by the parent irqchip. +- DEPRECATED: gpiochip_set_chained_irqchip(): sets up a chained cascaded irq + handler for a gpio_chip from a parent IRQ and passes the struct gpio_chip* + as handler data. Notice that we pass is as the handler data, since the + irqchip data is likely used by the parent irqchip. - gpiochip_set_nested_irqchip(): sets up a nested cascaded irq handler for a gpio_chip from a parent IRQ. As the parent IRQ has usually been diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e013d417a936..af72ffa02963 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1718,6 +1718,240 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gpiochip, } EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip); +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + +/** + * gpiochip_set_hierarchical_irqchip() - connects a hierarchical irqchip + * to a gpiochip + * @gc: the gpiochip to set the irqchip hierarchical handler to + * @irqchip: the irqchip to handle this level of the hierarchy, the interrupt + * will then percolate up to the parent + */ +static void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gc, + struct irq_chip *irqchip) +{ + /* DT will deal with mapping each IRQ as we go along */ + if (is_of_node(gc->irq.fwnode)) + return; + + /* + * This is for legacy and boardfile "irqchip" fwnodes: allocate + * irqs upfront instead of dynamically since we don't have the + * dynamic type of allocation that hardware description languages + * provide. Once all GPIO drivers using board files are gone from + * the kernel we can delete this code, but for a transitional period + * it is necessary to keep this around. + */ + if (is_fwnode_irqchip(gc->irq.fwnode)) { + int i; + int ret; + + for (i = 0; i < gc->ngpio; i++) { + struct irq_fwspec fwspec; + unsigned int parent_hwirq; + unsigned int parent_type; + struct gpio_irq_chip *girq = &gc->irq; + + /* + * We call the child to parent translation function + * only to check if the child IRQ is valid or not. + * Just pick the rising edge type here as that is what + * we likely need to support. + */ + ret = girq->child_to_parent_hwirq(gc, i, + IRQ_TYPE_EDGE_RISING, + &parent_hwirq, + &parent_type); + if (ret) { + chip_err(gc, "skip set-up on hwirq %d\n", + i); + continue; + } + + fwspec.fwnode = gc->irq.fwnode; + /* This is the hwirq for the GPIO line side of things */ + fwspec.param[0] = i; + /* Just pick something */ + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; + fwspec.param_count = 2; + ret = __irq_domain_alloc_irqs(gc->irq.domain, + /* just pick something */ + -1, + 1, + NUMA_NO_NODE, + &fwspec, + false, + NULL); + if (ret < 0) { + chip_err(gc, + "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n", + i, parent_hwirq, + ret); + } + } + } + + chip_err(gc, "%s unknown fwnode type proceed anyway\n", __func__); + + return; +} + +static int gpiochip_hierarchy_irq_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + /* We support standard DT translation */ + if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) { + return irq_domain_translate_twocell(d, fwspec, hwirq, type); + } + + /* This is for board files and others not using DT */ + if (is_fwnode_irqchip(fwspec->fwnode)) { + int ret; + + ret = irq_domain_translate_twocell(d, fwspec, hwirq, type); + if (ret) + return ret; + WARN_ON(*type == IRQ_TYPE_NONE); + return 0; + } + return -EINVAL; +} + +static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, + unsigned int irq, + unsigned int nr_irqs, + void *data) +{ + struct gpio_chip *gc = d->host_data; + irq_hw_number_t hwirq; + unsigned int type = IRQ_TYPE_NONE; + struct irq_fwspec *fwspec = data; + int ret; + int i; + + chip_info(gc, "called %s\n", __func__); + + ret = gpiochip_hierarchy_irq_domain_translate(d, fwspec, &hwirq, &type); + if (ret) + return ret; + + chip_info(gc, "allocate IRQ %d..%d, hwirq %lu..%lu\n", + irq, irq + nr_irqs - 1, + hwirq, hwirq + nr_irqs - 1); + + for (i = 0; i < nr_irqs; i++) { + struct irq_fwspec parent_fwspec; + unsigned int parent_hwirq; + unsigned int parent_type; + struct gpio_irq_chip *girq = &gc->irq; + + ret = girq->child_to_parent_hwirq(gc, hwirq, type, + &parent_hwirq, &parent_type); + if (ret) { + chip_err(gc, "can't look up hwirq %lu\n", hwirq); + return ret; + } + chip_info(gc, "found parent hwirq %u\n", parent_hwirq); + + /* + * We set handle_bad_irq because the .set_type() should + * always be invoked and set the right type of handler. + */ + irq_domain_set_info(d, + irq + i, + hwirq + i, + gc->irq.chip, + gc, + handle_bad_irq, + NULL, NULL); + irq_set_probe(irq + i); + + /* + * Create a IRQ fwspec to send up to the parent irqdomain: + * specify the hwirq we address on the parent and tie it + * all together up the chain. + */ + parent_fwspec.fwnode = d->parent->fwnode; + parent_fwspec.param_count = 2; + parent_fwspec.param[0] = parent_hwirq; + /* This parent only handles asserted level IRQs */ + parent_fwspec.param[1] = parent_type; + chip_info(gc, "alloc_irqs_parent for %d parent hwirq %d\n", + irq + i, parent_hwirq); + ret = irq_domain_alloc_irqs_parent(d, irq + i, 1, + &parent_fwspec); + if (ret) + chip_err(gc, + "failed to allocate parent hwirq %d for hwirq %lu\n", + parent_hwirq, hwirq); + } + + return 0; +} + +static const struct irq_domain_ops gpiochip_hierarchy_domain_ops = { + .activate = gpiochip_irq_domain_activate, + .deactivate = gpiochip_irq_domain_deactivate, + .translate = gpiochip_hierarchy_irq_domain_translate, + .alloc = gpiochip_hierarchy_irq_domain_alloc, + .free = irq_domain_free_irqs_common, +}; + +static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) +{ + if (!gc->irq.parent_domain) { + chip_err(gc, "missing parent irqdomain\n"); + return -EINVAL; + } + + if (!gc->irq.parent_domain || + !gc->irq.child_to_parent_hwirq || + !gc->irq.fwnode) { + chip_err(gc, "missing irqdomain vital data\n"); + return -EINVAL; + } + + gc->irq.domain = irq_domain_create_hierarchy( + gc->irq.parent_domain, + IRQ_DOMAIN_FLAG_HIERARCHY, + gc->ngpio, + gc->irq.fwnode, + &gpiochip_hierarchy_domain_ops, + gc); + + if (!gc->irq.domain) { + chip_err(gc, "failed to add hierarchical domain\n"); + return -EINVAL; + } + + gpiochip_set_hierarchical_irqchip(gc, gc->irq.chip); + + chip_info(gc, "set up hierarchical irqdomain\n"); + + return 0; +} + +static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) +{ + return !!gc->irq.parent_domain; +} + +#else + +static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) +{ + return -EINVAL; +} + +static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) +{ + return false; +} + +#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ + /** * gpiochip_irq_map() - maps an IRQ into a GPIO irqchip * @d: the irqdomain used by this irqchip @@ -1786,6 +2020,11 @@ static const struct irq_domain_ops gpiochip_domain_ops = { .xlate = irq_domain_xlate_twocell, }; +/* + * TODO: move these activate/deactivate in under the hierarchicial + * irqchip implementation as static once SPMI and SSBI (all external + * users) are phased over. + */ /** * gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ * @domain: The IRQ domain used by this IRQ chip @@ -1825,10 +2064,23 @@ EXPORT_SYMBOL_GPL(gpiochip_irq_domain_deactivate); static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) { + struct irq_domain *domain = chip->irq.domain; + if (!gpiochip_irqchip_irq_valid(chip, offset)) return -ENXIO; - return irq_create_mapping(chip->irq.domain, offset); + if (irq_domain_is_hierarchy(domain)) { + struct irq_fwspec spec; + + spec.fwnode = domain->fwnode; + spec.param_count = 2; + spec.param[0] = offset; + spec.param[1] = IRQ_TYPE_NONE; + + return irq_create_fwspec_mapping(&spec); + } + + return irq_create_mapping(domain, offset); } static int gpiochip_irq_reqres(struct irq_data *d) @@ -1905,7 +2157,7 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, struct lock_class_key *request_key) { struct irq_chip *irqchip = gpiochip->irq.chip; - const struct irq_domain_ops *ops; + const struct irq_domain_ops *ops = NULL; struct device_node *np; unsigned int type; unsigned int i; @@ -1941,16 +2193,25 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, gpiochip->irq.lock_key = lock_key; gpiochip->irq.request_key = request_key; - if (gpiochip->irq.domain_ops) - ops = gpiochip->irq.domain_ops; - else - ops = &gpiochip_domain_ops; - - gpiochip->irq.domain = irq_domain_add_simple(np, gpiochip->ngpio, - gpiochip->irq.first, - ops, gpiochip); - if (!gpiochip->irq.domain) - return -EINVAL; + /* If a parent irqdomain is provided, let's build a hierarchy */ + if (gpiochip_hierarchy_is_hierarchical(gpiochip)) { + int ret = gpiochip_hierarchy_add_domain(gpiochip); + if (ret) + return ret; + } else { + /* Some drivers provide custom irqdomain ops */ + if (gpiochip->irq.domain_ops) + ops = gpiochip->irq.domain_ops; + + if (!ops) + ops = &gpiochip_domain_ops; + gpiochip->irq.domain = irq_domain_add_simple(np, + gpiochip->ngpio, + gpiochip->irq.first, + ops, gpiochip); + if (!gpiochip->irq.domain) + return -EINVAL; + } if (gpiochip->irq.parent_handler) { void *data = gpiochip->irq.parent_handler_data ?: gpiochip; diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index a1d273c96016..e32d02cb2d08 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -22,6 +22,9 @@ enum gpiod_flags; #ifdef CONFIG_GPIOLIB #ifdef CONFIG_GPIOLIB_IRQCHIP + +struct gpio_chip; + /** * struct gpio_irq_chip - GPIO interrupt controller */ @@ -48,6 +51,49 @@ struct gpio_irq_chip { */ const struct irq_domain_ops *domain_ops; +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + /** + * @fwnode: + * + * Firmware node corresponding to this gpiochip/irqchip, necessary + * for hierarchical irqdomain support. + */ + struct fwnode_handle *fwnode; + + /** + * @parent_domain: + * + * If non-NULL, will be set as the parent of this GPIO interrupt + * controller's IRQ domain to establish a hierarchical interrupt + * domain. The presence of this will activate the hierarchical + * interrupt support. + */ + struct irq_domain *parent_domain; + + /** + * @child_to_parent_hwirq: + * + * This callback translates a child hardware IRQ offset to a parent + * hardware IRQ offset on a hierarchical interrupt chip. The child + * hardware IRQs correspond to the GPIO index 0..ngpio-1 (see the + * ngpio field of struct gpio_chip) and the corresponding parent + * hardware IRQ and type (such as IRQ_TYPE_*) shall be returned by + * the driver. The driver can calculate this from an offset or using + * a lookup table or whatever method is best for this chip. Return + * 0 on successful translation in the driver. + * + * If some ranges of hardware IRQs do not have a corresponding parent + * HWIRQ, return -EINVAL, but also make sure to fill in @valid_mask and + * @need_valid_mask to make these GPIO lines unavailable for + * translation. + */ + int (*child_to_parent_hwirq)(struct gpio_chip *chip, + unsigned int child_hwirq, + unsigned int child_type, + unsigned int *parent_hwirq, + unsigned int *parent_type); +#endif + /** * @handler: * From patchwork Mon Jun 24 13:25:29 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 167569 Delivered-To: patch@linaro.org Received: by 2002:a92:4782:0:0:0:0:0 with SMTP id e2csp4228550ilk; Mon, 24 Jun 2019 06:25:46 -0700 (PDT) X-Google-Smtp-Source: APXvYqxXI3HN1cNDTzzmsoxMWh8M7XRVd46fvqnxz6wMF3ltuBf+CBUUW72EZJ3apCH3pHUxYa8A X-Received: by 2002:a17:902:a411:: with SMTP id p17mr24045249plq.104.1561382746798; Mon, 24 Jun 2019 06:25:46 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1561382746; cv=none; d=google.com; s=arc-20160816; b=BPS1OIZeV7rJd2hARadDmgh52a/dwpgOZEM+0Xm1i76ZcnB03+Sj9IfXLe48KRqYYN Do6SOxhmMOy3r9K9PJKgBVcKLQyS4PmvU3abzKqNKpdL09vn1AtL3rAo46wib2i+FMVw xfwUKT6A1wx0ImYdYpKITJ4A0Q51vd1+NdSwQy3TijnmQ8tVUT13uftix3FGZCutEmPi QIxjtSLc6fWcYlcu07vpZJ6QOLo/r8fj4ZGFw4xAGJqZBpaYRWZogshi1/tMn4MdiAm/ YVuboUblBBnI3HsUw2hNO4rVB/rKrD8eI4W7Q4p+kPVJh8ppNnzmqzociVl3zWAKFhV6 VAZQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=kPvpd9jFtLKCPgzqAlx5DtxQDxnYWtZQ6ePrjtiwaEA=; b=Bg7MtLbq8NilSFG6jZSnWl8etijyakWZVPF8GrnCGhyQK6ywS/gShrSMv0UhdefYU/ qdAOf5tV8fDaxRVTO+iaU9KhPPb5fwEzIEBnuyGEwieQrJJl1z2opfUDfRExXWGb4UWk R+R4fTuQmK3q/qNeMXbftxQ20C8Qu1sg3i/Rn64lBvp/leBYeqhEfws/VkiyHrrYDUaf e5ihIs2ARPgAMIDK38OZYS3rVPoFnHMgmbetfJ6ltjiIwGtPrHwtTG5FGLDGUWtgYufS u8OGJd6IFVzZ7C7vhCwTsiXaTK98VEfYVt7cUIAXpL48WUrLCk2jqBZc6De+h4rRkLX+ AwQQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=HesgNxGj; spf=pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-gpio-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id l1si10373860plb.302.2019.06.24.06.25.46; Mon, 24 Jun 2019 06:25:46 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=HesgNxGj; spf=pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-gpio-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729168AbfFXNZq (ORCPT + 5 others); Mon, 24 Jun 2019 09:25:46 -0400 Received: from mail-lj1-f193.google.com ([209.85.208.193]:42422 "EHLO mail-lj1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728635AbfFXNZp (ORCPT ); Mon, 24 Jun 2019 09:25:45 -0400 Received: by mail-lj1-f193.google.com with SMTP id t28so12589201lje.9 for ; Mon, 24 Jun 2019 06:25:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=kPvpd9jFtLKCPgzqAlx5DtxQDxnYWtZQ6ePrjtiwaEA=; b=HesgNxGjMhBzCWbu5b1z1is2fdQnvw43rdh8cqxYyzhtedFAtR+3fbUTd7ldMS5CLn nO7EHrpi0dLB60TgATrHWpVkWLWNW0oqR6B4OJnGiOa52BefF4hwpJkWhghNcvcltATw pnAqNxkAZYsGBCiOhWlm7ei7XxqQU7a6E9OLbBKnupFJ1GbDx0WrGYuE0D6yKlT9j3X4 k2TOzPwfQRX0F7lMMlWH6Q/FVbfCJ+NhJ9LtXP0UJTovxRChClJ2jfUfdigO36uDq7/b a4ywCWJfdYHXvDaHDHPyMIB63A9yMTZEK5RFbJJRlQOns4jCtdBzRhsr/ZgYZ21fv0g9 vVVA== 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:mime-version:content-transfer-encoding; bh=kPvpd9jFtLKCPgzqAlx5DtxQDxnYWtZQ6ePrjtiwaEA=; b=jhzNddnrICYF/7xDbohwFS+ccrJO5eyxoj0zmSIgV5/4NTYTlMeC0Rz8L5LCbibuA8 MuD5pGA+bX4rP5lR8D7p0A5Ic1huVyoN0dVonBtKDrJmaLolceKHcpm5LKRaK+jqFISS /nWi7ipyAxb8RZt43PKBnYdo+vCM6+0OVJM2wt7QWS95tlDEN4o9IjjNDWjSc0pw5kuP RTh09rNh2XPyVcIVF1BVMPFUpSkVVjJBiFrTFPvZPspP5thBMkogvXktz9K7dTDPAhPq GFiWRCCMu7oqtvFAuwPMASGUM69Cl4bO33h2iBdpE7xH4KMPdNuyJXawyOAXFXwZ78mu 1vlg== X-Gm-Message-State: APjAAAXXV2P2+gfFdNXAdvyT935t68E+BuE4HnkLc4QGdP4EQhU5ghsi SuJGDGjfOKr/VZLvfvIezrW+hBwLx3g= X-Received: by 2002:a2e:6e0e:: with SMTP id j14mr15016273ljc.85.1561382742581; Mon, 24 Jun 2019 06:25:42 -0700 (PDT) Received: from localhost.bredbandsbolaget (c-22cd225c.014-348-6c756e10.bbcust.telenor.se. [92.34.205.34]) by smtp.gmail.com with ESMTPSA id t4sm416102ljh.9.2019.06.24.06.25.41 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Mon, 24 Jun 2019 06:25:41 -0700 (PDT) From: Linus Walleij To: linux-gpio@vger.kernel.org Cc: Bartosz Golaszewski , Linus Walleij , Thomas Gleixner , Marc Zyngier , Lina Iyer , Jon Hunter , Sowjanya Komatineni , Bitan Biswas , linux-tegra@vger.kernel.org, Thierry Reding , Brian Masney Subject: [PATCH 2/4 v1] gpio: ixp4xx: Convert to hieararchical GPIOLIB_IRQCHIP Date: Mon, 24 Jun 2019 15:25:29 +0200 Message-Id: <20190624132531.6184-2-linus.walleij@linaro.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190624132531.6184-1-linus.walleij@linaro.org> References: <20190624132531.6184-1-linus.walleij@linaro.org> MIME-Version: 1.0 Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This modifies the IXP4xx driver to use the new helpers to handle the remapping of parent to child hardware irqs in the gpiolib core. This pulls the majority of the code out of the driver and use the generic code in gpiolib. Cc: Thomas Gleixner Cc: Marc Zyngier Cc: Lina Iyer Cc: Jon Hunter Cc: Sowjanya Komatineni Cc: Bitan Biswas Cc: linux-tegra@vger.kernel.org Cc: Thierry Reding Cc: Brian Masney Signed-off-by: Linus Walleij --- ChangeLog RFC->v1: - Fixed some bugs in dereferencing the gpio_chip first from the irqchip data, then dereference the local state container from the gpio_chip - Adapted to changes in the core patch like provide as translation callback rather than a table. - Tested on the Linksys NSLU2 and works like a charm --- drivers/gpio/Kconfig | 2 +- drivers/gpio/gpio-ixp4xx.c | 277 +++++++++---------------------------- 2 files changed, 63 insertions(+), 216 deletions(-) -- 2.21.0 diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8023d03ec362..72027b0c5bf4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -291,7 +291,7 @@ config GPIO_IXP4XX depends on ARM # For depends on ARCH_IXP4XX select GPIO_GENERIC - select IRQ_DOMAIN + select GPIOLIB_IRQCHIP select IRQ_DOMAIN_HIERARCHY help Say yes here to support the GPIO functionality of a number of Intel diff --git a/drivers/gpio/gpio-ixp4xx.c b/drivers/gpio/gpio-ixp4xx.c index 670c2a85a35b..8bd23e80c61f 100644 --- a/drivers/gpio/gpio-ixp4xx.c +++ b/drivers/gpio/gpio-ixp4xx.c @@ -47,7 +47,6 @@ * @dev: containing device for this instance * @fwnode: the fwnode for this GPIO chip * @gc: gpiochip for this instance - * @domain: irqdomain for this chip instance * @base: remapped I/O-memory base * @irq_edge: Each bit represents an IRQ: 1: edge-triggered, * 0: level triggered @@ -56,48 +55,22 @@ struct ixp4xx_gpio { struct device *dev; struct fwnode_handle *fwnode; struct gpio_chip gc; - struct irq_domain *domain; void __iomem *base; unsigned long long irq_edge; }; -/** - * struct ixp4xx_gpio_map - IXP4 GPIO to parent IRQ map - * @gpio_offset: offset of the IXP4 GPIO line - * @parent_hwirq: hwirq on the parent IRQ controller - */ -struct ixp4xx_gpio_map { - int gpio_offset; - int parent_hwirq; -}; - -/* GPIO lines 0..12 have corresponding IRQs, GPIOs 13..15 have no IRQs */ -const struct ixp4xx_gpio_map ixp4xx_gpiomap[] = { - { .gpio_offset = 0, .parent_hwirq = 6 }, - { .gpio_offset = 1, .parent_hwirq = 7 }, - { .gpio_offset = 2, .parent_hwirq = 19 }, - { .gpio_offset = 3, .parent_hwirq = 20 }, - { .gpio_offset = 4, .parent_hwirq = 21 }, - { .gpio_offset = 5, .parent_hwirq = 22 }, - { .gpio_offset = 6, .parent_hwirq = 23 }, - { .gpio_offset = 7, .parent_hwirq = 24 }, - { .gpio_offset = 8, .parent_hwirq = 25 }, - { .gpio_offset = 9, .parent_hwirq = 26 }, - { .gpio_offset = 10, .parent_hwirq = 27 }, - { .gpio_offset = 11, .parent_hwirq = 28 }, - { .gpio_offset = 12, .parent_hwirq = 29 }, -}; - static void ixp4xx_gpio_irq_ack(struct irq_data *d) { - struct ixp4xx_gpio *g = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct ixp4xx_gpio *g = gpiochip_get_data(gc); __raw_writel(BIT(d->hwirq), g->base + IXP4XX_REG_GPIS); } static void ixp4xx_gpio_irq_unmask(struct irq_data *d) { - struct ixp4xx_gpio *g = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct ixp4xx_gpio *g = gpiochip_get_data(gc); /* ACK when unmasking if not edge-triggered */ if (!(g->irq_edge & BIT(d->hwirq))) @@ -108,7 +81,8 @@ static void ixp4xx_gpio_irq_unmask(struct irq_data *d) static int ixp4xx_gpio_irq_set_type(struct irq_data *d, unsigned int type) { - struct ixp4xx_gpio *g = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct ixp4xx_gpio *g = gpiochip_get_data(gc); int line = d->hwirq; unsigned long flags; u32 int_style; @@ -187,122 +161,31 @@ static struct irq_chip ixp4xx_gpio_irqchip = { .irq_set_type = ixp4xx_gpio_irq_set_type, }; -static int ixp4xx_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) -{ - struct ixp4xx_gpio *g = gpiochip_get_data(gc); - struct irq_fwspec fwspec; - - fwspec.fwnode = g->fwnode; - fwspec.param_count = 2; - fwspec.param[0] = offset; - fwspec.param[1] = IRQ_TYPE_NONE; - - return irq_create_fwspec_mapping(&fwspec); -} - -static int ixp4xx_gpio_irq_domain_translate(struct irq_domain *domain, - struct irq_fwspec *fwspec, - unsigned long *hwirq, - unsigned int *type) +static int ixp4xx_gpio_child_to_parent_hwirq(struct gpio_chip *gc, + unsigned int child, + unsigned int child_type, + unsigned int *parent, + unsigned int *parent_type) { - int ret; + /* All these interrupts are level high in the CPU */ + *parent_type = IRQ_TYPE_LEVEL_HIGH; - /* We support standard DT translation */ - if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) { - return irq_domain_translate_twocell(domain, fwspec, - hwirq, type); + /* GPIO lines 0..12 have dedicated IRQs */ + if (child == 0) { + *parent = 6; + return 0; } - - /* This goes away when we transition to DT */ - if (is_fwnode_irqchip(fwspec->fwnode)) { - ret = irq_domain_translate_twocell(domain, fwspec, - hwirq, type); - if (ret) - return ret; - WARN_ON(*type == IRQ_TYPE_NONE); + if (child == 1) { + *parent = 7; return 0; } - return -EINVAL; -} - -static int ixp4xx_gpio_irq_domain_alloc(struct irq_domain *d, - unsigned int irq, unsigned int nr_irqs, - void *data) -{ - struct ixp4xx_gpio *g = d->host_data; - irq_hw_number_t hwirq; - unsigned int type = IRQ_TYPE_NONE; - struct irq_fwspec *fwspec = data; - int ret; - int i; - - ret = ixp4xx_gpio_irq_domain_translate(d, fwspec, &hwirq, &type); - if (ret) - return ret; - - dev_dbg(g->dev, "allocate IRQ %d..%d, hwirq %lu..%lu\n", - irq, irq + nr_irqs - 1, - hwirq, hwirq + nr_irqs - 1); - - for (i = 0; i < nr_irqs; i++) { - struct irq_fwspec parent_fwspec; - const struct ixp4xx_gpio_map *map; - int j; - - /* Not all lines support IRQs */ - for (j = 0; j < ARRAY_SIZE(ixp4xx_gpiomap); j++) { - map = &ixp4xx_gpiomap[j]; - if (map->gpio_offset == hwirq) - break; - } - if (j == ARRAY_SIZE(ixp4xx_gpiomap)) { - dev_err(g->dev, "can't look up hwirq %lu\n", hwirq); - return -EINVAL; - } - dev_dbg(g->dev, "found parent hwirq %u\n", map->parent_hwirq); - - /* - * We set handle_bad_irq because the .set_type() should - * always be invoked and set the right type of handler. - */ - irq_domain_set_info(d, - irq + i, - hwirq + i, - &ixp4xx_gpio_irqchip, - g, - handle_bad_irq, - NULL, NULL); - irq_set_probe(irq + i); - - /* - * Create a IRQ fwspec to send up to the parent irqdomain: - * specify the hwirq we address on the parent and tie it - * all together up the chain. - */ - parent_fwspec.fwnode = d->parent->fwnode; - parent_fwspec.param_count = 2; - parent_fwspec.param[0] = map->parent_hwirq; - /* This parent only handles asserted level IRQs */ - parent_fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; - dev_dbg(g->dev, "alloc_irqs_parent for %d parent hwirq %d\n", - irq + i, map->parent_hwirq); - ret = irq_domain_alloc_irqs_parent(d, irq + i, 1, - &parent_fwspec); - if (ret) - dev_err(g->dev, - "failed to allocate parent hwirq %d for hwirq %lu\n", - map->parent_hwirq, hwirq); + if (child >= 2 && child <= 12) { + *parent = child + 17; + return 0; } - - return 0; + return -EINVAL; } -static const struct irq_domain_ops ixp4xx_gpio_irqdomain_ops = { - .translate = ixp4xx_gpio_irq_domain_translate, - .alloc = ixp4xx_gpio_irq_domain_alloc, - .free = irq_domain_free_irqs_common, -}; - static int ixp4xx_gpio_probe(struct platform_device *pdev) { unsigned long flags; @@ -311,8 +194,8 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) struct irq_domain *parent; struct resource *res; struct ixp4xx_gpio *g; + struct gpio_irq_chip *girq; int ret; - int i; g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL); if (!g) @@ -326,6 +209,35 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) return PTR_ERR(g->base); } + /* + * When we convert to device tree we will simply look up the + * parent irqdomain using irq_find_host(parent) as parent comes + * from IRQCHIP_DECLARE(), then use of_node_to_fwnode() to get + * the fwnode. For now we need this boardfile style code. + */ + if (np) { + struct device_node *irq_parent; + + irq_parent = of_irq_find_parent(np); + if (!irq_parent) { + dev_err(dev, "no IRQ parent node\n"); + return -ENODEV; + } + parent = irq_find_host(irq_parent); + if (!parent) { + dev_err(dev, "no IRQ parent domain\n"); + return -ENODEV; + } + g->fwnode = of_node_to_fwnode(np); + } else { + parent = ixp4xx_get_irq_domain(); + g->fwnode = irq_domain_alloc_fwnode(g->base); + if (!g->fwnode) { + dev_err(dev, "no domain base\n"); + return -ENODEV; + } + } + /* * Make sure GPIO 14 and 15 are NOT used as clocks but GPIO on * specific machines. @@ -360,7 +272,6 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) dev_err(dev, "unable to init generic GPIO\n"); return ret; } - g->gc.to_irq = ixp4xx_gpio_to_irq; g->gc.ngpio = 16; g->gc.label = "IXP4XX_GPIO_CHIP"; /* @@ -372,86 +283,22 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) g->gc.parent = &pdev->dev; g->gc.owner = THIS_MODULE; + girq = &g->gc.irq; + girq->chip = &ixp4xx_gpio_irqchip; + girq->fwnode = g->fwnode; + girq->parent_domain = parent; + girq->child_to_parent_hwirq = ixp4xx_gpio_child_to_parent_hwirq; + girq->handler = handle_bad_irq; + girq->default_type = IRQ_TYPE_NONE; + ret = devm_gpiochip_add_data(dev, &g->gc, g); if (ret) { dev_err(dev, "failed to add SoC gpiochip\n"); return ret; } - /* - * When we convert to device tree we will simply look up the - * parent irqdomain using irq_find_host(parent) as parent comes - * from IRQCHIP_DECLARE(), then use of_node_to_fwnode() to get - * the fwnode. For now we need this boardfile style code. - */ - if (np) { - struct device_node *irq_parent; - - irq_parent = of_irq_find_parent(np); - if (!irq_parent) { - dev_err(dev, "no IRQ parent node\n"); - return -ENODEV; - } - parent = irq_find_host(irq_parent); - if (!parent) { - dev_err(dev, "no IRQ parent domain\n"); - return -ENODEV; - } - g->fwnode = of_node_to_fwnode(np); - } else { - parent = ixp4xx_get_irq_domain(); - g->fwnode = irq_domain_alloc_fwnode(g->base); - if (!g->fwnode) { - dev_err(dev, "no domain base\n"); - return -ENODEV; - } - } - g->domain = irq_domain_create_hierarchy(parent, - IRQ_DOMAIN_FLAG_HIERARCHY, - ARRAY_SIZE(ixp4xx_gpiomap), - g->fwnode, - &ixp4xx_gpio_irqdomain_ops, - g); - if (!g->domain) { - irq_domain_free_fwnode(g->fwnode); - dev_err(dev, "no hierarchical irq domain\n"); - return ret; - } - - /* - * After adding OF support, this is no longer needed: irqs - * will be allocated for the respective fwnodes. - */ - if (!np) { - for (i = 0; i < ARRAY_SIZE(ixp4xx_gpiomap); i++) { - const struct ixp4xx_gpio_map *map = &ixp4xx_gpiomap[i]; - struct irq_fwspec fwspec; - - fwspec.fwnode = g->fwnode; - /* This is the hwirq for the GPIO line side of things */ - fwspec.param[0] = map->gpio_offset; - fwspec.param[1] = IRQ_TYPE_EDGE_RISING; - fwspec.param_count = 2; - ret = __irq_domain_alloc_irqs(g->domain, - -1, /* just pick something */ - 1, - NUMA_NO_NODE, - &fwspec, - false, - NULL); - if (ret < 0) { - irq_domain_free_fwnode(g->fwnode); - dev_err(dev, - "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n", - map->gpio_offset, map->parent_hwirq, - ret); - return ret; - } - } - } - platform_set_drvdata(pdev, g); - dev_info(dev, "IXP4 GPIO @%p registered\n", g->base); + dev_info(dev, "IXP4 GPIO registered\n"); return 0; } From patchwork Mon Jun 24 13:25:30 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 167570 Delivered-To: patch@linaro.org Received: by 2002:a92:4782:0:0:0:0:0 with SMTP id e2csp4228599ilk; Mon, 24 Jun 2019 06:25:48 -0700 (PDT) X-Google-Smtp-Source: APXvYqy+8g/dkS6iqueNpLeuFfODR2L304pVLxBQM7lKbyAkn6VLxm9VSNDo8WCQxX3aZSuLAznW X-Received: by 2002:a63:f959:: with SMTP id q25mr13841973pgk.357.1561382748538; Mon, 24 Jun 2019 06:25:48 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1561382748; cv=none; d=google.com; s=arc-20160816; b=Tb9xUAmdEv0YkT5SLo1hgeFbCi8piN9oer0sCxE+055quoi5DHu5zycAHrXUPT4QE7 /HNUdcCasYKLLdMIn2WI2b9o0dL7sicEyZnX+gtX0Lfcx+sY83YkVhyijDfX4J58+xmQ Ot5fXdtjzmkXSJ7vBPJGrlQj5KynJ4SawC0NX1MqV630n92UR5o6wjf5jBrnVNoYxxnQ rAIT4R8+bAVaGKEPHGqk7FgVRJeOmq6/u/Pp5MuRNb3gawKBXxVV7GWc9ptc10QAT4g6 wEvSCCOdAUlpdxwx+oYyG/eowq8bfqjE8gz8X4Y9qqVnJwa6UeZHJZiCpiHzVvOkj990 jQGQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=zxhVdDBf+iMI+xbUAtfGZ/rBs/v9txMUOMKAZKhXGHo=; b=onkHC0TR5GV5q3o385GdSChQBZtZUzHZ/93Ype1Cf3ayIc+r2Njo2lgtcZ+sMdkzyh RhqPJvNgS1yz2kSxHdA9Ked4c873BKfo59jvCvg0QOtOM+ZL7cym/f6+qnwgu6Azwu01 SuSQc2tIdqpAMAjrLrTvhg++1qSbyC+zM2teQ/FDuvPc/5N2EulmiNYaPa+Bz9i3ykl1 ttctFWzE5aygYsadk7sp4w5JrlbiB/Uz8Si+0QL/5hq6zN33U54vYm290QFj0MXjeOrW Sj1SgymXkhWRN7bJkR/Y514xzCQBhTt8hrTw9IPfD4PjcAxh6Nq3MWXnP2NpxGue8nNd CELw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=RMGbhNIS; spf=pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-gpio-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id l1si10373860plb.302.2019.06.24.06.25.48; Mon, 24 Jun 2019 06:25:48 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=RMGbhNIS; spf=pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-gpio-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729037AbfFXNZs (ORCPT + 5 others); Mon, 24 Jun 2019 09:25:48 -0400 Received: from mail-lj1-f195.google.com ([209.85.208.195]:45923 "EHLO mail-lj1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729145AbfFXNZr (ORCPT ); Mon, 24 Jun 2019 09:25:47 -0400 Received: by mail-lj1-f195.google.com with SMTP id m23so12562783lje.12 for ; Mon, 24 Jun 2019 06:25:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=zxhVdDBf+iMI+xbUAtfGZ/rBs/v9txMUOMKAZKhXGHo=; b=RMGbhNISQ7WBrqZHZu9CKPRIxOA9T1cW6n/dRc3GbNK3WFXkH9ZGqw1ncAtsEdQMyH PQrujDOovQq3qNOktpshCBxmLRcXhHpHM4WHXTrKfHBT5r5j/Yxbn+kwdSEG4VNYWOwG jaJO3M0vWI54TTUA5HfC42jHCPOFMPgQti46lUf/4rrLOfc/szzCmBOj0gXKQnA+YJ72 7NIe74fpOwS3EIdtfaSnScrYNufoHKalzbRbxMvsqBuLciemUnu6qsNwBNWXJkZR2fYB d09oFD65rHBSAjCcHwIHY0cWsXfiFNvsuP9W7kHh5UqEQO9GQ8GPCQMZyBBWI5mbRUKC NXKA== 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:mime-version:content-transfer-encoding; bh=zxhVdDBf+iMI+xbUAtfGZ/rBs/v9txMUOMKAZKhXGHo=; b=sSxL+TplbQagatxFItUSVDvbPyATv+Oh8SG/jjEKFMapmg9+QXft67ReKeg0gO7Jor 4cKWgU/hXf9fdW4qdH+iHdzxl82xdobVJP+yq/PksD481ny72v0+jRaWDsvSBDMOWZeK PohUrGad/NuBuTU/JrmsB4egJsbSWkbbanGi7YGGGVhCcJiIlJEiGOdvsKa4j6SJ10/c wzl24aWmzoiwV5sSoa5qJAer6cmkAybC45bd1jXC/W8f27PcuKmmyrUIsTUxuFt4AlP0 ESDJhlT0jrLRAqyyncR/IJaiGcm0KWP6otdmJZjAMSzdDf6YHT314PrP/wNUB0u/lPvg x3ow== X-Gm-Message-State: APjAAAVFBP3eBJI0GowWD1ye08WP4dxE7HNayG4QrO2/cVlBhEw9dhws HgvhHlnuEnFFWKRbIku1Wbku3zL9R5c= X-Received: by 2002:a2e:b1c1:: with SMTP id e1mr16089810lja.228.1561382744674; Mon, 24 Jun 2019 06:25:44 -0700 (PDT) Received: from localhost.bredbandsbolaget (c-22cd225c.014-348-6c756e10.bbcust.telenor.se. [92.34.205.34]) by smtp.gmail.com with ESMTPSA id t4sm416102ljh.9.2019.06.24.06.25.43 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Mon, 24 Jun 2019 06:25:43 -0700 (PDT) From: Linus Walleij To: linux-gpio@vger.kernel.org Cc: Bartosz Golaszewski , Linus Walleij , David Daney , Thierry Reding , Brian Masney Subject: [PATCH 3/4 v1] RFT: gpio: thunderx: Switch to GPIOLIB_IRQCHIP Date: Mon, 24 Jun 2019 15:25:30 +0200 Message-Id: <20190624132531.6184-3-linus.walleij@linaro.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190624132531.6184-1-linus.walleij@linaro.org> References: <20190624132531.6184-1-linus.walleij@linaro.org> MIME-Version: 1.0 Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Use the new infrastructure for hierarchical irqchips in gpiolib. The major part of the rewrite was dues to the fact that the driver was passing around a per-irq pointer to struct thunderx_line * data container, and the central handlers will assume struct gpio_chip * to be passed to we need to use the hwirq as index to look up the struct thunderx_line * for each IRQ. The pushing and pop:ing of the irqdomain was confusing because I've never seen this before, but I tried to replicate it as best I could. I have no chance to test or debug this so I need help. Cc: David Daney Cc: Thierry Reding Cc: Brian Masney Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-thunderx.c | 163 ++++++++++++----------------------- 2 files changed, 57 insertions(+), 107 deletions(-) -- 2.21.0 diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 72027b0c5bf4..ee34716a39aa 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -541,6 +541,7 @@ config GPIO_THUNDERX tristate "Cavium ThunderX/OCTEON-TX GPIO" depends on ARCH_THUNDER || (64BIT && COMPILE_TEST) depends on PCI_MSI + select GPIOLIB_IRQCHIP select IRQ_DOMAIN_HIERARCHY select IRQ_FASTEOI_HIERARCHY_HANDLERS help diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index 715371b5102a..ddad5c7ea617 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -53,7 +53,6 @@ struct thunderx_line { struct thunderx_gpio { struct gpio_chip chip; u8 __iomem *register_base; - struct irq_domain *irqd; struct msix_entry *msix_entries; /* per line MSI-X */ struct thunderx_line *line_entries; /* per line irq info */ raw_spinlock_t lock; @@ -283,54 +282,60 @@ static void thunderx_gpio_set_multiple(struct gpio_chip *chip, } } -static void thunderx_gpio_irq_ack(struct irq_data *data) +static void thunderx_gpio_irq_ack(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_INTR, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static void thunderx_gpio_irq_mask(struct irq_data *data) +static void thunderx_gpio_irq_mask(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_ENA_W1C, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static void thunderx_gpio_irq_mask_ack(struct irq_data *data) +static void thunderx_gpio_irq_mask_ack(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_ENA_W1C | GPIO_INTR_INTR, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static void thunderx_gpio_irq_unmask(struct irq_data *data) +static void thunderx_gpio_irq_unmask(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_ENA_W1S, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static int thunderx_gpio_irq_set_type(struct irq_data *data, +static int thunderx_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); - struct thunderx_gpio *txgpio = txline->txgpio; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); + struct thunderx_line *txline = + &txgpio->line_entries[irqd_to_hwirq(d)]; u64 bit_cfg; - irqd_set_trigger_type(data, flow_type); + irqd_set_trigger_type(d, flow_type); bit_cfg = txline->fil_bits | GPIO_BIT_CFG_INT_EN; if (flow_type & IRQ_TYPE_EDGE_BOTH) { - irq_set_handler_locked(data, handle_fasteoi_ack_irq); + irq_set_handler_locked(d, handle_fasteoi_ack_irq); bit_cfg |= GPIO_BIT_CFG_INT_TYPE; } else { - irq_set_handler_locked(data, handle_fasteoi_mask_irq); + irq_set_handler_locked(d, handle_fasteoi_mask_irq); } raw_spin_lock(&txgpio->lock); @@ -359,33 +364,6 @@ static void thunderx_gpio_irq_disable(struct irq_data *data) irq_chip_disable_parent(data); } -static int thunderx_gpio_irq_request_resources(struct irq_data *data) -{ - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); - struct thunderx_gpio *txgpio = txline->txgpio; - int r; - - r = gpiochip_lock_as_irq(&txgpio->chip, txline->line); - if (r) - return r; - - r = irq_chip_request_resources_parent(data); - if (r) - gpiochip_unlock_as_irq(&txgpio->chip, txline->line); - - return r; -} - -static void thunderx_gpio_irq_release_resources(struct irq_data *data) -{ - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); - struct thunderx_gpio *txgpio = txline->txgpio; - - irq_chip_release_resources_parent(data); - - gpiochip_unlock_as_irq(&txgpio->chip, txline->line); -} - /* * Interrupts are chained from underlying MSI-X vectors. We have * these irq_chip functions to be able to handle level triggering @@ -402,48 +380,22 @@ static struct irq_chip thunderx_gpio_irq_chip = { .irq_unmask = thunderx_gpio_irq_unmask, .irq_eoi = irq_chip_eoi_parent, .irq_set_affinity = irq_chip_set_affinity_parent, - .irq_request_resources = thunderx_gpio_irq_request_resources, - .irq_release_resources = thunderx_gpio_irq_release_resources, .irq_set_type = thunderx_gpio_irq_set_type, .flags = IRQCHIP_SET_TYPE_MASKED }; -static int thunderx_gpio_irq_translate(struct irq_domain *d, - struct irq_fwspec *fwspec, - irq_hw_number_t *hwirq, - unsigned int *type) +static int thunderx_gpio_child_to_parent_hwirq(struct gpio_chip *gc, + unsigned int child, + unsigned int child_type, + unsigned int *parent, + unsigned int *parent_type) { - struct thunderx_gpio *txgpio = d->host_data; - - if (WARN_ON(fwspec->param_count < 2)) - return -EINVAL; - if (fwspec->param[0] >= txgpio->chip.ngpio) - return -EINVAL; - *hwirq = fwspec->param[0]; - *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; - return 0; -} - -static int thunderx_gpio_irq_alloc(struct irq_domain *d, unsigned int virq, - unsigned int nr_irqs, void *arg) -{ - struct thunderx_line *txline = arg; + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); - return irq_domain_set_hwirq_and_chip(d, virq, txline->line, - &thunderx_gpio_irq_chip, txline); -} - -static const struct irq_domain_ops thunderx_gpio_irqd_ops = { - .alloc = thunderx_gpio_irq_alloc, - .translate = thunderx_gpio_irq_translate -}; - -static int thunderx_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) -{ - struct thunderx_gpio *txgpio = gpiochip_get_data(chip); - - return irq_find_mapping(txgpio->irqd, offset); + *parent = txgpio->base_msi + (2 * child); + *parent_type = IRQ_TYPE_LEVEL_HIGH; + return 0; } static int thunderx_gpio_probe(struct pci_dev *pdev, @@ -453,6 +405,7 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, struct device *dev = &pdev->dev; struct thunderx_gpio *txgpio; struct gpio_chip *chip; + struct gpio_irq_chip *girq; int ngpio, i; int err = 0; @@ -497,8 +450,8 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, } txgpio->msix_entries = devm_kcalloc(dev, - ngpio, sizeof(struct msix_entry), - GFP_KERNEL); + ngpio, sizeof(struct msix_entry), + GFP_KERNEL); if (!txgpio->msix_entries) { err = -ENOMEM; goto out; @@ -539,27 +492,6 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, if (err < 0) goto out; - /* - * Push GPIO specific irqdomain on hierarchy created as a side - * effect of the pci_enable_msix() - */ - txgpio->irqd = irq_domain_create_hierarchy(irq_get_irq_data(txgpio->msix_entries[0].vector)->domain, - 0, 0, of_node_to_fwnode(dev->of_node), - &thunderx_gpio_irqd_ops, txgpio); - if (!txgpio->irqd) { - err = -ENOMEM; - goto out; - } - - /* Push on irq_data and the domain for each line. */ - for (i = 0; i < ngpio; i++) { - err = irq_domain_push_irq(txgpio->irqd, - txgpio->msix_entries[i].vector, - &txgpio->line_entries[i]); - if (err < 0) - dev_err(dev, "irq_domain_push_irq: %d\n", err); - } - chip->label = KBUILD_MODNAME; chip->parent = dev; chip->owner = THIS_MODULE; @@ -574,11 +506,28 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, chip->set = thunderx_gpio_set; chip->set_multiple = thunderx_gpio_set_multiple; chip->set_config = thunderx_gpio_set_config; - chip->to_irq = thunderx_gpio_to_irq; + girq = &chip->irq; + girq->chip = &thunderx_gpio_irq_chip; + girq->fwnode = of_node_to_fwnode(dev->of_node); + girq->parent_domain = + irq_get_irq_data(txgpio->msix_entries[0].vector)->domain; + girq->child_to_parent_hwirq = thunderx_gpio_child_to_parent_hwirq; + girq->handler = handle_bad_irq; + girq->default_type = IRQ_TYPE_NONE; + err = devm_gpiochip_add_data(dev, chip, txgpio); if (err) goto out; + /* Push on irq_data and the domain for each line. */ + for (i = 0; i < ngpio; i++) { + err = irq_domain_push_irq(chip->irq.domain, + txgpio->msix_entries[i].vector, + chip); + if (err < 0) + dev_err(dev, "irq_domain_push_irq: %d\n", err); + } + dev_info(dev, "ThunderX GPIO: %d lines with base %d.\n", ngpio, chip->base); return 0; @@ -593,10 +542,10 @@ static void thunderx_gpio_remove(struct pci_dev *pdev) struct thunderx_gpio *txgpio = pci_get_drvdata(pdev); for (i = 0; i < txgpio->chip.ngpio; i++) - irq_domain_pop_irq(txgpio->irqd, + irq_domain_pop_irq(txgpio->chip.irq.domain, txgpio->msix_entries[i].vector); - irq_domain_remove(txgpio->irqd); + irq_domain_remove(txgpio->chip.irq.domain); pci_set_drvdata(pdev, NULL); } From patchwork Mon Jun 24 13:25:31 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 167571 Delivered-To: patch@linaro.org Received: by 2002:a92:4782:0:0:0:0:0 with SMTP id e2csp4228666ilk; Mon, 24 Jun 2019 06:25:51 -0700 (PDT) X-Google-Smtp-Source: APXvYqxKpHP8/uaFDNX21vidsQOkb+N7tV/ggQAZrMhZf8YbaKeHT0TOfHeObPkEZBKK4TeEmDlG X-Received: by 2002:a17:90a:8c82:: with SMTP id b2mr24394113pjo.97.1561382751692; Mon, 24 Jun 2019 06:25:51 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1561382751; cv=none; d=google.com; s=arc-20160816; b=hx/BlgPZhuW8Q9luqcqB7uowX4ewJkv1aEI1w8d24tFlzuShj9UtkO4nMIJBigBqcx uR8TyyHZED0FZCD4SHTcwGjBxCrKLzugO3B5x3N+z0vdJmhf7NzaXDRTtheeMJ1axBW3 1cnnnLCsyT3mitRqMTZSTPGj5NYAecCAuZ+LVEOQHv9+AP1e2dDrD9ev6W4tr7JuQ5Jz 8k6l4dqBuZS2XG9HSvYzHqoQBnNBlunjOTeHEY/YO8C4zLnU5Q66v4eYNZDHiZOsOZZ8 5qYyu8+57uFi61LXrGXJU3nDc20BxnPZTrFgtyKZEn2gMJOOAGZT5UHZW/0t9OXBctId yLLA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=XvkEGh7ir7mmkkbpaDzcUH0nZuivAvkrC1Svkm3hBJg=; b=oVGWbcpbAHVluClNhpcIk+OwJkFCYdE7OEyG6Jbh20lc42ZTUDKTIzU438efUupNC6 WojwxkxPNikExXkHyRvJtawpDirA3J+VE3BC0XE6sHdfBwUBYPzZB+AWmGV0Am2hjO8d YZkevEcgVRXP5TjES97NIIAta8wuU61rHU8aEmN9cQI6iAZOfqMDmISwau9TZjf7Pvpi FGNgxOVkWOvpl/IGdGmerod8VZKeer12dQjKaTcrn0iX1QsvTOdhbQXiWIX9O5FnYnry jf+5qtLhWcmzvzwVRrerFQwuQUu9YzS2h6AdnQk7/Q2jXlOAZZtueeaHyrnlBQu1Dv27 uszg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=RXbZ+Ryz; spf=pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-gpio-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id l1si10373860plb.302.2019.06.24.06.25.51; Mon, 24 Jun 2019 06:25:51 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=RXbZ+Ryz; spf=pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-gpio-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729212AbfFXNZv (ORCPT + 5 others); Mon, 24 Jun 2019 09:25:51 -0400 Received: from mail-lj1-f194.google.com ([209.85.208.194]:33070 "EHLO mail-lj1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729145AbfFXNZu (ORCPT ); Mon, 24 Jun 2019 09:25:50 -0400 Received: by mail-lj1-f194.google.com with SMTP id h10so12643678ljg.0 for ; Mon, 24 Jun 2019 06:25:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=XvkEGh7ir7mmkkbpaDzcUH0nZuivAvkrC1Svkm3hBJg=; b=RXbZ+Ryz/YZDB1xCj6nlgT8INrIjdsVptbm0ipS6cOKtFsk08MWiXScTWrLyOq32+h fClN+yDmL4HtfAG/Y2BpeHBuHmFSRkaxFNSH7Y/yh4L453qhMujIOV3A93dN7P8LLjWE SiWHaGSbgDhaBu52bUilYFXRKOyVoeZUfbEDO4Cw0d9oA7ismDJYlXoB19lK0Ul4JRt+ RvpY3GjjrRGf7DmsKwgTpZ/oYQAM97wzLPKUZvVBVwSgD55VKrp3v3v2yoWxfSUsMuc9 T38uLiD/P3MFixZbXUu1E4PFOUoczdP3wwINej0Cxilw9jiAp0SdQGPDfp3AlwScU+h6 pTjA== 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:mime-version:content-transfer-encoding; bh=XvkEGh7ir7mmkkbpaDzcUH0nZuivAvkrC1Svkm3hBJg=; b=LT2IhFRq5pVT7ynnpl5CrrmnQVZmkXSE8cboHVOreUNwBtnrexVXWqIxJYZeEcSCUD tx3Mp80aPy7+0tOZIba0mF6F9vJ7l5Iozw2mK9f9YodkJ+51a0uoDdtk+BM9w244s4US XWsj/CU2i2hYPOMJbkC9pA1iPnvqztsk0VzOi14VFvvGe1wPRLO3UYgEAHEtn9zR6UpB EAginjC11ODLeOHZinWQgPxhC5EsLaN6/Kcw7lvSFh68rL8CDHqL7Sm/hU3Ta+D4KSRn uMBG3hj9jmmolUoLBLt2e2mW0XPI45D8+g9as+FkpNK1mW90S2Oai0OYx5HX2kWematJ JE+g== X-Gm-Message-State: APjAAAWMFWgWrlm0fxjSIS4ud3f/KtMFhUvGjNxk9xkphLjq891LTZwH aXXdiBT1EPCABDeTMcJkc1sHk/ObwH4= X-Received: by 2002:a2e:3c1a:: with SMTP id j26mr35361968lja.230.1561382747448; Mon, 24 Jun 2019 06:25:47 -0700 (PDT) Received: from localhost.bredbandsbolaget (c-22cd225c.014-348-6c756e10.bbcust.telenor.se. [92.34.205.34]) by smtp.gmail.com with ESMTPSA id t4sm416102ljh.9.2019.06.24.06.25.45 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Mon, 24 Jun 2019 06:25:46 -0700 (PDT) From: Linus Walleij To: linux-gpio@vger.kernel.org Cc: Bartosz Golaszewski , Linus Walleij , Masahiro Yamada , Thierry Reding , Brian Masney Subject: [PATCH 4/4 v1] RFT: gpio: uniphier: Switch to GPIOLIB_IRQCHIP Date: Mon, 24 Jun 2019 15:25:31 +0200 Message-Id: <20190624132531.6184-4-linus.walleij@linaro.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190624132531.6184-1-linus.walleij@linaro.org> References: <20190624132531.6184-1-linus.walleij@linaro.org> MIME-Version: 1.0 Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Use the new infrastructure for hierarchical irqchips in gpiolib. I have no chance to test or debug this so I need help. Cc: Masahiro Yamada Cc: Thierry Reding Cc: Brian Masney Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-uniphier.c | 172 +++++++++-------------------------- 2 files changed, 45 insertions(+), 128 deletions(-) -- 2.21.0 diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ee34716a39aa..806d642498a6 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -552,6 +552,7 @@ config GPIO_UNIPHIER tristate "UniPhier GPIO support" depends on ARCH_UNIPHIER || COMPILE_TEST depends on OF_GPIO + select GPIOLIB_IRQCHIP select IRQ_DOMAIN_HIERARCHY help Say yes here to support UniPhier GPIOs. diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c index 93cdcc41e9fb..e960836b9c01 100644 --- a/drivers/gpio/gpio-uniphier.c +++ b/drivers/gpio/gpio-uniphier.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -30,7 +29,6 @@ struct uniphier_gpio_priv { struct gpio_chip chip; struct irq_chip irq_chip; - struct irq_domain *domain; void __iomem *regs; spinlock_t lock; u32 saved_vals[0]; @@ -162,49 +160,33 @@ static void uniphier_gpio_set_multiple(struct gpio_chip *chip, } } -static int uniphier_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) +static void uniphier_gpio_irq_mask(struct irq_data *d) { - struct irq_fwspec fwspec; - - if (offset < UNIPHIER_GPIO_IRQ_OFFSET) - return -ENXIO; - - fwspec.fwnode = of_node_to_fwnode(chip->parent->of_node); - fwspec.param_count = 2; - fwspec.param[0] = offset - UNIPHIER_GPIO_IRQ_OFFSET; - /* - * IRQ_TYPE_NONE is rejected by the parent irq domain. Set LEVEL_HIGH - * temporarily. Anyway, ->irq_set_type() will override it later. - */ - fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; - - return irq_create_fwspec_mapping(&fwspec); -} - -static void uniphier_gpio_irq_mask(struct irq_data *data) -{ - struct uniphier_gpio_priv *priv = data->chip_data; - u32 mask = BIT(data->hwirq); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct uniphier_gpio_priv *priv = gpiochip_get_data(gc); + u32 mask = BIT(d->hwirq); uniphier_gpio_reg_update(priv, UNIPHIER_GPIO_IRQ_EN, mask, 0); - return irq_chip_mask_parent(data); + return irq_chip_mask_parent(d); } -static void uniphier_gpio_irq_unmask(struct irq_data *data) +static void uniphier_gpio_irq_unmask(struct irq_data *d) { - struct uniphier_gpio_priv *priv = data->chip_data; - u32 mask = BIT(data->hwirq); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct uniphier_gpio_priv *priv = gpiochip_get_data(gc); + u32 mask = BIT(d->hwirq); uniphier_gpio_reg_update(priv, UNIPHIER_GPIO_IRQ_EN, mask, mask); - return irq_chip_unmask_parent(data); + return irq_chip_unmask_parent(d); } -static int uniphier_gpio_irq_set_type(struct irq_data *data, unsigned int type) +static int uniphier_gpio_irq_set_type(struct irq_data *d, unsigned int type) { - struct uniphier_gpio_priv *priv = data->chip_data; - u32 mask = BIT(data->hwirq); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct uniphier_gpio_priv *priv = gpiochip_get_data(gc); + u32 mask = BIT(d->hwirq); u32 val = 0; if (type == IRQ_TYPE_EDGE_BOTH) { @@ -216,7 +198,7 @@ static int uniphier_gpio_irq_set_type(struct irq_data *data, unsigned int type) /* To enable both edge detection, the noise filter must be enabled. */ uniphier_gpio_reg_update(priv, UNIPHIER_GPIO_IRQ_FLT_EN, mask, val); - return irq_chip_set_type_parent(data, type); + return irq_chip_set_type_parent(d, type); } static int uniphier_gpio_irq_get_parent_hwirq(struct uniphier_gpio_priv *priv, @@ -245,82 +227,6 @@ static int uniphier_gpio_irq_get_parent_hwirq(struct uniphier_gpio_priv *priv, return -ENOENT; } -static int uniphier_gpio_irq_domain_translate(struct irq_domain *domain, - struct irq_fwspec *fwspec, - unsigned long *out_hwirq, - unsigned int *out_type) -{ - if (WARN_ON(fwspec->param_count < 2)) - return -EINVAL; - - *out_hwirq = fwspec->param[0]; - *out_type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; - - return 0; -} - -static int uniphier_gpio_irq_domain_alloc(struct irq_domain *domain, - unsigned int virq, - unsigned int nr_irqs, void *arg) -{ - struct uniphier_gpio_priv *priv = domain->host_data; - struct irq_fwspec parent_fwspec; - irq_hw_number_t hwirq; - unsigned int type; - int ret; - - if (WARN_ON(nr_irqs != 1)) - return -EINVAL; - - ret = uniphier_gpio_irq_domain_translate(domain, arg, &hwirq, &type); - if (ret) - return ret; - - ret = uniphier_gpio_irq_get_parent_hwirq(priv, hwirq); - if (ret < 0) - return ret; - - /* parent is UniPhier AIDET */ - parent_fwspec.fwnode = domain->parent->fwnode; - parent_fwspec.param_count = 2; - parent_fwspec.param[0] = ret; - parent_fwspec.param[1] = (type == IRQ_TYPE_EDGE_BOTH) ? - IRQ_TYPE_EDGE_FALLING : type; - - ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, - &priv->irq_chip, priv); - if (ret) - return ret; - - return irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec); -} - -static int uniphier_gpio_irq_domain_activate(struct irq_domain *domain, - struct irq_data *data, bool early) -{ - struct uniphier_gpio_priv *priv = domain->host_data; - struct gpio_chip *chip = &priv->chip; - - return gpiochip_lock_as_irq(chip, data->hwirq + UNIPHIER_GPIO_IRQ_OFFSET); -} - -static void uniphier_gpio_irq_domain_deactivate(struct irq_domain *domain, - struct irq_data *data) -{ - struct uniphier_gpio_priv *priv = domain->host_data; - struct gpio_chip *chip = &priv->chip; - - gpiochip_unlock_as_irq(chip, data->hwirq + UNIPHIER_GPIO_IRQ_OFFSET); -} - -static const struct irq_domain_ops uniphier_gpio_irq_domain_ops = { - .alloc = uniphier_gpio_irq_domain_alloc, - .free = irq_domain_free_irqs_common, - .activate = uniphier_gpio_irq_domain_activate, - .deactivate = uniphier_gpio_irq_domain_deactivate, - .translate = uniphier_gpio_irq_domain_translate, -}; - static void uniphier_gpio_hw_init(struct uniphier_gpio_priv *priv) { /* @@ -338,6 +244,26 @@ static unsigned int uniphier_gpio_get_nbanks(unsigned int ngpio) return DIV_ROUND_UP(ngpio, UNIPHIER_GPIO_LINES_PER_BANK); } +static int uniphier_gpio_child_to_parent_hwirq(struct gpio_chip *gc, + unsigned int child, + unsigned int child_type, + unsigned int *parent, + unsigned int *parent_type) +{ + struct uniphier_gpio_priv *priv = gpiochip_get_data(gc); + int ret; + + ret = uniphier_gpio_irq_get_parent_hwirq(priv, child); + if (ret < 0) + return ret; + *parent = ret; + if (child_type == IRQ_TYPE_EDGE_BOTH) + *parent_type = IRQ_TYPE_EDGE_FALLING; + else + *parent_type = child_type; + return 0; +} + static int uniphier_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -345,6 +271,7 @@ static int uniphier_gpio_probe(struct platform_device *pdev) struct irq_domain *parent_domain; struct uniphier_gpio_priv *priv; struct gpio_chip *chip; + struct gpio_irq_chip *girq; struct irq_chip *irq_chip; unsigned int nregs; u32 ngpios; @@ -386,7 +313,6 @@ static int uniphier_gpio_probe(struct platform_device *pdev) chip->get = uniphier_gpio_get; chip->set = uniphier_gpio_set; chip->set_multiple = uniphier_gpio_set_multiple; - chip->to_irq = uniphier_gpio_to_irq; chip->base = -1; chip->ngpio = ngpios; @@ -398,34 +324,25 @@ static int uniphier_gpio_probe(struct platform_device *pdev) irq_chip->irq_set_affinity = irq_chip_set_affinity_parent; irq_chip->irq_set_type = uniphier_gpio_irq_set_type; + girq = &chip->irq; + girq->chip = irq_chip; + girq->fwnode = of_node_to_fwnode(dev->of_node); + girq->parent_domain = parent_domain; + girq->child_to_parent_hwirq = uniphier_gpio_child_to_parent_hwirq; + girq->handler = handle_bad_irq; + girq->default_type = IRQ_TYPE_NONE; + uniphier_gpio_hw_init(priv); ret = devm_gpiochip_add_data(dev, chip, priv); if (ret) return ret; - priv->domain = irq_domain_create_hierarchy( - parent_domain, 0, - UNIPHIER_GPIO_IRQ_MAX_NUM, - of_node_to_fwnode(dev->of_node), - &uniphier_gpio_irq_domain_ops, priv); - if (!priv->domain) - return -ENOMEM; - platform_set_drvdata(pdev, priv); return 0; } -static int uniphier_gpio_remove(struct platform_device *pdev) -{ - struct uniphier_gpio_priv *priv = platform_get_drvdata(pdev); - - irq_domain_remove(priv->domain); - - return 0; -} - static int __maybe_unused uniphier_gpio_suspend(struct device *dev) { struct uniphier_gpio_priv *priv = dev_get_drvdata(dev); @@ -485,7 +402,6 @@ MODULE_DEVICE_TABLE(of, uniphier_gpio_match); static struct platform_driver uniphier_gpio_driver = { .probe = uniphier_gpio_probe, - .remove = uniphier_gpio_remove, .driver = { .name = "uniphier-gpio", .of_match_table = uniphier_gpio_match,