[PATCH] pinctrl-bcm2835.c: fix race condition when setting gpio dir
Stefan Wahren
stefan.wahren at i2se.com
Thu Apr 20 03:34:42 PDT 2023
Hi Hans,
Am 20.04.23 um 08:47 schrieb Hans Verkuil:
> In the past setting the pin direction called pinctrl_gpio_direction()
> which uses a mutex to serialize this. That was changed to set the
> direction directly in the pin controller driver, but that lost the
> serialization mechanism. Since the direction of multiple pins are in
> the same register you can have a race condition, something that was
> in fact observed with the cec-gpio driver.
>
> Add a new spinlock to serialize writing to the FSEL registers.
>
> Signed-off-by: Hans Verkuil <hverkuil-cisco at xs4all.nl>
> Fixes: 1a4541b68e25 ("pinctrl-bcm2835: don't call pinctrl_gpio_direction()")
> ---
> drivers/pinctrl/bcm/pinctrl-bcm2835.c | 19 +++++++++++++++----
> 1 file changed, 15 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
> index 8e2551a08c37..345b9dea5ff6 100644
> --- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c
> +++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
> @@ -90,6 +90,8 @@ struct bcm2835_pinctrl {
> struct pinctrl_gpio_range gpio_range;
>
> raw_spinlock_t irq_lock[BCM2835_NUM_BANKS];
> + /* Protect FSEL registers */
> + spinlock_t fsel_lock;
> };
>
> /* pins are just named GPIO0..GPIO53 */
> @@ -284,14 +286,21 @@ static inline void bcm2835_pinctrl_fsel_set(
> struct bcm2835_pinctrl *pc, unsigned pin,
> enum bcm2835_fsel fsel)
> {
> - u32 val = bcm2835_gpio_rd(pc, FSEL_REG(pin));
> - enum bcm2835_fsel cur = (val >> FSEL_SHIFT(pin)) & BCM2835_FSEL_MASK;
> + u32 val;
> + enum bcm2835_fsel cur;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&pc->fsel_lock, flags);
> + val = bcm2835_gpio_rd(pc, FSEL_REG(pin));
> + cur = (val >> FSEL_SHIFT(pin)) & BCM2835_FSEL_MASK;
>
> dev_dbg(pc->dev, "read %08x (%u => %s)\n", val, pin,
> - bcm2835_functions[cur]);
> + bcm2835_functions[cur]);
>
> - if (cur == fsel)
> + if (cur == fsel) {
> + spin_unlock_irqrestore(&pc->fsel_lock, flags);
> return;
How about adding a jump label to the latter spin_unlock_irqrestore and
using a goto here?
> + }
>
> if (cur != BCM2835_FSEL_GPIO_IN && fsel != BCM2835_FSEL_GPIO_IN) {
> /* always transition through GPIO_IN */
> @@ -309,6 +318,7 @@ static inline void bcm2835_pinctrl_fsel_set(
> dev_dbg(pc->dev, "write %08x (%u <= %s)\n", val, pin,
> bcm2835_functions[fsel]);
> bcm2835_gpio_wr(pc, FSEL_REG(pin), val);
> + spin_unlock_irqrestore(&pc->fsel_lock, flags);
> }
>
> static int bcm2835_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
> @@ -1248,6 +1258,7 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
> pc->gpio_chip = *pdata->gpio_chip;
> pc->gpio_chip.parent = dev;
>
> + spin_lock_init(&pc->fsel_lock);
> for (i = 0; i < BCM2835_NUM_BANKS; i++) {
> unsigned long events;
> unsigned offset;
More information about the linux-rpi-kernel
mailing list