linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 REBASED] pwm: pca9685: fix pwm/gpio inter-operation
@ 2020-04-01 17:01 Clemens Gruber
  2020-04-03 15:24 ` Sven Van Asbroeck
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Clemens Gruber @ 2020-04-01 17:01 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Andy Shevchenko, Matthias Schiffer,
	Uwe Kleine-König, linux-kernel, Sven Van Asbroeck,
	YueHaibing, Mika Westerberg, Clemens Gruber

From: Sven Van Asbroeck <TheSven73@gmail.com>

This driver allows pwms to be requested as gpios via gpiolib.
Obviously, it should not be allowed to request a gpio when its
corresponding pwm is already requested (and vice versa).
So it requires some exclusion code.

Given that the pwm and gpio cores are not synchronized with
respect to each other, this exclusion code will also require
proper synchronization.

Such a mechanism was in place, but was inadvertently removed
by Uwe's clean-up patch.

Upon revisiting the synchronization mechanism, we found that
theoretically, it could allow two threads to successfully
request conflicting pwms / gpios.

Replace with a bitmap which tracks pwm in-use, plus a mutex.
As long as pwm and gpio's respective request/free functions
modify the in-use bitmap while holding the mutex, proper
synchronization will be guaranteed.

Reported-by: YueHaibing <yuehaibing@huawei.com>
Fixes: e926b12c611c ("pwm: Clear chip_data in pwm_put()")
Cc: Mika Westerberg <mika.westerberg@linux.intel.com>
Cc: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Cc: YueHaibing <yuehaibing@huawei.com>
Link: https://lkml.org/lkml/2019/5/31/963
Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Tested-by: Clemens Gruber <clemens.gruber@pqgruber.com>
[cg: Tested on an i.MX6Q board with two NXP PCA9685 chips]

---
 drivers/pwm/pwm-pca9685.c | 85 ++++++++++++++++++++++-----------------
 1 file changed, 48 insertions(+), 37 deletions(-)

diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index 20bdc59a0cbb..76cd22bd6614 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -20,6 +20,7 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/pm_runtime.h>
+#include <linux/bitmap.h>
 
 /*
  * Because the PCA9685 has only one prescaler per chip, changing the period of
@@ -73,6 +74,7 @@ struct pca9685 {
 #if IS_ENABLED(CONFIG_GPIOLIB)
 	struct mutex lock;
 	struct gpio_chip gpio;
+	DECLARE_BITMAP(pwms_inuse, PCA9685_MAXCHAN + 1);
 #endif
 };
 
@@ -82,51 +84,51 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip)
 }
 
 #if IS_ENABLED(CONFIG_GPIOLIB)
-static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset)
+static bool pca9685_pwm_test_and_set_inuse(struct pca9685 *pca, int pwm_idx)
 {
-	struct pca9685 *pca = gpiochip_get_data(gpio);
-	struct pwm_device *pwm;
+	bool is_inuse;
 
 	mutex_lock(&pca->lock);
-
-	pwm = &pca->chip.pwms[offset];
-
-	if (pwm->flags & (PWMF_REQUESTED | PWMF_EXPORTED)) {
-		mutex_unlock(&pca->lock);
-		return -EBUSY;
+	if (pwm_idx >= PCA9685_MAXCHAN) {
+		/*
+		 * "all LEDs" channel:
+		 * pretend already in use if any of the PWMs are requested
+		 */
+		if (!bitmap_empty(pca->pwms_inuse, PCA9685_MAXCHAN)) {
+			is_inuse = true;
+			goto out;
+		}
+	} else {
+		/*
+		 * regular channel:
+		 * pretend already in use if the "all LEDs" channel is requested
+		 */
+		if (test_bit(PCA9685_MAXCHAN, pca->pwms_inuse)) {
+			is_inuse = true;
+			goto out;
+		}
 	}
-
-	pwm_set_chip_data(pwm, (void *)1);
-
+	is_inuse = test_and_set_bit(pwm_idx, pca->pwms_inuse);
+out:
 	mutex_unlock(&pca->lock);
-	pm_runtime_get_sync(pca->chip.dev);
-	return 0;
+	return is_inuse;
 }
 
-static bool pca9685_pwm_is_gpio(struct pca9685 *pca, struct pwm_device *pwm)
+static void pca9685_pwm_clear_inuse(struct pca9685 *pca, int pwm_idx)
 {
-	bool is_gpio = false;
-
 	mutex_lock(&pca->lock);
+	clear_bit(pwm_idx, pca->pwms_inuse);
+	mutex_unlock(&pca->lock);
+}
 
-	if (pwm->hwpwm >= PCA9685_MAXCHAN) {
-		unsigned int i;
-
-		/*
-		 * Check if any of the GPIOs are requested and in that case
-		 * prevent using the "all LEDs" channel.
-		 */
-		for (i = 0; i < pca->gpio.ngpio; i++)
-			if (gpiochip_is_requested(&pca->gpio, i)) {
-				is_gpio = true;
-				break;
-			}
-	} else if (pwm_get_chip_data(pwm)) {
-		is_gpio = true;
-	}
+static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset)
+{
+	struct pca9685 *pca = gpiochip_get_data(gpio);
 
-	mutex_unlock(&pca->lock);
-	return is_gpio;
+	if (pca9685_pwm_test_and_set_inuse(pca, offset))
+		return -EBUSY;
+	pm_runtime_get_sync(pca->chip.dev);
+	return 0;
 }
 
 static int pca9685_pwm_gpio_get(struct gpio_chip *gpio, unsigned int offset)
@@ -161,6 +163,7 @@ static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset)
 
 	pca9685_pwm_gpio_set(gpio, offset, 0);
 	pm_runtime_put(pca->chip.dev);
+	pca9685_pwm_clear_inuse(pca, offset);
 }
 
 static int pca9685_pwm_gpio_get_direction(struct gpio_chip *chip,
@@ -212,12 +215,17 @@ static int pca9685_pwm_gpio_probe(struct pca9685 *pca)
 	return devm_gpiochip_add_data(dev, &pca->gpio, pca);
 }
 #else
-static inline bool pca9685_pwm_is_gpio(struct pca9685 *pca,
-				       struct pwm_device *pwm)
+static inline bool pca9685_pwm_test_and_set_inuse(struct pca9685 *pca,
+						  int pwm_idx)
 {
 	return false;
 }
 
+static inline void
+pca9685_pwm_clear_inuse(struct pca9685 *pca, int pwm_idx)
+{
+}
+
 static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca)
 {
 	return 0;
@@ -399,7 +407,7 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
 {
 	struct pca9685 *pca = to_pca(chip);
 
-	if (pca9685_pwm_is_gpio(pca, pwm))
+	if (pca9685_pwm_test_and_set_inuse(pca, pwm->hwpwm))
 		return -EBUSY;
 	pm_runtime_get_sync(chip->dev);
 
@@ -408,8 +416,11 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
 
 static void pca9685_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 {
+	struct pca9685 *pca = to_pca(chip);
+
 	pca9685_pwm_disable(chip, pwm);
 	pm_runtime_put(chip->dev);
+	pca9685_pwm_clear_inuse(pca, pwm->hwpwm);
 }
 
 static const struct pwm_ops pca9685_pwm_ops = {
-- 
2.26.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v2 REBASED] pwm: pca9685: fix pwm/gpio inter-operation
  2020-04-01 17:01 [PATCH v2 REBASED] pwm: pca9685: fix pwm/gpio inter-operation Clemens Gruber
@ 2020-04-03 15:24 ` Sven Van Asbroeck
  2020-04-03 15:30 ` Sven Van Asbroeck
  2020-04-03 19:44 ` Thierry Reding
  2 siblings, 0 replies; 4+ messages in thread
From: Sven Van Asbroeck @ 2020-04-03 15:24 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Andy Shevchenko, Matthias Schiffer,
	Uwe Kleine-König, Linux Kernel Mailing List, YueHaibing,
	Mika Westerberg

On Wed, Apr 1, 2020 at 1:01 PM Clemens Gruber
<clemens.gruber@pqgruber.com> wrote:
>
> From: Sven Van Asbroeck <TheSven73@gmail.com>
>
> Tested-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> [cg: Tested on an i.MX6Q board with two NXP PCA9685 chips]

Thank you Clemens for testing and rebasing this patch, much appreciated !

The synchronization method introduced here still looks correct to me. So:

Reviewed-by: Sven Van Asbroeck <TheSven73@gmail.com> # cg's rebase

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2 REBASED] pwm: pca9685: fix pwm/gpio inter-operation
  2020-04-01 17:01 [PATCH v2 REBASED] pwm: pca9685: fix pwm/gpio inter-operation Clemens Gruber
  2020-04-03 15:24 ` Sven Van Asbroeck
@ 2020-04-03 15:30 ` Sven Van Asbroeck
  2020-04-03 19:44 ` Thierry Reding
  2 siblings, 0 replies; 4+ messages in thread
From: Sven Van Asbroeck @ 2020-04-03 15:30 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Andy Shevchenko, Matthias Schiffer,
	Uwe Kleine-König, Linux Kernel Mailing List, YueHaibing,
	Mika Westerberg

PS perhaps you could also include a link to the mailing list
discussion which prompted the revival of this patch?

Link: https://lore.kernel.org/lkml/20200330160238.GD2817345@ulmo/

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2 REBASED] pwm: pca9685: fix pwm/gpio inter-operation
  2020-04-01 17:01 [PATCH v2 REBASED] pwm: pca9685: fix pwm/gpio inter-operation Clemens Gruber
  2020-04-03 15:24 ` Sven Van Asbroeck
  2020-04-03 15:30 ` Sven Van Asbroeck
@ 2020-04-03 19:44 ` Thierry Reding
  2 siblings, 0 replies; 4+ messages in thread
From: Thierry Reding @ 2020-04-03 19:44 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Andy Shevchenko, Matthias Schiffer,
	Uwe Kleine-König, linux-kernel, Sven Van Asbroeck,
	YueHaibing, Mika Westerberg

[-- Attachment #1: Type: text/plain, Size: 1732 bytes --]

On Wed, Apr 01, 2020 at 07:01:06PM +0200, Clemens Gruber wrote:
> From: Sven Van Asbroeck <TheSven73@gmail.com>
> 
> This driver allows pwms to be requested as gpios via gpiolib.
> Obviously, it should not be allowed to request a gpio when its
> corresponding pwm is already requested (and vice versa).
> So it requires some exclusion code.
> 
> Given that the pwm and gpio cores are not synchronized with
> respect to each other, this exclusion code will also require
> proper synchronization.
> 
> Such a mechanism was in place, but was inadvertently removed
> by Uwe's clean-up patch.
> 
> Upon revisiting the synchronization mechanism, we found that
> theoretically, it could allow two threads to successfully
> request conflicting pwms / gpios.
> 
> Replace with a bitmap which tracks pwm in-use, plus a mutex.
> As long as pwm and gpio's respective request/free functions
> modify the in-use bitmap while holding the mutex, proper
> synchronization will be guaranteed.
> 
> Reported-by: YueHaibing <yuehaibing@huawei.com>
> Fixes: e926b12c611c ("pwm: Clear chip_data in pwm_put()")
> Cc: Mika Westerberg <mika.westerberg@linux.intel.com>
> Cc: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> Cc: YueHaibing <yuehaibing@huawei.com>
> Link: https://lkml.org/lkml/2019/5/31/963
> Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
> Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> Tested-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> [cg: Tested on an i.MX6Q board with two NXP PCA9685 chips]
> 
> ---
>  drivers/pwm/pwm-pca9685.c | 85 ++++++++++++++++++++++-----------------
>  1 file changed, 48 insertions(+), 37 deletions(-)

Applied, thanks.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2020-04-03 19:44 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-01 17:01 [PATCH v2 REBASED] pwm: pca9685: fix pwm/gpio inter-operation Clemens Gruber
2020-04-03 15:24 ` Sven Van Asbroeck
2020-04-03 15:30 ` Sven Van Asbroeck
2020-04-03 19:44 ` Thierry Reding

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).