From patchwork Mon May 11 14:52:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Geert Uytterhoeven X-Patchwork-Id: 1287846 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=glider.be Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49LP8W6jNyz9sRf for ; Tue, 12 May 2020 00:54:03 +1000 (AEST) Received: from localhost ([::1]:42784 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jY9of-0005IW-Ir for incoming@patchwork.ozlabs.org; Mon, 11 May 2020 10:54:01 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34480) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jY9nt-0004w4-Qf for qemu-devel@nongnu.org; Mon, 11 May 2020 10:53:16 -0400 Received: from baptiste.telenet-ops.be ([2a02:1800:120:4::f00:13]:59820) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1jY9ns-0000dR-Gj for qemu-devel@nongnu.org; Mon, 11 May 2020 10:53:13 -0400 Received: from ramsan ([IPv6:2a02:1810:ac12:ed60:702c:fb99:3097:2049]) by baptiste.telenet-ops.be with bizsmtp id dSsy2200E0GoAb601SsyU5; Mon, 11 May 2020 16:53:08 +0200 Received: from rox.of.borg ([192.168.97.57]) by ramsan with esmtp (Exim 4.90_1) (envelope-from ) id 1jY9ne-00082b-0K; Mon, 11 May 2020 16:52:58 +0200 Received: from geert by rox.of.borg with local (Exim 4.90_1) (envelope-from ) id 1jY9nd-0005zR-UX; Mon, 11 May 2020 16:52:57 +0200 From: Geert Uytterhoeven To: Linus Walleij , Bartosz Golaszewski , Jonathan Corbet , Harish Jenny K N , Eugeniu Rosca Subject: [PATCH v7 1/6] i2c: i801: Use GPIO_LOOKUP() helper macro Date: Mon, 11 May 2020 16:52:52 +0200 Message-Id: <20200511145257.22970-2-geert+renesas@glider.be> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200511145257.22970-1-geert+renesas@glider.be> References: <20200511145257.22970-1-geert+renesas@glider.be> Received-SPF: none client-ip=2a02:1800:120:4::f00:13; envelope-from=geert@linux-m68k.org; helo=baptiste.telenet-ops.be X-detected-operating-system: by eggs.gnu.org: No matching host in p0f cache. That's all we know. X-Spam_score_int: -23 X-Spam_score: -2.4 X-Spam_bar: -- X-Spam_report: (-2.4 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.249, RCVD_IN_DNSWL_LOW=-0.7, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Peter Maydell , qemu-devel@nongnu.org, Geert Uytterhoeven , linux-doc@vger.kernel.org, Marc Zyngier , Magnus Damm , Christoffer Dall , linux-kernel@vger.kernel.org, linux-renesas-soc@vger.kernel.org, linux-gpio@vger.kernel.org, Alexander Graf , Paolo Bonzini , Phil Reid Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" i801_add_mux() fills in the GPIO lookup table by manually populating an array of gpiod_lookup structures. Use the existing GPIO_LOOKUP() helper macro instead, to relax a dependency on the gpiod_lookup structure's member names. Signed-off-by: Geert Uytterhoeven Reviewed-by: Jean Delvare --- This is now commit be1b92c133cc91b2 ("i2c: i801: Use GPIO_LOOKUP() helper macro") in i2c/for-next. v7: - Add Reviewed-by, v6: - New. --- drivers/i2c/busses/i2c-i801.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index a9c03f5c34825a95..4f333889489c882a 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1439,9 +1439,9 @@ static int i801_add_mux(struct i801_priv *priv) return -ENOMEM; lookup->dev_id = "i2c-mux-gpio"; for (i = 0; i < mux_config->n_gpios; i++) { - lookup->table[i].chip_label = mux_config->gpio_chip; - lookup->table[i].chip_hwnum = mux_config->gpios[i]; - lookup->table[i].con_id = "mux"; + lookup->table[i] = (struct gpiod_lookup) + GPIO_LOOKUP(mux_config->gpio_chip, + mux_config->gpios[i], "mux", 0); } gpiod_add_lookup_table(lookup); priv->lookup = lookup; From patchwork Mon May 11 14:52:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Geert Uytterhoeven X-Patchwork-Id: 1287849 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=glider.be Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49LPB92LDZz9sPF for ; Tue, 12 May 2020 00:55:28 +1000 (AEST) Received: from localhost ([::1]:49508 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jY9q1-0000ww-NK for incoming@patchwork.ozlabs.org; Mon, 11 May 2020 10:55:25 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34488) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jY9nu-0004wj-Ln for qemu-devel@nongnu.org; Mon, 11 May 2020 10:53:17 -0400 Received: from andre.telenet-ops.be ([2a02:1800:120:4::f00:15]:36894) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1jY9ns-0000dV-JW for qemu-devel@nongnu.org; Mon, 11 May 2020 10:53:14 -0400 Received: from ramsan ([IPv6:2a02:1810:ac12:ed60:702c:fb99:3097:2049]) by andre.telenet-ops.be with bizsmtp id dSsy220060GoAb601Ssy9H; Mon, 11 May 2020 16:53:08 +0200 Received: from rox.of.borg ([192.168.97.57]) by ramsan with esmtp (Exim 4.90_1) (envelope-from ) id 1jY9ne-00082e-2B; Mon, 11 May 2020 16:52:58 +0200 Received: from geert by rox.of.borg with local (Exim 4.90_1) (envelope-from ) id 1jY9ne-0005zT-0R; Mon, 11 May 2020 16:52:58 +0200 From: Geert Uytterhoeven To: Linus Walleij , Bartosz Golaszewski , Jonathan Corbet , Harish Jenny K N , Eugeniu Rosca Subject: [PATCH v7 2/6] mfd: sm501: Use GPIO_LOOKUP_IDX() helper macro Date: Mon, 11 May 2020 16:52:53 +0200 Message-Id: <20200511145257.22970-3-geert+renesas@glider.be> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200511145257.22970-1-geert+renesas@glider.be> References: <20200511145257.22970-1-geert+renesas@glider.be> Received-SPF: none client-ip=2a02:1800:120:4::f00:15; envelope-from=geert@linux-m68k.org; helo=andre.telenet-ops.be X-detected-operating-system: by eggs.gnu.org: No matching host in p0f cache. That's all we know. X-Spam_score_int: -23 X-Spam_score: -2.4 X-Spam_bar: -- X-Spam_report: (-2.4 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.249, RCVD_IN_DNSWL_LOW=-0.7, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Peter Maydell , qemu-devel@nongnu.org, Geert Uytterhoeven , linux-doc@vger.kernel.org, Marc Zyngier , Magnus Damm , Christoffer Dall , linux-kernel@vger.kernel.org, linux-renesas-soc@vger.kernel.org, linux-gpio@vger.kernel.org, Alexander Graf , Paolo Bonzini , Phil Reid Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" i801_add_mux() fills in the GPIO lookup table by manually populating an array of gpiod_lookup structures. Use the existing GPIO_LOOKUP_IDX() helper macro instead, to relax a dependency on the gpiod_lookup structure's member names. Signed-off-by: Geert Uytterhoeven Acked-by: Lee Jones --- v7: - Add Acked-by, v6: - New. --- drivers/mfd/sm501.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index e49787e6bb93e5c8..ccd62b963952814e 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -1145,22 +1145,14 @@ static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm, return -ENOMEM; lookup->dev_id = "i2c-gpio"; - if (iic->pin_sda < 32) - lookup->table[0].chip_label = "SM501-LOW"; - else - lookup->table[0].chip_label = "SM501-HIGH"; - lookup->table[0].chip_hwnum = iic->pin_sda % 32; - lookup->table[0].con_id = NULL; - lookup->table[0].idx = 0; - lookup->table[0].flags = GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN; - if (iic->pin_scl < 32) - lookup->table[1].chip_label = "SM501-LOW"; - else - lookup->table[1].chip_label = "SM501-HIGH"; - lookup->table[1].chip_hwnum = iic->pin_scl % 32; - lookup->table[1].con_id = NULL; - lookup->table[1].idx = 1; - lookup->table[1].flags = GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN; + lookup->table[0] = (struct gpiod_lookup) + GPIO_LOOKUP_IDX(iic->pin_sda < 32 ? "SM501-LOW" : "SM501-HIGH", + iic->pin_sda % 32, NULL, 0, + GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN); + lookup->table[1] = (struct gpiod_lookup) + GPIO_LOOKUP_IDX(iic->pin_scl < 32 ? "SM501-LOW" : "SM501-HIGH", + iic->pin_scl % 32, NULL, 1, + GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN); gpiod_add_lookup_table(lookup); icd = dev_get_platdata(&pdev->dev); From patchwork Mon May 11 14:52:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Geert Uytterhoeven X-Patchwork-Id: 1287850 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=glider.be Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49LPBM6XtJz9sPF for ; Tue, 12 May 2020 00:55:39 +1000 (AEST) Received: from localhost ([::1]:50876 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jY9qD-0001ym-Mf for incoming@patchwork.ozlabs.org; Mon, 11 May 2020 10:55:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34496) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jY9nv-0004xK-VZ for qemu-devel@nongnu.org; Mon, 11 May 2020 10:53:17 -0400 Received: from baptiste.telenet-ops.be ([2a02:1800:120:4::f00:13]:59816) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1jY9ns-0000dP-UQ for qemu-devel@nongnu.org; Mon, 11 May 2020 10:53:15 -0400 Received: from ramsan ([IPv6:2a02:1810:ac12:ed60:702c:fb99:3097:2049]) by baptiste.telenet-ops.be with bizsmtp id dSsy2200G0GoAb601SsyU6; Mon, 11 May 2020 16:53:08 +0200 Received: from rox.of.borg ([192.168.97.57]) by ramsan with esmtp (Exim 4.90_1) (envelope-from ) id 1jY9ne-00082j-3k; Mon, 11 May 2020 16:52:58 +0200 Received: from geert by rox.of.borg with local (Exim 4.90_1) (envelope-from ) id 1jY9ne-0005zX-2C; Mon, 11 May 2020 16:52:58 +0200 From: Geert Uytterhoeven To: Linus Walleij , Bartosz Golaszewski , Jonathan Corbet , Harish Jenny K N , Eugeniu Rosca Subject: [PATCH v7 3/6] gpiolib: Add support for GPIO lookup by line name Date: Mon, 11 May 2020 16:52:54 +0200 Message-Id: <20200511145257.22970-4-geert+renesas@glider.be> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200511145257.22970-1-geert+renesas@glider.be> References: <20200511145257.22970-1-geert+renesas@glider.be> Received-SPF: none client-ip=2a02:1800:120:4::f00:13; envelope-from=geert@linux-m68k.org; helo=baptiste.telenet-ops.be X-detected-operating-system: by eggs.gnu.org: No matching host in p0f cache. That's all we know. X-Spam_score_int: -23 X-Spam_score: -2.4 X-Spam_bar: -- X-Spam_report: (-2.4 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.249, RCVD_IN_DNSWL_LOW=-0.7, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Peter Maydell , qemu-devel@nongnu.org, Geert Uytterhoeven , linux-doc@vger.kernel.org, Marc Zyngier , Magnus Damm , Christoffer Dall , linux-kernel@vger.kernel.org, linux-renesas-soc@vger.kernel.org, linux-gpio@vger.kernel.org, Alexander Graf , Paolo Bonzini , Phil Reid Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" Currently a GPIO lookup table can only refer to a specific GPIO by a tuple, consisting of a GPIO controller label and a GPIO offset inside the controller. However, a GPIO may also carry a line name, defined by DT or ACPI. If present, the line name is the most use-centric way to refer to a GPIO. Hence add support for looking up GPIOs by line name. Note that there is no guarantee that GPIO line names are globally unique, so this will use the first match found. Implement this by reusing the existing gpiod_lookup infrastructure. Rename gpiod_lookup.chip_label to gpiod_lookup.key, to make it clear that this field can have two meanings, and update the kerneldoc and GPIO_LOOKUP*() macros. Signed-off-by: Geert Uytterhoeven Reviewed-by: Ulrich Hecht Reviewed-by: Eugeniu Rosca Tested-by: Eugeniu Rosca --- v7: - Document non-uniqueness of line names, - Rebase on top of commit a0b66a73785ccc8f ("gpio: Rename variable in core APIs"), v6: - Update Documentation/driver-api/gpio/board.rst, - Reword rationale, v5: - Add Reviewed-by, Tested-by, v4: - Add Reviewed-by, - Rename gpiod_lookup.chip_label. - Use U16_MAX instead of (u16)-1, v3: - New. --- Documentation/driver-api/gpio/board.rst | 15 ++++++++++----- drivers/gpio/gpiolib.c | 22 +++++++++++++++++----- include/linux/gpio/machine.h | 17 ++++++++++------- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst index ce91518bf9f48ded..191fa867826a45c3 100644 --- a/Documentation/driver-api/gpio/board.rst +++ b/Documentation/driver-api/gpio/board.rst @@ -113,13 +113,15 @@ files that desire to do so need to include the following header:: GPIOs are mapped by the means of tables of lookups, containing instances of the gpiod_lookup structure. Two macros are defined to help declaring such mappings:: - GPIO_LOOKUP(chip_label, chip_hwnum, con_id, flags) - GPIO_LOOKUP_IDX(chip_label, chip_hwnum, con_id, idx, flags) + GPIO_LOOKUP(key, chip_hwnum, con_id, flags) + GPIO_LOOKUP_IDX(key, chip_hwnum, con_id, idx, flags) where - - chip_label is the label of the gpiod_chip instance providing the GPIO - - chip_hwnum is the hardware number of the GPIO within the chip + - key is either the label of the gpiod_chip instance providing the GPIO, or + the GPIO line name + - chip_hwnum is the hardware number of the GPIO within the chip, or U16_MAX + to indicate that key is a GPIO line name - con_id is the name of the GPIO function from the device point of view. It can be NULL, in which case it will match any function. - idx is the index of the GPIO within the function. @@ -135,7 +137,10 @@ where In the future, these flags might be extended to support more properties. -Note that GPIO_LOOKUP() is just a shortcut to GPIO_LOOKUP_IDX() where idx = 0. +Note that: + 1. GPIO line names are not guaranteed to be globally unique, so the first + match found will be used. + 2. GPIO_LOOKUP() is just a shortcut to GPIO_LOOKUP_IDX() where idx = 0. A lookup table can then be defined as follows, with an empty entry defining its end. The 'dev_id' field of the table is the identifier of the device that will diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 98822412f565073a..2e4aacbad8fe586b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4645,7 +4645,7 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, if (!table) return desc; - for (p = &table->table[0]; p->chip_label; p++) { + for (p = &table->table[0]; p->key; p++) { struct gpio_chip *gc; /* idx must always match exactly */ @@ -4656,18 +4656,30 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, if (p->con_id && (!con_id || strcmp(p->con_id, con_id))) continue; - gc = find_chip_by_name(p->chip_label); + if (p->chip_hwnum == U16_MAX) { + desc = gpio_name_to_desc(p->key); + if (desc) { + *flags = p->flags; + return desc; + } + + dev_warn(dev, "cannot find GPIO line %s, deferring\n", + p->key); + return ERR_PTR(-EPROBE_DEFER); + } + + gc = find_chip_by_name(p->key); if (!gc) { /* * As the lookup table indicates a chip with - * p->chip_label should exist, assume it may + * p->key should exist, assume it may * still appear later and let the interested * consumer be probed again or let the Deferred * Probe infrastructure handle the error. */ dev_warn(dev, "cannot find GPIO chip %s, deferring\n", - p->chip_label); + p->key); return ERR_PTR(-EPROBE_DEFER); } @@ -4698,7 +4710,7 @@ static int platform_gpio_count(struct device *dev, const char *con_id) if (!table) return -ENOENT; - for (p = &table->table[0]; p->chip_label; p++) { + for (p = &table->table[0]; p->key; p++) { if ((con_id && p->con_id && !strcmp(con_id, p->con_id)) || (!con_id && !p->con_id)) count++; diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h index 1ebe5be05d5f81fa..781a053abbb99a1d 100644 --- a/include/linux/gpio/machine.h +++ b/include/linux/gpio/machine.h @@ -20,8 +20,11 @@ enum gpio_lookup_flags { /** * struct gpiod_lookup - lookup table - * @chip_label: name of the chip the GPIO belongs to - * @chip_hwnum: hardware number (i.e. relative to the chip) of the GPIO + * @key: either the name of the chip the GPIO belongs to, or the GPIO line name + * Note that GPIO line names are not guaranteed to be globally unique, + * so this will use the first match found! + * @chip_hwnum: hardware number (i.e. relative to the chip) of the GPIO, or + * U16_MAX to indicate that @key is a GPIO line name * @con_id: name of the GPIO from the device's point of view * @idx: index of the GPIO in case several GPIOs share the same name * @flags: bitmask of gpio_lookup_flags GPIO_* values @@ -30,7 +33,7 @@ enum gpio_lookup_flags { * functions using platform data. */ struct gpiod_lookup { - const char *chip_label; + const char *key; u16 chip_hwnum; const char *con_id; unsigned int idx; @@ -63,17 +66,17 @@ struct gpiod_hog { /* * Simple definition of a single GPIO under a con_id */ -#define GPIO_LOOKUP(_chip_label, _chip_hwnum, _con_id, _flags) \ - GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _con_id, 0, _flags) +#define GPIO_LOOKUP(_key, _chip_hwnum, _con_id, _flags) \ + GPIO_LOOKUP_IDX(_key, _chip_hwnum, _con_id, 0, _flags) /* * Use this macro if you need to have several GPIOs under the same con_id. * Each GPIO needs to use a different index and can be accessed using * gpiod_get_index() */ -#define GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _con_id, _idx, _flags) \ +#define GPIO_LOOKUP_IDX(_key, _chip_hwnum, _con_id, _idx, _flags) \ { \ - .chip_label = _chip_label, \ + .key = _key, \ .chip_hwnum = _chip_hwnum, \ .con_id = _con_id, \ .idx = _idx, \ From patchwork Mon May 11 14:52:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Geert Uytterhoeven X-Patchwork-Id: 1287847 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=glider.be Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49LP8q37W0z9sRf for ; Tue, 12 May 2020 00:54:19 +1000 (AEST) Received: from localhost ([::1]:44070 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jY9ou-000601-WA for incoming@patchwork.ozlabs.org; Mon, 11 May 2020 10:54:17 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34494) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jY9nv-0004wn-Aq for qemu-devel@nongnu.org; Mon, 11 May 2020 10:53:17 -0400 Received: from andre.telenet-ops.be ([2a02:1800:120:4::f00:15]:36892) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1jY9ns-0000dU-Vh for qemu-devel@nongnu.org; Mon, 11 May 2020 10:53:15 -0400 Received: from ramsan ([IPv6:2a02:1810:ac12:ed60:702c:fb99:3097:2049]) by andre.telenet-ops.be with bizsmtp id dSsy2200Y0GoAb601Ssy9J; Mon, 11 May 2020 16:53:08 +0200 Received: from rox.of.borg ([192.168.97.57]) by ramsan with esmtp (Exim 4.90_1) (envelope-from ) id 1jY9ne-00082m-5N; Mon, 11 May 2020 16:52:58 +0200 Received: from geert by rox.of.borg with local (Exim 4.90_1) (envelope-from ) id 1jY9ne-0005za-3m; Mon, 11 May 2020 16:52:58 +0200 From: Geert Uytterhoeven To: Linus Walleij , Bartosz Golaszewski , Jonathan Corbet , Harish Jenny K N , Eugeniu Rosca Subject: [PATCH v7 4/6] gpio: Add GPIO Aggregator Date: Mon, 11 May 2020 16:52:55 +0200 Message-Id: <20200511145257.22970-5-geert+renesas@glider.be> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200511145257.22970-1-geert+renesas@glider.be> References: <20200511145257.22970-1-geert+renesas@glider.be> Received-SPF: none client-ip=2a02:1800:120:4::f00:15; envelope-from=geert@linux-m68k.org; helo=andre.telenet-ops.be X-detected-operating-system: by eggs.gnu.org: No matching host in p0f cache. That's all we know. X-Spam_score_int: -23 X-Spam_score: -2.4 X-Spam_bar: -- X-Spam_report: (-2.4 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.249, RCVD_IN_DNSWL_LOW=-0.7, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Peter Maydell , qemu-devel@nongnu.org, Geert Uytterhoeven , linux-doc@vger.kernel.org, Marc Zyngier , Magnus Damm , Christoffer Dall , linux-kernel@vger.kernel.org, linux-renesas-soc@vger.kernel.org, linux-gpio@vger.kernel.org, Alexander Graf , Paolo Bonzini , Phil Reid Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" GPIO controllers are exported to userspace using /dev/gpiochip* character devices. Access control to these devices is provided by standard UNIX file system permissions, on an all-or-nothing basis: either a GPIO controller is accessible for a user, or it is not. Currently no mechanism exists to control access to individual GPIOs. Hence add a GPIO driver to aggregate existing GPIOs, and expose them as a new gpiochip. This supports the following use cases: - Aggregating GPIOs using Sysfs This is useful for implementing access control, and assigning a set of GPIOs to a specific user or virtual machine. - Generic GPIO Driver This is useful for industrial control, where it can provide userspace access to a simple GPIO-operated device described in DT, cfr. e.g. spidev for SPI-operated devices. Signed-off-by: Geert Uytterhoeven Reviewed-by: Eugeniu Rosca Tested-by: Eugeniu Rosca --- v7: - No changes, v6: - Use gpiod_to_chip() instead of open-coding, - Drop debug print of gpio_desc.label, as it usually just points to the GPIO Aggregator itself, - Drop no longer needed #include "gpiolib.h", - Fix missing offset translation in gpio_fwd_set_config(), - Use GPIO_LOOKUP_IDX() to populate struct gpiod_lookup, v5: - Add Reviewed-by, Tested-by, v4: - Remove unused assignment to n in isrange(), - Check correct pointer after aggr->lookups->dev_id allocation, - Preinitialize flags to 0 in gpio_fwd_[gs]et_multiple() to avoid may-be-used-uninitialized warning, - Drop controversial GPIO repeater, - Update for gpiod_lookup.chip_label rename, - Use %pe to format error pointers, - Use U16_MAX instead of (u16)-1, - Correct comment indentation, - Use skip_spaces() helper, - Rename a and b to first_index resp. last_index, - Add comment to tmp[] use, - Improve Kconfig help text, - Include for gpiod_[gs]et_*(), - Drop unneeded valid_mask handling, - Add comment about sleeping and .set_config() support, v3: - Absorb GPIO forwarder, - Integrate GPIO Repeater and Generic GPIO driver functionality, - Use the aggregator parameters to create a GPIO lookup table instead of an array of GPIO descriptors, which allows to simplify the code: 1. This removes the need for calling gpio_name_to_desc(), gpiochip_find(), gpiochip_get_desc(), and gpiod_request(), 2. This allows the platform device to always use devm_gpiod_get_index(), regardless of the origin of the GPIOs, - Move parameter parsing from platform device probe to sysfs attribute store, removing the need for platform data passing, - Use more devm_*() functions to simplify cleanup, - Add pr_fmt(), - General refactoring, v2: - Add missing initialization of i in gpio_virt_agg_probe(), - Update for removed .need_valid_mask field and changed .init_valid_mask() signature, - Drop "virtual", rename to gpio-aggregator, - Drop bogus FIXME related to gpiod_set_transitory() expectations, - Use new GPIO Forwarder Helper, - Lift limit on the maximum number of GPIOs, - Improve parsing: - add support for specifying GPIOs by line name, - add support for specifying GPIO chips by ID, - add support for GPIO offset ranges, - names and offset specifiers must be separated by whitespace, - GPIO offsets must separated by spaces, - Use str_has_prefix() and kstrtouint(). --- drivers/gpio/Kconfig | 12 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-aggregator.c | 568 +++++++++++++++++++++++++++++++++ 3 files changed, 581 insertions(+) create mode 100644 drivers/gpio/gpio-aggregator.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8ef2179fb9991b78..c92e0a16c352f3d1 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1541,6 +1541,18 @@ config GPIO_VIPERBOARD endmenu +config GPIO_AGGREGATOR + tristate "GPIO Aggregator" + help + Say yes here to enable the GPIO Aggregator, which provides a way to + aggregate existing GPIO lines into a new virtual GPIO chip. + This can serve the following purposes: + - Assign permissions for a collection of GPIO lines to a user, + - Export a collection of GPIO lines to a virtual machine, + - Provide a generic driver for a GPIO-operated device in an + industrial control context, to be operated from userspace using + the GPIO chardev interface. + config GPIO_MOCKUP tristate "GPIO Testing Driver" select IRQ_SIM diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index b2cfc21a97f3e52b..65bf3940e33cf734 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o +obj-$(CONFIG_GPIO_AGGREGATOR) += gpio-aggregator.o obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c new file mode 100644 index 0000000000000000..9b0adbdddbfccb30 --- /dev/null +++ b/drivers/gpio/gpio-aggregator.c @@ -0,0 +1,568 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// GPIO Aggregator +// +// Copyright (C) 2019-2020 Glider bv + +#define DRV_NAME "gpio-aggregator" +#define pr_fmt(fmt) DRV_NAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * GPIO Aggregator sysfs interface + */ + +struct gpio_aggregator { + struct gpiod_lookup_table *lookups; + struct platform_device *pdev; + char args[]; +}; + +static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */ +static DEFINE_IDR(gpio_aggregator_idr); + +static char *get_arg(char **args) +{ + char *start = *args, *end; + + start = skip_spaces(start); + if (!*start) + return NULL; + + if (*start == '"') { + /* Quoted arg */ + end = strchr(++start, '"'); + if (!end) + return ERR_PTR(-EINVAL); + } else { + /* Unquoted arg */ + for (end = start; *end && !isspace(*end); end++) ; + } + + if (*end) + *end++ = '\0'; + + *args = end; + return start; +} + +static bool isrange(const char *s) +{ + size_t n; + + if (IS_ERR_OR_NULL(s)) + return false; + + while (1) { + n = strspn(s, "0123456789"); + if (!n) + return false; + + s += n; + + switch (*s++) { + case '\0': + return true; + + case '-': + case ',': + break; + + default: + return false; + } + } +} + +static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key, + int hwnum, unsigned int *n) +{ + struct gpiod_lookup_table *lookups; + + lookups = krealloc(aggr->lookups, struct_size(lookups, table, *n + 2), + GFP_KERNEL); + if (!lookups) + return -ENOMEM; + + lookups->table[*n] = + (struct gpiod_lookup)GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0); + + (*n)++; + memset(&lookups->table[*n], 0, sizeof(lookups->table[*n])); + + aggr->lookups = lookups; + return 0; +} + +static int aggr_parse(struct gpio_aggregator *aggr) +{ + unsigned int first_index, last_index, i, n = 0; + char *name, *offsets, *first, *last, *next; + char *args = aggr->args; + int error; + + for (name = get_arg(&args), offsets = get_arg(&args); name; + offsets = get_arg(&args)) { + if (IS_ERR(name)) { + pr_err("Cannot get GPIO specifier: %pe\n", name); + return PTR_ERR(name); + } + + if (!isrange(offsets)) { + /* Named GPIO line */ + error = aggr_add_gpio(aggr, name, U16_MAX, &n); + if (error) + return error; + + name = offsets; + continue; + } + + /* GPIO chip + offset(s) */ + for (first = offsets; *first; first = next) { + next = strchrnul(first, ','); + if (*next) + *next++ = '\0'; + + last = strchr(first, '-'); + if (last) + *last++ = '\0'; + + if (kstrtouint(first, 10, &first_index)) { + pr_err("Cannot parse GPIO index %s\n", first); + return -EINVAL; + } + + if (!last) { + last_index = first_index; + } else if (kstrtouint(last, 10, &last_index)) { + pr_err("Cannot parse GPIO index %s\n", last); + return -EINVAL; + } + + for (i = first_index; i <= last_index; i++) { + error = aggr_add_gpio(aggr, name, i, &n); + if (error) + return error; + } + } + + name = get_arg(&args); + } + + if (!n) { + pr_err("No GPIOs specified\n"); + return -EINVAL; + } + + return 0; +} + +static ssize_t new_device_store(struct device_driver *driver, const char *buf, + size_t count) +{ + struct gpio_aggregator *aggr; + struct platform_device *pdev; + int res, id; + + /* kernfs guarantees string termination, so count + 1 is safe */ + aggr = kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL); + if (!aggr) + return -ENOMEM; + + memcpy(aggr->args, buf, count + 1); + + aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1), + GFP_KERNEL); + if (!aggr->lookups) { + res = -ENOMEM; + goto free_ga; + } + + mutex_lock(&gpio_aggregator_lock); + id = idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL); + mutex_unlock(&gpio_aggregator_lock); + + if (id < 0) { + res = id; + goto free_table; + } + + aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id); + if (!aggr->lookups->dev_id) { + res = -ENOMEM; + goto remove_idr; + } + + res = aggr_parse(aggr); + if (res) + goto free_dev_id; + + gpiod_add_lookup_table(aggr->lookups); + + pdev = platform_device_register_simple(DRV_NAME, id, NULL, 0); + if (IS_ERR(pdev)) { + res = PTR_ERR(pdev); + goto remove_table; + } + + aggr->pdev = pdev; + return count; + +remove_table: + gpiod_remove_lookup_table(aggr->lookups); +free_dev_id: + kfree(aggr->lookups->dev_id); +remove_idr: + mutex_lock(&gpio_aggregator_lock); + idr_remove(&gpio_aggregator_idr, id); + mutex_unlock(&gpio_aggregator_lock); +free_table: + kfree(aggr->lookups); +free_ga: + kfree(aggr); + return res; +} + +static DRIVER_ATTR_WO(new_device); + +static void gpio_aggregator_free(struct gpio_aggregator *aggr) +{ + platform_device_unregister(aggr->pdev); + gpiod_remove_lookup_table(aggr->lookups); + kfree(aggr->lookups->dev_id); + kfree(aggr->lookups); + kfree(aggr); +} + +static ssize_t delete_device_store(struct device_driver *driver, + const char *buf, size_t count) +{ + struct gpio_aggregator *aggr; + unsigned int id; + int error; + + if (!str_has_prefix(buf, DRV_NAME ".")) + return -EINVAL; + + error = kstrtouint(buf + strlen(DRV_NAME "."), 10, &id); + if (error) + return error; + + mutex_lock(&gpio_aggregator_lock); + aggr = idr_remove(&gpio_aggregator_idr, id); + mutex_unlock(&gpio_aggregator_lock); + if (!aggr) + return -ENOENT; + + gpio_aggregator_free(aggr); + return count; +} +static DRIVER_ATTR_WO(delete_device); + +static struct attribute *gpio_aggregator_attrs[] = { + &driver_attr_new_device.attr, + &driver_attr_delete_device.attr, + NULL, +}; +ATTRIBUTE_GROUPS(gpio_aggregator); + +static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data) +{ + gpio_aggregator_free(p); + return 0; +} + +static void __exit gpio_aggregator_remove_all(void) +{ + mutex_lock(&gpio_aggregator_lock); + idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL); + idr_destroy(&gpio_aggregator_idr); + mutex_unlock(&gpio_aggregator_lock); +} + + +/* + * GPIO Forwarder + */ + +struct gpiochip_fwd { + struct gpio_chip chip; + struct gpio_desc **descs; + union { + struct mutex mlock; /* protects tmp[] if can_sleep */ + spinlock_t slock; /* protects tmp[] if !can_sleep */ + }; + unsigned long tmp[]; /* values and descs for multiple ops */ +}; + +static int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct gpiochip_fwd *fwd = gpiochip_get_data(chip); + + return gpiod_get_direction(fwd->descs[offset]); +} + +static int gpio_fwd_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct gpiochip_fwd *fwd = gpiochip_get_data(chip); + + return gpiod_direction_input(fwd->descs[offset]); +} + +static int gpio_fwd_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct gpiochip_fwd *fwd = gpiochip_get_data(chip); + + return gpiod_direction_output(fwd->descs[offset], value); +} + +static int gpio_fwd_get(struct gpio_chip *chip, unsigned int offset) +{ + struct gpiochip_fwd *fwd = gpiochip_get_data(chip); + + return gpiod_get_value(fwd->descs[offset]); +} + +static int gpio_fwd_get_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + struct gpiochip_fwd *fwd = gpiochip_get_data(chip); + unsigned long *values, flags = 0; + struct gpio_desc **descs; + unsigned int i, j = 0; + int error; + + if (chip->can_sleep) + mutex_lock(&fwd->mlock); + else + spin_lock_irqsave(&fwd->slock, flags); + + /* Both values bitmap and desc pointers are stored in tmp[] */ + values = &fwd->tmp[0]; + descs = (void *)&fwd->tmp[BITS_TO_LONGS(fwd->chip.ngpio)]; + + bitmap_clear(values, 0, fwd->chip.ngpio); + for_each_set_bit(i, mask, fwd->chip.ngpio) + descs[j++] = fwd->descs[i]; + + error = gpiod_get_array_value(j, descs, NULL, values); + if (!error) { + j = 0; + for_each_set_bit(i, mask, fwd->chip.ngpio) + __assign_bit(i, bits, test_bit(j++, values)); + } + + if (chip->can_sleep) + mutex_unlock(&fwd->mlock); + else + spin_unlock_irqrestore(&fwd->slock, flags); + + return error; +} + +static void gpio_fwd_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct gpiochip_fwd *fwd = gpiochip_get_data(chip); + + gpiod_set_value(fwd->descs[offset], value); +} + +static void gpio_fwd_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + struct gpiochip_fwd *fwd = gpiochip_get_data(chip); + unsigned long *values, flags = 0; + struct gpio_desc **descs; + unsigned int i, j = 0; + + if (chip->can_sleep) + mutex_lock(&fwd->mlock); + else + spin_lock_irqsave(&fwd->slock, flags); + + /* Both values bitmap and desc pointers are stored in tmp[] */ + values = &fwd->tmp[0]; + descs = (void *)&fwd->tmp[BITS_TO_LONGS(fwd->chip.ngpio)]; + + for_each_set_bit(i, mask, fwd->chip.ngpio) { + __assign_bit(j, values, test_bit(i, bits)); + descs[j++] = fwd->descs[i]; + } + + gpiod_set_array_value(j, descs, NULL, values); + + if (chip->can_sleep) + mutex_unlock(&fwd->mlock); + else + spin_unlock_irqrestore(&fwd->slock, flags); +} + +static int gpio_fwd_set_config(struct gpio_chip *chip, unsigned int offset, + unsigned long config) +{ + struct gpiochip_fwd *fwd = gpiochip_get_data(chip); + + return gpiod_set_config(fwd->descs[offset], config); +} + +/** + * gpiochip_fwd_create() - Create a new GPIO forwarder + * @dev: Parent device pointer + * @ngpios: Number of GPIOs in the forwarder. + * @descs: Array containing the GPIO descriptors to forward to. + * This array must contain @ngpios entries, and must not be deallocated + * before the forwarder has been destroyed again. + * + * This function creates a new gpiochip, which forwards all GPIO operations to + * the passed GPIO descriptors. + * + * Return: An opaque object pointer, or an ERR_PTR()-encoded negative error + * code on failure. + */ +static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev, + unsigned int ngpios, + struct gpio_desc *descs[]) +{ + const char *label = dev_name(dev); + struct gpiochip_fwd *fwd; + struct gpio_chip *chip; + unsigned int i; + int error; + + fwd = devm_kzalloc(dev, struct_size(fwd, tmp, + BITS_TO_LONGS(ngpios) + ngpios), GFP_KERNEL); + if (!fwd) + return ERR_PTR(-ENOMEM); + + chip = &fwd->chip; + + /* + * If any of the GPIO lines are sleeping, then the entire forwarder + * will be sleeping. + * If any of the chips support .set_config(), then the forwarder will + * support setting configs. + */ + for (i = 0; i < ngpios; i++) { + struct gpio_chip *parent = gpiod_to_chip(descs[i]); + + dev_dbg(dev, "%u => gpio-%d\n", i, desc_to_gpio(descs[i])); + + if (gpiod_cansleep(descs[i])) + chip->can_sleep = true; + if (parent && parent->set_config) + chip->set_config = gpio_fwd_set_config; + } + + chip->label = label; + chip->parent = dev; + chip->owner = THIS_MODULE; + chip->get_direction = gpio_fwd_get_direction; + chip->direction_input = gpio_fwd_direction_input; + chip->direction_output = gpio_fwd_direction_output; + chip->get = gpio_fwd_get; + chip->get_multiple = gpio_fwd_get_multiple; + chip->set = gpio_fwd_set; + chip->set_multiple = gpio_fwd_set_multiple; + chip->base = -1; + chip->ngpio = ngpios; + fwd->descs = descs; + + if (chip->can_sleep) + mutex_init(&fwd->mlock); + else + spin_lock_init(&fwd->slock); + + error = devm_gpiochip_add_data(dev, chip, fwd); + if (error) + return ERR_PTR(error); + + return fwd; +} + + +/* + * GPIO Aggregator platform device + */ + +static int gpio_aggregator_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_desc **descs; + struct gpiochip_fwd *fwd; + int i, n; + + n = gpiod_count(dev, NULL); + if (n < 0) + return n; + + descs = devm_kmalloc_array(dev, n, sizeof(*descs), GFP_KERNEL); + if (!descs) + return -ENOMEM; + + for (i = 0; i < n; i++) { + descs[i] = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS); + if (IS_ERR(descs[i])) + return PTR_ERR(descs[i]); + } + + fwd = gpiochip_fwd_create(dev, n, descs); + if (IS_ERR(fwd)) + return PTR_ERR(fwd); + + platform_set_drvdata(pdev, fwd); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id gpio_aggregator_dt_ids[] = { + /* + * Add GPIO-operated devices controlled from userspace below, + * or use "driver_override" in sysfs + */ + {}, +}; +MODULE_DEVICE_TABLE(of, gpio_aggregator_dt_ids); +#endif + +static struct platform_driver gpio_aggregator_driver = { + .probe = gpio_aggregator_probe, + .driver = { + .name = DRV_NAME, + .groups = gpio_aggregator_groups, + .of_match_table = of_match_ptr(gpio_aggregator_dt_ids), + }, +}; + +static int __init gpio_aggregator_init(void) +{ + return platform_driver_register(&gpio_aggregator_driver); +} +module_init(gpio_aggregator_init); + +static void __exit gpio_aggregator_exit(void) +{ + gpio_aggregator_remove_all(); + platform_driver_unregister(&gpio_aggregator_driver); +} +module_exit(gpio_aggregator_exit); + +MODULE_AUTHOR("Geert Uytterhoeven "); +MODULE_DESCRIPTION("GPIO Aggregator"); +MODULE_LICENSE("GPL v2"); From patchwork Mon May 11 14:52:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Geert Uytterhoeven X-Patchwork-Id: 1287851 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=glider.be Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49LPBq03brz9sPF for ; Tue, 12 May 2020 00:56:03 +1000 (AEST) Received: from localhost ([::1]:53112 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jY9qa-00033g-NM for incoming@patchwork.ozlabs.org; Mon, 11 May 2020 10:56:00 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34506) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jY9nx-0004yB-Bg for qemu-devel@nongnu.org; Mon, 11 May 2020 10:53:18 -0400 Received: from xavier.telenet-ops.be ([2a02:1800:120:4::f00:14]:49174) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1jY9nt-0000dk-4K for qemu-devel@nongnu.org; Mon, 11 May 2020 10:53:17 -0400 Received: from ramsan ([IPv6:2a02:1810:ac12:ed60:702c:fb99:3097:2049]) by xavier.telenet-ops.be with bizsmtp id dSsy2200A0GoAb601SsyQe; Mon, 11 May 2020 16:53:09 +0200 Received: from rox.of.borg ([192.168.97.57]) by ramsan with esmtp (Exim 4.90_1) (envelope-from ) id 1jY9ne-00082p-6p; Mon, 11 May 2020 16:52:58 +0200 Received: from geert by rox.of.borg with local (Exim 4.90_1) (envelope-from ) id 1jY9ne-0005zd-5G; Mon, 11 May 2020 16:52:58 +0200 From: Geert Uytterhoeven To: Linus Walleij , Bartosz Golaszewski , Jonathan Corbet , Harish Jenny K N , Eugeniu Rosca Subject: [PATCH v7 5/6] docs: gpio: Add GPIO Aggregator documentation Date: Mon, 11 May 2020 16:52:56 +0200 Message-Id: <20200511145257.22970-6-geert+renesas@glider.be> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200511145257.22970-1-geert+renesas@glider.be> References: <20200511145257.22970-1-geert+renesas@glider.be> Received-SPF: none client-ip=2a02:1800:120:4::f00:14; envelope-from=geert@linux-m68k.org; helo=xavier.telenet-ops.be X-detected-operating-system: by eggs.gnu.org: No matching host in p0f cache. That's all we know. X-Spam_score_int: -23 X-Spam_score: -2.4 X-Spam_bar: -- X-Spam_report: (-2.4 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.249, RCVD_IN_DNSWL_LOW=-0.7, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Peter Maydell , qemu-devel@nongnu.org, Geert Uytterhoeven , linux-doc@vger.kernel.org, Marc Zyngier , Magnus Damm , Christoffer Dall , linux-kernel@vger.kernel.org, linux-renesas-soc@vger.kernel.org, linux-gpio@vger.kernel.org, Alexander Graf , Paolo Bonzini , Phil Reid Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" Document the GPIO Aggregator, and the two typical use-cases. Signed-off-by: Geert Uytterhoeven Reviewed-by: Ulrich Hecht Reviewed-by: Eugeniu Rosca Tested-by: Eugeniu Rosca --- v7: - No changes, v6: - Fix "allows" without object: -> provides a mechanism to aggregate GPIOs, -> provides access control for a set of one or more GPIOs, -> allows the user to communicate, - Drop "gpiochipN" support, - Extend example, v5: - Add Reviewed-by, Tested-by, - Fix inconsistent indentation, v4: - Add Reviewed-by, - Drop controversial GPIO repeater, - Clarify industrial control use case, - Fix typo s/communicated/communicate/, - Replace abstract frobnicator example by concrete door example with gpio-line-names, v3: - New. --- .../admin-guide/gpio/gpio-aggregator.rst | 111 ++++++++++++++++++ Documentation/admin-guide/gpio/index.rst | 1 + 2 files changed, 112 insertions(+) create mode 100644 Documentation/admin-guide/gpio/gpio-aggregator.rst diff --git a/Documentation/admin-guide/gpio/gpio-aggregator.rst b/Documentation/admin-guide/gpio/gpio-aggregator.rst new file mode 100644 index 0000000000000000..5cd1e7221756504c --- /dev/null +++ b/Documentation/admin-guide/gpio/gpio-aggregator.rst @@ -0,0 +1,111 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +GPIO Aggregator +=============== + +The GPIO Aggregator provides a mechanism to aggregate GPIOs, and expose them as +a new gpio_chip. This supports the following use cases. + + +Aggregating GPIOs using Sysfs +----------------------------- + +GPIO controllers are exported to userspace using /dev/gpiochip* character +devices. Access control to these devices is provided by standard UNIX file +system permissions, on an all-or-nothing basis: either a GPIO controller is +accessible for a user, or it is not. + +The GPIO Aggregator provides access control for a set of one or more GPIOs, by +aggregating them into a new gpio_chip, which can be assigned to a group or user +using standard UNIX file ownership and permissions. Furthermore, this +simplifies and hardens exporting GPIOs to a virtual machine, as the VM can just +grab the full GPIO controller, and no longer needs to care about which GPIOs to +grab and which not, reducing the attack surface. + +Aggregated GPIO controllers are instantiated and destroyed by writing to +write-only attribute files in sysfs. + + /sys/bus/platform/drivers/gpio-aggregator/ + + "new_device" ... + Userspace may ask the kernel to instantiate an aggregated GPIO + controller by writing a string describing the GPIOs to + aggregate to the "new_device" file, using the format + + .. code-block:: none + + [] [ ] ... + + Where: + + "" ... + is a GPIO line name, + + "" ... + is a GPIO chip label, and + + "" ... + is a comma-separated list of GPIO offsets and/or + GPIO offset ranges denoted by dashes. + + Example: Instantiate a new GPIO aggregator by aggregating GPIO + line 19 of "e6052000.gpio" and GPIO lines 20-21 of + "e6050000.gpio" into a new gpio_chip: + + .. code-block:: sh + + $ echo 'e6052000.gpio 19 e6050000.gpio 20-21' > new_device + + "delete_device" ... + Userspace may ask the kernel to destroy an aggregated GPIO + controller after use by writing its device name to the + "delete_device" file. + + Example: Destroy the previously-created aggregated GPIO + controller, assumed to be "gpio-aggregator.0": + + .. code-block:: sh + + $ echo gpio-aggregator.0 > delete_device + + +Generic GPIO Driver +------------------- + +The GPIO Aggregator can also be used as a generic driver for a simple +GPIO-operated device described in DT, without a dedicated in-kernel driver. +This is useful in industrial control, and is not unlike e.g. spidev, which +allows the user to communicate with an SPI device from userspace. + +Binding a device to the GPIO Aggregator is performed either by modifying the +gpio-aggregator driver, or by writing to the "driver_override" file in Sysfs. + +Example: If "door" is a GPIO-operated device described in DT, using its own +compatible value:: + + door { + compatible = "myvendor,mydoor"; + + gpios = <&gpio2 19 GPIO_ACTIVE_HIGH>, + <&gpio2 20 GPIO_ACTIVE_LOW>; + gpio-line-names = "open", "lock"; + }; + +it can be bound to the GPIO Aggregator by either: + +1. Adding its compatible value to ``gpio_aggregator_dt_ids[]``, +2. Binding manually using "driver_override": + +.. code-block:: sh + + $ echo gpio-aggregator > /sys/bus/platform/devices/door/driver_override + $ echo door > /sys/bus/platform/drivers/gpio-aggregator/bind + +After that, a new gpiochip "door" has been created: + +.. code-block:: sh + + $ gpioinfo door + gpiochip12 - 2 lines: + line 0: "open" unused input active-high + line 1: "lock" unused input active-high diff --git a/Documentation/admin-guide/gpio/index.rst b/Documentation/admin-guide/gpio/index.rst index a244ba4e87d5398a..ef2838638e967777 100644 --- a/Documentation/admin-guide/gpio/index.rst +++ b/Documentation/admin-guide/gpio/index.rst @@ -7,6 +7,7 @@ gpio .. toctree:: :maxdepth: 1 + gpio-aggregator sysfs .. only:: subproject and html From patchwork Mon May 11 14:52:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Geert Uytterhoeven X-Patchwork-Id: 1287845 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=glider.be Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49LP8M6YRjz9sSJ for ; Tue, 12 May 2020 00:53:55 +1000 (AEST) Received: from localhost ([::1]:42318 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jY9oX-00050Y-NN for incoming@patchwork.ozlabs.org; Mon, 11 May 2020 10:53:53 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34502) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jY9nw-0004xk-9d for qemu-devel@nongnu.org; Mon, 11 May 2020 10:53:17 -0400 Received: from xavier.telenet-ops.be ([2a02:1800:120:4::f00:14]:49176) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1jY9nt-0000dl-3B for qemu-devel@nongnu.org; Mon, 11 May 2020 10:53:16 -0400 Received: from ramsan ([IPv6:2a02:1810:ac12:ed60:702c:fb99:3097:2049]) by xavier.telenet-ops.be with bizsmtp id dSsy2200D0GoAb601SsyQf; Mon, 11 May 2020 16:53:09 +0200 Received: from rox.of.borg ([192.168.97.57]) by ramsan with esmtp (Exim 4.90_1) (envelope-from ) id 1jY9ne-00082s-8S; Mon, 11 May 2020 16:52:58 +0200 Received: from geert by rox.of.borg with local (Exim 4.90_1) (envelope-from ) id 1jY9ne-0005zg-6l; Mon, 11 May 2020 16:52:58 +0200 From: Geert Uytterhoeven To: Linus Walleij , Bartosz Golaszewski , Jonathan Corbet , Harish Jenny K N , Eugeniu Rosca Subject: [PATCH v7 6/6] MAINTAINERS: Add GPIO Aggregator section Date: Mon, 11 May 2020 16:52:57 +0200 Message-Id: <20200511145257.22970-7-geert+renesas@glider.be> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200511145257.22970-1-geert+renesas@glider.be> References: <20200511145257.22970-1-geert+renesas@glider.be> Received-SPF: none client-ip=2a02:1800:120:4::f00:14; envelope-from=geert@linux-m68k.org; helo=xavier.telenet-ops.be X-detected-operating-system: by eggs.gnu.org: No matching host in p0f cache. That's all we know. X-Spam_score_int: -23 X-Spam_score: -2.4 X-Spam_bar: -- X-Spam_report: (-2.4 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.249, RCVD_IN_DNSWL_LOW=-0.7, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Peter Maydell , qemu-devel@nongnu.org, Geert Uytterhoeven , linux-doc@vger.kernel.org, Marc Zyngier , Magnus Damm , Christoffer Dall , linux-kernel@vger.kernel.org, linux-renesas-soc@vger.kernel.org, linux-gpio@vger.kernel.org, Alexander Graf , Paolo Bonzini , Phil Reid Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" Add a maintainership section for the GPIO Aggregator, covering documentation and driver source code. Signed-off-by: Geert Uytterhoeven Reviewed-by: Eugeniu Rosca Tested-by: Eugeniu Rosca --- v7: - Maintained => Supported, v6: - No changes, v5: - Add Reviewed-by, Tested-by, v4: - Drop controversial GPIO repeater, v3: - New. --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index fb124a4a29cb539e..4f14c1a88ad45c5b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7232,6 +7232,13 @@ F: Documentation/firmware-guide/acpi/gpio-properties.rst F: drivers/gpio/gpiolib-acpi.c F: drivers/gpio/gpiolib-acpi.h +GPIO AGGREGATOR +M: Geert Uytterhoeven +L: linux-gpio@vger.kernel.org +S: Supported +F: Documentation/admin-guide/gpio/gpio-aggregator.rst +F: drivers/gpio/gpio-aggregator.c + GPIO IR Transmitter M: Sean Young L: linux-media@vger.kernel.org