Commit 1bee963d authored by Maxime Ripard's avatar Maxime Ripard Committed by Linus Walleij

pinctrl: sunxi: Add spinlocks

The current code use no locking at all, which is obviously not that
great and can lead to concurrency issues, especially with the newer SMP
SoCs from Allwinner.

Add some locking where it's needed.
Signed-off-by: default avatarMaxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@stericsson.com>
parent df7b34f4
...@@ -278,6 +278,7 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, ...@@ -278,6 +278,7 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev,
{ {
struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
struct sunxi_pinctrl_group *g = &pctl->groups[group]; struct sunxi_pinctrl_group *g = &pctl->groups[group];
unsigned long flags;
u32 val, mask; u32 val, mask;
u16 strength; u16 strength;
u8 dlevel; u8 dlevel;
...@@ -295,22 +296,35 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, ...@@ -295,22 +296,35 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev,
* 3: 40mA * 3: 40mA
*/ */
dlevel = strength / 10 - 1; dlevel = strength / 10 - 1;
spin_lock_irqsave(&pctl->lock, flags);
val = readl(pctl->membase + sunxi_dlevel_reg(g->pin)); val = readl(pctl->membase + sunxi_dlevel_reg(g->pin));
mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(g->pin); mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(g->pin);
writel((val & ~mask) | dlevel << sunxi_dlevel_offset(g->pin), writel((val & ~mask) | dlevel << sunxi_dlevel_offset(g->pin),
pctl->membase + sunxi_dlevel_reg(g->pin)); pctl->membase + sunxi_dlevel_reg(g->pin));
spin_unlock_irqrestore(&pctl->lock, flags);
break; break;
case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_UP:
spin_lock_irqsave(&pctl->lock, flags);
val = readl(pctl->membase + sunxi_pull_reg(g->pin)); val = readl(pctl->membase + sunxi_pull_reg(g->pin));
mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin); mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin);
writel((val & ~mask) | 1 << sunxi_pull_offset(g->pin), writel((val & ~mask) | 1 << sunxi_pull_offset(g->pin),
pctl->membase + sunxi_pull_reg(g->pin)); pctl->membase + sunxi_pull_reg(g->pin));
spin_unlock_irqrestore(&pctl->lock, flags);
break; break;
case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_DOWN:
spin_lock_irqsave(&pctl->lock, flags);
val = readl(pctl->membase + sunxi_pull_reg(g->pin)); val = readl(pctl->membase + sunxi_pull_reg(g->pin));
mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin); mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin);
writel((val & ~mask) | 2 << sunxi_pull_offset(g->pin), writel((val & ~mask) | 2 << sunxi_pull_offset(g->pin),
pctl->membase + sunxi_pull_reg(g->pin)); pctl->membase + sunxi_pull_reg(g->pin));
spin_unlock_irqrestore(&pctl->lock, flags);
break; break;
default: default:
break; break;
...@@ -360,11 +374,17 @@ static void sunxi_pmx_set(struct pinctrl_dev *pctldev, ...@@ -360,11 +374,17 @@ static void sunxi_pmx_set(struct pinctrl_dev *pctldev,
u8 config) u8 config)
{ {
struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
unsigned long flags;
u32 val, mask;
spin_lock_irqsave(&pctl->lock, flags);
u32 val = readl(pctl->membase + sunxi_mux_reg(pin)); val = readl(pctl->membase + sunxi_mux_reg(pin));
u32 mask = MUX_PINS_MASK << sunxi_mux_offset(pin); mask = MUX_PINS_MASK << sunxi_mux_offset(pin);
writel((val & ~mask) | config << sunxi_mux_offset(pin), writel((val & ~mask) | config << sunxi_mux_offset(pin),
pctl->membase + sunxi_mux_reg(pin)); pctl->membase + sunxi_mux_reg(pin));
spin_unlock_irqrestore(&pctl->lock, flags);
} }
static int sunxi_pmx_enable(struct pinctrl_dev *pctldev, static int sunxi_pmx_enable(struct pinctrl_dev *pctldev,
...@@ -464,7 +484,12 @@ static void sunxi_pinctrl_gpio_set(struct gpio_chip *chip, ...@@ -464,7 +484,12 @@ static void sunxi_pinctrl_gpio_set(struct gpio_chip *chip,
struct sunxi_pinctrl *pctl = dev_get_drvdata(chip->dev); struct sunxi_pinctrl *pctl = dev_get_drvdata(chip->dev);
u32 reg = sunxi_data_reg(offset); u32 reg = sunxi_data_reg(offset);
u8 index = sunxi_data_offset(offset); u8 index = sunxi_data_offset(offset);
u32 regval = readl(pctl->membase + reg); unsigned long flags;
u32 regval;
spin_lock_irqsave(&pctl->lock, flags);
regval = readl(pctl->membase + reg);
if (value) if (value)
regval |= BIT(index); regval |= BIT(index);
...@@ -472,6 +497,8 @@ static void sunxi_pinctrl_gpio_set(struct gpio_chip *chip, ...@@ -472,6 +497,8 @@ static void sunxi_pinctrl_gpio_set(struct gpio_chip *chip,
regval &= ~(BIT(index)); regval &= ~(BIT(index));
writel(regval, pctl->membase + reg); writel(regval, pctl->membase + reg);
spin_unlock_irqrestore(&pctl->lock, flags);
} }
static int sunxi_pinctrl_gpio_of_xlate(struct gpio_chip *gc, static int sunxi_pinctrl_gpio_of_xlate(struct gpio_chip *gc,
...@@ -532,6 +559,7 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d, ...@@ -532,6 +559,7 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d,
struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
u32 reg = sunxi_irq_cfg_reg(d->hwirq); u32 reg = sunxi_irq_cfg_reg(d->hwirq);
u8 index = sunxi_irq_cfg_offset(d->hwirq); u8 index = sunxi_irq_cfg_offset(d->hwirq);
unsigned long flags;
u32 regval; u32 regval;
u8 mode; u8 mode;
...@@ -555,10 +583,14 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d, ...@@ -555,10 +583,14 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d,
return -EINVAL; return -EINVAL;
} }
spin_lock_irqsave(&pctl->lock, flags);
regval = readl(pctl->membase + reg); regval = readl(pctl->membase + reg);
regval &= ~IRQ_CFG_IRQ_MASK; regval &= ~IRQ_CFG_IRQ_MASK;
writel(regval | (mode << index), pctl->membase + reg); writel(regval | (mode << index), pctl->membase + reg);
spin_unlock_irqrestore(&pctl->lock, flags);
return 0; return 0;
} }
...@@ -569,14 +601,19 @@ static void sunxi_pinctrl_irq_mask_ack(struct irq_data *d) ...@@ -569,14 +601,19 @@ static void sunxi_pinctrl_irq_mask_ack(struct irq_data *d)
u8 ctrl_idx = sunxi_irq_ctrl_offset(d->hwirq); u8 ctrl_idx = sunxi_irq_ctrl_offset(d->hwirq);
u32 status_reg = sunxi_irq_status_reg(d->hwirq); u32 status_reg = sunxi_irq_status_reg(d->hwirq);
u8 status_idx = sunxi_irq_status_offset(d->hwirq); u8 status_idx = sunxi_irq_status_offset(d->hwirq);
unsigned long flags;
u32 val; u32 val;
spin_lock_irqsave(&pctl->lock, flags);
/* Mask the IRQ */ /* Mask the IRQ */
val = readl(pctl->membase + ctrl_reg); val = readl(pctl->membase + ctrl_reg);
writel(val & ~(1 << ctrl_idx), pctl->membase + ctrl_reg); writel(val & ~(1 << ctrl_idx), pctl->membase + ctrl_reg);
/* Clear the IRQ */ /* Clear the IRQ */
writel(1 << status_idx, pctl->membase + status_reg); writel(1 << status_idx, pctl->membase + status_reg);
spin_unlock_irqrestore(&pctl->lock, flags);
} }
static void sunxi_pinctrl_irq_mask(struct irq_data *d) static void sunxi_pinctrl_irq_mask(struct irq_data *d)
...@@ -584,11 +621,16 @@ static void sunxi_pinctrl_irq_mask(struct irq_data *d) ...@@ -584,11 +621,16 @@ static void sunxi_pinctrl_irq_mask(struct irq_data *d)
struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
u32 reg = sunxi_irq_ctrl_reg(d->hwirq); u32 reg = sunxi_irq_ctrl_reg(d->hwirq);
u8 idx = sunxi_irq_ctrl_offset(d->hwirq); u8 idx = sunxi_irq_ctrl_offset(d->hwirq);
unsigned long flags;
u32 val; u32 val;
spin_lock_irqsave(&pctl->lock, flags);
/* Mask the IRQ */ /* Mask the IRQ */
val = readl(pctl->membase + reg); val = readl(pctl->membase + reg);
writel(val & ~(1 << idx), pctl->membase + reg); writel(val & ~(1 << idx), pctl->membase + reg);
spin_unlock_irqrestore(&pctl->lock, flags);
} }
static void sunxi_pinctrl_irq_unmask(struct irq_data *d) static void sunxi_pinctrl_irq_unmask(struct irq_data *d)
...@@ -597,6 +639,7 @@ static void sunxi_pinctrl_irq_unmask(struct irq_data *d) ...@@ -597,6 +639,7 @@ static void sunxi_pinctrl_irq_unmask(struct irq_data *d)
struct sunxi_desc_function *func; struct sunxi_desc_function *func;
u32 reg = sunxi_irq_ctrl_reg(d->hwirq); u32 reg = sunxi_irq_ctrl_reg(d->hwirq);
u8 idx = sunxi_irq_ctrl_offset(d->hwirq); u8 idx = sunxi_irq_ctrl_offset(d->hwirq);
unsigned long flags;
u32 val; u32 val;
func = sunxi_pinctrl_desc_find_function_by_pin(pctl, func = sunxi_pinctrl_desc_find_function_by_pin(pctl,
...@@ -606,9 +649,13 @@ static void sunxi_pinctrl_irq_unmask(struct irq_data *d) ...@@ -606,9 +649,13 @@ static void sunxi_pinctrl_irq_unmask(struct irq_data *d)
/* Change muxing to INT mode */ /* Change muxing to INT mode */
sunxi_pmx_set(pctl->pctl_dev, pctl->irq_array[d->hwirq], func->muxval); sunxi_pmx_set(pctl->pctl_dev, pctl->irq_array[d->hwirq], func->muxval);
spin_lock_irqsave(&pctl->lock, flags);
/* Unmask the IRQ */ /* Unmask the IRQ */
val = readl(pctl->membase + reg); val = readl(pctl->membase + reg);
writel(val | (1 << idx), pctl->membase + reg); writel(val | (1 << idx), pctl->membase + reg);
spin_unlock_irqrestore(&pctl->lock, flags);
} }
static struct irq_chip sunxi_pinctrl_irq_chip = { static struct irq_chip sunxi_pinctrl_irq_chip = {
...@@ -761,6 +808,8 @@ static int sunxi_pinctrl_probe(struct platform_device *pdev) ...@@ -761,6 +808,8 @@ static int sunxi_pinctrl_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, pctl); platform_set_drvdata(pdev, pctl);
spin_lock_init(&pctl->lock);
pctl->membase = of_iomap(node, 0); pctl->membase = of_iomap(node, 0);
if (!pctl->membase) if (!pctl->membase)
return -ENOMEM; return -ENOMEM;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#define __PINCTRL_SUNXI_H #define __PINCTRL_SUNXI_H
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/spinlock.h>
#define PA_BASE 0 #define PA_BASE 0
#define PB_BASE 32 #define PB_BASE 32
...@@ -407,6 +408,7 @@ struct sunxi_pinctrl { ...@@ -407,6 +408,7 @@ struct sunxi_pinctrl {
unsigned ngroups; unsigned ngroups;
int irq; int irq;
int irq_array[SUNXI_IRQ_NUMBER]; int irq_array[SUNXI_IRQ_NUMBER];
spinlock_t lock;
struct pinctrl_dev *pctl_dev; struct pinctrl_dev *pctl_dev;
}; };
......
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