Commit fdcfec11 authored by Glenn Langedock's avatar Glenn Langedock Committed by Bartosz Golaszewski

gpio: zynq: protect direction in/out with a spinlock

Fix race condition when changing the direction (in/out) of the GPIO pin.
The read-modify-write sequence (as coded in the driver) isn't atomic and
requires synchronization (spinlock).
Signed-off-by: default avatarGlenn Langedock <Glenn.Langedock@barco.com>
Signed-off-by: default avatarMichal Simek <michal.simek@xilinx.com>
Signed-off-by: default avatarSrinivas Neeli <srinivas.neeli@xilinx.com>
Signed-off-by: default avatarBartosz Golaszewski <bgolaszewski@baylibre.com>
parent 8346b92e
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
...@@ -116,6 +117,7 @@ struct gpio_regs { ...@@ -116,6 +117,7 @@ struct gpio_regs {
* @irq: interrupt for the GPIO device * @irq: interrupt for the GPIO device
* @p_data: pointer to platform data * @p_data: pointer to platform data
* @context: context registers * @context: context registers
* @dirlock: lock used for direction in/out synchronization
*/ */
struct zynq_gpio { struct zynq_gpio {
struct gpio_chip chip; struct gpio_chip chip;
...@@ -124,6 +126,7 @@ struct zynq_gpio { ...@@ -124,6 +126,7 @@ struct zynq_gpio {
int irq; int irq;
const struct zynq_platform_data *p_data; const struct zynq_platform_data *p_data;
struct gpio_regs context; struct gpio_regs context;
spinlock_t dirlock; /* lock */
}; };
/** /**
...@@ -297,6 +300,7 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) ...@@ -297,6 +300,7 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
{ {
u32 reg; u32 reg;
unsigned int bank_num, bank_pin_num; unsigned int bank_num, bank_pin_num;
unsigned long flags;
struct zynq_gpio *gpio = gpiochip_get_data(chip); struct zynq_gpio *gpio = gpiochip_get_data(chip);
zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
...@@ -310,9 +314,11 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) ...@@ -310,9 +314,11 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
return -EINVAL; return -EINVAL;
/* clear the bit in direction mode reg to set the pin as input */ /* clear the bit in direction mode reg to set the pin as input */
spin_lock_irqsave(&gpio->dirlock, flags);
reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
reg &= ~BIT(bank_pin_num); reg &= ~BIT(bank_pin_num);
writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
spin_unlock_irqrestore(&gpio->dirlock, flags);
return 0; return 0;
} }
...@@ -334,11 +340,13 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, ...@@ -334,11 +340,13 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
{ {
u32 reg; u32 reg;
unsigned int bank_num, bank_pin_num; unsigned int bank_num, bank_pin_num;
unsigned long flags;
struct zynq_gpio *gpio = gpiochip_get_data(chip); struct zynq_gpio *gpio = gpiochip_get_data(chip);
zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
/* set the GPIO pin as output */ /* set the GPIO pin as output */
spin_lock_irqsave(&gpio->dirlock, flags);
reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
reg |= BIT(bank_pin_num); reg |= BIT(bank_pin_num);
writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
...@@ -347,6 +355,7 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, ...@@ -347,6 +355,7 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num)); reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num));
reg |= BIT(bank_pin_num); reg |= BIT(bank_pin_num);
writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num)); writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num));
spin_unlock_irqrestore(&gpio->dirlock, flags);
/* set the state of the pin */ /* set the state of the pin */
zynq_gpio_set_value(chip, pin, state); zynq_gpio_set_value(chip, pin, state);
...@@ -885,6 +894,8 @@ static int zynq_gpio_probe(struct platform_device *pdev) ...@@ -885,6 +894,8 @@ static int zynq_gpio_probe(struct platform_device *pdev)
return ret; return ret;
} }
spin_lock_init(&gpio->dirlock);
pm_runtime_set_active(&pdev->dev); pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
ret = pm_runtime_get_sync(&pdev->dev); ret = pm_runtime_get_sync(&pdev->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