Commit 984245b6 authored by Vladimir Oltean's avatar Vladimir Oltean Committed by Linus Walleij
Browse files

pinctrl: armada-37xx: make irq_lock a raw spinlock to avoid invalid wait context



The irqchip->irq_set_type method is called by __irq_set_trigger() under
the desc->lock raw spinlock.

The armada-37xx implementation, armada_37xx_irq_set_type(), takes a
plain spinlock, the kind that becomes sleepable on RT.

Therefore, this is an invalid locking scheme for which we get a kernel
splat stating just that ("[ BUG: Invalid wait context ]"), because the
context in which the plain spinlock may sleep is atomic due to the raw
spinlock. We need to go raw spinlocks all the way.

Replace the driver's irq_lock with a raw spinlock, to disable preemption
even on RT.

Cc: <stable@vger.kernel.org> # 5.15+
Fixes: 2f227605 ("pinctrl: armada-37xx: Add irqchip support")
Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Link: https://lore.kernel.org/r/20220716233745.1704677-2-vladimir.oltean@nxp.com


Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 41ef3c1a
Loading
Loading
Loading
Loading
+19 −19
Original line number Diff line number Diff line
@@ -102,7 +102,7 @@ struct armada_37xx_pinctrl {
	struct device			*dev;
	struct gpio_chip		gpio_chip;
	struct irq_chip			irq_chip;
	spinlock_t			irq_lock;
	raw_spinlock_t			irq_lock;
	struct pinctrl_desc		pctl;
	struct pinctrl_dev		*pctl_dev;
	struct armada_37xx_pin_group	*groups;
@@ -523,9 +523,9 @@ static void armada_37xx_irq_ack(struct irq_data *d)
	unsigned long flags;

	armada_37xx_irq_update_reg(&reg, d);
	spin_lock_irqsave(&info->irq_lock, flags);
	raw_spin_lock_irqsave(&info->irq_lock, flags);
	writel(d->mask, info->base + reg);
	spin_unlock_irqrestore(&info->irq_lock, flags);
	raw_spin_unlock_irqrestore(&info->irq_lock, flags);
}

static void armada_37xx_irq_mask(struct irq_data *d)
@@ -536,10 +536,10 @@ static void armada_37xx_irq_mask(struct irq_data *d)
	unsigned long flags;

	armada_37xx_irq_update_reg(&reg, d);
	spin_lock_irqsave(&info->irq_lock, flags);
	raw_spin_lock_irqsave(&info->irq_lock, flags);
	val = readl(info->base + reg);
	writel(val & ~d->mask, info->base + reg);
	spin_unlock_irqrestore(&info->irq_lock, flags);
	raw_spin_unlock_irqrestore(&info->irq_lock, flags);
}

static void armada_37xx_irq_unmask(struct irq_data *d)
@@ -550,10 +550,10 @@ static void armada_37xx_irq_unmask(struct irq_data *d)
	unsigned long flags;

	armada_37xx_irq_update_reg(&reg, d);
	spin_lock_irqsave(&info->irq_lock, flags);
	raw_spin_lock_irqsave(&info->irq_lock, flags);
	val = readl(info->base + reg);
	writel(val | d->mask, info->base + reg);
	spin_unlock_irqrestore(&info->irq_lock, flags);
	raw_spin_unlock_irqrestore(&info->irq_lock, flags);
}

static int armada_37xx_irq_set_wake(struct irq_data *d, unsigned int on)
@@ -564,14 +564,14 @@ static int armada_37xx_irq_set_wake(struct irq_data *d, unsigned int on)
	unsigned long flags;

	armada_37xx_irq_update_reg(&reg, d);
	spin_lock_irqsave(&info->irq_lock, flags);
	raw_spin_lock_irqsave(&info->irq_lock, flags);
	val = readl(info->base + reg);
	if (on)
		val |= (BIT(d->hwirq % GPIO_PER_REG));
	else
		val &= ~(BIT(d->hwirq % GPIO_PER_REG));
	writel(val, info->base + reg);
	spin_unlock_irqrestore(&info->irq_lock, flags);
	raw_spin_unlock_irqrestore(&info->irq_lock, flags);

	return 0;
}
@@ -583,7 +583,7 @@ static int armada_37xx_irq_set_type(struct irq_data *d, unsigned int type)
	u32 val, reg = IRQ_POL;
	unsigned long flags;

	spin_lock_irqsave(&info->irq_lock, flags);
	raw_spin_lock_irqsave(&info->irq_lock, flags);
	armada_37xx_irq_update_reg(&reg, d);
	val = readl(info->base + reg);
	switch (type) {
@@ -607,11 +607,11 @@ static int armada_37xx_irq_set_type(struct irq_data *d, unsigned int type)
		break;
	}
	default:
		spin_unlock_irqrestore(&info->irq_lock, flags);
		raw_spin_unlock_irqrestore(&info->irq_lock, flags);
		return -EINVAL;
	}
	writel(val, info->base + reg);
	spin_unlock_irqrestore(&info->irq_lock, flags);
	raw_spin_unlock_irqrestore(&info->irq_lock, flags);

	return 0;
}
@@ -626,7 +626,7 @@ static int armada_37xx_edge_both_irq_swap_pol(struct armada_37xx_pinctrl *info,

	regmap_read(info->regmap, INPUT_VAL + 4*reg_idx, &l);

	spin_lock_irqsave(&info->irq_lock, flags);
	raw_spin_lock_irqsave(&info->irq_lock, flags);
	p = readl(info->base + IRQ_POL + 4 * reg_idx);
	if ((p ^ l) & (1 << bit_num)) {
		/*
@@ -647,7 +647,7 @@ static int armada_37xx_edge_both_irq_swap_pol(struct armada_37xx_pinctrl *info,
		ret = -1;
	}

	spin_unlock_irqrestore(&info->irq_lock, flags);
	raw_spin_unlock_irqrestore(&info->irq_lock, flags);
	return ret;
}

@@ -664,11 +664,11 @@ static void armada_37xx_irq_handler(struct irq_desc *desc)
		u32 status;
		unsigned long flags;

		spin_lock_irqsave(&info->irq_lock, flags);
		raw_spin_lock_irqsave(&info->irq_lock, flags);
		status = readl_relaxed(info->base + IRQ_STATUS + 4 * i);
		/* Manage only the interrupt that was enabled */
		status &= readl_relaxed(info->base + IRQ_EN + 4 * i);
		spin_unlock_irqrestore(&info->irq_lock, flags);
		raw_spin_unlock_irqrestore(&info->irq_lock, flags);
		while (status) {
			u32 hwirq = ffs(status) - 1;
			u32 virq = irq_find_mapping(d, hwirq +
@@ -695,12 +695,12 @@ static void armada_37xx_irq_handler(struct irq_desc *desc)

update_status:
			/* Update status in case a new IRQ appears */
			spin_lock_irqsave(&info->irq_lock, flags);
			raw_spin_lock_irqsave(&info->irq_lock, flags);
			status = readl_relaxed(info->base +
					       IRQ_STATUS + 4 * i);
			/* Manage only the interrupt that was enabled */
			status &= readl_relaxed(info->base + IRQ_EN + 4 * i);
			spin_unlock_irqrestore(&info->irq_lock, flags);
			raw_spin_unlock_irqrestore(&info->irq_lock, flags);
		}
	}
	chained_irq_exit(chip, desc);
@@ -731,7 +731,7 @@ static int armada_37xx_irqchip_register(struct platform_device *pdev,
	struct device *dev = &pdev->dev;
	unsigned int i, nr_irq_parent;

	spin_lock_init(&info->irq_lock);
	raw_spin_lock_init(&info->irq_lock);

	nr_irq_parent = of_irq_count(np);
	if (!nr_irq_parent) {