Commit bc998a73 authored by Bartosz Golaszewski's avatar Bartosz Golaszewski Committed by Mark Brown

regmap: irq: handle HW using separate rising/falling edge interrupts

Some interrupt controllers use separate bits for controlling rising
and falling edge interrupts in the mask register i.e. they have one
interrupt for rising edge and one for falling.

We already handle the case where we have a single interrupt in the
mask register and a separate type configuration register.

Add a new switch to regmap_irq_chip which tells the framework to use
the mask_base address for configuring the edge of the interrupts that
define type_falling/rising_mask values.

For such interrupts we never update the type_base bits. For interrupts
that don't define type masks or their regmap irq chip doesn't set the
type_in_mask to true everything stays the same.
Signed-off-by: default avatarBartosz Golaszewski <bgolaszewski@baylibre.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 43fac323
...@@ -157,6 +157,8 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -157,6 +157,8 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
} }
} }
/* Don't update the type bits if we're using mask bits for irq type. */
if (!d->chip->type_in_mask) {
for (i = 0; i < d->chip->num_type_reg; i++) { for (i = 0; i < d->chip->num_type_reg; i++) {
if (!d->type_buf_def[i]) if (!d->type_buf_def[i])
continue; continue;
...@@ -172,6 +174,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -172,6 +174,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
dev_err(d->map->dev, "Failed to sync type in %x\n", dev_err(d->map->dev, "Failed to sync type in %x\n",
reg); reg);
} }
}
if (d->chip->runtime_pm) if (d->chip->runtime_pm)
pm_runtime_put(map->dev); pm_runtime_put(map->dev);
...@@ -194,8 +197,27 @@ static void regmap_irq_enable(struct irq_data *data) ...@@ -194,8 +197,27 @@ static void regmap_irq_enable(struct irq_data *data)
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
struct regmap *map = d->map; struct regmap *map = d->map;
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
unsigned int mask, type;
type = irq_data->type_falling_mask | irq_data->type_rising_mask;
/*
* The type_in_mask flag means that the underlying hardware uses
* separate mask bits for rising and falling edge interrupts, but
* we want to make them into a single virtual interrupt with
* configurable edge.
*
* If the interrupt we're enabling defines the falling or rising
* masks then instead of using the regular mask bits for this
* interrupt, use the value previously written to the type buffer
* at the corresponding offset in regmap_irq_set_type().
*/
if (d->chip->type_in_mask && type)
mask = d->type_buf[irq_data->reg_offset / map->reg_stride];
else
mask = irq_data->mask;
d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask; d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~mask;
} }
static void regmap_irq_disable(struct irq_data *data) static void regmap_irq_disable(struct irq_data *data)
...@@ -430,6 +452,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -430,6 +452,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
struct regmap_irq_chip_data *d; struct regmap_irq_chip_data *d;
int i; int i;
int ret = -ENOMEM; int ret = -ENOMEM;
int num_type_reg;
u32 reg; u32 reg;
u32 unmask_offset; u32 unmask_offset;
...@@ -479,13 +502,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -479,13 +502,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
goto err_alloc; goto err_alloc;
} }
if (chip->num_type_reg) { num_type_reg = chip->type_in_mask ? chip->num_regs : chip->num_type_reg;
d->type_buf_def = kcalloc(chip->num_type_reg, if (num_type_reg) {
d->type_buf_def = kcalloc(num_type_reg,
sizeof(unsigned int), GFP_KERNEL); sizeof(unsigned int), GFP_KERNEL);
if (!d->type_buf_def) if (!d->type_buf_def)
goto err_alloc; goto err_alloc;
d->type_buf = kcalloc(chip->num_type_reg, sizeof(unsigned int), d->type_buf = kcalloc(num_type_reg, sizeof(unsigned int),
GFP_KERNEL); GFP_KERNEL);
if (!d->type_buf) if (!d->type_buf)
goto err_alloc; goto err_alloc;
...@@ -600,7 +624,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -600,7 +624,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
} }
} }
if (chip->num_type_reg) { if (chip->num_type_reg && !chip->type_in_mask) {
for (i = 0; i < chip->num_irqs; i++) { for (i = 0; i < chip->num_irqs; i++) {
reg = chip->irqs[i].type_reg_offset / map->reg_stride; reg = chip->irqs[i].type_reg_offset / map->reg_stride;
d->type_buf_def[reg] |= chip->irqs[i].type_rising_mask | d->type_buf_def[reg] |= chip->irqs[i].type_rising_mask |
......
...@@ -1137,6 +1137,9 @@ struct regmap_irq { ...@@ -1137,6 +1137,9 @@ struct regmap_irq {
* @ack_invert: Inverted ack register: cleared bits for ack. * @ack_invert: Inverted ack register: cleared bits for ack.
* @wake_invert: Inverted wake register: cleared bits are wake enabled. * @wake_invert: Inverted wake register: cleared bits are wake enabled.
* @type_invert: Invert the type flags. * @type_invert: Invert the type flags.
* @type_in_mask: Use the mask registers for controlling irq type. For
* interrupts defining type_rising/falling_mask use mask_base
* for edge configuration and never update bits in type_base.
* @runtime_pm: Hold a runtime PM lock on the device when accessing it. * @runtime_pm: Hold a runtime PM lock on the device when accessing it.
* *
* @num_regs: Number of registers in each control bank. * @num_regs: Number of registers in each control bank.
...@@ -1175,6 +1178,7 @@ struct regmap_irq_chip { ...@@ -1175,6 +1178,7 @@ struct regmap_irq_chip {
bool wake_invert:1; bool wake_invert:1;
bool runtime_pm:1; bool runtime_pm:1;
bool type_invert:1; bool type_invert:1;
bool type_in_mask:1;
int num_regs; int num_regs;
......
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