Skip to content

Commit 9cc5f23

Browse files
Sven Van Asbroeckthierryreding
authored andcommitted
pwm: pca9685: Fix PWM/GPIO inter-operation
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 PWMm 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 in commit e926b12 ("pwm: Clear chip_data in pwm_put()"). 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 PWMs 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 <[email protected]> Fixes: e926b12 ("pwm: Clear chip_data in pwm_put()") Cc: Mika Westerberg <[email protected]> Cc: Uwe Kleine-König <[email protected]> Cc: YueHaibing <[email protected]> Link: https://lkml.org/lkml/2019/5/31/963 Signed-off-by: Sven Van Asbroeck <[email protected]> Reviewed-by: Mika Westerberg <[email protected]> [cg: Tested on an i.MX6Q board with two NXP PCA9685 chips] Tested-by: Clemens Gruber <[email protected]> Reviewed-by: Sven Van Asbroeck <[email protected]> # cg's rebase Link: https://lore.kernel.org/lkml/20200330160238.GD2817345@ulmo/ Signed-off-by: Thierry Reding <[email protected]>
1 parent 374c110 commit 9cc5f23

File tree

1 file changed

+48
-37
lines changed

1 file changed

+48
-37
lines changed

drivers/pwm/pwm-pca9685.c

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/slab.h>
2121
#include <linux/delay.h>
2222
#include <linux/pm_runtime.h>
23+
#include <linux/bitmap.h>
2324

2425
/*
2526
* Because the PCA9685 has only one prescaler per chip, changing the period of
@@ -73,6 +74,7 @@ struct pca9685 {
7374
#if IS_ENABLED(CONFIG_GPIOLIB)
7475
struct mutex lock;
7576
struct gpio_chip gpio;
77+
DECLARE_BITMAP(pwms_inuse, PCA9685_MAXCHAN + 1);
7678
#endif
7779
};
7880

@@ -82,51 +84,51 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip)
8284
}
8385

8486
#if IS_ENABLED(CONFIG_GPIOLIB)
85-
static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset)
87+
static bool pca9685_pwm_test_and_set_inuse(struct pca9685 *pca, int pwm_idx)
8688
{
87-
struct pca9685 *pca = gpiochip_get_data(gpio);
88-
struct pwm_device *pwm;
89+
bool is_inuse;
8990

9091
mutex_lock(&pca->lock);
91-
92-
pwm = &pca->chip.pwms[offset];
93-
94-
if (pwm->flags & (PWMF_REQUESTED | PWMF_EXPORTED)) {
95-
mutex_unlock(&pca->lock);
96-
return -EBUSY;
92+
if (pwm_idx >= PCA9685_MAXCHAN) {
93+
/*
94+
* "all LEDs" channel:
95+
* pretend already in use if any of the PWMs are requested
96+
*/
97+
if (!bitmap_empty(pca->pwms_inuse, PCA9685_MAXCHAN)) {
98+
is_inuse = true;
99+
goto out;
100+
}
101+
} else {
102+
/*
103+
* regular channel:
104+
* pretend already in use if the "all LEDs" channel is requested
105+
*/
106+
if (test_bit(PCA9685_MAXCHAN, pca->pwms_inuse)) {
107+
is_inuse = true;
108+
goto out;
109+
}
97110
}
98-
99-
pwm_set_chip_data(pwm, (void *)1);
100-
111+
is_inuse = test_and_set_bit(pwm_idx, pca->pwms_inuse);
112+
out:
101113
mutex_unlock(&pca->lock);
102-
pm_runtime_get_sync(pca->chip.dev);
103-
return 0;
114+
return is_inuse;
104115
}
105116

106-
static bool pca9685_pwm_is_gpio(struct pca9685 *pca, struct pwm_device *pwm)
117+
static void pca9685_pwm_clear_inuse(struct pca9685 *pca, int pwm_idx)
107118
{
108-
bool is_gpio = false;
109-
110119
mutex_lock(&pca->lock);
120+
clear_bit(pwm_idx, pca->pwms_inuse);
121+
mutex_unlock(&pca->lock);
122+
}
111123

112-
if (pwm->hwpwm >= PCA9685_MAXCHAN) {
113-
unsigned int i;
114-
115-
/*
116-
* Check if any of the GPIOs are requested and in that case
117-
* prevent using the "all LEDs" channel.
118-
*/
119-
for (i = 0; i < pca->gpio.ngpio; i++)
120-
if (gpiochip_is_requested(&pca->gpio, i)) {
121-
is_gpio = true;
122-
break;
123-
}
124-
} else if (pwm_get_chip_data(pwm)) {
125-
is_gpio = true;
126-
}
124+
static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset)
125+
{
126+
struct pca9685 *pca = gpiochip_get_data(gpio);
127127

128-
mutex_unlock(&pca->lock);
129-
return is_gpio;
128+
if (pca9685_pwm_test_and_set_inuse(pca, offset))
129+
return -EBUSY;
130+
pm_runtime_get_sync(pca->chip.dev);
131+
return 0;
130132
}
131133

132134
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)
161163

162164
pca9685_pwm_gpio_set(gpio, offset, 0);
163165
pm_runtime_put(pca->chip.dev);
166+
pca9685_pwm_clear_inuse(pca, offset);
164167
}
165168

166169
static int pca9685_pwm_gpio_get_direction(struct gpio_chip *chip,
@@ -212,12 +215,17 @@ static int pca9685_pwm_gpio_probe(struct pca9685 *pca)
212215
return devm_gpiochip_add_data(dev, &pca->gpio, pca);
213216
}
214217
#else
215-
static inline bool pca9685_pwm_is_gpio(struct pca9685 *pca,
216-
struct pwm_device *pwm)
218+
static inline bool pca9685_pwm_test_and_set_inuse(struct pca9685 *pca,
219+
int pwm_idx)
217220
{
218221
return false;
219222
}
220223

224+
static inline void
225+
pca9685_pwm_clear_inuse(struct pca9685 *pca, int pwm_idx)
226+
{
227+
}
228+
221229
static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca)
222230
{
223231
return 0;
@@ -399,7 +407,7 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
399407
{
400408
struct pca9685 *pca = to_pca(chip);
401409

402-
if (pca9685_pwm_is_gpio(pca, pwm))
410+
if (pca9685_pwm_test_and_set_inuse(pca, pwm->hwpwm))
403411
return -EBUSY;
404412
pm_runtime_get_sync(chip->dev);
405413

@@ -408,8 +416,11 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
408416

409417
static void pca9685_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
410418
{
419+
struct pca9685 *pca = to_pca(chip);
420+
411421
pca9685_pwm_disable(chip, pwm);
412422
pm_runtime_put(chip->dev);
423+
pca9685_pwm_clear_inuse(pca, pwm->hwpwm);
413424
}
414425

415426
static const struct pwm_ops pca9685_pwm_ops = {

0 commit comments

Comments
 (0)