gpio: adp5588: Fix sleep-in-atomic-context bug

This fixes:
[BUG] gpio: gpio-adp5588: A possible sleep-in-atomic-context bug
                          in adp5588_gpio_write()
[BUG] gpio: gpio-adp5588: A possible sleep-in-atomic-context bug
                          in adp5588_gpio_direction_input()

Reported-by: Jia-Ju Bai <baijiaju1990@gmail.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
Michael Hennerich 2018-08-13 15:57:44 +02:00 committed by Linus Walleij
parent 5b394b2ddf
commit 6537886cdc

View file

@ -41,6 +41,8 @@ struct adp5588_gpio {
uint8_t int_en[3]; uint8_t int_en[3];
uint8_t irq_mask[3]; uint8_t irq_mask[3];
uint8_t irq_stat[3]; uint8_t irq_stat[3];
uint8_t int_input_en[3];
uint8_t int_lvl_cached[3];
}; };
static int adp5588_gpio_read(struct i2c_client *client, u8 reg) static int adp5588_gpio_read(struct i2c_client *client, u8 reg)
@ -173,12 +175,28 @@ static void adp5588_irq_bus_sync_unlock(struct irq_data *d)
struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
int i; int i;
for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
if (dev->int_input_en[i]) {
mutex_lock(&dev->lock);
dev->dir[i] &= ~dev->int_input_en[i];
dev->int_input_en[i] = 0;
adp5588_gpio_write(dev->client, GPIO_DIR1 + i,
dev->dir[i]);
mutex_unlock(&dev->lock);
}
if (dev->int_lvl_cached[i] != dev->int_lvl[i]) {
dev->int_lvl_cached[i] = dev->int_lvl[i];
adp5588_gpio_write(dev->client, GPIO_INT_LVL1 + i,
dev->int_lvl[i]);
}
if (dev->int_en[i] ^ dev->irq_mask[i]) { if (dev->int_en[i] ^ dev->irq_mask[i]) {
dev->int_en[i] = dev->irq_mask[i]; dev->int_en[i] = dev->irq_mask[i];
adp5588_gpio_write(dev->client, GPIO_INT_EN1 + i, adp5588_gpio_write(dev->client, GPIO_INT_EN1 + i,
dev->int_en[i]); dev->int_en[i]);
} }
}
mutex_unlock(&dev->irq_lock); mutex_unlock(&dev->irq_lock);
} }
@ -221,9 +239,7 @@ static int adp5588_irq_set_type(struct irq_data *d, unsigned int type)
else else
return -EINVAL; return -EINVAL;
adp5588_gpio_direction_input(&dev->gpio_chip, gpio); dev->int_input_en[bank] |= bit;
adp5588_gpio_write(dev->client, GPIO_INT_LVL1 + bank,
dev->int_lvl[bank]);
return 0; return 0;
} }