{"id":819520,"url":"http://patchwork.ozlabs.org/api/patches/819520/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-gpio/patch/20170928095628.21966-2-thierry.reding@gmail.com/","project":{"id":42,"url":"http://patchwork.ozlabs.org/api/projects/42/?format=json","name":"Linux GPIO development","link_name":"linux-gpio","list_id":"linux-gpio.vger.kernel.org","list_email":"linux-gpio@vger.kernel.org","web_url":"","scm_url":"","webscm_url":"","list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20170928095628.21966-2-thierry.reding@gmail.com>","list_archive_url":null,"date":"2017-09-28T09:56:13","name":"[v2,01/16] gpio: Implement tighter IRQ chip integration","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"5e183c8dd91ae0591a24de002f0a1c7d7fb960c0","submitter":{"id":26234,"url":"http://patchwork.ozlabs.org/api/people/26234/?format=json","name":"Thierry Reding","email":"thierry.reding@gmail.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/linux-gpio/patch/20170928095628.21966-2-thierry.reding@gmail.com/mbox/","series":[{"id":5529,"url":"http://patchwork.ozlabs.org/api/series/5529/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-gpio/list/?series=5529","date":"2017-09-28T09:56:12","name":"gpio: Tight IRQ chip integration and banked infrastructure","version":2,"mbox":"http://patchwork.ozlabs.org/series/5529/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/819520/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/819520/checks/","tags":{},"related":[],"headers":{"Return-Path":"<linux-gpio-owner@vger.kernel.org>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@bilbo.ozlabs.org","Authentication-Results":["ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=linux-gpio-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"g7xT6BUe\"; dkim-atps=neutral"],"Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3y2qtx5nBnz9t5x\n\tfor <incoming@patchwork.ozlabs.org>;\n\tThu, 28 Sep 2017 20:00:25 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1752629AbdI1J4j (ORCPT <rfc822;incoming@patchwork.ozlabs.org>);\n\tThu, 28 Sep 2017 05:56:39 -0400","from mail-qt0-f196.google.com ([209.85.216.196]:49852 \"EHLO\n\tmail-qt0-f196.google.com\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S1752591AbdI1J4g (ORCPT\n\t<rfc822; linux-gpio@vger.kernel.org>); Thu, 28 Sep 2017 05:56:36 -0400","by mail-qt0-f196.google.com with SMTP id o3so859454qte.6;\n\tThu, 28 Sep 2017 02:56:36 -0700 (PDT)","from localhost\n\t(p200300E41BCC8100EA54DC343767CF80.dip0.t-ipconnect.de.\n\t[2003:e4:1bcc:8100:ea54:dc34:3767:cf80])\n\tby smtp.gmail.com with ESMTPSA id\n\tw127sm715000qkc.95.2017.09.28.02.56.34\n\t(version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256);\n\tThu, 28 Sep 2017 02:56:35 -0700 (PDT)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=gmail.com; s=20161025;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references;\n\tbh=YiGXZarOMK7RbFSj49mffONtsLV955fwIJeqcgKq5oY=;\n\tb=g7xT6BUew8g+5mn0szX5ADW6/loYy+d7mumq9rY0qnIQCmFBHTCPMVs8zBufGucuZw\n\tMWwN03BIhbqErwspEPwCxPSnQVFNiE06qcd+lu1Y4ecd+ODnoNG27hWlKNJG2pdyfVje\n\tyOkyy4igPOu2tpXg31v4JUP1/1shQOf0EW0Qw7TfI893EbDfrfvg3faNvTeNQsyjW3ad\n\tXMn02z5dUCgofLd0XrczUKnm28HgpputEeEE8cEXvuln4Dj7gGhUC+eHymuUB2QIYkIN\n\tn+DHfN62/L8GkV9QSzcHsiudL3PPtMvBggtyl81pcncqDE6vN1QuY4xxLGvCKEQdR6Io\n\tJ1dg==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references;\n\tbh=YiGXZarOMK7RbFSj49mffONtsLV955fwIJeqcgKq5oY=;\n\tb=rRevQcmfhnfDWfFvHOPjURuqCL7qjd3RRhWkP5Hyoyvi026ksMZhqBJkvXHbZIuF2/\n\tbdZNfOumQ7klJB8tL/A2zTXteie2GWSgyXIa33eor2ZPtSto323+u5wRthn01RbA79gJ\n\tLvqmWPkRKGcciG9GBAhUjZ7PQQ2+UOYszO1mxrOWGcASBs1/z4uSI9e1VWAq1Azdblx9\n\tBpSJCeaQ/OlPKXqAKn0gwBKV2ycHRVaFrt+cl7glXRfDdX/DKVsF6OMR3lSLwhNZc/W/\n\tzyd33dAdG4XqiGlHgRTKqJZFgKuOdDj/YnwkOyjpaq/WQ5Ar+UezPigCvttSng34p3Fd\n\trVig==","X-Gm-Message-State":"AMCzsaUfZkemXyttUWunIvoUH5BjTlZGACaGZxQa/OwX6g4k+494FUWh\n\tpMVKNZFwcmzGpQo7dKDhMoA=","X-Google-Smtp-Source":"AOwi7QADPxjyPYOFQG7qZL0VsYRcWkWh8+03DziX3vuIX7qQNBBDFB11KRdzS7mIsSvVgPhqbIfH9A==","X-Received":"by 10.200.56.215 with SMTP id g23mr25056qtc.238.1506592595876;\n\tThu, 28 Sep 2017 02:56:35 -0700 (PDT)","From":"Thierry Reding <thierry.reding@gmail.com>","To":"Linus Walleij <linus.walleij@linaro.org>","Cc":"Jonathan Hunter <jonathanh@nvidia.com>, linux-gpio@vger.kernel.org,\n\tlinux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org","Subject":"[PATCH v2 01/16] gpio: Implement tighter IRQ chip integration","Date":"Thu, 28 Sep 2017 11:56:13 +0200","Message-Id":"<20170928095628.21966-2-thierry.reding@gmail.com>","X-Mailer":"git-send-email 2.14.1","In-Reply-To":"<20170928095628.21966-1-thierry.reding@gmail.com>","References":"<20170928095628.21966-1-thierry.reding@gmail.com>","Sender":"linux-gpio-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<linux-gpio.vger.kernel.org>","X-Mailing-List":"linux-gpio@vger.kernel.org"},"content":"From: Thierry Reding <treding@nvidia.com>\n\nCurrently GPIO drivers are required to a GPIO chip and the corresponding\nIRQ chip separately, which can result in a lot of boilerplate. Introduce\na new struct gpio_irq_chip, embedded in a struct gpio_chip, that drivers\ncan fill in if they want the GPIO core to automatically register the IRQ\nchip associated with a GPIO chip.\n\nSigned-off-by: Thierry Reding <treding@nvidia.com>\n---\n drivers/gpio/gpiolib.c      | 146 +++++++++++++++++++++++++++++++++++++++++++-\n include/linux/gpio/driver.h |  64 +++++++++++++++++++\n 2 files changed, 207 insertions(+), 3 deletions(-)","diff":"diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c\nindex eb80dac4e26a..b34d9cbd5809 100644\n--- a/drivers/gpio/gpiolib.c\n+++ b/drivers/gpio/gpiolib.c\n@@ -72,6 +72,7 @@ static LIST_HEAD(gpio_lookup_list);\n LIST_HEAD(gpio_devices);\n \n static void gpiochip_free_hogs(struct gpio_chip *chip);\n+static int gpiochip_add_irqchip(struct gpio_chip *gpiochip);\n static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);\n static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip);\n static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip);\n@@ -1260,6 +1261,10 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)\n \tif (status)\n \t\tgoto err_remove_from_list;\n \n+\tstatus = gpiochip_add_irqchip(chip);\n+\tif (status)\n+\t\tgoto err_remove_chip;\n+\n \tstatus = of_gpiochip_add(chip);\n \tif (status)\n \t\tgoto err_remove_chip;\n@@ -1626,8 +1631,8 @@ EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip);\n  * gpiochip by assigning the gpiochip as chip data, and using the irqchip\n  * stored inside the gpiochip.\n  */\n-static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,\n-\t\t\t    irq_hw_number_t hwirq)\n+int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,\n+\t\t     irq_hw_number_t hwirq)\n {\n \tstruct gpio_chip *chip = d->host_data;\n \n@@ -1655,8 +1660,9 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,\n \n \treturn 0;\n }\n+EXPORT_SYMBOL_GPL(gpiochip_irq_map);\n \n-static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq)\n+void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq)\n {\n \tstruct gpio_chip *chip = d->host_data;\n \n@@ -1665,6 +1671,7 @@ static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq)\n \tirq_set_chip_and_handler(irq, NULL, NULL);\n \tirq_set_chip_data(irq, NULL);\n }\n+EXPORT_SYMBOL_GPL(gpiochip_irq_unmap);\n \n static const struct irq_domain_ops gpiochip_domain_ops = {\n \t.map\t= gpiochip_irq_map,\n@@ -1705,6 +1712,124 @@ static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset)\n \treturn irq_create_mapping(chip->irqdomain, offset);\n }\n \n+/**\n+ * gpiochip_add_irqchip() - adds an IRQ chip to a GPIO chip\n+ * @gpiochip: the GPIO chip to add the IRQ chip to\n+ */\n+static int gpiochip_add_irqchip(struct gpio_chip *gpiochip)\n+{\n+\tstruct irq_chip *irqchip = gpiochip->irqchip;\n+\tconst struct irq_domain_ops *ops;\n+\tstruct device_node *np;\n+\tunsigned int type;\n+\tunsigned int i;\n+\n+\tif (!irqchip)\n+\t\treturn 0;\n+\n+\tif (gpiochip->irq.parent_handler && gpiochip->can_sleep) {\n+\t\tchip_err(gpiochip, \"you cannot have chained interrupts on a \"\n+\t\t\t \"chip that may sleep\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\ttype = gpiochip->irq_default_type;\n+\tnp = gpiochip->parent->of_node;\n+\n+#ifdef CONFIG_OF_GPIO\n+\t/*\n+\t * If the gpiochip has an assigned OF node this takes precedence\n+\t * FIXME: get rid of this and use gpiochip->parent->of_node\n+\t * everywhere\n+\t */\n+\tif (gpiochip->of_node)\n+\t\tnp = gpiochip->of_node;\n+#endif\n+\n+\t/*\n+\t * Specifying a default trigger is a terrible idea if DT or ACPI is\n+\t * used to configure the interrupts, as you may end up with\n+\t * conflicting triggers. Tell the user, and reset to NONE.\n+\t */\n+\tif (WARN(np && type != IRQ_TYPE_NONE,\n+\t\t \"%s: Ignoring %u default trigger\\n\", np->full_name, type))\n+\t\ttype = IRQ_TYPE_NONE;\n+\n+\tif (has_acpi_companion(gpiochip->parent) && type != IRQ_TYPE_NONE) {\n+\t\tacpi_handle_warn(ACPI_HANDLE(gpiochip->parent),\n+\t\t\t\t \"Ignoring %u default trigger\\n\", type);\n+\t\ttype = IRQ_TYPE_NONE;\n+\t}\n+\n+\tgpiochip->to_irq = gpiochip_to_irq;\n+\tgpiochip->irq_default_type = type;\n+\n+\tif (gpiochip->irq.domain_ops)\n+\t\tops = gpiochip->irq.domain_ops;\n+\telse\n+\t\tops = &gpiochip_domain_ops;\n+\n+\tgpiochip->irqdomain = irq_domain_add_simple(np, gpiochip->ngpio,\n+\t\t\t\t\t\t    gpiochip->irq_base,\n+\t\t\t\t\t\t    ops, gpiochip);\n+\tif (!gpiochip->irqdomain)\n+\t\treturn -EINVAL;\n+\n+\t/*\n+\t * It is possible for a driver to override this, but only if the\n+\t * alternative functions are both implemented.\n+\t */\n+\tif (!irqchip->irq_request_resources &&\n+\t    !irqchip->irq_release_resources) {\n+\t\tirqchip->irq_request_resources = gpiochip_irq_reqres;\n+\t\tirqchip->irq_release_resources = gpiochip_irq_relres;\n+\t}\n+\n+\tif (gpiochip->irq.parent_handler) {\n+\t\tvoid *data = gpiochip->irq.parent_handler_data ?: gpiochip;\n+\n+\t\tfor (i = 0; i < gpiochip->irq.num_parents; i++) {\n+\t\t\t/*\n+\t\t\t * The parent IRQ chip is already using the chip_data\n+\t\t\t * for this IRQ chip, so our callbacks simply use the\n+\t\t\t * handler_data.\n+\t\t\t */\n+\t\t\tirq_set_chained_handler_and_data(gpiochip->irq.parents[i],\n+\t\t\t\t\t\t\t gpiochip->irq.parent_handler,\n+\t\t\t\t\t\t\t data);\n+\t\t}\n+\n+\t\tgpiochip->irq_nested = false;\n+\t} else {\n+\t\tgpiochip->irq_nested = true;\n+\t}\n+\n+\t/*\n+\t * Prepare the mapping since the IRQ chip shall be orthogonal to any\n+\t * GPIO chip calls.\n+\t */\n+\tfor (i = 0; i < gpiochip->ngpio; i++) {\n+\t\tunsigned int irq;\n+\n+\t\tif (!gpiochip_irqchip_irq_valid(gpiochip, i))\n+\t\t\tcontinue;\n+\n+\t\tirq = irq_create_mapping(gpiochip->irqdomain, i);\n+\t\tif (!irq) {\n+\t\t\tchip_err(gpiochip,\n+\t\t\t\t \"failed to create IRQ mapping for GPIO#%u\\n\",\n+\t\t\t\t i);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tirq_set_parent(irq, gpiochip->irq.map[i]);\n+\t}\n+\n+\tacpi_gpiochip_request_interrupts(gpiochip);\n+\n+\treturn 0;\n+}\n+\n /**\n  * gpiochip_irqchip_remove() - removes an irqchip added to a gpiochip\n  * @gpiochip: the gpiochip to remove the irqchip from\n@@ -1722,6 +1847,16 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)\n \t\tirq_set_handler_data(gpiochip->irq_chained_parent, NULL);\n \t}\n \n+\tif (gpiochip->irqchip) {\n+\t\tstruct gpio_irq_chip *irq = &gpiochip->irq;\n+\t\tunsigned int i;\n+\n+\t\tfor (i = 0; i < irq->num_parents; i++) {\n+\t\t\tirq_set_chained_handler(irq->parents[i], NULL);\n+\t\t\tirq_set_handler_data(irq->parents[i], NULL);\n+\t\t}\n+\t}\n+\n \t/* Remove all IRQ mappings and delete the domain */\n \tif (gpiochip->irqdomain) {\n \t\tfor (offset = 0; offset < gpiochip->ngpio; offset++) {\n@@ -1842,6 +1977,11 @@ EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_key);\n \n #else /* CONFIG_GPIOLIB_IRQCHIP */\n \n+static inline int gpiochip_add_irqchip(struct gpio_chip *gpiochip)\n+{\n+\treturn 0;\n+}\n+\n static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) {}\n static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip)\n {\ndiff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h\nindex c97f8325e8bf..6100b171817e 100644\n--- a/include/linux/gpio/driver.h\n+++ b/include/linux/gpio/driver.h\n@@ -19,6 +19,58 @@ struct module;\n \n #ifdef CONFIG_GPIOLIB\n \n+#ifdef CONFIG_GPIOLIB_IRQCHIP\n+/**\n+ * struct gpio_irq_chip - GPIO interrupt controller\n+ */\n+struct gpio_irq_chip {\n+\t/**\n+\t * @domain_ops:\n+\t *\n+\t * Table of interrupt domain operations for this IRQ chip.\n+\t */\n+\tconst struct irq_domain_ops *domain_ops;\n+\n+\t/**\n+\t * @parent_handler:\n+\t *\n+\t * The interrupt handler for the GPIO chip's parent interrupts, may be\n+\t * NULL if the parent interrupts are nested rather than cascaded.\n+\t */\n+\tirq_flow_handler_t parent_handler;\n+\n+\t/**\n+\t * @parent_handler_data:\n+\t *\n+\t * Data associated, and passed to, the handler for the parent\n+\t * interrupt.\n+\t */\n+\tvoid *parent_handler_data;\n+\n+\t/**\n+\t * @num_parents:\n+\t *\n+\t * The number of interrupt parents of a GPIO chip.\n+\t */\n+\tunsigned int num_parents;\n+\n+\t/**\n+\t * @parents:\n+\t *\n+\t * A list of interrupt parents of a GPIO chip. This is owned by the\n+\t * driver, so the core will only reference this list, not modify it.\n+\t */\n+\tunsigned int *parents;\n+\n+\t/**\n+\t * @map:\n+\t *\n+\t * A list of interrupt parents for each line of a GPIO chip.\n+\t */\n+\tunsigned int *map;\n+};\n+#endif\n+\n /**\n  * struct gpio_chip - abstract a GPIO controller\n  * @label: a functional name for the GPIO device, such as a part\n@@ -173,6 +225,14 @@ struct gpio_chip {\n \tbool\t\t\tirq_need_valid_mask;\n \tunsigned long\t\t*irq_valid_mask;\n \tstruct lock_class_key\t*lock_key;\n+\n+\t/**\n+\t * @irq:\n+\t *\n+\t * Integrates interrupt chip functionality with the GPIO chip. Can be\n+\t * used to handle IRQs for most practical cases.\n+\t */\n+\tstruct gpio_irq_chip irq;\n #endif\n \n #if defined(CONFIG_OF_GPIO)\n@@ -264,6 +324,10 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev,\n \n #ifdef CONFIG_GPIOLIB_IRQCHIP\n \n+int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,\n+\t\t     irq_hw_number_t hwirq);\n+void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq);\n+\n void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,\n \t\tstruct irq_chip *irqchip,\n \t\tunsigned int parent_irq,\n","prefixes":["v2","01/16"]}