From patchwork Sun Jun 2 20:54:23 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 165581 Delivered-To: patch@linaro.org Received: by 2002:ac9:2a84:0:0:0:0:0 with SMTP id p4csp3472879oca; Sun, 2 Jun 2019 15:36:46 -0700 (PDT) X-Google-Smtp-Source: APXvYqzOlWfHaD7EjW26bz/bPEr8YW7Z+yZ6dqg9BZ88mbX3QC7MpRcLSkxudxLZEw1nGBrCN+Yr X-Received: by 2002:a17:902:26c:: with SMTP id 99mr26090518plc.215.1559508881596; Sun, 02 Jun 2019 13:54:41 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1559508881; cv=none; d=google.com; s=arc-20160816; b=A4m7NXX/HCVaX6jCCply9sTjXKUHfpEA8KJl3cg+BALE0WVT1JAlFfVnQ2QePAqaOi yGf4bGLtWH6hDgY8hdnvOM6M6VoW40RlySLTSeLNlOpFckwrMU2wrGHkjkYyosM0DhBe /y3pU1xv/or/WI4i3ll/MOHRgbupG+fyAIei2dfUCZAKGabQxIK3BNwP5MmDLacp7fCK sPS4spGaplPrharUPZVUrwtOzVYuApaRZDdDcUXw+HLw5EjRE92NA3paeQmEaev3sQSl Kv6rBO3dq1jf9SWL1eZFnq55OJCELkLOxQnD92pBGucSyNobiMH1Z+oZ32dclRIvLqFd xicQ== 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=FDPHE1Omey9pKh6uQlk5sF4pqwqc0zOMcpBtUnWLsMo=; b=ZwsCEqi4gTV2nyt0gg9YbLMJ0GQAZtymXpoM8J373qOUEfnxsUh0io8IMe9t+dn9iz DYBTaYbSmZg5jjAq4cfpBTHIj0HBr8HVGyiimQmKYaiZZyu7beaemKopV0+EFxO4GNcP oSlTigBvMWoDVDwvKHRF5gOWDo0BK+Mx+9HFCacDMNAKY/Nyf+ysydrPZUYzVeKoPE/j qYFbgZexFaAHjC55PQ2lzaWHSilFNxVjpUst1SFnNDl1gUXQjrXbEr/b1Ik7pZScmMQZ jCrZvAlygqKOBJ6cSyLeK4FyB3MA0buOaHbUkuW+e+iV4chNpQ5Fnc9V0nHYxo1+SMxT KFxw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=UqQnTvd4; 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 s24si933950plq.23.2019.06.02.13.54.38; Sun, 02 Jun 2019 13:54:41 -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=UqQnTvd4; 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 S1726270AbfFBUyc (ORCPT + 5 others); Sun, 2 Jun 2019 16:54:32 -0400 Received: from mail-wr1-f65.google.com ([209.85.221.65]:39060 "EHLO mail-wr1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726869AbfFBUyc (ORCPT ); Sun, 2 Jun 2019 16:54:32 -0400 Received: by mail-wr1-f65.google.com with SMTP id x4so9967866wrt.6 for ; Sun, 02 Jun 2019 13:54:30 -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=FDPHE1Omey9pKh6uQlk5sF4pqwqc0zOMcpBtUnWLsMo=; b=UqQnTvd4c2oorFEg4gg+JA0RUGFWpZc1MSAg9snrb2Lv8/IE0vQ3r6c1waGQQPDPzQ lh5dN64I78dnoTRb3rmrXvitYVmwKKY1zTGBvcjWygdrm4nwpgGpsaRDQroxutrHi1HI 62vyDjlKHsPcio0Y6sXTrz2fBecgD+DYs8YmS7IoBSRkBM+7i9879cB8nWQPbMzPnLRq /e7kuNKH4KIqkN21JilZQNZYE8f7aYbFhCgoAbRd8US3OKytArZlBI/KZ4IeBApQ10bS N36icgeLsgUiOPIVyqRXNLuREip3G/fJWJJsMQSFXoZ1EsNe4dbvQt5rVFxl/ZRalA7x /pEw== 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=FDPHE1Omey9pKh6uQlk5sF4pqwqc0zOMcpBtUnWLsMo=; b=WQIVVJIXKc1r1nMJ1uDYnhE7aBVYjOG7FELWgx79X6ywKvyepeOw8CT+Gy8efxThbO zBt+pA3dzOio4TnZ70z0+t4iDoVb3gfXm68/nP1ZreuzvbpRc1wCfGd18Su33hBDfgn2 yTTlMM4ut762NTZReM0N/FuFlNQaGfMIcvl0YNEszwmLqoOwb3dsFsFreiEZIvtJAo/t bgaEv7YS+5IP9HDm1zhkeDc4AleRIGG8zQSLPdDT+4E3tLhEdlmLlYbLscN2JGIsx/Y5 cxz2cErLfgKAB1AqHEMldyQYhnE936NGhI/lD/TiJPFrD5MfXs11otBa2gdsQ3RMPRcp Czlg== X-Gm-Message-State: APjAAAXTnu4s+Q7qz764b2FOHX+bIXBra2SKB/cFqlG63V1nkxjnJR+E VhPIVOEGg5IGe1dnrx06kCPGa5DAPPY= X-Received: by 2002:a05:6000:1150:: with SMTP id d16mr74996wrx.63.1559508867902; Sun, 02 Jun 2019 13:54:27 -0700 (PDT) Received: from localhost.localdomain (catv-89-135-96-219.catv.broadband.hu. [89.135.96.219]) by smtp.gmail.com with ESMTPSA id h90sm38620365wrh.15.2019.06.02.13.54.27 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Sun, 02 Jun 2019 13:54:27 -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 Subject: [PATCH 1/2] RFC: gpio: Add support for hierarchical IRQ domains Date: Sun, 2 Jun 2019 22:54:23 +0200 Message-Id: <20190602205424.28674-1-linus.walleij@linaro.org> X-Mailer: git-send-email 2.20.1 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 map of one element per interrupt where the hwirq on the GPIO chip is mapped to the hwirq on the parent irqchip along with type. Users have to pass the interrupt map, fwnode, and parent irqdomain before calling gpiochip_irqchip_add(). The consumer will then call gpiochio_set_hierarchical_irqchip() analogous to the earlier functions for chained and nested irqchips. 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. Cc: Thomas Gleixner Cc: Marc Zyngier Cc: Lina Iyer Cc: Jon Hunter Cc: Sowjanya Komatineni Cc: Bitan Biswas Cc: linux-tegra@vger.kernel.org Signed-off-by: Thierry Reding Signed-off-by: Linus Walleij --- This is my idea for deeper integration of hierarchical irqs into the gpiolib core. Admittedly based on my own IXP4xx driver which is provided as an example conversion in patch 2/2, let me know what you think. Based on heavy patching on top of Thierry's patch, not mangled beyond recognition, sorry for that, I just wanted to convey my idea here. --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpiolib.c | 218 ++++++++++++++++++++++++++++++++++-- include/linux/gpio/driver.h | 54 +++++++++ 3 files changed, 266 insertions(+), 7 deletions(-) -- 2.20.1 Signed-off-by: Thierry Reding diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3d17d40fa635..23a121c2e176 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -45,6 +45,7 @@ config GPIO_ACPI config GPIOLIB_IRQCHIP select IRQ_DOMAIN + select IRQ_DOMAIN_HIERARCHY bool config DEBUG_GPIO diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e013d417a936..f976e95e54f5 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1718,6 +1718,175 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gpiochip, } EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip); +/** + * gpiochip_set_hierarchical_irqchip() - connects a hierarchical irqchip + * to a gpiochip + * @gpiochip: 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 + */ +void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gpiochip, + struct irq_chip *irqchip) +{ + if (!gpiochip->irq.domain) { + chip_err(gpiochip, "called %s before setting up irqchip\n", + __func__); + return; + } + if (!gpiochip->irq.parent_domain) { + chip_err(gpiochip, "tried to create a hierarchy on non-hierarchical GPIO irqchip\n"); + return; + } + /* DT will deal with mapping each IRQ as we go along */ + if (is_of_node(gpiochip->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. + */ + if (is_fwnode_irqchip(gpiochip->irq.fwnode)) { + int i; + int ret; + + for (i = 0; i < gpiochip->irq.parent_n_irq_maps; i++) { + const struct gpiochip_hierarchy_map *map = + &gpiochip->irq.parent_irq_map[i]; + struct irq_fwspec fwspec; + + fwspec.fwnode = gpiochip->irq.fwnode; + /* This is the hwirq for the GPIO line side of things */ + fwspec.param[0] = map->hwirq; + /* Just pick something */ + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; + fwspec.param_count = 2; + ret = __irq_domain_alloc_irqs(gpiochip->irq.domain, + /* just pick something */ + -1, + 1, + NUMA_NO_NODE, + &fwspec, + false, + NULL); + if (ret < 0) { + chip_err(gpiochip, + "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n", + map->hwirq, map->parent_hwirq, + ret); + } + } + } + + chip_err(gpiochip, "%s unknown fwnode type proceed anyway\n", __func__); + + return; +} +EXPORT_SYMBOL_GPL(gpiochip_set_hierarchical_irqchip); + +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 *chip = d->host_data; + irq_hw_number_t hwirq; + unsigned int type = IRQ_TYPE_NONE; + struct irq_fwspec *fwspec = data; + int ret; + int i; + + ret = gpiochip_hierarchy_irq_domain_translate(d, fwspec, &hwirq, &type); + if (ret) + return ret; + + chip_info(chip, "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 gpiochip_hierarchy_map *map = NULL; + int j; + + /* Find the map, maybe not all lines support IRQs */ + for (j = 0; j < chip->irq.parent_n_irq_maps; j++) { + map = &chip->irq.parent_irq_map[j]; + if (map->hwirq == hwirq) + break; + } + if (j == chip->irq.parent_n_irq_maps) { + chip_err(chip, "can't look up hwirq %lu\n", hwirq); + return -EINVAL; + } + chip_dbg(chip, "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, + chip->irq.chip, + chip, + 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] = map->parent_type; + chip_dbg(chip, "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) + chip_err(chip, + "failed to allocate parent hwirq %d for hwirq %lu\n", + map->parent_hwirq, hwirq); + } + + return 0; +} + +static const struct irq_domain_ops gpiochip_hierarchy_domain_ops = { + .translate = gpiochip_hierarchy_irq_domain_translate, + .alloc = gpiochip_hierarchy_irq_domain_alloc, + .free = irq_domain_free_irqs_common, +}; + /** * gpiochip_irq_map() - maps an IRQ into a GPIO irqchip * @d: the irqdomain used by this irqchip @@ -1825,10 +1994,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 +2087,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,14 +2123,36 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, gpiochip->irq.lock_key = lock_key; gpiochip->irq.request_key = request_key; + /* Some drivers provide custom irqdomain ops */ 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 a parent irqdomain is provided, let's build a hierarchy */ + if (gpiochip->irq.parent_domain) { + if (!ops) + ops = &gpiochip_hierarchy_domain_ops; + if (!gpiochip->irq.parent_domain || + !gpiochip->irq.parent_irq_map || + !gpiochip->irq.parent_n_irq_maps || + !gpiochip->irq.fwnode) { + chip_err(gpiochip, "missing irqdomain vital data\n"); + return -EINVAL; + } + gpiochip->irq.domain = irq_domain_create_hierarchy( + gpiochip->irq.parent_domain, + IRQ_DOMAIN_FLAG_HIERARCHY, + gpiochip->irq.parent_n_irq_maps, + gpiochip->irq.fwnode, + ops, + gpiochip); + } else { + 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; diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index a1d273c96016..65193878a0e1 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -22,6 +22,21 @@ enum gpiod_flags; #ifdef CONFIG_GPIOLIB #ifdef CONFIG_GPIOLIB_IRQCHIP + +/** + * struct gpiochip_hierarchy_map - hierarchical IRQ GPIO to parent IRQ map + * @hwirq: offset of the GPIO line (irq line) locally on the gpio_chip + * @parent_hwirq: hwirq on the parent IRQ controller that this local hardware + * irq is mapped to + * @parent_type: what type of IRQ the parent uses for this line, such + * as most typical IRQ_TYPE_LEVEL_HIGH. + */ +struct gpiochip_hierarchy_map { + int hwirq; + int parent_hwirq; + unsigned int parent_type; +}; + /** * struct gpio_irq_chip - GPIO interrupt controller */ @@ -48,6 +63,42 @@ struct gpio_irq_chip { */ const struct irq_domain_ops *domain_ops; + /** + * @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; + + /** + * @parent_irq_map: + * + * This should contain an array defining the maps between any hardware + * GPIO line on the gpio_chip and the corresponding hardware IRQ on the + * parent IRQ controller (irqchip) and the parent IRQ type for the + * line when using hierarchical interrupts. + */ + const struct gpiochip_hierarchy_map *parent_irq_map; + + /** + * @parent_n_irq_maps: + * + * The number of parent irq map tuples in the array above, used for + * hierarchical interrupt support. + */ + int parent_n_irq_maps; + /** * @handler: * @@ -489,6 +540,9 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gpiochip, struct irq_chip *irqchip, unsigned int parent_irq); +void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gpiochip, + struct irq_chip *irqchip); + int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip, struct irq_chip *irqchip, unsigned int first_irq, From patchwork Sun Jun 2 20:54:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 165582 Delivered-To: patch@linaro.org Received: by 2002:ac9:2a84:0:0:0:0:0 with SMTP id p4csp3474358oca; Sun, 2 Jun 2019 15:39:09 -0700 (PDT) X-Google-Smtp-Source: APXvYqzn583nhAppIEev/vr/IOPzLF23QFY72eO4xMaSQrNhwyu1flI0bWYEKoeFFDx3DO9VWA7q X-Received: by 2002:a17:90a:a505:: with SMTP id a5mr7917245pjq.27.1559508881879; Sun, 02 Jun 2019 13:54:41 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1559508881; cv=none; d=google.com; s=arc-20160816; b=UtW+Jb8Cphe8VRMSPmJl88pqwZcBzxbS2iPGQlckbAI4GklcQJ9bppcuLDAWpuvrND E/rX/B1aUgt9eLRWnjMWR3M3zbaEQGLMwpVVkF0R/QMLvV6wyirbL2QQY+OxDcEHP7/4 xJu5+PtNbhmAQSxqoXCbR/iIomyP8+zwKragZ/LX7J4536hR+DDbkZuERW/I6wetj9a6 /d6MtsQyjtOYdnVl5OzZgJ7sQME5ozyB0oLDV+MoCw7BRrZkvhjnHYwF6JxHmsaNwjKw AYJhwZx090xJBfm/DrxFDsT/2BUx0oU0Ip8cGEqylqLrIPTdAIQYId92d/wRUb72gGgN NdzA== 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=86Q/XsXcrnKtDhtp2ZmVwkDN4kscs4/XU9T8ExBYYqU=; b=q+rCm7rr/AQRNuSpdB3kTUHsB/7VNHhsFUbI1jZ6i5YRA+MZSB+n78Iss7luyXxptg 0LVRgEVa+TSOj+oybKieKBqKCYvHaOqjYtjqR0LvZr0KhvbEW/ln5nGubCxpWZ0Ip6m5 XJ0Tl47jfumDMRQmY0PI7tRG62YrdTRs+d1qd9srtLpFiEO0SdzjI4YtgsjmJaXVSQu9 0LybqFTX1oeJhyGGulPA0hr6hHhsjSsu0ShC1KNfevFUqJzXXT1a7tyQNi1S44xdi4Lr QqaMeRu/dC96ZPmdZI4FIfpfJtijxjYryLSImoazaw8buxbRf+M7D/WIW54y/nKn4E71 aYKA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=OStQnlQl; 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 s24si933950plq.23.2019.06.02.13.54.41; Sun, 02 Jun 2019 13:54:41 -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=OStQnlQl; 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 S1726869AbfFBUyg (ORCPT + 5 others); Sun, 2 Jun 2019 16:54:36 -0400 Received: from mail-wr1-f66.google.com ([209.85.221.66]:43509 "EHLO mail-wr1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726908AbfFBUyg (ORCPT ); Sun, 2 Jun 2019 16:54:36 -0400 Received: by mail-wr1-f66.google.com with SMTP id r18so943353wrm.10 for ; Sun, 02 Jun 2019 13:54:34 -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=86Q/XsXcrnKtDhtp2ZmVwkDN4kscs4/XU9T8ExBYYqU=; b=OStQnlQl4MR49Ax7IM7BNsCSy6DLq4ekQpsf1qSYB+BK5rQbM4F1+QtMCCsx3JYseD +ni5WUCzRHp1cAe1q3+cl0gIyrqTS4/0p1cwRC+rFLq6SJ4anrZARNBxqtwYH2/ch18W WEBrbFJH41s2nn2ivhFcLI1nprn1msD6/TgfjHqOzrNM7VCfrQtZzioEcoChsqf74wqT fOEADXapl9ibNg/CWg7Z6cGATKOHQhx29y806fHF8Ft1AoB1QBz4cQ6RKVvoqbiQ5M8A 1ds5RUGvl3xFAWjvoFdxgH+BL5rXaMg5C+4qLiBZbIM3Hglsha5PwSwG7f6jhr9Yg4rx sVxw== 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=86Q/XsXcrnKtDhtp2ZmVwkDN4kscs4/XU9T8ExBYYqU=; b=okD+9mTfqnTk7uOZ0mpI0lZaLRoZT3HsgqXWcuhW/c3ZiHww2JBPOf8mPn3nWUaKu5 ulGFyDcKa+YZWC4xZ2lT/1U9Gdjaq5b1vyLQkxWOx9U7gvQp/dVue5RqBmtK71YSrpZ+ /FwwomqzzJFOT+1A7EhL6A8dYwp0LACRyq5+sm0jURkgNlO/qk/nqlEL3qnVlhI3hnmU Herm8+6zg96MhrIAjaOYOWNa7SZTAPKBg2nqdHENBbECzLoApapu/farsQuFtqb9+J90 tCtmBUfqUlcuZSShC/mQP3xH5x4x2iZNm1Z9uaAZAFm75K0OLmuXIm2SemNZ3zypU0kQ 2vcQ== X-Gm-Message-State: APjAAAXJBt9DdIE2C9N0/OlFGLHSgaAxzkjJRCwcNEWBaHj4qHmRC+Sv HzMZe+0lt13lyC15xJc5V7SVg8XERSs= X-Received: by 2002:a5d:6191:: with SMTP id j17mr49976wru.172.1559508873212; Sun, 02 Jun 2019 13:54:33 -0700 (PDT) Received: from localhost.localdomain (catv-89-135-96-219.catv.broadband.hu. [89.135.96.219]) by smtp.gmail.com with ESMTPSA id h90sm38620365wrh.15.2019.06.02.13.54.30 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Sun, 02 Jun 2019 13:54:31 -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 Subject: [PATCH 2/2] RFC: gpio: ixp4xx: Convert to hieararchical GPIOLIB_IRQCHIP Date: Sun, 2 Jun 2019 22:54:24 +0200 Message-Id: <20190602205424.28674-2-linus.walleij@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190602205424.28674-1-linus.walleij@linaro.org> References: <20190602205424.28674-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. Cc: Thomas Gleixner Cc: Marc Zyngier Cc: Lina Iyer Cc: Jon Hunter Cc: Sowjanya Komatineni Cc: Bitan Biswas Cc: linux-tegra@vger.kernel.org Signed-off-by: Linus Walleij --- Warning: not even compile tested as my IXP4xx setup is at home and I'm travelling. I just wanted to get this out there as an example of how I imagine this would work with existing drivers. --- drivers/gpio/Kconfig | 3 +- drivers/gpio/gpio-ixp4xx.c | 211 +++++-------------------------------- 2 files changed, 28 insertions(+), 186 deletions(-) -- 2.20.1 diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 23a121c2e176..3aef6327f994 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -289,8 +289,7 @@ config GPIO_IXP4XX depends on ARM # For depends on ARCH_IXP4XX select GPIO_GENERIC - select IRQ_DOMAIN - select IRQ_DOMAIN_HIERARCHY + select GPIOLIB_IRQCHIP help Say yes here to support the GPIO functionality of a number of Intel IXP4xx series of chips. diff --git a/drivers/gpio/gpio-ixp4xx.c b/drivers/gpio/gpio-ixp4xx.c index 670c2a85a35b..429d9d519ed4 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,36 +55,25 @@ 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 }, +const struct gpiochip_hierarchy_map ixp4xx_gpiomap[] = { + { .hwirq = 0, .parent_hwirq = 6, .parent_type = IRQ_TYPE_LEVEL_HIGH }, + { .hwirq = 1, .parent_hwirq = 7, .parent_type = IRQ_TYPE_LEVEL_HIGH }, + { .hwirq = 2, .parent_hwirq = 19, .parent_type = IRQ_TYPE_LEVEL_HIGH }, + { .hwirq = 3, .parent_hwirq = 20, .parent_type = IRQ_TYPE_LEVEL_HIGH }, + { .hwirq = 4, .parent_hwirq = 21, .parent_type = IRQ_TYPE_LEVEL_HIGH }, + { .hwirq = 5, .parent_hwirq = 22, .parent_type = IRQ_TYPE_LEVEL_HIGH }, + { .hwirq = 6, .parent_hwirq = 23, .parent_type = IRQ_TYPE_LEVEL_HIGH }, + { .hwirq = 7, .parent_hwirq = 24, .parent_type = IRQ_TYPE_LEVEL_HIGH }, + { .hwirq = 8, .parent_hwirq = 25, .parent_type = IRQ_TYPE_LEVEL_HIGH }, + { .hwirq = 9, .parent_hwirq = 26, .parent_type = IRQ_TYPE_LEVEL_HIGH }, + { .hwirq = 10, .parent_hwirq = 27, .parent_type = IRQ_TYPE_LEVEL_HIGH }, + { .hwirq = 11, .parent_hwirq = 28, .parent_type = IRQ_TYPE_LEVEL_HIGH }, + { .hwirq = 12, .parent_hwirq = 29, .parent_type = IRQ_TYPE_LEVEL_HIGH }, }; static void ixp4xx_gpio_irq_ack(struct irq_data *d) @@ -187,122 +175,6 @@ 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) -{ - int ret; - - /* We support standard DT translation */ - if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) { - return irq_domain_translate_twocell(domain, fwspec, - hwirq, type); - } - - /* 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); - 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); - } - - return 0; -} - -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; @@ -406,49 +278,20 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) 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; - } - } - } + g->gc.irq.fwnode = g->fwnode; + g->gc.irq.parent_domain = parent; + g->gc.irq.parent_irq_map = &ixp4xx_gpiomap; + g->gc.irq.parent_n_irq_maps = ARRAY_SIZE(ixp4xx_gpiomap); + ret = gpiochip_irqchip_add(&g->gc, &ixp4xx_gpio_irqchip, + 0, handle_bad_irq, + IRQ_TYPE_NONE); + if (ret) { + dev_info(dev, "could not add irqchip\n"); + irq_domain_free_fwnode(g->fwnode); + return -ENODEV; + } + gpiochip_set_hierarchical_irqchip(&g->gc, &ixp4xx_gpio_irqchip); platform_set_drvdata(pdev, g); dev_info(dev, "IXP4 GPIO @%p registered\n", g->base);