Linux GPIO subsystem development
 help / color / mirror / Atom feed
From: Shawn Guo <shengchao.guo@oss.qualcomm.com>
To: Linus Walleij <linusw@kernel.org>
Cc: Bartosz Golaszewski <brgl@kernel.org>,
	Bjorn Andersson <andersson@kernel.org>,
	Yu Zhang <yu.zhang@oss.qualcomm.com>,
	linux-gpio@vger.kernel.org, linux-arm-msm@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	Shawn Guo <shengchao.guo@oss.qualcomm.com>
Subject: [PATCH 1/2] gpio: shared-proxy: track direction instead of reading it back
Date: Tue, 15 Sep 2026 09:44:46 +0800	[thread overview]
Message-ID: <20260915014447.282121-2-shengchao.guo@oss.qualcomm.com> (raw)
In-Reply-To: <20260915014447.282121-1-shengchao.guo@oss.qualcomm.com>

On boards where more than one consumer shares a Qualcomm SPMI PMIC GPIO,
all but the first consumer fail to configure the line as output:

  reg-fixed-voltage regulator-wcn-core-vm-1p35: setup of GPIO (default) failed: -1
  reg-fixed-voltage regulator-wcn-core-vm-1p35: error -EPERM: can't get GPIO

The first consumer takes the usecnt == 1 branch and really does set the
underlying line to output.  Later consumers instead read the direction
back with gpiod_get_direction() and refuse with -EPERM if it reports
input.  pmic_gpio_get_direction() derives the direction from the pad's
input buffer (pad->input_enabled), which pmic_gpio_direction_output()
never clears, so a pad whose input buffer is enabled at power-up reports
input forever and the proxy rejects every consumer after the first.

The readback is not needed: the proxy is the only entity configuring the
line, so it already knows which direction it asked for.  Record that in
struct gpio_shared_desc and compare against the recorded value.  Keep a
readback in the get_direction() callback for the case where no proxy has
configured the line yet, and reset the recorded direction once the last
user goes away so that the next requester establishes it again.

Assisted-by: LLM
Fixes: e992d54c6f97 ("gpio: shared-proxy: implement the shared GPIO proxy driver")
Signed-off-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
---
 drivers/gpio/gpio-shared-proxy.c | 43 +++++++++++++++++++-------------
 drivers/gpio/gpiolib-shared.c    |  1 +
 drivers/gpio/gpiolib-shared.h    |  1 +
 3 files changed, 27 insertions(+), 18 deletions(-)

diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c
index bc69b8729d19..a8f0d08c6d9f 100644
--- a/drivers/gpio/gpio-shared-proxy.c
+++ b/drivers/gpio/gpio-shared-proxy.c
@@ -116,6 +116,8 @@ static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset)
 	}
 
 	proxy->shared_desc->usecnt--;
+	if (!shared_desc->usecnt)
+		shared_desc->dir = -1;
 
 	dev_dbg(proxy->dev, "Shared GPIO freed, number of users: %u\n",
 		proxy->shared_desc->usecnt);
@@ -155,22 +157,24 @@ static int gpio_shared_proxy_direction_input(struct gpio_chip *gc,
 	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;
+	int ret;
 
 	guard(mutex)(&shared_desc->mutex);
 
-	if (shared_desc->usecnt == 1) {
+	if (shared_desc->usecnt == 1 || shared_desc->dir < 0) {
 		dev_dbg(proxy->dev,
-			"Only one user of this shared GPIO, allowing to set direction to input\n");
+			"Setting the direction of the shared GPIO to input\n");
 
-		return gpiod_direction_input(desc);
-	}
+		ret = gpiod_direction_input(desc);
+		if (ret)
+			return ret;
 
-	dir = gpiod_get_direction(desc);
-	if (dir < 0)
-		return dir;
+		shared_desc->dir = GPIO_LINE_DIRECTION_IN;
+
+		return 0;
+	}
 
-	if (dir == GPIO_LINE_DIRECTION_OUT) {
+	if (shared_desc->dir == GPIO_LINE_DIRECTION_OUT) {
 		dev_dbg(proxy->dev,
 			"Shared GPIO's direction already set to output, refusing to change\n");
 		return -EPERM;
@@ -185,19 +189,20 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc,
 	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;
+	int ret;
 
 	guard(mutex)(&shared_desc->mutex);
 
-	if (shared_desc->usecnt == 1) {
+	if (shared_desc->usecnt == 1 || shared_desc->dir < 0) {
 		dev_dbg(proxy->dev,
-			"Only one user of this shared GPIO, allowing to set direction to output with value '%s'\n",
+			"Setting the direction of the shared GPIO to output with value '%s'\n",
 			str_high_low(value));
 
 		ret = gpiod_direction_output(desc, value);
 		if (ret)
 			return ret;
 
+		shared_desc->dir = GPIO_LINE_DIRECTION_OUT;
 		shared_desc->def_val = value;
 		shared_desc->votecnt = 0;
 		proxy->voted_change = false;
@@ -205,11 +210,7 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc,
 		return 0;
 	}
 
-	dir = gpiod_get_direction(desc);
-	if (dir < 0)
-		return dir;
-
-	if (dir == GPIO_LINE_DIRECTION_IN) {
+	if (shared_desc->dir == GPIO_LINE_DIRECTION_IN) {
 		dev_dbg(proxy->dev,
 			"Shared GPIO's direction already set to input, refusing to change\n");
 		return -EPERM;
@@ -240,8 +241,14 @@ static int gpio_shared_proxy_get_direction(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(mutex)(&shared_desc->mutex);
+
+	if (shared_desc->dir < 0)
+		return gpiod_get_direction(shared_desc->desc);
 
-	return gpiod_get_direction(proxy->shared_desc->desc);
+	return shared_desc->dir;
 }
 
 static int gpio_shared_proxy_to_irq(struct gpio_chip *gc, unsigned int offset)
diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index 5f9623e40b0f..8267b5089244 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -661,6 +661,7 @@ gpiod_shared_desc_create(struct gpio_shared_entry *entry)
 	}
 
 	shared_desc->desc = &gdev->descs[entry->offset];
+	shared_desc->dir = -1;
 	mutex_init(&shared_desc->mutex);
 
 	return shared_desc;
diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h
index 618756f6c6aa..0ef3bc7a7916 100644
--- a/drivers/gpio/gpiolib-shared.h
+++ b/drivers/gpio/gpiolib-shared.h
@@ -43,6 +43,7 @@ struct gpio_shared_desc {
 	unsigned int usecnt;
 	unsigned int votecnt;
 	int def_val;
+	int dir; /* GPIO_LINE_DIRECTION_* as configured by the proxies, -1 if unset */
 	struct mutex mutex; /* serializes all proxy operations on this descriptor */
 };
 
-- 
2.43.0


  reply	other threads:[~2026-09-15  1:45 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-15  1:44 [PATCH 0/2] Fix sharing a PMIC GPIO between multiple consumers Shawn Guo
2026-09-15  1:44 ` Shawn Guo [this message]
2026-09-16  8:48   ` [PATCH 1/2] gpio: shared-proxy: track direction instead of reading it back Bartosz Golaszewski
2026-09-16  9:00   ` Bartosz Golaszewski
2026-09-18 14:43     ` Shawn Guo
2026-09-21  9:55       ` Bartosz Golaszewski
2026-09-15  1:44 ` [PATCH 2/2] pinctrl: qcom: spmi-gpio: make direction changes exclusive Shawn Guo
2026-09-15 13:11   ` Neil Armstrong
2026-09-18  1:11     ` Shawn Guo
2026-09-18  8:05       ` Bartosz Golaszewski
2026-09-21 13:29         ` Shawn Guo
2026-09-22  7:00         ` Shawn Guo
2026-09-16  8:49 ` [PATCH 0/2] Fix sharing a PMIC GPIO between multiple consumers Bartosz Golaszewski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260915014447.282121-2-shengchao.guo@oss.qualcomm.com \
    --to=shengchao.guo@oss.qualcomm.com \
    --cc=andersson@kernel.org \
    --cc=brgl@kernel.org \
    --cc=linusw@kernel.org \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-gpio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=yu.zhang@oss.qualcomm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox