diff options
-rw-r--r-- | drivers/pinctrl/stm32/pinctrl-stm32.c | 51 |
1 files changed, 50 insertions, 1 deletions
diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c index 335aea59c8d1..92e35fb930be 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32.c +++ b/drivers/pinctrl/stm32/pinctrl-stm32.c @@ -111,6 +111,8 @@ struct stm32_pinctrl { struct stm32_desc_pin *pins; u32 npins; u32 pkg; + u16 irqmux_map; + spinlock_t irqmux_lock; }; static inline int stm32_gpio_pin(int gpio) @@ -359,9 +361,53 @@ 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(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); - 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, @@ -390,6 +436,7 @@ static const struct irq_domain_ops stm32_gpio_domain_ops = { .alloc = stm32_gpio_domain_alloc, .free = irq_domain_free_irqs_common, .activate = stm32_gpio_domain_activate, + .deactivate = stm32_gpio_domain_deactivate, }; /* Pinctrl functions */ @@ -1350,6 +1397,8 @@ int stm32_pctl_probe(struct platform_device *pdev) pctl->hwlock = hwspin_lock_request_specific(hwlock_id); } + spin_lock_init(&pctl->irqmux_lock); + pctl->dev = dev; pctl->match_data = match->data; |