diff options
Diffstat (limited to 'drivers/gpio/gpio-pca953x.c')
-rw-r--r-- | drivers/gpio/gpio-pca953x.c | 55 |
1 files changed, 42 insertions, 13 deletions
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index d63c1030e6ac..b852e4997629 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -215,6 +215,8 @@ struct pca953x_chip { DECLARE_BITMAP(irq_stat, MAX_LINE); DECLARE_BITMAP(irq_trig_raise, MAX_LINE); DECLARE_BITMAP(irq_trig_fall, MAX_LINE); + DECLARE_BITMAP(irq_trig_level_high, MAX_LINE); + DECLARE_BITMAP(irq_trig_level_low, MAX_LINE); #endif atomic_t wakeup_path; @@ -570,7 +572,8 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) return !!(reg_val & bit); } -static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) +static int pca953x_gpio_set_value(struct gpio_chip *gc, unsigned int off, + int val) { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 outreg = chip->recalc_addr(chip, chip->regs->output, off); @@ -578,7 +581,7 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) guard(mutex)(&chip->i2c_lock); - regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); + return regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); } static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off) @@ -616,8 +619,8 @@ static int pca953x_gpio_get_multiple(struct gpio_chip *gc, return 0; } -static void pca953x_gpio_set_multiple(struct gpio_chip *gc, - unsigned long *mask, unsigned long *bits) +static int pca953x_gpio_set_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) { struct pca953x_chip *chip = gpiochip_get_data(gc); DECLARE_BITMAP(reg_val, MAX_LINE); @@ -627,11 +630,11 @@ static void pca953x_gpio_set_multiple(struct gpio_chip *gc, ret = pca953x_read_regs(chip, chip->regs->output, reg_val); if (ret) - return; + return ret; bitmap_replace(reg_val, reg_val, bits, mask, gc->ngpio); - pca953x_write_regs(chip, chip->regs->output, reg_val); + return pca953x_write_regs(chip, chip->regs->output, reg_val); } static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip, @@ -693,10 +696,10 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) gc->direction_input = pca953x_gpio_direction_input; gc->direction_output = pca953x_gpio_direction_output; gc->get = pca953x_gpio_get_value; - gc->set = pca953x_gpio_set_value; + gc->set_rv = pca953x_gpio_set_value; gc->get_direction = pca953x_gpio_get_direction; gc->get_multiple = pca953x_gpio_get_multiple; - gc->set_multiple = pca953x_gpio_set_multiple; + gc->set_multiple_rv = pca953x_gpio_set_multiple; gc->set_config = pca953x_gpio_set_config; gc->can_sleep = true; @@ -773,6 +776,8 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) pca953x_read_regs(chip, chip->regs->direction, reg_direction); bitmap_or(irq_mask, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio); + bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_high, gc->ngpio); + bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_low, gc->ngpio); bitmap_complement(reg_direction, reg_direction, gc->ngpio); bitmap_and(irq_mask, irq_mask, reg_direction, gc->ngpio); @@ -790,13 +795,15 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type) struct device *dev = &chip->client->dev; irq_hw_number_t hwirq = irqd_to_hwirq(d); - if (!(type & IRQ_TYPE_EDGE_BOTH)) { + if (!(type & IRQ_TYPE_SENSE_MASK)) { dev_err(dev, "irq %d: unsupported type %d\n", d->irq, type); return -EINVAL; } assign_bit(hwirq, chip->irq_trig_fall, type & IRQ_TYPE_EDGE_FALLING); assign_bit(hwirq, chip->irq_trig_raise, type & IRQ_TYPE_EDGE_RISING); + assign_bit(hwirq, chip->irq_trig_level_low, type & IRQ_TYPE_LEVEL_LOW); + assign_bit(hwirq, chip->irq_trig_level_high, type & IRQ_TYPE_LEVEL_HIGH); return 0; } @@ -809,6 +816,8 @@ static void pca953x_irq_shutdown(struct irq_data *d) clear_bit(hwirq, chip->irq_trig_raise); clear_bit(hwirq, chip->irq_trig_fall); + clear_bit(hwirq, chip->irq_trig_level_low); + clear_bit(hwirq, chip->irq_trig_level_high); } static void pca953x_irq_print_chip(struct irq_data *data, struct seq_file *p) @@ -839,6 +848,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin DECLARE_BITMAP(cur_stat, MAX_LINE); DECLARE_BITMAP(new_stat, MAX_LINE); DECLARE_BITMAP(trigger, MAX_LINE); + DECLARE_BITMAP(edges, MAX_LINE); int ret; ret = pca953x_read_regs(chip, chip->regs->input, cur_stat); @@ -856,13 +866,26 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin bitmap_copy(chip->irq_stat, new_stat, gc->ngpio); - if (bitmap_empty(trigger, gc->ngpio)) - return false; + if (bitmap_empty(chip->irq_trig_level_high, gc->ngpio) && + bitmap_empty(chip->irq_trig_level_low, gc->ngpio)) { + if (bitmap_empty(trigger, gc->ngpio)) + return false; + } bitmap_and(cur_stat, chip->irq_trig_fall, old_stat, gc->ngpio); bitmap_and(old_stat, chip->irq_trig_raise, new_stat, gc->ngpio); - bitmap_or(new_stat, old_stat, cur_stat, gc->ngpio); - bitmap_and(pending, new_stat, trigger, gc->ngpio); + bitmap_or(edges, old_stat, cur_stat, gc->ngpio); + bitmap_and(pending, edges, trigger, gc->ngpio); + + bitmap_and(cur_stat, new_stat, chip->irq_trig_level_high, gc->ngpio); + bitmap_and(cur_stat, cur_stat, chip->irq_mask, gc->ngpio); + bitmap_or(pending, pending, cur_stat, gc->ngpio); + + bitmap_complement(cur_stat, new_stat, gc->ngpio); + bitmap_and(cur_stat, cur_stat, reg_direction, gc->ngpio); + bitmap_and(old_stat, cur_stat, chip->irq_trig_level_low, gc->ngpio); + bitmap_and(old_stat, old_stat, chip->irq_mask, gc->ngpio); + bitmap_or(pending, pending, old_stat, gc->ngpio); return !bitmap_empty(pending, gc->ngpio); } @@ -1203,6 +1226,8 @@ static int pca953x_restore_context(struct pca953x_chip *chip) guard(mutex)(&chip->i2c_lock); + if (chip->client->irq > 0) + enable_irq(chip->client->irq); regcache_cache_only(chip->regmap, false); regcache_mark_dirty(chip->regmap); ret = pca953x_regcache_sync(chip); @@ -1215,6 +1240,10 @@ static int pca953x_restore_context(struct pca953x_chip *chip) static void pca953x_save_context(struct pca953x_chip *chip) { guard(mutex)(&chip->i2c_lock); + + /* Disable IRQ to prevent early triggering while regmap "cache only" is on */ + if (chip->client->irq > 0) + disable_irq(chip->client->irq); regcache_cache_only(chip->regmap, true); } |