Commit db0f0325 authored by Fabien Dessenne's avatar Fabien Dessenne Committed by Linus Walleij
Browse files

pinctrl: stm32: check for IRQ MUX validity during alloc()



Considering the following irq_domain_ops call chain:
- .alloc() is called when a clients calls platform_get_irq() or
  gpiod_to_irq()
- .activate() is called next, when the clients calls
  request_threaded_irq()
Check for the IRQ MUX conflict during the first stage (alloc instead of
activate). This avoids to provide the client with an IRQ that can't be
used.

Signed-off-by: default avatarFabien Dessenne <fabien.dessenne@foss.st.com>
Link: https://lore.kernel.org/r/20210617144602.2557619-1-fabien.dessenne@foss.st.com


Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent fa99e701
Loading
Loading
Loading
Loading
+40 −39
Original line number Diff line number Diff line
@@ -414,33 +414,15 @@ static int stm32_gpio_domain_activate(struct irq_domain *d,
{
	struct stm32_gpio_bank *bank = d->host_data;
	struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent);
	unsigned long flags;
	int ret = 0;

	/*
	 * gpio irq mux is shared between several banks, a lock has to be done
	 * to avoid overriding.
	 */
	spin_lock_irqsave(&pctl->irqmux_lock, flags);

	if (pctl->hwlock) {
		ret = hwspin_lock_timeout_in_atomic(pctl->hwlock,
						    HWSPNLCK_TIMEOUT);
		if (ret) {
			dev_err(pctl->dev, "Can't get hwspinlock\n");
			goto unlock;
		}
			return ret;
		}

	if (pctl->irqmux_map & BIT(irq_data->hwirq)) {
		dev_err(pctl->dev, "irq line %ld already requested.\n",
			irq_data->hwirq);
		ret = -EBUSY;
		if (pctl->hwlock)
			hwspin_unlock_in_atomic(pctl->hwlock);
		goto unlock;
	} else {
		pctl->irqmux_map |= BIT(irq_data->hwirq);
	}

	regmap_field_write(pctl->irqmux[irq_data->hwirq], bank->bank_ioport_nr);
@@ -448,33 +430,38 @@ static int stm32_gpio_domain_activate(struct irq_domain *d,
	if (pctl->hwlock)
		hwspin_unlock_in_atomic(pctl->hwlock);

unlock:
	spin_unlock_irqrestore(&pctl->irqmux_lock, flags);
	return ret;
}

static void stm32_gpio_domain_deactivate(struct irq_domain *d,
					 struct irq_data *irq_data)
static int stm32_gpio_domain_alloc(struct irq_domain *d,
				   unsigned int virq,
				   unsigned int nr_irqs, void *data)
{
	struct stm32_gpio_bank *bank = d->host_data;
	struct irq_fwspec *fwspec = data;
	struct irq_fwspec parent_fwspec;
	struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent);
	irq_hw_number_t hwirq = fwspec->param[0];
	unsigned long flags;
	int ret = 0;

	/*
	 * Check first that the IRQ MUX of that line is free.
	 * gpio irq mux is shared between several banks, protect with a lock
	 */
	spin_lock_irqsave(&pctl->irqmux_lock, flags);
	pctl->irqmux_map &= ~BIT(irq_data->hwirq);
	spin_unlock_irqrestore(&pctl->irqmux_lock, flags);

	if (pctl->irqmux_map & BIT(hwirq)) {
		dev_err(pctl->dev, "irq line %ld already requested.\n", hwirq);
		ret = -EBUSY;
	} else {
		pctl->irqmux_map |= BIT(hwirq);
	}

static int stm32_gpio_domain_alloc(struct irq_domain *d,
				   unsigned int virq,
				   unsigned int nr_irqs, void *data)
{
	struct stm32_gpio_bank *bank = d->host_data;
	struct irq_fwspec *fwspec = data;
	struct irq_fwspec parent_fwspec;
	irq_hw_number_t hwirq;
	spin_unlock_irqrestore(&pctl->irqmux_lock, flags);
	if (ret)
		return ret;

	hwirq = fwspec->param[0];
	parent_fwspec.fwnode = d->parent->fwnode;
	parent_fwspec.param_count = 2;
	parent_fwspec.param[0] = fwspec->param[0];
@@ -486,12 +473,26 @@ static int stm32_gpio_domain_alloc(struct irq_domain *d,
	return irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &parent_fwspec);
}

static void stm32_gpio_domain_free(struct irq_domain *d, unsigned int virq,
				   unsigned int nr_irqs)
{
	struct stm32_gpio_bank *bank = d->host_data;
	struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent);
	struct irq_data *irq_data = irq_domain_get_irq_data(d, virq);
	unsigned long flags, hwirq = irq_data->hwirq;

	irq_domain_free_irqs_common(d, virq, nr_irqs);

	spin_lock_irqsave(&pctl->irqmux_lock, flags);
	pctl->irqmux_map &= ~BIT(hwirq);
	spin_unlock_irqrestore(&pctl->irqmux_lock, flags);
}

static const struct irq_domain_ops stm32_gpio_domain_ops = {
	.translate	= stm32_gpio_domain_translate,
	.alloc		= stm32_gpio_domain_alloc,
	.free           = irq_domain_free_irqs_common,
	.free		= stm32_gpio_domain_free,
	.activate	= stm32_gpio_domain_activate,
	.deactivate	= stm32_gpio_domain_deactivate,
};

/* Pinctrl functions */