Commit 6e20fb18 authored by Roland Stigge's avatar Roland Stigge Committed by Linus Torvalds

drivers/gpio/pca953x.c: add a mutex to fix race condition

Add a mutex to register communication and handling.  Without the mutex,
GPIOs didn't switch as expected when toggled in a fast sequence of
status changes of multiple outputs.
Signed-off-by: default avatarRoland Stigge <stigge@antcom.de>
Acked-by: default avatarEric Miao <eric.y.miao@gmail.com>
Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: Marc Zyngier <maz@misterjones.org>
Cc: Ben Gardner <bgardner@wabtec.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 01e05e9a
...@@ -60,6 +60,7 @@ struct pca953x_chip { ...@@ -60,6 +60,7 @@ struct pca953x_chip {
unsigned gpio_start; unsigned gpio_start;
uint16_t reg_output; uint16_t reg_output;
uint16_t reg_direction; uint16_t reg_direction;
struct mutex i2c_lock;
#ifdef CONFIG_GPIO_PCA953X_IRQ #ifdef CONFIG_GPIO_PCA953X_IRQ
struct mutex irq_lock; struct mutex irq_lock;
...@@ -119,13 +120,17 @@ static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off) ...@@ -119,13 +120,17 @@ static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
chip = container_of(gc, struct pca953x_chip, gpio_chip); chip = container_of(gc, struct pca953x_chip, gpio_chip);
mutex_lock(&chip->i2c_lock);
reg_val = chip->reg_direction | (1u << off); reg_val = chip->reg_direction | (1u << off);
ret = pca953x_write_reg(chip, PCA953X_DIRECTION, reg_val); ret = pca953x_write_reg(chip, PCA953X_DIRECTION, reg_val);
if (ret) if (ret)
return ret; goto exit;
chip->reg_direction = reg_val; chip->reg_direction = reg_val;
return 0; ret = 0;
exit:
mutex_unlock(&chip->i2c_lock);
return ret;
} }
static int pca953x_gpio_direction_output(struct gpio_chip *gc, static int pca953x_gpio_direction_output(struct gpio_chip *gc,
...@@ -137,6 +142,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc, ...@@ -137,6 +142,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
chip = container_of(gc, struct pca953x_chip, gpio_chip); chip = container_of(gc, struct pca953x_chip, gpio_chip);
mutex_lock(&chip->i2c_lock);
/* set output level */ /* set output level */
if (val) if (val)
reg_val = chip->reg_output | (1u << off); reg_val = chip->reg_output | (1u << off);
...@@ -145,7 +151,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc, ...@@ -145,7 +151,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
ret = pca953x_write_reg(chip, PCA953X_OUTPUT, reg_val); ret = pca953x_write_reg(chip, PCA953X_OUTPUT, reg_val);
if (ret) if (ret)
return ret; goto exit;
chip->reg_output = reg_val; chip->reg_output = reg_val;
...@@ -153,10 +159,13 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc, ...@@ -153,10 +159,13 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
reg_val = chip->reg_direction & ~(1u << off); reg_val = chip->reg_direction & ~(1u << off);
ret = pca953x_write_reg(chip, PCA953X_DIRECTION, reg_val); ret = pca953x_write_reg(chip, PCA953X_DIRECTION, reg_val);
if (ret) if (ret)
return ret; goto exit;
chip->reg_direction = reg_val; chip->reg_direction = reg_val;
return 0; ret = 0;
exit:
mutex_unlock(&chip->i2c_lock);
return ret;
} }
static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
...@@ -167,7 +176,9 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) ...@@ -167,7 +176,9 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
chip = container_of(gc, struct pca953x_chip, gpio_chip); chip = container_of(gc, struct pca953x_chip, gpio_chip);
mutex_lock(&chip->i2c_lock);
ret = pca953x_read_reg(chip, PCA953X_INPUT, &reg_val); ret = pca953x_read_reg(chip, PCA953X_INPUT, &reg_val);
mutex_unlock(&chip->i2c_lock);
if (ret < 0) { if (ret < 0) {
/* NOTE: diagnostic already emitted; that's all we should /* NOTE: diagnostic already emitted; that's all we should
* do unless gpio_*_value_cansleep() calls become different * do unless gpio_*_value_cansleep() calls become different
...@@ -187,6 +198,7 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) ...@@ -187,6 +198,7 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
chip = container_of(gc, struct pca953x_chip, gpio_chip); chip = container_of(gc, struct pca953x_chip, gpio_chip);
mutex_lock(&chip->i2c_lock);
if (val) if (val)
reg_val = chip->reg_output | (1u << off); reg_val = chip->reg_output | (1u << off);
else else
...@@ -194,9 +206,11 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) ...@@ -194,9 +206,11 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
ret = pca953x_write_reg(chip, PCA953X_OUTPUT, reg_val); ret = pca953x_write_reg(chip, PCA953X_OUTPUT, reg_val);
if (ret) if (ret)
return; goto exit;
chip->reg_output = reg_val; chip->reg_output = reg_val;
exit:
mutex_unlock(&chip->i2c_lock);
} }
static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
...@@ -517,6 +531,8 @@ static int __devinit pca953x_probe(struct i2c_client *client, ...@@ -517,6 +531,8 @@ static int __devinit pca953x_probe(struct i2c_client *client,
chip->names = pdata->names; chip->names = pdata->names;
mutex_init(&chip->i2c_lock);
/* initialize cached registers from their original values. /* initialize cached registers from their original values.
* we can't share this chip with another i2c master. * we can't share this chip with another i2c master.
*/ */
......
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