Skip to content

Commit b7badd7

Browse files
Hans VerkuilLinus Walleij
authored andcommitted
pinctrl-bcm2835.c: fix race condition when setting gpio dir
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@xs4all.nl> Fixes: 1a4541b ("pinctrl-bcm2835: don't call pinctrl_gpio_direction()") Link: https://lore.kernel.org/r/4302b66b-ca20-0f19-d2aa-ee8661118863@xs4all.nl Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
1 parent b19a1d8 commit b7badd7

File tree

1 file changed

+15
-4
lines changed

1 file changed

+15
-4
lines changed

drivers/pinctrl/bcm/pinctrl-bcm2835.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ struct bcm2835_pinctrl {
9090
struct pinctrl_gpio_range gpio_range;
9191

9292
raw_spinlock_t irq_lock[BCM2835_NUM_BANKS];
93+
/* Protect FSEL registers */
94+
spinlock_t fsel_lock;
9395
};
9496

9597
/* pins are just named GPIO0..GPIO53 */
@@ -284,14 +286,19 @@ static inline void bcm2835_pinctrl_fsel_set(
284286
struct bcm2835_pinctrl *pc, unsigned pin,
285287
enum bcm2835_fsel fsel)
286288
{
287-
u32 val = bcm2835_gpio_rd(pc, FSEL_REG(pin));
288-
enum bcm2835_fsel cur = (val >> FSEL_SHIFT(pin)) & BCM2835_FSEL_MASK;
289+
u32 val;
290+
enum bcm2835_fsel cur;
291+
unsigned long flags;
292+
293+
spin_lock_irqsave(&pc->fsel_lock, flags);
294+
val = bcm2835_gpio_rd(pc, FSEL_REG(pin));
295+
cur = (val >> FSEL_SHIFT(pin)) & BCM2835_FSEL_MASK;
289296

290297
dev_dbg(pc->dev, "read %08x (%u => %s)\n", val, pin,
291-
bcm2835_functions[cur]);
298+
bcm2835_functions[cur]);
292299

293300
if (cur == fsel)
294-
return;
301+
goto unlock;
295302

296303
if (cur != BCM2835_FSEL_GPIO_IN && fsel != BCM2835_FSEL_GPIO_IN) {
297304
/* always transition through GPIO_IN */
@@ -309,6 +316,9 @@ static inline void bcm2835_pinctrl_fsel_set(
309316
dev_dbg(pc->dev, "write %08x (%u <= %s)\n", val, pin,
310317
bcm2835_functions[fsel]);
311318
bcm2835_gpio_wr(pc, FSEL_REG(pin), val);
319+
320+
unlock:
321+
spin_unlock_irqrestore(&pc->fsel_lock, flags);
312322
}
313323

314324
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)
12481258
pc->gpio_chip = *pdata->gpio_chip;
12491259
pc->gpio_chip.parent = dev;
12501260

1261+
spin_lock_init(&pc->fsel_lock);
12511262
for (i = 0; i < BCM2835_NUM_BANKS; i++) {
12521263
unsigned long events;
12531264
unsigned offset;

0 commit comments

Comments
 (0)