All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andrew Jeffery <andrew@aj.id.au>
To: linux-leds@vger.kernel.org, linux-gpio@vger.kernel.org
Cc: clg@kaod.org, robh+dt@kernel.org, joel@jms.id.au, pavel@ucw.cz,
	linus.walleij@linaro.org, devicetree@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
	andy.shevchenko@gmail.com
Subject: [PATCH 1/2] leds: pca955x: Make the gpiochip always expose all pins
Date: Tue, 21 Sep 2021 14:09:35 +0930	[thread overview]
Message-ID: <20210921043936.468001-2-andrew@aj.id.au> (raw)
In-Reply-To: <20210921043936.468001-1-andrew@aj.id.au>

The devicetree binding allows specifying which pins are GPIO vs LED.
Limiting the instantiated gpiochip to just these pins as the driver
currently does requires an arbitrary mapping between pins and GPIOs, but
such a mapping is not implemented by the driver. As a result,
specifying GPIOs in such a way that they don't map 1-to-1 to pin indexes
does not function as expected.

Establishing such a mapping is more complex than not and even if we did,
doing so leads to a slightly hairy userspace experience as the behaviour
of the PCA955x gpiochip would depend on how the pins are assigned in the
devicetree. Instead, always expose all pins via the gpiochip to provide
a stable interface and track which pins are in use.

Specifying a pin as `type = <PCA955X_TYPE_GPIO>;` in the devicetree
becomes a no-op.

I've assessed the impact of this change by looking through all of the
affected devicetrees as of the tag leds-5.15-rc1:

```
$ git grep -l 'pca955[0123]' $(find . -name dts -type d)
arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts
arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts
arch/arm/boot/dts/aspeed-bmc-opp-mihawk.dts
arch/arm/boot/dts/aspeed-bmc-opp-mowgli.dts
arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
arch/arm/boot/dts/aspeed-bmc-opp-tacoma.dts
arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
```

These are all IBM-associated platforms. I've analysed both the
devicetrees and schematics where necessary to determine whether any
systems hit the hazard of the current broken behaviour. For the most
part, the systems specify the pins as either all LEDs or all GPIOs, or
at least do so in a way such that the broken behaviour isn't exposed.

The main counter-point to this observation is the Everest system whose
devicetree describes a large number of PCA955x devices and in some cases
has pin assignments that hit the hazard. However, there does not seem to
be any use of the affected GPIOs in the userspace associated with
Everest.

Regardless, any use of the hazardous GPIOs in Everest is already broken,
so let's fix the interface and then fix any already broken userspace
with it.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/leds/leds-pca955x.c | 63 +++++++++++++++++++------------------
 1 file changed, 33 insertions(+), 30 deletions(-)

diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index a6b5699aeae4..77c0f461ab95 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -37,6 +37,7 @@
  *  bits the chip supports.
  */
 
+#include <linux/bitops.h>
 #include <linux/ctype.h>
 #include <linux/delay.h>
 #include <linux/err.h>
@@ -118,6 +119,7 @@ struct pca955x {
 	struct pca955x_led *leds;
 	struct pca955x_chipdef	*chipdef;
 	struct i2c_client	*client;
+	unsigned long active_pins;
 #ifdef CONFIG_LEDS_PCA955X_GPIO
 	struct gpio_chip gpio;
 #endif
@@ -360,12 +362,15 @@ static int pca955x_read_input(struct i2c_client *client, int n, u8 *val)
 static int pca955x_gpio_request_pin(struct gpio_chip *gc, unsigned int offset)
 {
 	struct pca955x *pca955x = gpiochip_get_data(gc);
-	struct pca955x_led *led = &pca955x->leds[offset];
 
-	if (led->type == PCA955X_TYPE_GPIO)
-		return 0;
+	return test_and_set_bit(offset, &pca955x->active_pins) ? -EBUSY : 0;
+}
 
-	return -EBUSY;
+static void pca955x_gpio_free_pin(struct gpio_chip *gc, unsigned int offset)
+{
+	struct pca955x *pca955x = gpiochip_get_data(gc);
+
+	clear_bit(offset, &pca955x->active_pins);
 }
 
 static int pca955x_set_value(struct gpio_chip *gc, unsigned int offset,
@@ -489,7 +494,6 @@ static int pca955x_probe(struct i2c_client *client)
 	struct i2c_adapter *adapter;
 	int i, err;
 	struct pca955x_platform_data *pdata;
-	int ngpios = 0;
 	bool set_default_label = false;
 	bool keep_pwm = false;
 	char default_label[8];
@@ -567,9 +571,7 @@ static int pca955x_probe(struct i2c_client *client)
 
 		switch (pca955x_led->type) {
 		case PCA955X_TYPE_NONE:
-			break;
 		case PCA955X_TYPE_GPIO:
-			ngpios++;
 			break;
 		case PCA955X_TYPE_LED:
 			led = &pca955x_led->led_cdev;
@@ -613,6 +615,8 @@ static int pca955x_probe(struct i2c_client *client)
 			if (err)
 				return err;
 
+			set_bit(i, &pca955x->active_pins);
+
 			/*
 			 * For default-state == "keep", let the core update the
 			 * brightness from the hardware, then check the
@@ -650,31 +654,30 @@ static int pca955x_probe(struct i2c_client *client)
 		return err;
 
 #ifdef CONFIG_LEDS_PCA955X_GPIO
-	if (ngpios) {
-		pca955x->gpio.label = "gpio-pca955x";
-		pca955x->gpio.direction_input = pca955x_gpio_direction_input;
-		pca955x->gpio.direction_output = pca955x_gpio_direction_output;
-		pca955x->gpio.set = pca955x_gpio_set_value;
-		pca955x->gpio.get = pca955x_gpio_get_value;
-		pca955x->gpio.request = pca955x_gpio_request_pin;
-		pca955x->gpio.can_sleep = 1;
-		pca955x->gpio.base = -1;
-		pca955x->gpio.ngpio = ngpios;
-		pca955x->gpio.parent = &client->dev;
-		pca955x->gpio.owner = THIS_MODULE;
+	pca955x->gpio.label = "gpio-pca955x";
+	pca955x->gpio.direction_input = pca955x_gpio_direction_input;
+	pca955x->gpio.direction_output = pca955x_gpio_direction_output;
+	pca955x->gpio.set = pca955x_gpio_set_value;
+	pca955x->gpio.get = pca955x_gpio_get_value;
+	pca955x->gpio.request = pca955x_gpio_request_pin;
+	pca955x->gpio.free = pca955x_gpio_free_pin;
+	pca955x->gpio.can_sleep = 1;
+	pca955x->gpio.base = -1;
+	pca955x->gpio.ngpio = chip->bits;
+	pca955x->gpio.parent = &client->dev;
+	pca955x->gpio.owner = THIS_MODULE;
 
-		err = devm_gpiochip_add_data(&client->dev, &pca955x->gpio,
-					     pca955x);
-		if (err) {
-			/* Use data->gpio.dev as a flag for freeing gpiochip */
-			pca955x->gpio.parent = NULL;
-			dev_warn(&client->dev, "could not add gpiochip\n");
-			return err;
-		}
-		dev_info(&client->dev, "gpios %i...%i\n",
-			 pca955x->gpio.base, pca955x->gpio.base +
-			 pca955x->gpio.ngpio - 1);
+	err = devm_gpiochip_add_data(&client->dev, &pca955x->gpio,
+				     pca955x);
+	if (err) {
+		/* Use data->gpio.dev as a flag for freeing gpiochip */
+		pca955x->gpio.parent = NULL;
+		dev_warn(&client->dev, "could not add gpiochip\n");
+		return err;
 	}
+	dev_info(&client->dev, "gpios %i...%i\n",
+		 pca955x->gpio.base, pca955x->gpio.base +
+		 pca955x->gpio.ngpio - 1);
 #endif
 
 	return 0;
-- 
2.30.2


WARNING: multiple messages have this Message-ID (diff)
From: Andrew Jeffery <andrew@aj.id.au>
To: linux-leds@vger.kernel.org, linux-gpio@vger.kernel.org
Cc: clg@kaod.org, robh+dt@kernel.org, joel@jms.id.au, pavel@ucw.cz,
	linus.walleij@linaro.org, devicetree@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
	andy.shevchenko@gmail.com
Subject: [PATCH 1/2] leds: pca955x: Make the gpiochip always expose all pins
Date: Tue, 21 Sep 2021 14:09:35 +0930	[thread overview]
Message-ID: <20210921043936.468001-2-andrew@aj.id.au> (raw)
In-Reply-To: <20210921043936.468001-1-andrew@aj.id.au>

The devicetree binding allows specifying which pins are GPIO vs LED.
Limiting the instantiated gpiochip to just these pins as the driver
currently does requires an arbitrary mapping between pins and GPIOs, but
such a mapping is not implemented by the driver. As a result,
specifying GPIOs in such a way that they don't map 1-to-1 to pin indexes
does not function as expected.

Establishing such a mapping is more complex than not and even if we did,
doing so leads to a slightly hairy userspace experience as the behaviour
of the PCA955x gpiochip would depend on how the pins are assigned in the
devicetree. Instead, always expose all pins via the gpiochip to provide
a stable interface and track which pins are in use.

Specifying a pin as `type = <PCA955X_TYPE_GPIO>;` in the devicetree
becomes a no-op.

I've assessed the impact of this change by looking through all of the
affected devicetrees as of the tag leds-5.15-rc1:

```
$ git grep -l 'pca955[0123]' $(find . -name dts -type d)
arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts
arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts
arch/arm/boot/dts/aspeed-bmc-opp-mihawk.dts
arch/arm/boot/dts/aspeed-bmc-opp-mowgli.dts
arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
arch/arm/boot/dts/aspeed-bmc-opp-tacoma.dts
arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
```

These are all IBM-associated platforms. I've analysed both the
devicetrees and schematics where necessary to determine whether any
systems hit the hazard of the current broken behaviour. For the most
part, the systems specify the pins as either all LEDs or all GPIOs, or
at least do so in a way such that the broken behaviour isn't exposed.

The main counter-point to this observation is the Everest system whose
devicetree describes a large number of PCA955x devices and in some cases
has pin assignments that hit the hazard. However, there does not seem to
be any use of the affected GPIOs in the userspace associated with
Everest.

Regardless, any use of the hazardous GPIOs in Everest is already broken,
so let's fix the interface and then fix any already broken userspace
with it.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/leds/leds-pca955x.c | 63 +++++++++++++++++++------------------
 1 file changed, 33 insertions(+), 30 deletions(-)

diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index a6b5699aeae4..77c0f461ab95 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -37,6 +37,7 @@
  *  bits the chip supports.
  */
 
+#include <linux/bitops.h>
 #include <linux/ctype.h>
 #include <linux/delay.h>
 #include <linux/err.h>
@@ -118,6 +119,7 @@ struct pca955x {
 	struct pca955x_led *leds;
 	struct pca955x_chipdef	*chipdef;
 	struct i2c_client	*client;
+	unsigned long active_pins;
 #ifdef CONFIG_LEDS_PCA955X_GPIO
 	struct gpio_chip gpio;
 #endif
@@ -360,12 +362,15 @@ static int pca955x_read_input(struct i2c_client *client, int n, u8 *val)
 static int pca955x_gpio_request_pin(struct gpio_chip *gc, unsigned int offset)
 {
 	struct pca955x *pca955x = gpiochip_get_data(gc);
-	struct pca955x_led *led = &pca955x->leds[offset];
 
-	if (led->type == PCA955X_TYPE_GPIO)
-		return 0;
+	return test_and_set_bit(offset, &pca955x->active_pins) ? -EBUSY : 0;
+}
 
-	return -EBUSY;
+static void pca955x_gpio_free_pin(struct gpio_chip *gc, unsigned int offset)
+{
+	struct pca955x *pca955x = gpiochip_get_data(gc);
+
+	clear_bit(offset, &pca955x->active_pins);
 }
 
 static int pca955x_set_value(struct gpio_chip *gc, unsigned int offset,
@@ -489,7 +494,6 @@ static int pca955x_probe(struct i2c_client *client)
 	struct i2c_adapter *adapter;
 	int i, err;
 	struct pca955x_platform_data *pdata;
-	int ngpios = 0;
 	bool set_default_label = false;
 	bool keep_pwm = false;
 	char default_label[8];
@@ -567,9 +571,7 @@ static int pca955x_probe(struct i2c_client *client)
 
 		switch (pca955x_led->type) {
 		case PCA955X_TYPE_NONE:
-			break;
 		case PCA955X_TYPE_GPIO:
-			ngpios++;
 			break;
 		case PCA955X_TYPE_LED:
 			led = &pca955x_led->led_cdev;
@@ -613,6 +615,8 @@ static int pca955x_probe(struct i2c_client *client)
 			if (err)
 				return err;
 
+			set_bit(i, &pca955x->active_pins);
+
 			/*
 			 * For default-state == "keep", let the core update the
 			 * brightness from the hardware, then check the
@@ -650,31 +654,30 @@ static int pca955x_probe(struct i2c_client *client)
 		return err;
 
 #ifdef CONFIG_LEDS_PCA955X_GPIO
-	if (ngpios) {
-		pca955x->gpio.label = "gpio-pca955x";
-		pca955x->gpio.direction_input = pca955x_gpio_direction_input;
-		pca955x->gpio.direction_output = pca955x_gpio_direction_output;
-		pca955x->gpio.set = pca955x_gpio_set_value;
-		pca955x->gpio.get = pca955x_gpio_get_value;
-		pca955x->gpio.request = pca955x_gpio_request_pin;
-		pca955x->gpio.can_sleep = 1;
-		pca955x->gpio.base = -1;
-		pca955x->gpio.ngpio = ngpios;
-		pca955x->gpio.parent = &client->dev;
-		pca955x->gpio.owner = THIS_MODULE;
+	pca955x->gpio.label = "gpio-pca955x";
+	pca955x->gpio.direction_input = pca955x_gpio_direction_input;
+	pca955x->gpio.direction_output = pca955x_gpio_direction_output;
+	pca955x->gpio.set = pca955x_gpio_set_value;
+	pca955x->gpio.get = pca955x_gpio_get_value;
+	pca955x->gpio.request = pca955x_gpio_request_pin;
+	pca955x->gpio.free = pca955x_gpio_free_pin;
+	pca955x->gpio.can_sleep = 1;
+	pca955x->gpio.base = -1;
+	pca955x->gpio.ngpio = chip->bits;
+	pca955x->gpio.parent = &client->dev;
+	pca955x->gpio.owner = THIS_MODULE;
 
-		err = devm_gpiochip_add_data(&client->dev, &pca955x->gpio,
-					     pca955x);
-		if (err) {
-			/* Use data->gpio.dev as a flag for freeing gpiochip */
-			pca955x->gpio.parent = NULL;
-			dev_warn(&client->dev, "could not add gpiochip\n");
-			return err;
-		}
-		dev_info(&client->dev, "gpios %i...%i\n",
-			 pca955x->gpio.base, pca955x->gpio.base +
-			 pca955x->gpio.ngpio - 1);
+	err = devm_gpiochip_add_data(&client->dev, &pca955x->gpio,
+				     pca955x);
+	if (err) {
+		/* Use data->gpio.dev as a flag for freeing gpiochip */
+		pca955x->gpio.parent = NULL;
+		dev_warn(&client->dev, "could not add gpiochip\n");
+		return err;
 	}
+	dev_info(&client->dev, "gpios %i...%i\n",
+		 pca955x->gpio.base, pca955x->gpio.base +
+		 pca955x->gpio.ngpio - 1);
 #endif
 
 	return 0;
-- 
2.30.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  reply	other threads:[~2021-09-21  4:40 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-09-21  4:39 [PATCH 0/2] leds: pca955x: Expose GPIOs for all pins Andrew Jeffery
2021-09-21  4:39 ` Andrew Jeffery
2021-09-21  4:39 ` Andrew Jeffery [this message]
2021-09-21  4:39   ` [PATCH 1/2] leds: pca955x: Make the gpiochip always expose " Andrew Jeffery
2021-11-01  2:07   ` Andrew Jeffery
2021-11-01  2:07     ` Andrew Jeffery
2021-11-09 11:03   ` Linus Walleij
2021-11-09 11:03     ` Linus Walleij
2021-11-16  2:44     ` Joel Stanley
2021-11-16  2:44       ` Joel Stanley
2021-09-21  4:39 ` [PATCH 2/2] leds: pca955x: Allow zero LEDs to be specified Andrew Jeffery
2021-09-21  4:39   ` Andrew Jeffery
2021-09-21 12:10 ` [PATCH 0/2] leds: pca955x: Expose GPIOs for all pins Andy Shevchenko
2021-09-21 12:10   ` Andy Shevchenko
2021-09-23 21:48 ` Linus Walleij
2021-09-23 21:48   ` Linus Walleij
2021-09-24  3:49 ` Joel Stanley
2021-09-24  3:49   ` Joel Stanley
2021-09-24  6:41 ` Cédric Le Goater
2022-02-21  7:33   ` Joel Stanley
2022-02-21  7:33     ` Joel Stanley
2022-03-02  8:54     ` Pavel Machek
2022-03-02  8:54       ` Pavel Machek
2022-03-03  0:30       ` Andrew Jeffery
2022-03-03  0:30         ` Andrew Jeffery

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=20210921043936.468001-2-andrew@aj.id.au \
    --to=andrew@aj.id.au \
    --cc=andy.shevchenko@gmail.com \
    --cc=clg@kaod.org \
    --cc=devicetree@vger.kernel.org \
    --cc=joel@jms.id.au \
    --cc=linus.walleij@linaro.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-aspeed@lists.ozlabs.org \
    --cc=linux-gpio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-leds@vger.kernel.org \
    --cc=pavel@ucw.cz \
    --cc=robh+dt@kernel.org \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.