Commit 8eb2dfee authored by Alexandre Torgue's avatar Alexandre Torgue Committed by Linus Walleij

pinctrl: stm32: add lock mechanism for irqmux selection

GPIOs are split between several banks (A, B, ...) and each bank can have
up to 16 lines. Those GPIOs could be used as interrupt lines thanks to
exti lines. As there are only 16 exti lines, a mux is used to select which
gpio line is connected to which exti line. Mapping is done as follow:

-A0, B0, C0.. -->exti_line_0 (X0 selected by mux_0)
-A1, B1, C1.. -->exti_line_1 (X1 selected by mux_1)
...

This patch adds a protection to avoid overriding on mux_n for exti_line_n.
Signed-off-by: default avatarAlexandre Torgue <alexandre.torgue@st.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent a45623dd
...@@ -111,6 +111,8 @@ struct stm32_pinctrl { ...@@ -111,6 +111,8 @@ struct stm32_pinctrl {
struct stm32_desc_pin *pins; struct stm32_desc_pin *pins;
u32 npins; u32 npins;
u32 pkg; u32 pkg;
u16 irqmux_map;
spinlock_t irqmux_lock;
}; };
static inline int stm32_gpio_pin(int gpio) static inline int stm32_gpio_pin(int gpio)
...@@ -359,9 +361,53 @@ static int stm32_gpio_domain_activate(struct irq_domain *d, ...@@ -359,9 +361,53 @@ static int stm32_gpio_domain_activate(struct irq_domain *d,
{ {
struct stm32_gpio_bank *bank = d->host_data; struct stm32_gpio_bank *bank = d->host_data;
struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); 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(pctl->hwlock, HWSPINLOCK_TIMEOUT);
if (ret) {
dev_err(pctl->dev, "Can't get hwspinlock\n");
goto unlock;
}
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(pctl->hwlock);
goto unlock;
} else {
pctl->irqmux_map |= BIT(irq_data->hwirq);
}
regmap_field_write(pctl->irqmux[irq_data->hwirq], bank->bank_ioport_nr); regmap_field_write(pctl->irqmux[irq_data->hwirq], bank->bank_ioport_nr);
return 0;
if (pctl->hwlock)
hwspin_unlock(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)
{
struct stm32_gpio_bank *bank = d->host_data;
struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent);
unsigned long flags;
spin_lock_irqsave(&pctl->irqmux_lock, flags);
pctl->irqmux_map &= ~BIT(irq_data->hwirq);
spin_unlock_irqrestore(&pctl->irqmux_lock, flags);
} }
static int stm32_gpio_domain_alloc(struct irq_domain *d, static int stm32_gpio_domain_alloc(struct irq_domain *d,
...@@ -390,6 +436,7 @@ static const struct irq_domain_ops stm32_gpio_domain_ops = { ...@@ -390,6 +436,7 @@ static const struct irq_domain_ops stm32_gpio_domain_ops = {
.alloc = stm32_gpio_domain_alloc, .alloc = stm32_gpio_domain_alloc,
.free = irq_domain_free_irqs_common, .free = irq_domain_free_irqs_common,
.activate = stm32_gpio_domain_activate, .activate = stm32_gpio_domain_activate,
.deactivate = stm32_gpio_domain_deactivate,
}; };
/* Pinctrl functions */ /* Pinctrl functions */
...@@ -1350,6 +1397,8 @@ int stm32_pctl_probe(struct platform_device *pdev) ...@@ -1350,6 +1397,8 @@ int stm32_pctl_probe(struct platform_device *pdev)
pctl->hwlock = hwspin_lock_request_specific(hwlock_id); pctl->hwlock = hwspin_lock_request_specific(hwlock_id);
} }
spin_lock_init(&pctl->irqmux_lock);
pctl->dev = dev; pctl->dev = dev;
pctl->match_data = match->data; pctl->match_data = match->data;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment