From nobody Wed Dec 17 13:50:55 2025 Received: from mail-wm1-f45.google.com (mail-wm1-f45.google.com [209.85.128.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A0D3126E706 for ; Wed, 12 Nov 2025 13:55:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762955750; cv=none; b=CKfLrvZl2fwnO/lAInY3IZvI8j/GIyHNWwOsQhn/kt5gOzB0I69QrsiwxFkdm9fsvPOjgQBmQaA5vas9L6gsQ8Z6RuD/A/oQ9HXHclObezfxJuxcMp7IZqBHZ4qPYyq7nmvczT9nsQpGQ5ce+sdojfDYQZW6Uy6hS49om1Qf3V0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762955750; c=relaxed/simple; bh=oTUYMsdG/IVDKhQ3B77Q3TRt2359OvEG4o2hpeKnoPQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=hAxAwqCAMxEwTr/k96uMJFBF11yc1Unn4kxEyGoMzrafMi2j1loj3S07LRjTkCtXk3ZGVz9uI3qvqMeom6B4/7NCp4J6MCXPN7RL/YoupuhSKOhuP2fy9FhuS9R0bgEUB3tZ3L+JVAWmHiy2YGV+lZ7hImCJmlyQJu+6Ugm5Fxc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=AoblrZGB; arc=none smtp.client-ip=209.85.128.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="AoblrZGB" Received: by mail-wm1-f45.google.com with SMTP id 5b1f17b1804b1-475ca9237c2so4904005e9.3 for ; Wed, 12 Nov 2025 05:55:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1762955746; x=1763560546; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=okxpEuXb1rioQLTWx+EehnmQO9hSAr1CFW8NOoMs7fs=; b=AoblrZGBoH9zIkUhPp9D2ErBCpW+AAvvzTckmDfAXP0YUBOqTQewEU769zHAQa2lAu WgrRiGbM31XF2FMABpv0FtqOmzsGzmeeQv0Oftpr8jkGJc8aHHGCdV0MyIIlvE0wE56t EHpbvhnY7FB03Wj2xu4bNDUZZFQduOIWBUfWeKBV8+jip7Tqp1Cfnkvg1uEZVtswl6eg T1J4ibUtz1Mj3eqmgGUaUMVwi1NpzWZi/Swls/TOgDo+4z7EQEFXB0gSHuf1SNPIxcC4 R3/JhRyN1GgwNREEHNn8S2mbIMxUOU8ywQbkBhpbURIQwKclza+i4i+oAImEeNdDtu5+ w9Cg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762955746; x=1763560546; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=okxpEuXb1rioQLTWx+EehnmQO9hSAr1CFW8NOoMs7fs=; b=xINWMAaTviwqAWZg3nscaCxLQJTq7uxYdBCctAdEg3B92Fo4JsNPkEnVjHrWzXunXY qCAubuu3T2h47jncw7lK9LSNaqvS7RfdYne1HuMvGRUT2nY69RKke8+WmSMbVUIkHkZ6 DKAu5579l0oblrVyOt3sMPU4qFcJcl5YVCd0iHEs71GgLijtgHKthHsHptqPeIsI4HdG JX/WFStbZ9ynQ1m1HF/32a/HNLRsTB1UUIEuGfOXRyisonu7jyuyfxRO2xp3S0uufq8M +PnQoNSTrEL5cUaoqnEzgTyivKa6aqMWxJGbaUtVWu05Y4wm6bNTZkU85+8wrkv9cmaD JUZw== X-Forwarded-Encrypted: i=1; AJvYcCX/eYtqZ/8yhjSWiPVkgJ4/xHf0vTCDCaZ08mnLVwVa1XBu/3cqbHdi308EHnTR/DZiVRxhOAwnujlNeYA=@vger.kernel.org X-Gm-Message-State: AOJu0Yy/Z3SV8QKU1Q1qQy/OioUy058NmRhCkHRHCd5N8jOse8l5P8PK PqGgqC5AOQEcQFHm1O6Q6byPIYxE4GJKtXi6lMgNOMTMQm2S1aSFvo6Ett9Xo69Hbpc= X-Gm-Gg: ASbGncvvtTL+M5J62hba3WIxB266f44zu1pMXuQCd9pOs6QqrhxTGKqof6+jm4Zk6YD IPwGZQFqTkMrB46MvujRWOa/DGX2g9Q/bFXOY7vhO1oSpfTfKS79Yb74L/ADDeCXMs+gQZhVej6 B0YZkxabTk4mwPKpTl9HsuOdci5Ci7VMEByxn7R5KFx4sce0ZD6unFBe/E1is9/nZ/x7ZP3hRqM nej7rGZRQxDEAO+wA1C+dgTGJmXdPNZepa+HUDI+PKCzwXj2qJbwPOGTY7QWSm2PUxK7Azt63sV OJRyGh3MMtWE5dJ/9kFGYK7GFS4iRzqP+vxwoQ2LKMKGT4y10FSNC/HVabDfJ9o/pMfLkl0BCxb ex0bvgpEYtvy2qcp3uLb5VPVHcN7jZiVlRSbQ7kVqQ++E4nMCIS5dxdbzcTGAjIj8YnE= X-Google-Smtp-Source: AGHT+IFrq4LOscGgyiaHGmHOOTxjacCxX8HGFaagLmdraJ6fFxhX4nYpJwcLo5aDxi19wWPTXZZRRg== X-Received: by 2002:a05:600c:1c85:b0:477:76cb:4812 with SMTP id 5b1f17b1804b1-47787000e64mr32508585e9.0.1762955745795; Wed, 12 Nov 2025 05:55:45 -0800 (PST) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:2a6:e453:42d3:11]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47787e2b835sm37711685e9.2.2025.11.12.05.55.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 12 Nov 2025 05:55:44 -0800 (PST) From: Bartosz Golaszewski Date: Wed, 12 Nov 2025 14:55:33 +0100 Subject: [PATCH v4 04/10] gpio: shared-proxy: implement the shared GPIO proxy driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251112-gpio-shared-v4-4-b51f97b1abd8@linaro.org> References: <20251112-gpio-shared-v4-0-b51f97b1abd8@linaro.org> In-Reply-To: <20251112-gpio-shared-v4-0-b51f97b1abd8@linaro.org> To: Kees Cook , Mika Westerberg , Dmitry Torokhov , Andrew Morton , Linus Walleij , Manivannan Sadhasivam , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Saravana Kannan , Greg Kroah-Hartman , Andy Shevchenko , Bartosz Golaszewski , Catalin Marinas , Will Deacon , Srinivas Kandagatla , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai , Alexey Klimov , Bjorn Andersson , Konrad Dybcio Cc: linux-hardening@vger.kernel.org, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-sound@vger.kernel.org, linux-arm-msm@vger.kernel.org, Bartosz Golaszewski X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=12178; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=0FrWcL7+b43kJTdQOvLH0UXC/dEHaWLrwVCfSK/xBPY=; b=owEBbQKS/ZANAwAKAQWdLsv/NoTDAcsmYgBpFJHVf+xVOt8uIIE7Q3GL+Iw/0lWC7Onn98eAr Q057KyMCyuJAjMEAAEKAB0WIQSR5RMt5bVGHXuiZfwFnS7L/zaEwwUCaRSR1QAKCRAFnS7L/zaE w2xdD/0YjJ2WVr1x7Md1meYTvfYwZL+7Z/SMjXxODil5PrnPx7Hv8h9QcUoxqYym2nQPi4ON2aA 1IZxll6V80bXl4X99ItUTRNq9xaUIZ8Ayr3cBnLrGmmSeD5lIYgtgM+dHnMFIbXjyZsYQe49WXJ dLRQTs7Gc5vmoNHgF4YKt2bPoxD9JMAR7OU+PcM8twtXLFuXvpkaifEiA4yhpVcQcIOpd7KzCBo MWsqdTnOQHOwyxuS1/9x65Tp6ArxVY0Y4zTr0q2cQHhvgbfcRlqEmGQ81Kl7xbuhKhAsbbywKqm +nOcUb6qA5SKj+wgLsSp3Ufyg3JeiOoHuvLdfiSvGCaeoFyYtc9oVFukSr1ejYPZX/XzZNx0Pgf f8C+tohg8JQV0zVIHB7xh5z2mQD1aQQGbjCyQb9bh69ry8N7KJKzQZlPuidh8C6Be6EAjo/a/dq pmJk5/53afBHz5YciwGbJ5Lp5vN2GUjVZbe1RujsVjNr2r3BoQPHsKg9FY4rQM2jiwQOUl2Ojz/ T5G6kJOSm7Kvp1/d9p3TU2fLbwgbMh0FLOpS+3OeF3IkjVkU8BN3mlyIT6LeWyyYUh97neuRjJA ikUGW3EKjXCkqZM3E8B0wO6c5yla0AddUYCHDkMTXsXNhB7JRuQGCpxJDiurov/XS8JObvZolQW GIoPLoyHE55tAvA== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski Add a virtual GPIO proxy driver which arbitrates access to a single shared GPIO by multiple users. It works together with the core shared GPIO support from GPIOLIB and functions by acquiring a reference to a shared GPIO descriptor exposed by gpiolib-shared and making sure that the state of the GPIO stays consistent. In general: if there's only one user at the moment: allow it to do anything as if this was a normal GPIO (in essence: just propagate calls to the underlying real hardware driver). If there are more users: don't allow to change the direction set by the initial user, allow to change configuration options but warn about possible conflicts and finally: treat the output-high value as a reference counted, logical "GPIO enabled" setting, meaning: the GPIO value is set to high when the first user requests it to be high and back to low once the last user stops "voting" for high. Reviewed-by: Linus Walleij Acked-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 9 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-shared-proxy.c | 333 +++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 343 insertions(+) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f90b4d3e77f7cab46525b7adfcf114a21d276678..f910c20f0d5d7771f7f8f3d52ce= d7bce413d24f1 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -2025,6 +2025,15 @@ config GPIO_SIM This enables the GPIO simulator - a configfs-based GPIO testing driver. =20 +config GPIO_SHARED_PROXY + tristate "Proxy driver for non-exclusive GPIOs" + default m + depends on GPIO_SHARED || COMPILE_TEST + select AUXILIARY_BUS + help + This enables the GPIO shared proxy driver - an abstraction layer + for GPIO pins that are shared by multiple devices. + endmenu =20 menu "GPIO Debugging utilities" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 48f309c764e3286c23dbe604be933f7180f0b89a..2421a8fd3733e0b06c2581262aa= a9cd629f66c7d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -161,6 +161,7 @@ obj-$(CONFIG_ARCH_SA1100) +=3D gpio-sa1100.o obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) +=3D gpio-sama5d2-piobu.o obj-$(CONFIG_GPIO_SCH311X) +=3D gpio-sch311x.o obj-$(CONFIG_GPIO_SCH) +=3D gpio-sch.o +obj-$(CONFIG_GPIO_SHARED_PROXY) +=3D gpio-shared-proxy.o obj-$(CONFIG_GPIO_SIFIVE) +=3D gpio-sifive.o obj-$(CONFIG_GPIO_SIM) +=3D gpio-sim.o obj-$(CONFIG_GPIO_SIOX) +=3D gpio-siox.o diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-pr= oxy.c new file mode 100644 index 0000000000000000000000000000000000000000..3ef2c40ed15229074052eda93b6= ee56f0a2bfb72 --- /dev/null +++ b/drivers/gpio/gpio-shared-proxy.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpiolib-shared.h" + +struct gpio_shared_proxy_data { + struct gpio_chip gc; + struct gpio_shared_desc *shared_desc; + struct device *dev; + bool voted_high; +}; + +static int +gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy, + int (*set_func)(struct gpio_desc *desc, int value), + int value) +{ + struct gpio_shared_desc *shared_desc =3D proxy->shared_desc; + struct gpio_desc *desc =3D shared_desc->desc; + int ret =3D 0; + + gpio_shared_lockdep_assert(shared_desc); + + if (value) { + /* User wants to set value to high. */ + if (proxy->voted_high) + /* Already voted for high, nothing to do. */ + goto out; + + /* Haven't voted for high yet. */ + if (!shared_desc->highcnt) { + /* + * Current value is low, need to actually set value + * to high. + */ + ret =3D set_func(desc, 1); + if (ret) + goto out; + } + + shared_desc->highcnt++; + proxy->voted_high =3D true; + + goto out; + } + + /* Desired value is low. */ + if (!proxy->voted_high) + /* We didn't vote for high, nothing to do. */ + goto out; + + /* We previously voted for high. */ + if (shared_desc->highcnt =3D=3D 1) { + /* This is the last remaining vote for high, set value to low. */ + ret =3D set_func(desc, 0); + if (ret) + goto out; + } + + shared_desc->highcnt--; + proxy->voted_high =3D false; + +out: + if (shared_desc->highcnt) + dev_dbg(proxy->dev, + "Voted for value '%s', effective value is 'high', number of votes for '= high': %u\n", + str_high_low(value), shared_desc->highcnt); + else + dev_dbg(proxy->dev, "Voted for value 'low', effective value is 'low'\n"); + + return ret; +} + +static int gpio_shared_proxy_request(struct gpio_chip *gc, unsigned int of= fset) +{ + struct gpio_shared_proxy_data *proxy =3D gpiochip_get_data(gc); + struct gpio_shared_desc *shared_desc =3D proxy->shared_desc; + + guard(gpio_shared_desc_lock)(shared_desc); + + proxy->shared_desc->usecnt++; + + dev_dbg(proxy->dev, "Shared GPIO requested, number of users: %u\n", + proxy->shared_desc->usecnt); + + return 0; +} + +static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offs= et) +{ + struct gpio_shared_proxy_data *proxy =3D gpiochip_get_data(gc); + struct gpio_shared_desc *shared_desc =3D proxy->shared_desc; + + guard(gpio_shared_desc_lock)(shared_desc); + + proxy->shared_desc->usecnt--; + + dev_dbg(proxy->dev, "Shared GPIO freed, number of users: %u\n", + proxy->shared_desc->usecnt); +} + +static int gpio_shared_proxy_set_config(struct gpio_chip *gc, + unsigned int offset, unsigned long cfg) +{ + struct gpio_shared_proxy_data *proxy =3D gpiochip_get_data(gc); + struct gpio_shared_desc *shared_desc =3D proxy->shared_desc; + struct gpio_desc *desc =3D shared_desc->desc; + int ret; + + guard(gpio_shared_desc_lock)(shared_desc); + + if (shared_desc->usecnt > 1) { + if (shared_desc->cfg !=3D cfg) { + dev_dbg(proxy->dev, + "Shared GPIO's configuration already set, accepting changes but users = may conflict!!\n"); + } else { + dev_dbg(proxy->dev, "Equal config requested, nothing to do\n"); + return 0; + } + } + + ret =3D gpiod_set_config(desc, cfg); + if (ret && ret !=3D -ENOTSUPP) + return ret; + + shared_desc->cfg =3D cfg; + return 0; +} + +static int gpio_shared_proxy_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + struct gpio_shared_proxy_data *proxy =3D gpiochip_get_data(gc); + struct gpio_shared_desc *shared_desc =3D proxy->shared_desc; + struct gpio_desc *desc =3D shared_desc->desc; + int dir; + + guard(gpio_shared_desc_lock)(shared_desc); + + if (shared_desc->usecnt =3D=3D 1) { + dev_dbg(proxy->dev, + "Only one user of this shared GPIO, allowing to set direction to input\= n"); + + return gpiod_direction_input(desc); + } + + dir =3D gpiod_get_direction(desc); + if (dir < 0) + return dir; + + if (dir =3D=3D GPIO_LINE_DIRECTION_OUT) { + dev_dbg(proxy->dev, + "Shared GPIO's direction already set to output, refusing to change\n"); + return -EPERM; + } + + return 0; +} + +static int gpio_shared_proxy_direction_output(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct gpio_shared_proxy_data *proxy =3D gpiochip_get_data(gc); + struct gpio_shared_desc *shared_desc =3D proxy->shared_desc; + struct gpio_desc *desc =3D shared_desc->desc; + int ret, dir; + + guard(gpio_shared_desc_lock)(shared_desc); + + if (shared_desc->usecnt =3D=3D 1) { + dev_dbg(proxy->dev, + "Only one user of this shared GPIO, allowing to set direction to output= with value '%s'\n", + str_high_low(value)); + + ret =3D gpiod_direction_output(desc, value); + if (ret) + return ret; + + if (value) { + proxy->voted_high =3D true; + shared_desc->highcnt =3D 1; + } else { + proxy->voted_high =3D false; + shared_desc->highcnt =3D 0; + } + + return 0; + } + + dir =3D gpiod_get_direction(desc); + if (dir < 0) + return dir; + + if (dir =3D=3D GPIO_LINE_DIRECTION_IN) { + dev_dbg(proxy->dev, + "Shared GPIO's direction already set to input, refusing to change\n"); + return -EPERM; + } + + return gpio_shared_proxy_set_unlocked(proxy, gpiod_direction_output, valu= e); +} + +static int gpio_shared_proxy_get(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_shared_proxy_data *proxy =3D gpiochip_get_data(gc); + + return gpiod_get_value(proxy->shared_desc->desc); +} + +static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc, + unsigned int offset) +{ + struct gpio_shared_proxy_data *proxy =3D gpiochip_get_data(gc); + + return gpiod_get_value_cansleep(proxy->shared_desc->desc); +} + +static int gpio_shared_proxy_do_set(struct gpio_shared_proxy_data *proxy, + int (*set_func)(struct gpio_desc *desc, int value), + int value) +{ + guard(gpio_shared_desc_lock)(proxy->shared_desc); + + return gpio_shared_proxy_set_unlocked(proxy, set_func, value); +} + +static int gpio_shared_proxy_set(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct gpio_shared_proxy_data *proxy =3D gpiochip_get_data(gc); + + return gpio_shared_proxy_do_set(proxy, gpiod_set_value, value); +} + +static int gpio_shared_proxy_set_cansleep(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct gpio_shared_proxy_data *proxy =3D gpiochip_get_data(gc); + + return gpio_shared_proxy_do_set(proxy, gpiod_set_value_cansleep, value); +} + +static int gpio_shared_proxy_get_direction(struct gpio_chip *gc, + unsigned int offset) +{ + struct gpio_shared_proxy_data *proxy =3D gpiochip_get_data(gc); + + return gpiod_get_direction(proxy->shared_desc->desc); +} + +static int gpio_shared_proxy_to_irq(struct gpio_chip *gc, unsigned int off= set) +{ + struct gpio_shared_proxy_data *proxy =3D gpiochip_get_data(gc); + + return gpiod_to_irq(proxy->shared_desc->desc); +} + +static int gpio_shared_proxy_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct gpio_shared_proxy_data *proxy; + struct gpio_shared_desc *shared_desc; + struct device *dev =3D &adev->dev; + struct gpio_chip *gc; + + shared_desc =3D devm_gpiod_shared_get(dev); + if (IS_ERR(shared_desc)) + return PTR_ERR(shared_desc); + + proxy =3D devm_kzalloc(dev, sizeof(*proxy), GFP_KERNEL); + if (!proxy) + return -ENOMEM; + + proxy->shared_desc =3D shared_desc; + proxy->dev =3D dev; + + gc =3D &proxy->gc; + gc->base =3D -1; + gc->ngpio =3D 1; + gc->label =3D dev_name(dev); + gc->parent =3D dev; + gc->owner =3D THIS_MODULE; + gc->can_sleep =3D shared_desc->can_sleep; + + gc->request =3D gpio_shared_proxy_request; + gc->free =3D gpio_shared_proxy_free; + gc->set_config =3D gpio_shared_proxy_set_config; + gc->direction_input =3D gpio_shared_proxy_direction_input; + gc->direction_output =3D gpio_shared_proxy_direction_output; + if (gc->can_sleep) { + gc->set =3D gpio_shared_proxy_set_cansleep; + gc->get =3D gpio_shared_proxy_get_cansleep; + } else { + gc->set =3D gpio_shared_proxy_set; + gc->get =3D gpio_shared_proxy_get; + } + gc->get_direction =3D gpio_shared_proxy_get_direction; + gc->to_irq =3D gpio_shared_proxy_to_irq; + + return devm_gpiochip_add_data(dev, &proxy->gc, proxy); +} + +static const struct auxiliary_device_id gpio_shared_proxy_id_table[] =3D { + { .name =3D "gpiolib_shared.proxy" }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, gpio_shared_proxy_id_table); + +static struct auxiliary_driver gpio_shared_proxy_driver =3D { + .driver =3D { + .name =3D "gpio-shared-proxy", + }, + .probe =3D gpio_shared_proxy_probe, + .id_table =3D gpio_shared_proxy_id_table, +}; +module_auxiliary_driver(gpio_shared_proxy_driver); + +MODULE_AUTHOR("Bartosz Golaszewski "); +MODULE_DESCRIPTION("Shared GPIO mux driver."); +MODULE_LICENSE("GPL"); --=20 2.51.0