lkml.org 
[lkml]   [2020]   [Jun]   [17]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH V4 1/7] gpio: zynq: protect direction in/out with a spinlock
    Date
    From: Glenn Langedock <Glenn.Langedock@barco.com>

    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: Glenn Langedock <Glenn.Langedock@barco.com>
    Signed-off-by: Michal Simek <michal.simek@xilinx.com>
    Signed-off-by: Srinivas Neeli <srinivas.neeli@xilinx.com>
    ---
    drivers/gpio/gpio-zynq.c | 11 +++++++++++
    1 file changed, 11 insertions(+)

    diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
    index 05ba16fffdad..fb93b35ab19e 100644
    --- a/drivers/gpio/gpio-zynq.c
    +++ b/drivers/gpio/gpio-zynq.c
    @@ -10,6 +10,7 @@
    #include <linux/gpio/driver.h>
    #include <linux/init.h>
    #include <linux/interrupt.h>
    +#include <linux/spinlock.h>
    #include <linux/io.h>
    #include <linux/module.h>
    #include <linux/platform_device.h>
    @@ -116,6 +117,7 @@ struct gpio_regs {
    * @irq: interrupt for the GPIO device
    * @p_data: pointer to platform data
    * @context: context registers
    + * @dirlock: lock used for direction in/out synchronization
    */
    struct zynq_gpio {
    struct gpio_chip chip;
    @@ -124,6 +126,7 @@ struct zynq_gpio {
    int irq;
    const struct zynq_platform_data *p_data;
    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)
    {
    u32 reg;
    unsigned int bank_num, bank_pin_num;
    + unsigned long flags;
    struct zynq_gpio *gpio = gpiochip_get_data(chip);

    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)
    return -EINVAL;

    /* 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 &= ~BIT(bank_pin_num);
    writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
    + spin_unlock_irqrestore(&gpio->dirlock, flags);

    return 0;
    }
    @@ -334,11 +340,13 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
    {
    u32 reg;
    unsigned int bank_num, bank_pin_num;
    + unsigned long flags;
    struct zynq_gpio *gpio = gpiochip_get_data(chip);

    zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);

    /* 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 |= BIT(bank_pin_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,
    reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num));
    reg |= BIT(bank_pin_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 */
    zynq_gpio_set_value(chip, pin, state);
    @@ -885,6 +894,8 @@ static int zynq_gpio_probe(struct platform_device *pdev)
    return ret;
    }

    + spin_lock_init(&gpio->dirlock);
    +
    pm_runtime_set_active(&pdev->dev);
    pm_runtime_enable(&pdev->dev);
    ret = pm_runtime_get_sync(&pdev->dev);
    --
    2.7.4
    \
     
     \ /
      Last update: 2020-06-17 13:38    [W:2.236 / U:0.016 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site