From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id BC611CAC5AE for ; Wed, 24 Sep 2025 14:52:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References :Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=MSgowDlzhmLR7n/hdS2kMt37iKRjtkVmdn3+P096axs=; b=ZAIhUekWoeMLtwp4bDKvruW15y abgO4d+mFFTE+Qa60NVlhso6nEaksmuMtHXln01A4OyYkz0YbchaQBQcnWsFoejV+y/RFgoAl+1CO vFX49XZ2vQ7z4ZihkD+kFKD9RKRWvzjTjwKVq5hR1BqpZlsB76+PU5rAPPjfL91HoDwdkLdGWjRkr SYPHKhZ8h3zOCiXjLGRtZ3L60FFwW+a/BuM4g9h0/zO6OLONiPqonZzWoVamx0Li33MozADkX9D7t +KJc9AbmbUEFfHZ/P0D9iLwF/GdQWAasyBTsdoW1DWT96jz1J5iWAuHP3bK/aMZ1ib1TdbLGRGSYe 8buf+I7Q==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux)) id 1v1Qqw-00000000tRb-2bm3; Wed, 24 Sep 2025 14:52:18 +0000 Received: from mail-wr1-x430.google.com ([2a00:1450:4864:20::430]) by bombadil.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux)) id 1v1Qqi-00000000t8y-3om6 for linux-arm-kernel@lists.infradead.org; Wed, 24 Sep 2025 14:52:06 +0000 Received: by mail-wr1-x430.google.com with SMTP id ffacd0b85a97d-3b9edf4cf6cso6311054f8f.3 for ; Wed, 24 Sep 2025 07:52:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1758725523; x=1759330323; darn=lists.infradead.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=MSgowDlzhmLR7n/hdS2kMt37iKRjtkVmdn3+P096axs=; b=qczGNPaml+K0MLwVmJPKEOvgFRTCn8+bb99hy3qc7Fo7wq65GMl8dzNajG2IdgbTbK Lxb04OKLNvPAKfg8Ed8D23we7eCrA/GTWEchDZBfQnU6hBiaao6F6HIMhpksz6F4q1CX Z/D0YUVnVzVdnUyyK2jBXPY0efP78VyTH/3ggF/ZS0WXLrx+OzhJGFnrB3wwhWBVrmxi 3+s9ZY5FF1TK5C2//r5NSGB8f5TdOBt/0izekXZJbWg3xWAPBoaFxIavBlXjJZlDLAPM 0kzvFizGSRY1td2J+YDfgLEiuUb1CWQ36Wq+AF9VWCEmFI4Q5l9Adm/ebRU80VdxWWV6 6lRg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758725523; x=1759330323; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=MSgowDlzhmLR7n/hdS2kMt37iKRjtkVmdn3+P096axs=; b=vGp1MSB6MvosNo+YzGg9YYMtCFhFrzgRDye6gBEwj9pb1XE5toq7MZXNv+bmw5BWvC tdfKdzdcH6dNMjGuSmgLa6zc7gGq8vjPzd2GoptZkscKpmKW3w4vfbhfu2aCuYAcBMFN MGhVkkgQJgrJpCsdiFb4jht1ss5IwPOlQNnQ6J0HbBpiT958ba072wuJpnj1WUOMVLbV y5vhdHy+Mj3gV5ZQMZgEMTxyZYOBT7GLNyw004FbbPygdMyHOAxuNK9PfJT5Hp3LznM5 Cv3N5gOh3kS8MCkwetmLxwFrXJ6O3kj7Uxpiuuu3ipOLPXFFlAJIVnjvt5R8LF6vNoF/ tZsw== X-Forwarded-Encrypted: i=1; AJvYcCUEGFkMcpT6urWo3bSZttZ2NRF+ecw8+Lxuv3GSaHO/eu/4uZZdlNKwqcbc4c4CNA+/ZAC8ivxCy2UR77vGxAIi@lists.infradead.org X-Gm-Message-State: AOJu0Yz6XPtjFzgOecRVMctJwXmuzfbfjbk3aCoS5TFQWw+wiv93uKj/ fDsyDgqenY+8erDELljeXx6sCHH3Nr0BMrUDHDZOYH7vzcHcoC81oodBd6ILeS+k7pw= X-Gm-Gg: ASbGnctpc1w+HBsrON7O87VjIlHsxpneJCnn1u/VDAH8xF0p7njIT5hVNG4DwRAM0sV SbtQMRD7RVJTLohgTPATsntSYpPjmih2JWDTwkkQpJWd2xbR2sH6X7ACQJaI/9r5Uevz/9DOS2L Kbo1wdUleXu0Fo41jckNd7nQzYqetYwuhNiMqNCYtc5F1VL1JmSIQXV9ym1kHlWhJVyAP1WVLFb 7zJgJwMynF20tYBnuipHe8jHr5buiZzrZG9TmzkI/Sp+QWy9MO9i0jiFEVgYcU3ZWMWMMv4ka7p MA0fuNZ1xEF9fYYIEA6Y5aCScxNGM0NR2V5pzVDPbnL4MCMMVS/F2lvdHJxdEc2ttoDqcqDZnMG D73igayUTadqx2gGI9QEs41NHFxLF X-Google-Smtp-Source: AGHT+IEyXrLx96T6QsIa8eF3uCo4FfQct7eHaeDlUb6PK1yPrstccT+eXxcbpNJurK4UN8hglLiDIQ== X-Received: by 2002:a05:6000:2486:b0:3ed:e1d8:bd73 with SMTP id ffacd0b85a97d-40e4d9c9330mr205855f8f.57.1758725522647; Wed, 24 Sep 2025 07:52:02 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:1729:c9b6:7a46:b4a3]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3ee0fbc7188sm29514750f8f.37.2025.09.24.07.52.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Sep 2025 07:52:01 -0700 (PDT) From: Bartosz Golaszewski Date: Wed, 24 Sep 2025 16:51:33 +0200 Subject: [PATCH RFC 5/9] gpio: shared-proxy: implement the shared GPIO proxy driver MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20250924-gpio-shared-v1-5-775e7efeb1a3@linaro.org> References: <20250924-gpio-shared-v1-0-775e7efeb1a3@linaro.org> In-Reply-To: <20250924-gpio-shared-v1-0-775e7efeb1a3@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 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=11907; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=sfRoEgGj7pFTcA+n5dZu5OkxqjXNqNa9C5JDXNfemak=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBo1AWGpmtkXlBA5OHbVuuWQ7dGjcVco9DBJIZ8K fP8ukS/waWJAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCaNQFhgAKCRARpy6gFHHX ck0BD/98hcjOxep9HWr0VdKe/QouS7zxedAcWxWEJoMrhkgG1BLWJn535Te3HNz/Tynze+mRBL2 k8OsfoyLfQ7kuvDsMiJJYTGDNeUZzjgvRbSqlukONXP+fAbFVer7RiIu6iOpVEkd0EIH1wx9FJw xnH3SFt+Tg7TbwCsf6q3xjOrZe7iXIGFCtOn6iz3EShqRdJ678/At4IGPnN5N+pWJEInqiTHehc c1de5mkBKXtKhAnKffUxNqNOBw177wIf+Mr/q87uJP5s3NIbhSkO1x7Wgfovssk42njcuuqigBp X0pSHMEjSzmOE4arnFD80o0Q+lVUezJXnRPAmtNYN8k3v9cBQE16+RPkImcnKUBfhlO085MnYJN G8MBI+Yyr7Lr0fIrlQbRe2WnbjTvp7scVRLBM81BMN/kuTSlqHcSRAivsBzDwr0AwCHuTDrXef9 OqsEQPL9fGaOxVtNSXBiyE+yu7RdFvzvh3xWKKNDhMoOlLT0ZqQ65LwqdVDuKfibEB0d9O6ns55 77wWwBf60k++C9fUYEUmXZydRs7RRVyb7CKCQqassR1f1YUY061diwTRhyOPGVLP5EsugEpxVfN eAhuOPTQHAC+8g3a6kveR0eW2FY9wLriKQM3f1I9YY3fSgcjNnA2Fttjlm0nT1dn4I7p4OxaQwg OUq+8Fe82eMv9iA== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20250924_075205_047297_B0F13E18 X-CRM114-Status: GOOD ( 31.26 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org 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. Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 9 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-shared-proxy.c | 328 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 338 insertions(+) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 679a7385a9776eef96a86ca4f429ee83ac939362..de8e4febf344fc19a633cd7efe8412fe12166fb8 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. +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 menu "GPIO Debugging utilities" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index f702f7e27e5b4017e7eab3019dae4ec912d534f8..d0020bc70b84f6fb9949165070c21725a60f47e2 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -160,6 +160,7 @@ obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o +obj-$(CONFIG_GPIO_SHARED_PROXY) += gpio-shared-proxy.o obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o obj-$(CONFIG_GPIO_SIM) += gpio-sim.o obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c new file mode 100644 index 0000000000000000000000000000000000000000..804038d33a40e45c8577f278faea79145a91a5bd --- /dev/null +++ b/drivers/gpio/gpio-shared-proxy.c @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Linaro Ltd. + */ + +#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 = proxy->shared_desc; + struct gpio_desc *desc = shared_desc->desc; + int ret = 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 = set_func(desc, 1); + if (ret) + goto out; + } + + shared_desc->highcnt++; + proxy->voted_high = 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 == 1) { + /* This is the last remaining vote for high, set value to low. */ + ret = set_func(desc, 0); + if (ret) + goto out; + } + + shared_desc->highcnt--; + proxy->voted_high = 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", + value ? "high" : "low", 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 offset) +{ + struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); + struct gpio_shared_desc *shared_desc = 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 offset) +{ + struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); + struct gpio_shared_desc *shared_desc = 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 = gpiochip_get_data(gc); + struct gpio_shared_desc *shared_desc = proxy->shared_desc; + struct gpio_desc *desc = shared_desc->desc; + int ret; + + guard(gpio_shared_desc_lock)(shared_desc); + + if (shared_desc->usecnt > 1) { + if (shared_desc->cfg != 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 = gpiod_set_config(desc, cfg); + if (ret && ret != -ENOTSUPP) + return ret; + + shared_desc->cfg = cfg; + return 0; +} + +static int gpio_shared_proxy_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); + struct gpio_shared_desc *shared_desc = proxy->shared_desc; + struct gpio_desc *desc = shared_desc->desc; + int dir; + + guard(gpio_shared_desc_lock)(shared_desc); + + if (shared_desc->usecnt == 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 = gpiod_get_direction(desc); + if (dir < 0) + return dir; + + if (dir == 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 = gpiochip_get_data(gc); + struct gpio_shared_desc *shared_desc = proxy->shared_desc; + struct gpio_desc *desc = shared_desc->desc; + int ret, dir; + + guard(gpio_shared_desc_lock)(shared_desc); + + if (shared_desc->usecnt == 1) { + dev_dbg(proxy->dev, + "Only one user of this shared GPIO, allowing to set direction to output\n"); + + ret = gpiod_direction_output(desc, value); + if (ret) + return ret; + + if (value) { + proxy->voted_high = true; + shared_desc->highcnt = 1; + } else { + proxy->voted_high = false; + shared_desc->highcnt = 0; + } + + return 0; + } + + dir = gpiod_get_direction(desc); + if (dir < 0) + return dir; + + if (dir == 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, value); +} + +static int gpio_shared_proxy_get(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_shared_proxy_data *proxy = 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 = 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 = 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 = 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 = 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 offset) +{ + struct gpio_shared_proxy_data *proxy = 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 = &adev->dev; + struct gpio_chip *gc; + + shared_desc = devm_gpiod_shared_get(dev); + if (IS_ERR(shared_desc)) + return PTR_ERR(shared_desc); + + proxy = devm_kzalloc(dev, sizeof(*proxy), GFP_KERNEL); + if (!proxy) + return -ENOMEM; + + proxy->shared_desc = shared_desc; + proxy->dev = dev; + + gc = &proxy->gc; + gc->base = -1; + gc->ngpio = 1; + gc->label = dev_name(dev); + gc->parent = dev; + gc->owner = THIS_MODULE; + gc->can_sleep = shared_desc->can_sleep; + + gc->request = gpio_shared_proxy_request; + gc->free = gpio_shared_proxy_free; + gc->set_config = gpio_shared_proxy_set_config; + gc->direction_input = gpio_shared_proxy_direction_input; + gc->direction_output = gpio_shared_proxy_direction_output; + if (gc->can_sleep) { + gc->set = gpio_shared_proxy_set_cansleep; + gc->get = gpio_shared_proxy_get_cansleep; + } else { + gc->set = gpio_shared_proxy_set; + gc->get = gpio_shared_proxy_get; + } + gc->get_direction = gpio_shared_proxy_get_direction; + gc->to_irq = 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[] = { + { .name = "gpiolib_shared.proxy" }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, gpio_shared_proxy_id_table); + +static struct auxiliary_driver gpio_shared_proxy_driver = { + .driver = { + .name = "gpio-shared-proxy", + }, + .probe = gpio_shared_proxy_probe, + .id_table = 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"); -- 2.48.1