Skip to content
Snippets Groups Projects
Commit a6a77deb authored by Tobias Kahlki's avatar Tobias Kahlki Committed by Tobias Kahlki
Browse files

[DRIVER][PCA953X] Add level interrupt support

This adds level interrupt support to the pca953x driver. In the IRQ routine,
the input status register is compared to the irq_trig_level_high and
irq_trig_level_low parameters. If the value from the input status register
aligns with one of the parameters, a level interrupt is issued.
After processing all nested interrupts, the pending function is called again,
to determine if the level interrupt condition is still active. If this is the
case, the nested interrupts are processed again.
parent 31c0eae0
No related branches found
No related tags found
1 merge request!200Add drivers and devicetrees for Modular Vision with D18
......@@ -200,6 +200,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);
DECLARE_BITMAP(trigger_stat, MAX_LINE);
struct irq_chip irq_chip;
#endif
......@@ -680,6 +682,8 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct pca953x_chip *chip = gpiochip_get_data(gc);
DECLARE_BITMAP(irq_edge_mask, MAX_LINE);
DECLARE_BITMAP(irq_level_mask, MAX_LINE);
DECLARE_BITMAP(irq_mask, MAX_LINE);
DECLARE_BITMAP(reg_direction, MAX_LINE);
int level;
......@@ -697,7 +701,10 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
/* Switch direction to input if needed */
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_edge_mask, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio);
bitmap_or(irq_level_mask, chip->irq_trig_level_high, chip->irq_trig_level_low, gc->ngpio);
bitmap_or(irq_mask, irq_edge_mask, irq_level_mask, gc->ngpio);
bitmap_complement(reg_direction, reg_direction, gc->ngpio);
bitmap_and(irq_mask, irq_mask, reg_direction, gc->ngpio);
......@@ -714,7 +721,7 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type)
struct pca953x_chip *chip = gpiochip_get_data(gc);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
if (!(type & IRQ_TYPE_EDGE_BOTH)) {
if (!(type & (IRQ_TYPE_EDGE_BOTH | IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))) {
dev_err(&chip->client->dev, "irq %d: unsupported type %d\n",
d->irq, type);
return -EINVAL;
......@@ -722,6 +729,8 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type)
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_high, type & IRQ_TYPE_LEVEL_HIGH);
assign_bit(hwirq, chip->irq_trig_level_low, type & IRQ_TYPE_LEVEL_LOW);
return 0;
}
......@@ -732,6 +741,8 @@ static void pca953x_irq_shutdown(struct irq_data *d)
struct pca953x_chip *chip = gpiochip_get_data(gc);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
clear_bit(hwirq, chip->irq_trig_level_low);
clear_bit(hwirq, chip->irq_trig_level_high);
clear_bit(hwirq, chip->irq_trig_raise);
clear_bit(hwirq, chip->irq_trig_fall);
}
......@@ -743,6 +754,8 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin
DECLARE_BITMAP(old_stat, MAX_LINE);
DECLARE_BITMAP(cur_stat, MAX_LINE);
DECLARE_BITMAP(new_stat, MAX_LINE);
DECLARE_BITMAP(pending_edge, MAX_LINE);
DECLARE_BITMAP(pending_level, MAX_LINE);
DECLARE_BITMAP(trigger, MAX_LINE);
int ret;
......@@ -762,8 +775,13 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin
/* Apply filter for rising/falling edge selection */
bitmap_replace(new_stat, chip->irq_trig_fall, chip->irq_trig_raise, cur_stat, gc->ngpio);
bitmap_and(pending_edge, new_stat, trigger, gc->ngpio);
/* Apply filter for high/low level selection */
bitmap_replace(pending_level, chip->irq_trig_level_low, chip->irq_trig_level_high, cur_stat, gc->ngpio);
bitmap_and(pending, new_stat, trigger, gc->ngpio);
/* Combine edge and level pending states */
bitmap_or(pending, pending_edge, pending_level, gc->ngpio);
return !bitmap_empty(pending, gc->ngpio);
}
......@@ -802,27 +820,32 @@ static irqreturn_t pca953x_irq_handler(int irq, void *devid)
int level;
bool ret;
bitmap_zero(pending, MAX_LINE);
do {
bitmap_zero(pending, MAX_LINE);
mutex_lock(&chip->i2c_lock);
ret = pca953x_irq_pending(chip, pending);
mutex_unlock(&chip->i2c_lock);
mutex_lock(&chip->i2c_lock);
ret = pca953x_irq_pending(chip, pending);
mutex_unlock(&chip->i2c_lock);
if (ret) {
ret = 0;
if (ret) {
ret = 0;
for_each_set_bit(level, pending, gc->ngpio) {
int nested_irq = irq_find_mapping(gc->irq.domain, level);
for_each_set_bit(level, pending, gc->ngpio) {
int nested_irq = irq_find_mapping(gc->irq.domain, level);
if (unlikely(nested_irq <= 0)) {
dev_warn_ratelimited(gc->parent, "unmapped interrupt %d\n", level);
continue;
}
if (unlikely(nested_irq <= 0)) {
dev_warn_ratelimited(gc->parent, "unmapped interrupt %d\n", level);
continue;
}
handle_nested_irq(nested_irq);
ret = 1;
handle_nested_irq(nested_irq);
ret = 1;
}
}
}
if (bitmap_empty(chip->irq_trig_level_high, gc->ngpio) && bitmap_empty(chip->irq_trig_level_low, gc->ngpio))
break;
} while(ret);
return IRQ_RETVAL(ret);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment