Hi Mark,
Here's a bunch of cleanups for regmap-irq focused on simplifying the API
and generalizing it a bit. It's broken up into three refactors, focusing
on one area at a time.
* Patches 01 and 02 are straightforward bugfixes, independent of the
rest of the series. Neither of the bugs are triggered by in-tree
drivers but they might be worth picking up early anyhow.
* Patches 03-13 clean up everything related to configuring IRQ types.
* Patches 14-45 deal with mask/unmask registers. First, make unmask
registers behave more intuitively and usefully, and get rid of the
mask_invert flag in favor of describing inverted mask registers as
unmask registers. Second, make the mask_writeonly flag more useful
and enable it for two chips where it makes sense.
* Patches 46-49 refactor sub_irq_reg() as a get_irq_reg() callback,
and use that to eliminate the not_fixed_stride flag.
The approach I used when refactoring is pretty simple: (1) introduce new
functionality in regmap-irq, (2) convert the drivers, and (3) remove any
old code. Nothing should break in the middle.
The patches can be re-ordered to some extent if that's preferable, but
it's best to add get_irq_reg() last to avoid having to think about how
it interacts with features that'll be removed anyway.
I can't test most of the devices affected by this series so a lot of the
code is only build tested. I've tested on real hardware with my AXP192
patchset[1], although it only provides limited code coverage.
qcom-pm8008 in particular deserves careful testing - it used all of the
features touched by the refactors and required the most changes. Other
drivers only required trivial changes but there are three of them worth
mentioning: wcd943x, wcd9335, and wcd938x. They have suspicious looking
IRQ type definitions and I'm pretty sure aren't working properly, but
I can't fix them myself. The refactor shouldn't affect their behavior
so how / when / if they get fixed shouldn't be much of an issue.
Oh, and I added the 'mask_writeonly' flag and volatile ranges to the
stpmic1 driver based on its datasheet[2] as a small optimization. It's
probably fine but testing would be a good idea.
[1]: https://lore.kernel.org/linux-iio/20220618214009.2178567-1-aidanmacdonald.0x0@gmailcom/
[2]: https://www.st.com/resource/en/datasheet/stpmic1.pdf
Aidan MacDonald (49):
regmap-irq: Fix a bug in regmap_irq_enable() for type_in_mask chips
regmap-irq: Fix offset/index mismatch in read_sub_irq_data()
regmap-irq: Remove an unnecessary restriction on type_in_mask
regmap-irq: Introduce config registers for irq types
mfd: qcom-pm8008: Convert irq chip to config regs
mfd: wcd934x: Convert irq chip to config regs
sound: soc: codecs: wcd9335: Convert irq chip to config regs
sound: soc: codecs: wcd938x: Remove spurious type_base from irq chip
mfd: max77650: Remove useless type_invert flag
regmap-irq: Remove virtual registers support
regmap-irq: Remove old type register support, refactor
regmap-irq: Remove unused type_reg_stride field
regmap-irq: Remove unused type_invert flag
regmap-irq: Do not use regmap_irq_update_bits() for wake regs
regmap-irq: Change the behavior of mask_writeonly
regmap-irq: Rename regmap_irq_update_bits()
regmap-irq: Add broken_mask_unmask flag
mfd: qcom-pm8008: Add broken_mask_unmask irq chip flag
mfd: stpmic1: Add broken_mask_unmask irq chip flag
regmap-irq: Fix inverted handling of unmask registers
mfd: tps65090: replace irqchip mask_invert with unmask_base
mfd: sun4i-gpadc: replace irqchip mask_invert with unmask_base
mfd: sprd-sc27xx-spi: replace irqchip mask_invert with unmask_base
mfd: rt5033: replace irqchip mask_invert with unmask_base
mfd: rohm-bd71828: replace irqchip mask_invert with unmask_base
mfd: rn5t618: replace irqchip mask_invert with unmask_base
mfd: gateworks-gsc: replace irqchip mask_invert with unmask_base
mfd: axp20x: replace irqchip mask_invert with unmask_base
mfd: atc260x: replace irqchip mask_invert with unmask_base
mfd: 88pm800: replace irqchip mask_invert with unmask_base
mfd: max14577: replace irqchip mask_invert with unmask_base
mfd: max77693: replace irqchip mask_invert with unmask_base
mfd: rohm-bd718x7: drop useless mask_invert flag on irqchip
mfd: max77843: drop useless mask_invert flag on irqchip
extcon: max77843: replace irqchip mask_invert with unmask_base
extcon: sm5502: drop useless mask_invert flag on irqchip
extcon: rt8973a: drop useless mask_invert flag on irqchip
irqchip: sl28cpld: replace irqchip mask_invert with unmask_base
gpio: sl28cpld: replace irqchip mask_invert with unmask_base
mfd: stpmic1: Fix broken mask/unmask in irq chip
mfd: stpmic1: Enable mask_writeonly flag for irq chip
mfd: qcom-pm8008: Fix broken mask/unmask in irq chip
mfd: qcom-pm8008: Enable mask_writeonly flag for irq chip
regmap-irq: Remove broken_mask_unmask flag
regmap-irq: Remove mask_invert flag
regmap-irq: Refactor checks for status bulk read support
regmap-irq: Add get_irq_reg() callback
mfd: qcom-pm8008: Use get_irq_reg() for irq chip
regmap-irq: Remove not_fixed_stride flag
drivers/base/regmap/regmap-irq.c | 457 ++++++++++++++-----------------
drivers/extcon/extcon-max77843.c | 3 +-
drivers/extcon/extcon-rt8973a.c | 1 -
drivers/extcon/extcon-sm5502.c | 2 -
drivers/gpio/gpio-sl28cpld.c | 3 +-
drivers/irqchip/irq-sl28cpld.c | 3 +-
drivers/mfd/88pm800.c | 3 +-
drivers/mfd/atc260x-core.c | 6 +-
drivers/mfd/axp20x.c | 21 +-
drivers/mfd/gateworks-gsc.c | 3 +-
drivers/mfd/max14577.c | 7 +-
drivers/mfd/max77650.c | 1 -
drivers/mfd/max77693.c | 6 +-
drivers/mfd/max77843.c | 1 -
drivers/mfd/qcom-pm8008.c | 131 ++++-----
drivers/mfd/rn5t618.c | 3 +-
drivers/mfd/rohm-bd71828.c | 6 +-
drivers/mfd/rohm-bd718x7.c | 1 -
drivers/mfd/rt5033.c | 3 +-
drivers/mfd/sprd-sc27xx-spi.c | 3 +-
drivers/mfd/stpmic1.c | 7 +-
drivers/mfd/sun4i-gpadc.c | 3 +-
drivers/mfd/tps65090.c | 3 +-
drivers/mfd/wcd934x.c | 11 +-
include/linux/regmap.h | 59 ++--
sound/soc/codecs/wcd9335.c | 10 +-
sound/soc/codecs/wcd938x.c | 1 -
27 files changed, 332 insertions(+), 426 deletions(-)
--
2.35.1
We need to divide the sub-irq status register offset by register
stride to get an index for the status buffer to avoid an out of
bounds write when the register stride is greater than 1.
Fixes: a2d21848d921 ("regmap: regmap-irq: Add main status register support")
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/base/regmap/regmap-irq.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 4f785bc7981c..a6db605707b0 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -387,6 +387,7 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
subreg = &chip->sub_reg_offsets[b];
for (i = 0; i < subreg->num_regs; i++) {
unsigned int offset = subreg->offset[i];
+ unsigned int index = offset / map->reg_stride;
if (chip->not_fixed_stride)
ret = regmap_read(map,
@@ -395,7 +396,7 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
else
ret = regmap_read(map,
chip->status_base + offset,
- &data->status_buf[offset]);
+ &data->status_buf[index]);
if (ret)
break;
--
2.35.1
Config registers provide a more uniform approach to handling irq type
registers. They are essentially an extension of the virtual registers
used by the qcom-pm8008 driver.
Config registers can be represented as a 2D array:
config_base[0] reg0,0 reg0,1 reg0,2 reg0,3
config_base[1] reg1,0 reg1,1 reg1,2 reg1,3
config_base[2] reg2,0 reg2,1 reg2,2 reg2,3
There are 'num_config_bases' base registers, each of which is used to
address 'num_config_regs' registers. The addresses are calculated in
the same way as for other bases. It is assumed that an irq's type is
controlled by one column of registers; that column is identified by
the irq's 'type_reg_offset'.
The set_type_config() callback is responsible for updating the config
register contents. It receives an array of buffers (each represents a
row of registers) and the index of the column to update, along with
the 'struct regmap_irq' description and requested irq type.
Buffered values are written to registers in regmap_irq_sync_unlock().
Note that the entire register contents are overwritten, which is a
minor change in behavior from type registers via 'type_base'.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/base/regmap/regmap-irq.c | 102 ++++++++++++++++++++++++++++++-
include/linux/regmap.h | 12 ++++
2 files changed, 113 insertions(+), 1 deletion(-)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 59cfd4000e63..be35f2e41b8c 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -39,6 +39,7 @@ struct regmap_irq_chip_data {
unsigned int *type_buf;
unsigned int *type_buf_def;
unsigned int **virt_buf;
+ unsigned int **config_buf;
unsigned int irq_reg_stride;
unsigned int type_reg_stride;
@@ -231,6 +232,17 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
}
}
+ for (i = 0; i < d->chip->num_config_bases; i++) {
+ for (j = 0; j < d->chip->num_config_regs; j++) {
+ reg = sub_irq_reg(d, d->chip->config_base[i], j);
+ ret = regmap_write(map, reg, d->config_buf[i][j]);
+ if (ret != 0)
+ dev_err(d->map->dev,
+ "Failed to write config %x: %d\n",
+ reg, ret);
+ }
+ }
+
if (d->chip->runtime_pm)
pm_runtime_put(map->dev);
@@ -298,6 +310,10 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
reg = t->type_reg_offset / map->reg_stride;
+ if (d->chip->set_type_config)
+ return d->chip->set_type_config(d->config_buf, type,
+ irq_data, reg);
+
if (t->type_reg_mask)
d->type_buf[reg] &= ~t->type_reg_mask;
else
@@ -603,6 +619,62 @@ static const struct irq_domain_ops regmap_domain_ops = {
.xlate = irq_domain_xlate_onetwocell,
};
+/**
+ * regmap_irq_set_type_config_simple() - Simple IRQ type configuration callback.
+ *
+ * @buf: Buffer containing configuration register values, this is a 2D array of
+ * `num_config_bases` rows, each of `num_config_regs` elements.
+ * @type: The requested IRQ type.
+ * @irq_data: The IRQ being configured.
+ * @idx: Index of the irq's config registers within each array `buf[i]`
+ *
+ * This is a &struct regmap_irq_chip->set_type_config callback suitable for
+ * chips with one config register. Register values are updated according to
+ * the &struct regmap_irq_type data associated with an IRQ.
+ */
+int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type,
+ const struct regmap_irq *irq_data, int idx)
+{
+ const struct regmap_irq_type *t = &irq_data->type;
+
+ if (t->type_reg_mask)
+ buf[0][idx] &= ~t->type_reg_mask;
+ else
+ buf[0][idx] &= ~(t->type_falling_val |
+ t->type_rising_val |
+ t->type_level_low_val |
+ t->type_level_high_val);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_FALLING:
+ buf[0][idx] |= t->type_falling_val;
+ break;
+
+ case IRQ_TYPE_EDGE_RISING:
+ buf[0][idx] |= t->type_rising_val;
+ break;
+
+ case IRQ_TYPE_EDGE_BOTH:
+ buf[0][idx] |= (t->type_falling_val |
+ t->type_rising_val);
+ break;
+
+ case IRQ_TYPE_LEVEL_HIGH:
+ buf[0][idx] |= t->type_level_high_val;
+ break;
+
+ case IRQ_TYPE_LEVEL_LOW:
+ buf[0][idx] |= t->type_level_low_val;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(regmap_irq_set_type_config_simple);
+
/**
* regmap_add_irq_chip_fwnode() - Use standard regmap IRQ controller handling
*
@@ -728,6 +800,24 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
}
}
+ if (chip->num_config_bases && chip->num_config_regs) {
+ /*
+ * Create config_buf[num_config_bases][num_config_regs]
+ */
+ d->config_buf = kcalloc(chip->num_config_bases,
+ sizeof(*d->config_buf), GFP_KERNEL);
+ if (!d->config_buf)
+ goto err_alloc;
+
+ for (i = 0; i < chip->num_config_regs; i++) {
+ d->config_buf[i] = kcalloc(chip->num_config_regs,
+ sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!d->config_buf[i])
+ goto err_alloc;
+ }
+ }
+
d->irq_chip = regmap_irq_chip;
d->irq_chip.name = chip->name;
d->irq = irq;
@@ -904,6 +994,11 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
kfree(d->virt_buf[i]);
kfree(d->virt_buf);
}
+ if (d->config_buf) {
+ for (i = 0; i < chip->num_config_bases; i++)
+ kfree(d->config_buf[i]);
+ kfree(d->config_buf);
+ }
kfree(d);
return ret;
}
@@ -944,7 +1039,7 @@ EXPORT_SYMBOL_GPL(regmap_add_irq_chip);
void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
{
unsigned int virq;
- int hwirq;
+ int i, hwirq;
if (!d)
return;
@@ -974,6 +1069,11 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
kfree(d->mask_buf);
kfree(d->status_reg_buf);
kfree(d->status_buf);
+ if (d->config_buf) {
+ for (i = 0; i < d->chip->num_config_bases; i++)
+ kfree(d->config_buf[i]);
+ kfree(d->config_buf);
+ }
kfree(d);
}
EXPORT_SYMBOL_GPL(regmap_del_irq_chip);
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 8952fa3d0d59..e48d65756fb4 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -1460,6 +1460,7 @@ struct regmap_irq_sub_irq_map {
* @wake_base: Base address for wake enables. If zero unsupported.
* @type_base: Base address for irq type. If zero unsupported.
* @virt_reg_base: Base addresses for extra config regs.
+ * @config_base: Base address for IRQ type config regs. If null unsupported.
* @irq_reg_stride: Stride to use for chips where registers are not contiguous.
* @init_ack_masked: Ack all masked interrupts once during initalization.
* @mask_invert: Inverted mask register: cleared bits are masked out.
@@ -1489,12 +1490,15 @@ struct regmap_irq_sub_irq_map {
* If zero unsupported.
* @type_reg_stride: Stride to use for chips where type registers are not
* contiguous.
+ * @num_config_bases: Number of config base registers.
+ * @num_config_regs: Number of config registers for each config base register.
* @handle_pre_irq: Driver specific callback to handle interrupt from device
* before regmap_irq_handler process the interrupts.
* @handle_post_irq: Driver specific callback to handle interrupt from device
* after handling the interrupts in regmap_irq_handler().
* @set_type_virt: Driver specific callback to extend regmap_irq_set_type()
* and configure virt regs.
+ * @set_type_config: Callback used for configuring irq types.
* @irq_drv_data: Driver specific IRQ data which is passed as parameter when
* driver specific pre/post interrupt handler is called.
*
@@ -1517,6 +1521,7 @@ struct regmap_irq_chip {
unsigned int wake_base;
unsigned int type_base;
unsigned int *virt_reg_base;
+ const unsigned int *config_base;
unsigned int irq_reg_stride;
bool mask_writeonly:1;
bool init_ack_masked:1;
@@ -1539,17 +1544,24 @@ struct regmap_irq_chip {
int num_type_reg;
int num_virt_regs;
+ int num_config_bases;
+ int num_config_regs;
unsigned int type_reg_stride;
int (*handle_pre_irq)(void *irq_drv_data);
int (*handle_post_irq)(void *irq_drv_data);
int (*set_type_virt)(unsigned int **buf, unsigned int type,
unsigned long hwirq, int reg);
+ int (*set_type_config)(unsigned int **buf, unsigned int type,
+ const struct regmap_irq *irq_data, int idx);
void *irq_drv_data;
};
struct regmap_irq_chip_data;
+int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type,
+ const struct regmap_irq *irq_data, int idx);
+
int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
int irq_base, const struct regmap_irq_chip *chip,
struct regmap_irq_chip_data **data);
--
2.35.1
Switch the driver to config registers. This will allow the old
type register code in regmap-irq to be removed.
Signed-off-by: Aidan MacDonald <[email protected]>
---
sound/soc/codecs/wcd9335.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index 617a36a89dfe..727d4436142a 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -5020,16 +5020,22 @@ static const struct regmap_irq wcd9335_codec_irqs[] = {
},
};
+static const unsigned int wcd9335_config_regs[] = {
+ WCD9335_INTR_LEVEL0,
+};
+
static const struct regmap_irq_chip wcd9335_regmap_irq1_chip = {
.name = "wcd9335_pin1_irq",
.status_base = WCD9335_INTR_PIN1_STATUS0,
.mask_base = WCD9335_INTR_PIN1_MASK0,
.ack_base = WCD9335_INTR_PIN1_CLEAR0,
- .type_base = WCD9335_INTR_LEVEL0,
- .num_type_reg = 4,
.num_regs = 4,
.irqs = wcd9335_codec_irqs,
.num_irqs = ARRAY_SIZE(wcd9335_codec_irqs),
+ .config_base = wcd9335_config_regs,
+ .num_config_bases = ARRAY_SIZE(wcd9335_config_regs),
+ .num_config_regs = 4,
+ .set_type_config = regmap_irq_set_type_config_simple,
};
static int wcd9335_parse_dt(struct wcd9335_codec *wcd)
--
2.35.1
The type_invert flag does nothing when type_in_mask is set,
so get rid of it.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/max77650.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/mfd/max77650.c b/drivers/mfd/max77650.c
index 777485a33bc0..3c07fcdd9d07 100644
--- a/drivers/mfd/max77650.c
+++ b/drivers/mfd/max77650.c
@@ -138,7 +138,6 @@ static const struct regmap_irq_chip max77650_irq_chip = {
.status_base = MAX77650_REG_INT_GLBL,
.mask_base = MAX77650_REG_INTM_GLBL,
.type_in_mask = true,
- .type_invert = true,
.init_ack_masked = true,
.clear_on_unmask = true,
};
--
2.35.1
Virtual registers can be removed, since config registers implement
the same functionality.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/base/regmap/regmap-irq.c | 42 --------------------------------
include/linux/regmap.h | 9 -------
2 files changed, 51 deletions(-)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index be35f2e41b8c..5a3e255816fd 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -38,7 +38,6 @@ struct regmap_irq_chip_data {
unsigned int *wake_buf;
unsigned int *type_buf;
unsigned int *type_buf_def;
- unsigned int **virt_buf;
unsigned int **config_buf;
unsigned int irq_reg_stride;
@@ -218,20 +217,6 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
}
}
- if (d->chip->num_virt_regs) {
- for (i = 0; i < d->chip->num_virt_regs; i++) {
- for (j = 0; j < d->chip->num_regs; j++) {
- reg = sub_irq_reg(d, d->chip->virt_reg_base[i],
- j);
- ret = regmap_write(map, reg, d->virt_buf[i][j]);
- if (ret != 0)
- dev_err(d->map->dev,
- "Failed to write virt 0x%x: %d\n",
- reg, ret);
- }
- }
- }
-
for (i = 0; i < d->chip->num_config_bases; i++) {
for (j = 0; j < d->chip->num_config_regs; j++) {
reg = sub_irq_reg(d, d->chip->config_base[i], j);
@@ -346,10 +331,6 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
return -EINVAL;
}
- if (d->chip->set_type_virt)
- return d->chip->set_type_virt(d->virt_buf, type, data->hwirq,
- reg);
-
return 0;
}
@@ -782,24 +763,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
goto err_alloc;
}
- if (chip->num_virt_regs) {
- /*
- * Create virt_buf[chip->num_extra_config_regs][chip->num_regs]
- */
- d->virt_buf = kcalloc(chip->num_virt_regs, sizeof(*d->virt_buf),
- GFP_KERNEL);
- if (!d->virt_buf)
- goto err_alloc;
-
- for (i = 0; i < chip->num_virt_regs; i++) {
- d->virt_buf[i] = kcalloc(chip->num_regs,
- sizeof(unsigned int),
- GFP_KERNEL);
- if (!d->virt_buf[i])
- goto err_alloc;
- }
- }
-
if (chip->num_config_bases && chip->num_config_regs) {
/*
* Create config_buf[num_config_bases][num_config_regs]
@@ -989,11 +952,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
kfree(d->mask_buf);
kfree(d->status_buf);
kfree(d->status_reg_buf);
- if (d->virt_buf) {
- for (i = 0; i < chip->num_virt_regs; i++)
- kfree(d->virt_buf[i]);
- kfree(d->virt_buf);
- }
if (d->config_buf) {
for (i = 0; i < chip->num_config_bases; i++)
kfree(d->config_buf[i]);
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index e48d65756fb4..bb8c89a83b51 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -1459,7 +1459,6 @@ struct regmap_irq_sub_irq_map {
* Using zero value is possible with @use_ack bit.
* @wake_base: Base address for wake enables. If zero unsupported.
* @type_base: Base address for irq type. If zero unsupported.
- * @virt_reg_base: Base addresses for extra config regs.
* @config_base: Base address for IRQ type config regs. If null unsupported.
* @irq_reg_stride: Stride to use for chips where registers are not contiguous.
* @init_ack_masked: Ack all masked interrupts once during initalization.
@@ -1486,8 +1485,6 @@ struct regmap_irq_sub_irq_map {
* assigned based on the index in the array of the interrupt.
* @num_irqs: Number of descriptors.
* @num_type_reg: Number of type registers.
- * @num_virt_regs: Number of non-standard irq configuration registers.
- * If zero unsupported.
* @type_reg_stride: Stride to use for chips where type registers are not
* contiguous.
* @num_config_bases: Number of config base registers.
@@ -1496,8 +1493,6 @@ struct regmap_irq_sub_irq_map {
* before regmap_irq_handler process the interrupts.
* @handle_post_irq: Driver specific callback to handle interrupt from device
* after handling the interrupts in regmap_irq_handler().
- * @set_type_virt: Driver specific callback to extend regmap_irq_set_type()
- * and configure virt regs.
* @set_type_config: Callback used for configuring irq types.
* @irq_drv_data: Driver specific IRQ data which is passed as parameter when
* driver specific pre/post interrupt handler is called.
@@ -1520,7 +1515,6 @@ struct regmap_irq_chip {
unsigned int ack_base;
unsigned int wake_base;
unsigned int type_base;
- unsigned int *virt_reg_base;
const unsigned int *config_base;
unsigned int irq_reg_stride;
bool mask_writeonly:1;
@@ -1543,15 +1537,12 @@ struct regmap_irq_chip {
int num_irqs;
int num_type_reg;
- int num_virt_regs;
int num_config_bases;
int num_config_regs;
unsigned int type_reg_stride;
int (*handle_pre_irq)(void *irq_drv_data);
int (*handle_post_irq)(void *irq_drv_data);
- int (*set_type_virt)(unsigned int **buf, unsigned int type,
- unsigned long hwirq, int reg);
int (*set_type_config)(unsigned int **buf, unsigned int type,
const struct regmap_irq *irq_data, int idx);
void *irq_drv_data;
--
2.35.1
This chip does not set num_type_regs or define any supported IRQ types,
so regmap-irq can't configure its IRQ types. Including type_base in the
chip definition is therefore redundant.
Signed-off-by: Aidan MacDonald <[email protected]>
---
sound/soc/codecs/wcd938x.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c
index c1b61b997f69..acba253b791e 100644
--- a/sound/soc/codecs/wcd938x.c
+++ b/sound/soc/codecs/wcd938x.c
@@ -1298,7 +1298,6 @@ static struct regmap_irq_chip wcd938x_regmap_irq_chip = {
.num_regs = 3,
.status_base = WCD938X_DIGITAL_INTR_STATUS_0,
.mask_base = WCD938X_DIGITAL_INTR_MASK_0,
- .type_base = WCD938X_DIGITAL_INTR_LEVEL_0,
.ack_base = WCD938X_DIGITAL_INTR_CLEAR_0,
.use_ack = 1,
.runtime_pm = true,
--
2.35.1
regmap_irq_update_bits() is misnamed and should only be used for
updating mask registers, since it checks the mask_writeonly flag.
As there are no users of mask_writeonly, it is safe to replace
the wake register updates with regmap_update_bits().
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/base/regmap/regmap-irq.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index b24818ad36e6..dd22d13c54c8 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -157,11 +157,11 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
reg = sub_irq_reg(d, d->chip->wake_base, i);
if (d->wake_buf) {
if (d->chip->wake_invert)
- ret = regmap_irq_update_bits(d, reg,
+ ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i],
~d->wake_buf[i]);
else
- ret = regmap_irq_update_bits(d, reg,
+ ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i],
d->wake_buf[i]);
if (ret != 0)
@@ -823,11 +823,11 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
reg = sub_irq_reg(d, d->chip->wake_base, i);
if (chip->wake_invert)
- ret = regmap_irq_update_bits(d, reg,
+ ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i],
0);
else
- ret = regmap_irq_update_bits(d, reg,
+ ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i],
d->wake_buf[i]);
if (ret != 0) {
--
2.35.1
This function should only be used for updating mask bits, since
it checks the mask_writeonly flag. To avoid confusion, rename it
to regmap_irq_update_mask_bits().
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/base/regmap/regmap-irq.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 4c0d7f7aa544..875415fc3133 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -79,9 +79,9 @@ static void regmap_irq_lock(struct irq_data *data)
mutex_lock(&d->lock);
}
-static int regmap_irq_update_bits(struct regmap_irq_chip_data *d,
- unsigned int reg, unsigned int mask,
- unsigned int val)
+static int regmap_irq_update_mask_bits(struct regmap_irq_chip_data *d,
+ unsigned int reg, unsigned int mask,
+ unsigned int val)
{
if (d->chip->mask_writeonly)
return regmap_write(d->map, reg, val & mask);
@@ -129,11 +129,11 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
reg = sub_irq_reg(d, d->chip->mask_base, i);
if (d->chip->mask_invert) {
- ret = regmap_irq_update_bits(d, reg,
+ ret = regmap_irq_update_mask_bits(d, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
} else if (d->chip->unmask_base) {
/* set mask with mask_base register */
- ret = regmap_irq_update_bits(d, reg,
+ ret = regmap_irq_update_mask_bits(d, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
if (ret < 0)
dev_err(d->map->dev,
@@ -142,12 +142,12 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
unmask_offset = d->chip->unmask_base -
d->chip->mask_base;
/* clear mask with unmask_base register */
- ret = regmap_irq_update_bits(d,
+ ret = regmap_irq_update_mask_bits(d,
reg + unmask_offset,
d->mask_buf_def[i],
d->mask_buf[i]);
} else {
- ret = regmap_irq_update_bits(d, reg,
+ ret = regmap_irq_update_mask_bits(d, reg,
d->mask_buf_def[i], d->mask_buf[i]);
}
if (ret != 0)
@@ -761,17 +761,17 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
reg = sub_irq_reg(d, d->chip->mask_base, i);
if (chip->mask_invert)
- ret = regmap_irq_update_bits(d, reg,
+ ret = regmap_irq_update_mask_bits(d, reg,
d->mask_buf[i], ~d->mask_buf[i]);
else if (d->chip->unmask_base) {
unmask_offset = d->chip->unmask_base -
d->chip->mask_base;
- ret = regmap_irq_update_bits(d,
+ ret = regmap_irq_update_mask_bits(d,
reg + unmask_offset,
d->mask_buf[i],
d->mask_buf[i]);
} else
- ret = regmap_irq_update_bits(d, reg,
+ ret = regmap_irq_update_mask_bits(d, reg,
d->mask_buf[i], d->mask_buf[i]);
if (ret != 0) {
dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
--
2.35.1
There's no need to set the flag explicitly to false, since that
is the default value from zero initialization.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/max77843.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/mfd/max77843.c b/drivers/mfd/max77843.c
index 209ee24d9ce1..4da58eab1603 100644
--- a/drivers/mfd/max77843.c
+++ b/drivers/mfd/max77843.c
@@ -59,7 +59,6 @@ static const struct regmap_irq_chip max77843_irq_chip = {
.name = "max77843",
.status_base = MAX77843_SYS_REG_SYSINTSRC,
.mask_base = MAX77843_SYS_REG_SYSINTMASK,
- .mask_invert = false,
.num_regs = 1,
.irqs = max77843_irqs,
.num_irqs = ARRAY_SIZE(max77843_irqs),
--
2.35.1
The STPMIC1 has a normal "1 to disable" mask register with
separate set and clear registers. It's relying on masks and
unmasks being inverted from their intuitive meaning, so it
needs the broken_mask_unmask flag.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/stpmic1.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c
index eb3da558c3fb..2307d1b0269d 100644
--- a/drivers/mfd/stpmic1.c
+++ b/drivers/mfd/stpmic1.c
@@ -110,6 +110,7 @@ static const struct regmap_irq_chip stpmic1_regmap_irq_chip = {
.status_base = INT_PENDING_R1,
.mask_base = INT_CLEAR_MASK_R1,
.unmask_base = INT_SET_MASK_R1,
+ .broken_mask_unmask = true,
.ack_base = INT_CLEAR_R1,
.num_regs = STPMIC1_PMIC_NUM_IRQ_REGS,
.irqs = stpmic1_irqs,
--
2.35.1
Swap mask_base and unmask_base, and drop the broken_mask_unmask
flag since we're now expecting the registers to have their usual
behavior.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/qcom-pm8008.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/mfd/qcom-pm8008.c b/drivers/mfd/qcom-pm8008.c
index 18095e72714e..7bc6becfe7f4 100644
--- a/drivers/mfd/qcom-pm8008.c
+++ b/drivers/mfd/qcom-pm8008.c
@@ -45,8 +45,8 @@ enum {
#define PM8008_GPIO2_ADDR PM8008_PERIPH_3_BASE
#define PM8008_STATUS_BASE (PM8008_PERIPH_0_BASE | INT_LATCHED_STS_OFFSET)
-#define PM8008_MASK_BASE (PM8008_PERIPH_0_BASE | INT_EN_SET_OFFSET)
-#define PM8008_UNMASK_BASE (PM8008_PERIPH_0_BASE | INT_EN_CLR_OFFSET)
+#define PM8008_MASK_BASE (PM8008_PERIPH_0_BASE | INT_EN_CLR_OFFSET)
+#define PM8008_UNMASK_BASE (PM8008_PERIPH_0_BASE | INT_EN_SET_OFFSET)
#define PM8008_TYPE_BASE (PM8008_PERIPH_0_BASE | INT_SET_TYPE_OFFSET)
#define PM8008_ACK_BASE (PM8008_PERIPH_0_BASE | INT_LATCHED_CLR_OFFSET)
#define PM8008_POLARITY_HI_BASE (PM8008_PERIPH_0_BASE | INT_POL_HIGH_OFFSET)
@@ -141,7 +141,6 @@ static struct regmap_irq_chip pm8008_irq_chip = {
.status_base = PM8008_STATUS_BASE,
.mask_base = PM8008_MASK_BASE,
.unmask_base = PM8008_UNMASK_BASE,
- .broken_mask_unmask = true,
.ack_base = PM8008_ACK_BASE,
.config_base = pm8008_config_regs,
.num_config_bases = ARRAY_SIZE(pm8008_config_regs),
--
2.35.1
An inverted mask register can be represented more directly
as an unmask register. Drop the redundant mask_invert flag.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/base/regmap/regmap-irq.c | 30 ++++++++----------------------
include/linux/regmap.h | 2 --
2 files changed, 8 insertions(+), 24 deletions(-)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 8a718615fd09..0a8edaee064a 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -30,9 +30,6 @@ struct regmap_irq_chip_data {
int irq;
int wake_count;
- unsigned int mask_base;
- unsigned int unmask_base;
-
void *status_reg_buf;
unsigned int *main_status_buf;
unsigned int *status_buf;
@@ -126,8 +123,8 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
* suppress pointless writes.
*/
for (i = 0; i < d->chip->num_regs; i++) {
- if (d->mask_base) {
- reg = sub_irq_reg(d, d->mask_base, i);
+ if (d->chip->mask_base) {
+ reg = sub_irq_reg(d, d->chip->mask_base, i);
ret = regmap_irq_update_mask_bits(d, reg,
d->mask_buf_def[i], d->mask_buf[i]);
if (ret != 0)
@@ -135,8 +132,8 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
reg);
}
- if (d->unmask_base) {
- reg = sub_irq_reg(d, d->unmask_base, i);
+ if (d->chip->unmask_base) {
+ reg = sub_irq_reg(d, d->chip->unmask_base, i);
ret = regmap_irq_update_mask_bits(d, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
if (ret != 0)
@@ -721,17 +718,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
d->chip = chip;
d->irq_base = irq_base;
- /*
- * Swap role of mask_base and unmask_base if mask bits are inverted.
- */
- if (chip->mask_invert) {
- d->mask_base = chip->unmask_base;
- d->unmask_base = chip->mask_base;
- } else {
- d->mask_base = chip->mask_base;
- d->unmask_base = chip->unmask_base;
- }
-
if (chip->irq_reg_stride)
d->irq_reg_stride = chip->irq_reg_stride;
else
@@ -756,8 +742,8 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
for (i = 0; i < chip->num_regs; i++) {
d->mask_buf[i] = d->mask_buf_def[i];
- if (d->mask_base) {
- reg = sub_irq_reg(d, d->mask_base, i);
+ if (d->chip->mask_base) {
+ reg = sub_irq_reg(d, d->chip->mask_base, i);
ret = regmap_irq_update_mask_bits(d, reg,
d->mask_buf_def[i], d->mask_buf[i]);
if (ret != 0) {
@@ -767,8 +753,8 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
}
}
- if (d->unmask_base) {
- reg = sub_irq_reg(d, d->unmask_base, i);
+ if (d->chip->unmask_base) {
+ reg = sub_irq_reg(d, d->chip->unmask_base, i);
ret = regmap_irq_update_mask_bits(d, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
if (ret != 0) {
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index a3103c88e936..bb625a1edef9 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -1462,7 +1462,6 @@ struct regmap_irq_sub_irq_map {
* @config_base: Base address for IRQ type config regs. If null unsupported.
* @irq_reg_stride: Stride to use for chips where registers are not contiguous.
* @init_ack_masked: Ack all masked interrupts once during initalization.
- * @mask_invert: Inverted mask register: cleared bits are masked out.
* @use_ack: Use @ack register even if it is zero.
* @ack_invert: Inverted ack register: cleared bits for ack.
* @clear_ack: Use this to set 1 and 0 or vice-versa to clear interrupts.
@@ -1514,7 +1513,6 @@ struct regmap_irq_chip {
unsigned int irq_reg_stride;
bool mask_writeonly:1;
bool init_ack_masked:1;
- bool mask_invert:1;
bool use_ack:1;
bool ack_invert:1;
bool clear_ack:1;
--
2.35.1
An inverted mask register can be described more directly
as an unmask register.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/rt5033.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/mfd/rt5033.c b/drivers/mfd/rt5033.c
index f1236a9acf30..dc9bf4057a09 100644
--- a/drivers/mfd/rt5033.c
+++ b/drivers/mfd/rt5033.c
@@ -29,8 +29,7 @@ static const struct regmap_irq rt5033_irqs[] = {
static const struct regmap_irq_chip rt5033_irq_chip = {
.name = "rt5033",
.status_base = RT5033_REG_PMIC_IRQ_STAT,
- .mask_base = RT5033_REG_PMIC_IRQ_CTRL,
- .mask_invert = true,
+ .unmask_base = RT5033_REG_PMIC_IRQ_CTRL,
.num_regs = 1,
.irqs = rt5033_irqs,
.num_irqs = ARRAY_SIZE(rt5033_irqs),
--
2.35.1
No drivers currently use mask_writeonly, and in its current form
it seems a bit misleading. When set, mask registers will be
updated with regmap_write_bits() instead of regmap_update_bits(),
but regmap_write_bits() still does a read-modify-write under the
hood. It's not a write-only operation.
Performing a simple regmap_write() is probably more useful, since
it can be used for chips that have separate set & clear registers
for controlling mask bits. Such registers are normally volatile
and read as 0, so avoiding a register read minimizes bus traffic.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/base/regmap/regmap-irq.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index dd22d13c54c8..4c0d7f7aa544 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -84,7 +84,7 @@ static int regmap_irq_update_bits(struct regmap_irq_chip_data *d,
unsigned int val)
{
if (d->chip->mask_writeonly)
- return regmap_write_bits(d->map, reg, mask, val);
+ return regmap_write(d->map, reg, val & mask);
else
return regmap_update_bits(d->map, reg, mask, val);
}
--
2.35.1
The qcom-pm8008 appears to use "1 to enable" convention for
enabling interrupts, with separate set and clear registers.
It's relying on masks and unmasks being inverted from their
intuitive meaning, so it needs the broken_mask_unmask flag.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/qcom-pm8008.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/mfd/qcom-pm8008.c b/drivers/mfd/qcom-pm8008.c
index da16566f7883..18095e72714e 100644
--- a/drivers/mfd/qcom-pm8008.c
+++ b/drivers/mfd/qcom-pm8008.c
@@ -141,6 +141,7 @@ static struct regmap_irq_chip pm8008_irq_chip = {
.status_base = PM8008_STATUS_BASE,
.mask_base = PM8008_MASK_BASE,
.unmask_base = PM8008_UNMASK_BASE,
+ .broken_mask_unmask = true,
.ack_base = PM8008_ACK_BASE,
.config_base = pm8008_config_regs,
.num_config_bases = ARRAY_SIZE(pm8008_config_regs),
--
2.35.1
Clean up all the cruft related to not_fixed_stride. The same thing
can be accomplished with a custom get_irq_reg() callback.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/base/regmap/regmap-irq.c | 41 +++-----------------------------
include/linux/regmap.h | 7 ------
2 files changed, 3 insertions(+), 45 deletions(-)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index acbd6e22b0cd..0c9dd218614a 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -320,15 +320,8 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
unsigned int offset = subreg->offset[i];
unsigned int index = offset / map->reg_stride;
- if (chip->not_fixed_stride)
- ret = regmap_read(map,
- chip->status_base + offset,
- &data->status_buf[b]);
- else
- ret = regmap_read(map,
- chip->status_base + offset,
- &data->status_buf[index]);
-
+ ret = regmap_read(map, chip->status_base + offset,
+ &data->status_buf[index]);
if (ret)
break;
}
@@ -380,18 +373,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
* sake of simplicity. and add bulk reads only if needed
*/
for (i = 0; i < chip->num_main_regs; i++) {
- /*
- * For not_fixed_stride, don't use get_irq_reg().
- * It would produce an incorrect result.
- */
- if (data->chip->not_fixed_stride)
- reg = chip->main_status +
- (i * map->reg_stride *
- data->irq_reg_stride);
- else
- reg = data->get_irq_reg(data,
- chip->main_status, i);
-
+ reg = data->get_irq_reg(data, chip->main_status, i);
ret = regmap_read(map, reg, &data->main_status_buf[i]);
if (ret) {
dev_err(map->dev,
@@ -561,17 +543,6 @@ unsigned int regmap_irq_get_irq_reg_linear(struct regmap_irq_chip_data *data,
const struct regmap_irq_chip *chip = data->chip;
struct regmap *map = data->map;
- /*
- * NOTE: This is for backward compatibility only and will be removed
- * when not_fixed_stride is dropped (it's only used by qcom-pm8008).
- */
- if (chip->not_fixed_stride && chip->sub_reg_offsets) {
- struct regmap_irq_sub_irq_map *subreg;
-
- subreg = &chip->sub_reg_offsets[0];
- return base + subreg->offset[0];
- }
-
return base + index * (map->reg_stride * chip->irq_reg_stride);
}
EXPORT_SYMBOL_GPL(regmap_irq_get_irq_reg_linear);
@@ -674,12 +645,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
return -EINVAL;
}
- if (chip->not_fixed_stride) {
- for (i = 0; i < chip->num_regs; i++)
- if (chip->sub_reg_offsets[i].num_regs != 1)
- return -EINVAL;
- }
-
if (irq_base) {
irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0);
if (irq_base < 0) {
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index be51af0a2425..ecd3682de269 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -1446,9 +1446,6 @@ struct regmap_irq_chip_data;
* status_base. Should contain num_regs arrays.
* Can be provided for chips with more complex mapping than
* 1.st bit to 1.st sub-reg, 2.nd bit to 2.nd sub-reg, ...
- * When used with not_fixed_stride, each one-element array
- * member contains offset calculated as address from each
- * peripheral to first peripheral.
* @num_main_regs: Number of 'main status' irq registers for chips which have
* main_status set.
*
@@ -1474,9 +1471,6 @@ struct regmap_irq_chip_data;
* @clear_on_unmask: For chips with interrupts cleared on read: read the status
* registers before unmasking interrupts to clear any bits
* set when they were masked.
- * @not_fixed_stride: Used when chip peripherals are not laid out with fixed
- * stride. Must be used with sub_reg_offsets containing the
- * offsets to each peripheral.
* @status_invert: Inverted status register: cleared bits are active interrupts.
* @runtime_pm: Hold a runtime PM lock on the device when accessing it.
*
@@ -1529,7 +1523,6 @@ struct regmap_irq_chip {
bool runtime_pm:1;
bool type_in_mask:1;
bool clear_on_unmask:1;
- bool not_fixed_stride:1;
bool status_invert:1;
int num_regs;
--
2.35.1
There's no need to set the flag explicitly to false, since that
is the default value from zero initialization.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/extcon/extcon-sm5502.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c
index f706f5288257..8401e8b27788 100644
--- a/drivers/extcon/extcon-sm5502.c
+++ b/drivers/extcon/extcon-sm5502.c
@@ -227,7 +227,6 @@ static const struct regmap_irq_chip sm5502_muic_irq_chip = {
.name = "sm5502",
.status_base = SM5502_REG_INT1,
.mask_base = SM5502_REG_INTMASK1,
- .mask_invert = false,
.num_regs = 2,
.irqs = sm5502_irqs,
.num_irqs = ARRAY_SIZE(sm5502_irqs),
@@ -276,7 +275,6 @@ static const struct regmap_irq_chip sm5504_muic_irq_chip = {
.name = "sm5504",
.status_base = SM5502_REG_INT1,
.mask_base = SM5502_REG_INTMASK1,
- .mask_invert = false,
.num_regs = 2,
.irqs = sm5504_irqs,
.num_irqs = ARRAY_SIZE(sm5504_irqs),
--
2.35.1
Switch the driver to config registers. This will allow the old
type register code in regmap-irq to be removed.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/wcd934x.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/mfd/wcd934x.c b/drivers/mfd/wcd934x.c
index 68e2fa2fda99..07e884087f2c 100644
--- a/drivers/mfd/wcd934x.c
+++ b/drivers/mfd/wcd934x.c
@@ -55,17 +55,22 @@ static const struct regmap_irq wcd934x_irqs[] = {
WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_SOUNDWIRE, 2, BIT(4)),
};
+static const unsigned int wcd934x_config_regs[] = {
+ WCD934X_INTR_LEVEL0,
+};
+
static const struct regmap_irq_chip wcd934x_regmap_irq_chip = {
.name = "wcd934x_irq",
.status_base = WCD934X_INTR_PIN1_STATUS0,
.mask_base = WCD934X_INTR_PIN1_MASK0,
.ack_base = WCD934X_INTR_PIN1_CLEAR0,
- .type_base = WCD934X_INTR_LEVEL0,
- .num_type_reg = 4,
- .type_in_mask = false,
.num_regs = 4,
.irqs = wcd934x_irqs,
.num_irqs = ARRAY_SIZE(wcd934x_irqs),
+ .config_base = wcd934x_config_regs,
+ .num_config_bases = ARRAY_SIZE(wcd934x_config_regs),
+ .num_config_regs = 4,
+ .set_type_config = regmap_irq_set_type_config_simple,
};
static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg)
--
2.35.1
Use config registers to simplify the driver, putting all of the
code for irq type configuration in one place instead of splitting
it up arbitrarily between type and virtual registers.
Remove the initial register setting in pm8008_init(). The comment
indicates this is a hack to work around quirks in regmap-irq, but
this is not necessary if using config registers.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/qcom-pm8008.c | 76 +++++++++++----------------------------
1 file changed, 21 insertions(+), 55 deletions(-)
diff --git a/drivers/mfd/qcom-pm8008.c b/drivers/mfd/qcom-pm8008.c
index c472d7f8103c..da16566f7883 100644
--- a/drivers/mfd/qcom-pm8008.c
+++ b/drivers/mfd/qcom-pm8008.c
@@ -73,15 +73,16 @@ static struct regmap_irq_sub_irq_map pm8008_sub_reg_offsets[] = {
REGMAP_IRQ_MAIN_REG_OFFSET(p3_offs),
};
-static unsigned int pm8008_virt_regs[] = {
+static unsigned int pm8008_config_regs[] = {
+ PM8008_TYPE_BASE,
PM8008_POLARITY_HI_BASE,
PM8008_POLARITY_LO_BASE,
};
enum {
+ SET_TYPE_INDEX,
POLARITY_HI_INDEX,
POLARITY_LO_INDEX,
- PM8008_NUM_VIRT_REGS,
};
static struct regmap_irq pm8008_irqs[] = {
@@ -95,32 +96,36 @@ static struct regmap_irq pm8008_irqs[] = {
REGMAP_IRQ_REG(PM8008_IRQ_GPIO2, PM8008_GPIO2, BIT(0)),
};
-static int pm8008_set_type_virt(unsigned int **virt_buf,
- unsigned int type, unsigned long hwirq,
- int reg)
+static int pm8008_set_type_config(unsigned int **buf, unsigned int type,
+ const struct regmap_irq *irq_data, int idx)
{
switch (type) {
case IRQ_TYPE_EDGE_FALLING:
case IRQ_TYPE_LEVEL_LOW:
- virt_buf[POLARITY_HI_INDEX][reg] &= ~pm8008_irqs[hwirq].mask;
- virt_buf[POLARITY_LO_INDEX][reg] |= pm8008_irqs[hwirq].mask;
+ buf[POLARITY_HI_INDEX][idx] &= ~irq_data->mask;
+ buf[POLARITY_LO_INDEX][idx] |= irq_data->mask;
break;
case IRQ_TYPE_EDGE_RISING:
case IRQ_TYPE_LEVEL_HIGH:
- virt_buf[POLARITY_HI_INDEX][reg] |= pm8008_irqs[hwirq].mask;
- virt_buf[POLARITY_LO_INDEX][reg] &= ~pm8008_irqs[hwirq].mask;
+ buf[POLARITY_HI_INDEX][idx] |= irq_data->mask;
+ buf[POLARITY_LO_INDEX][idx] &= ~irq_data->mask;
break;
case IRQ_TYPE_EDGE_BOTH:
- virt_buf[POLARITY_HI_INDEX][reg] |= pm8008_irqs[hwirq].mask;
- virt_buf[POLARITY_LO_INDEX][reg] |= pm8008_irqs[hwirq].mask;
+ buf[POLARITY_HI_INDEX][idx] |= irq_data->mask;
+ buf[POLARITY_LO_INDEX][idx] |= irq_data->mask;
break;
default:
return -EINVAL;
}
+ if (type & IRQ_TYPE_EDGE_BOTH)
+ buf[SET_TYPE_INDEX][idx] |= irq_data->mask;
+ else
+ buf[SET_TYPE_INDEX][idx] &= ~irq_data->mask;
+
return 0;
}
@@ -128,20 +133,19 @@ static struct regmap_irq_chip pm8008_irq_chip = {
.name = "pm8008_irq",
.main_status = I2C_INTR_STATUS_BASE,
.num_main_regs = 1,
- .num_virt_regs = PM8008_NUM_VIRT_REGS,
.irqs = pm8008_irqs,
.num_irqs = ARRAY_SIZE(pm8008_irqs),
.num_regs = PM8008_NUM_PERIPHS,
.not_fixed_stride = true,
.sub_reg_offsets = pm8008_sub_reg_offsets,
- .set_type_virt = pm8008_set_type_virt,
.status_base = PM8008_STATUS_BASE,
.mask_base = PM8008_MASK_BASE,
.unmask_base = PM8008_UNMASK_BASE,
- .type_base = PM8008_TYPE_BASE,
.ack_base = PM8008_ACK_BASE,
- .virt_reg_base = pm8008_virt_regs,
- .num_type_reg = PM8008_NUM_PERIPHS,
+ .config_base = pm8008_config_regs,
+ .num_config_bases = ARRAY_SIZE(pm8008_config_regs),
+ .num_config_regs = PM8008_NUM_PERIPHS,
+ .set_type_config = pm8008_set_type_config,
};
static struct regmap_config qcom_mfd_regmap_cfg = {
@@ -150,34 +154,6 @@ static struct regmap_config qcom_mfd_regmap_cfg = {
.max_register = 0xFFFF,
};
-static int pm8008_init(struct pm8008_data *chip)
-{
- int rc;
-
- /*
- * Set TEMP_ALARM peripheral's TYPE so that the regmap-irq framework
- * reads this as the default value instead of zero, the HW default.
- * This is required to enable the writing of TYPE registers in
- * regmap_irq_sync_unlock().
- */
- rc = regmap_write(chip->regmap,
- (PM8008_TEMP_ALARM_ADDR | INT_SET_TYPE_OFFSET),
- BIT(0));
- if (rc)
- return rc;
-
- /* Do the same for GPIO1 and GPIO2 peripherals */
- rc = regmap_write(chip->regmap,
- (PM8008_GPIO1_ADDR | INT_SET_TYPE_OFFSET), BIT(0));
- if (rc)
- return rc;
-
- rc = regmap_write(chip->regmap,
- (PM8008_GPIO2_ADDR | INT_SET_TYPE_OFFSET), BIT(0));
-
- return rc;
-}
-
static int pm8008_probe_irq_peripherals(struct pm8008_data *chip,
int client_irq)
{
@@ -185,20 +161,10 @@ static int pm8008_probe_irq_peripherals(struct pm8008_data *chip,
struct regmap_irq_type *type;
struct regmap_irq_chip_data *irq_data;
- rc = pm8008_init(chip);
- if (rc) {
- dev_err(chip->dev, "Init failed: %d\n", rc);
- return rc;
- }
-
for (i = 0; i < ARRAY_SIZE(pm8008_irqs); i++) {
type = &pm8008_irqs[i].type;
- type->type_reg_offset = pm8008_irqs[i].reg_offset;
- type->type_rising_val = pm8008_irqs[i].mask;
- type->type_falling_val = pm8008_irqs[i].mask;
- type->type_level_high_val = 0;
- type->type_level_low_val = 0;
+ type->type_reg_offset = pm8008_irqs[i].reg_offset;
if (type->type_reg_offset == PM8008_MISC)
type->types_supported = IRQ_TYPE_EDGE_RISING;
--
2.35.1
Drop broken_mask_unmask flag; no drivers are relying on it anymore.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/base/regmap/regmap-irq.c | 9 +--------
include/linux/regmap.h | 1 -
2 files changed, 1 insertion(+), 9 deletions(-)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 082a2981120c..8a718615fd09 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -723,15 +723,8 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
/*
* Swap role of mask_base and unmask_base if mask bits are inverted.
- *
- * Historically, chips that specify both mask_base and unmask_base
- * got inverted mask behavior; this was arguably a bug in regmap-irq
- * and there was no way to get the normal, non-inverted behavior.
- * Those chips will set the broken_mask_unmask flag. They don't set
- * mask_invert so there is no need to worry about interactions with
- * that flag.
*/
- if (chip->mask_invert || chip->broken_mask_unmask) {
+ if (chip->mask_invert) {
d->mask_base = chip->unmask_base;
d->unmask_base = chip->mask_base;
} else {
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 0cf3c4a66946..a3103c88e936 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -1524,7 +1524,6 @@ struct regmap_irq_chip {
bool clear_on_unmask:1;
bool not_fixed_stride:1;
bool status_invert:1;
- bool broken_mask_unmask:1;
int num_regs;
--
2.35.1
An inverted mask register can be described more directly
as an unmask register.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/88pm800.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c
index eaf9845633b4..6d1192db13c1 100644
--- a/drivers/mfd/88pm800.c
+++ b/drivers/mfd/88pm800.c
@@ -398,9 +398,8 @@ static struct regmap_irq_chip pm800_irq_chip = {
.num_regs = 4,
.status_base = PM800_INT_STATUS1,
- .mask_base = PM800_INT_ENA_1,
+ .unmask_base = PM800_INT_ENA_1,
.ack_base = PM800_INT_STATUS1,
- .mask_invert = 1,
};
static int pm800_pages_init(struct pm80x_chip *chip)
--
2.35.1
An inverted mask register can be described more directly
as an unmask register.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/sun4i-gpadc.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/mfd/sun4i-gpadc.c b/drivers/mfd/sun4i-gpadc.c
index cfe14d9bf6dc..edc180d83a4b 100644
--- a/drivers/mfd/sun4i-gpadc.c
+++ b/drivers/mfd/sun4i-gpadc.c
@@ -34,9 +34,8 @@ static const struct regmap_irq_chip sun4i_gpadc_regmap_irq_chip = {
.name = "sun4i_gpadc_irq_chip",
.status_base = SUN4I_GPADC_INT_FIFOS,
.ack_base = SUN4I_GPADC_INT_FIFOS,
- .mask_base = SUN4I_GPADC_INT_FIFOC,
+ .unmask_base = SUN4I_GPADC_INT_FIFOC,
.init_ack_masked = true,
- .mask_invert = true,
.irqs = sun4i_gpadc_regmap_irq,
.num_irqs = ARRAY_SIZE(sun4i_gpadc_regmap_irq),
.num_regs = 1,
--
2.35.1
An inverted mask register can be described more directly
as an unmask register.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/rohm-bd71828.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c
index 714d9fcbf07b..3c5c6c393650 100644
--- a/drivers/mfd/rohm-bd71828.c
+++ b/drivers/mfd/rohm-bd71828.c
@@ -413,9 +413,8 @@ static struct regmap_irq_chip bd71828_irq_chip = {
.irqs = &bd71828_irqs[0],
.num_irqs = ARRAY_SIZE(bd71828_irqs),
.status_base = BD71828_REG_INT_BUCK,
- .mask_base = BD71828_REG_INT_MASK_BUCK,
+ .unmask_base = BD71828_REG_INT_MASK_BUCK,
.ack_base = BD71828_REG_INT_BUCK,
- .mask_invert = true,
.init_ack_masked = true,
.num_regs = 12,
.num_main_regs = 1,
@@ -430,9 +429,8 @@ static struct regmap_irq_chip bd71815_irq_chip = {
.irqs = &bd71815_irqs[0],
.num_irqs = ARRAY_SIZE(bd71815_irqs),
.status_base = BD71815_REG_INT_STAT_01,
- .mask_base = BD71815_REG_INT_EN_01,
+ .unmask_base = BD71815_REG_INT_EN_01,
.ack_base = BD71815_REG_INT_STAT_01,
- .mask_invert = true,
.init_ack_masked = true,
.num_regs = 12,
.num_main_regs = 1,
--
2.35.1
Swap mask_base and unmask_base, and drop the broken_mask_unmask
flag since we're now expecting the registers to have their usual
behavior.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/stpmic1.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c
index 2307d1b0269d..11f3d92acbc0 100644
--- a/drivers/mfd/stpmic1.c
+++ b/drivers/mfd/stpmic1.c
@@ -108,9 +108,8 @@ static const struct regmap_irq stpmic1_irqs[] = {
static const struct regmap_irq_chip stpmic1_regmap_irq_chip = {
.name = "pmic_irq",
.status_base = INT_PENDING_R1,
- .mask_base = INT_CLEAR_MASK_R1,
- .unmask_base = INT_SET_MASK_R1,
- .broken_mask_unmask = true,
+ .mask_base = INT_SET_MASK_R1,
+ .unmask_base = INT_CLEAR_MASK_R1,
.ack_base = INT_CLEAR_R1,
.num_regs = STPMIC1_PMIC_NUM_IRQ_REGS,
.irqs = stpmic1_irqs,
--
2.35.1
An inverted mask register can be described more directly
as an unmask register.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/extcon/extcon-max77843.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c
index 8e6e97ec65a8..1bc0426ce3f1 100644
--- a/drivers/extcon/extcon-max77843.c
+++ b/drivers/extcon/extcon-max77843.c
@@ -189,8 +189,7 @@ static const struct regmap_irq max77843_muic_irq[] = {
static const struct regmap_irq_chip max77843_muic_irq_chip = {
.name = "max77843-muic",
.status_base = MAX77843_MUIC_REG_INT1,
- .mask_base = MAX77843_MUIC_REG_INTMASK1,
- .mask_invert = true,
+ .unmask_base = MAX77843_MUIC_REG_INTMASK1,
.num_regs = 3,
.irqs = max77843_muic_irq,
.num_irqs = ARRAY_SIZE(max77843_muic_irq),
--
2.35.1
There's no need to set the flag explicitly to false, since that
is the default value from zero initialization.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/rohm-bd718x7.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c
index bfd81f78beae..ad6c0971a997 100644
--- a/drivers/mfd/rohm-bd718x7.c
+++ b/drivers/mfd/rohm-bd718x7.c
@@ -70,7 +70,6 @@ static struct regmap_irq_chip bd718xx_irq_chip = {
.mask_base = BD718XX_REG_MIRQ,
.ack_base = BD718XX_REG_IRQ,
.init_ack_masked = true,
- .mask_invert = false,
};
static const struct regmap_range pmic_status_range = {
--
2.35.1
An inverted mask register can be described more directly
as an unmask register.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/gpio/gpio-sl28cpld.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/gpio/gpio-sl28cpld.c b/drivers/gpio/gpio-sl28cpld.c
index 52404736ac86..2195f88c2048 100644
--- a/drivers/gpio/gpio-sl28cpld.c
+++ b/drivers/gpio/gpio-sl28cpld.c
@@ -70,8 +70,7 @@ static int sl28cpld_gpio_irq_init(struct platform_device *pdev,
irq_chip->num_irqs = ARRAY_SIZE(sl28cpld_gpio_irqs);
irq_chip->num_regs = 1;
irq_chip->status_base = base + GPIO_REG_IP;
- irq_chip->mask_base = base + GPIO_REG_IE;
- irq_chip->mask_invert = true;
+ irq_chip->unmask_base = base + GPIO_REG_IE;
irq_chip->ack_base = base + GPIO_REG_IP;
ret = devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(dev),
--
2.35.1
No chip has ever required this flag except for the max77650 where
it didn't have any effect. Drop it. The code that checked for it
has already been removed.
Signed-off-by: Aidan MacDonald <[email protected]>
---
include/linux/regmap.h | 2 --
1 file changed, 2 deletions(-)
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 1966ad4d0fa5..ee2567a0465c 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -1466,7 +1466,6 @@ struct regmap_irq_sub_irq_map {
* @ack_invert: Inverted ack register: cleared bits for ack.
* @clear_ack: Use this to set 1 and 0 or vice-versa to clear interrupts.
* @wake_invert: Inverted wake register: cleared bits are wake enabled.
- * @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.
@@ -1520,7 +1519,6 @@ struct regmap_irq_chip {
bool clear_ack:1;
bool wake_invert:1;
bool runtime_pm:1;
- bool type_invert:1;
bool type_in_mask:1;
bool clear_on_unmask:1;
bool not_fixed_stride:1;
--
2.35.1
There's no need to set the flag explicitly to false, since that
is the default value from zero initialization.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/extcon/extcon-rt8973a.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/extcon/extcon-rt8973a.c b/drivers/extcon/extcon-rt8973a.c
index 40c07f4d656e..02ba770acb27 100644
--- a/drivers/extcon/extcon-rt8973a.c
+++ b/drivers/extcon/extcon-rt8973a.c
@@ -192,7 +192,6 @@ static const struct regmap_irq_chip rt8973a_muic_irq_chip = {
.name = "rt8973a",
.status_base = RT8973A_REG_INT1,
.mask_base = RT8973A_REG_INTM1,
- .mask_invert = false,
.num_regs = 2,
.irqs = rt8973a_irqs,
.num_irqs = ARRAY_SIZE(rt8973a_irqs),
--
2.35.1
This flag is necessary to prepare for fixing the behavior of unmask
registers. Existing chips that set mask_base and unmask_base must
set broken_mask_unmask=1 to declare that they expect the mask bits
will be inverted in both registers, contrary to the usual behavior
of mask registers.
Signed-off-by: Aidan MacDonald <[email protected]>
---
include/linux/regmap.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index ee2567a0465c..21a70fd99493 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -1523,6 +1523,7 @@ struct regmap_irq_chip {
bool clear_on_unmask:1;
bool not_fixed_stride:1;
bool status_invert:1;
+ bool broken_mask_unmask:1;
int num_regs;
--
2.35.1
An inverted mask register can be described more directly
as an unmask register.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/irqchip/irq-sl28cpld.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/irqchip/irq-sl28cpld.c b/drivers/irqchip/irq-sl28cpld.c
index fbb354413ffa..f2172240172c 100644
--- a/drivers/irqchip/irq-sl28cpld.c
+++ b/drivers/irqchip/irq-sl28cpld.c
@@ -65,8 +65,7 @@ static int sl28cpld_intc_probe(struct platform_device *pdev)
irqchip->chip.num_irqs = ARRAY_SIZE(sl28cpld_irqs);
irqchip->chip.num_regs = 1;
irqchip->chip.status_base = base + INTC_IP;
- irqchip->chip.mask_base = base + INTC_IE;
- irqchip->chip.mask_invert = true;
+ irqchip->chip.unmask_base = base + INTC_IE;
irqchip->chip.ack_base = base + INTC_IP;
return devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(dev),
--
2.35.1
The PM8008 has separate set and clear registers for controlling
its interrupt masks. These are likely volatile registers which
read as 0, and writing a '1' bit sets or clears the corresponding
bit in the mask register.
The PM8008's regmap config doesn't enable a cache, so all register
access is already volatile. Adding the mask_writeonly flag should
reduce bus traffic by avoiding a read-modify-write on the mask
set/clear registers.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/qcom-pm8008.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/mfd/qcom-pm8008.c b/drivers/mfd/qcom-pm8008.c
index 7bc6becfe7f4..c778f2f87a17 100644
--- a/drivers/mfd/qcom-pm8008.c
+++ b/drivers/mfd/qcom-pm8008.c
@@ -141,6 +141,7 @@ static struct regmap_irq_chip pm8008_irq_chip = {
.status_base = PM8008_STATUS_BASE,
.mask_base = PM8008_MASK_BASE,
.unmask_base = PM8008_UNMASK_BASE,
+ .mask_writeonly = true,
.ack_base = PM8008_ACK_BASE,
.config_base = pm8008_config_regs,
.num_config_bases = ARRAY_SIZE(pm8008_config_regs),
--
2.35.1
The STPMIC1 has separate set and clear registers for controlling
its interrupt masks. These are volatile registers; writing a '1'
will set or clear the corresponding mask bit, and they read as 0.
Marking the registers volatile and using the mask_writeonly flag
should reduce bus traffic by avoiding a read-modify-write on the
mask set/clear registers.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/stpmic1.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c
index 11f3d92acbc0..a99f7b45df57 100644
--- a/drivers/mfd/stpmic1.c
+++ b/drivers/mfd/stpmic1.c
@@ -42,6 +42,8 @@ static const struct regmap_range stpmic1_volatile_ranges[] = {
regmap_reg_range(WCHDG_CR, WCHDG_CR),
regmap_reg_range(INT_PENDING_R1, INT_PENDING_R4),
regmap_reg_range(INT_SRC_R1, INT_SRC_R4),
+ regmap_reg_range(INT_SET_MASK_R1, INT_SET_MASK_R4),
+ regmap_reg_range(INT_CLEAR_MASK_R1, INT_CLEAR_MASK_R4),
};
static const struct regmap_access_table stpmic1_readable_table = {
@@ -110,6 +112,7 @@ static const struct regmap_irq_chip stpmic1_regmap_irq_chip = {
.status_base = INT_PENDING_R1,
.mask_base = INT_SET_MASK_R1,
.unmask_base = INT_CLEAR_MASK_R1,
+ .mask_writeonly = true,
.ack_base = INT_CLEAR_R1,
.num_regs = STPMIC1_PMIC_NUM_IRQ_REGS,
.irqs = stpmic1_irqs,
--
2.35.1
An inverted mask register can be described more directly
as an unmask register.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/rn5t618.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/mfd/rn5t618.c b/drivers/mfd/rn5t618.c
index 384acb459427..7ed002d090bd 100644
--- a/drivers/mfd/rn5t618.c
+++ b/drivers/mfd/rn5t618.c
@@ -80,8 +80,7 @@ static const struct regmap_irq_chip rc5t619_irq_chip = {
.num_irqs = ARRAY_SIZE(rc5t619_irqs),
.num_regs = 1,
.status_base = RN5T618_INTMON,
- .mask_base = RN5T618_INTEN,
- .mask_invert = true,
+ .unmask_base = RN5T618_INTEN,
};
static struct i2c_client *rn5t618_pm_power_off;
--
2.35.1
Replace the not_fixed_stride flag with a get_irq_reg() callback,
which expresses what we want to do here more directly instead of
relying on a convoluted hierarchy of offsets.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/qcom-pm8008.c | 56 +++++++++++++++++----------------------
1 file changed, 25 insertions(+), 31 deletions(-)
diff --git a/drivers/mfd/qcom-pm8008.c b/drivers/mfd/qcom-pm8008.c
index c778f2f87a17..f6407aa0bcfc 100644
--- a/drivers/mfd/qcom-pm8008.c
+++ b/drivers/mfd/qcom-pm8008.c
@@ -44,16 +44,6 @@ enum {
#define PM8008_GPIO1_ADDR PM8008_PERIPH_2_BASE
#define PM8008_GPIO2_ADDR PM8008_PERIPH_3_BASE
-#define PM8008_STATUS_BASE (PM8008_PERIPH_0_BASE | INT_LATCHED_STS_OFFSET)
-#define PM8008_MASK_BASE (PM8008_PERIPH_0_BASE | INT_EN_CLR_OFFSET)
-#define PM8008_UNMASK_BASE (PM8008_PERIPH_0_BASE | INT_EN_SET_OFFSET)
-#define PM8008_TYPE_BASE (PM8008_PERIPH_0_BASE | INT_SET_TYPE_OFFSET)
-#define PM8008_ACK_BASE (PM8008_PERIPH_0_BASE | INT_LATCHED_CLR_OFFSET)
-#define PM8008_POLARITY_HI_BASE (PM8008_PERIPH_0_BASE | INT_POL_HIGH_OFFSET)
-#define PM8008_POLARITY_LO_BASE (PM8008_PERIPH_0_BASE | INT_POL_LOW_OFFSET)
-
-#define PM8008_PERIPH_OFFSET(paddr) (paddr - PM8008_PERIPH_0_BASE)
-
struct pm8008_data {
struct device *dev;
struct regmap *regmap;
@@ -61,22 +51,10 @@ struct pm8008_data {
struct regmap_irq_chip_data *irq_data;
};
-static unsigned int p0_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_0_BASE)};
-static unsigned int p1_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_1_BASE)};
-static unsigned int p2_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_2_BASE)};
-static unsigned int p3_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_3_BASE)};
-
-static struct regmap_irq_sub_irq_map pm8008_sub_reg_offsets[] = {
- REGMAP_IRQ_MAIN_REG_OFFSET(p0_offs),
- REGMAP_IRQ_MAIN_REG_OFFSET(p1_offs),
- REGMAP_IRQ_MAIN_REG_OFFSET(p2_offs),
- REGMAP_IRQ_MAIN_REG_OFFSET(p3_offs),
-};
-
static unsigned int pm8008_config_regs[] = {
- PM8008_TYPE_BASE,
- PM8008_POLARITY_HI_BASE,
- PM8008_POLARITY_LO_BASE,
+ INT_SET_TYPE_OFFSET,
+ INT_POL_HIGH_OFFSET,
+ INT_POL_LOW_OFFSET,
};
enum {
@@ -96,6 +74,23 @@ static struct regmap_irq pm8008_irqs[] = {
REGMAP_IRQ_REG(PM8008_IRQ_GPIO2, PM8008_GPIO2, BIT(0)),
};
+static const unsigned int pm8008_periph_base[] = {
+ PM8008_PERIPH_0_BASE,
+ PM8008_PERIPH_1_BASE,
+ PM8008_PERIPH_2_BASE,
+ PM8008_PERIPH_3_BASE,
+};
+
+static unsigned int pm8008_get_irq_reg(struct regmap_irq_chip_data *data,
+ unsigned int base, int index)
+{
+ /* Simple linear addressing for the main status register */
+ if (base == I2C_INTR_STATUS_BASE)
+ return base + index;
+
+ return pm8008_periph_base[index] + base;
+}
+
static int pm8008_set_type_config(unsigned int **buf, unsigned int type,
const struct regmap_irq *irq_data, int idx)
{
@@ -136,17 +131,16 @@ static struct regmap_irq_chip pm8008_irq_chip = {
.irqs = pm8008_irqs,
.num_irqs = ARRAY_SIZE(pm8008_irqs),
.num_regs = PM8008_NUM_PERIPHS,
- .not_fixed_stride = true,
- .sub_reg_offsets = pm8008_sub_reg_offsets,
- .status_base = PM8008_STATUS_BASE,
- .mask_base = PM8008_MASK_BASE,
- .unmask_base = PM8008_UNMASK_BASE,
+ .status_base = INT_LATCHED_STS_OFFSET,
+ .mask_base = INT_EN_CLR_OFFSET,
+ .unmask_base = INT_EN_SET_OFFSET,
.mask_writeonly = true,
- .ack_base = PM8008_ACK_BASE,
+ .ack_base = INT_LATCHED_CLR_OFFSET,
.config_base = pm8008_config_regs,
.num_config_bases = ARRAY_SIZE(pm8008_config_regs),
.num_config_regs = PM8008_NUM_PERIPHS,
.set_type_config = pm8008_set_type_config,
+ .get_irq_reg = pm8008_get_irq_reg,
};
static struct regmap_config qcom_mfd_regmap_cfg = {
--
2.35.1
An inverted mask register can be described more directly
as an unmask register.
Also drop useless mask_invert flag from the pmic irq chip.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/max14577.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c
index 6c487fa14e9c..7a501dcc48f6 100644
--- a/drivers/mfd/max14577.c
+++ b/drivers/mfd/max14577.c
@@ -209,8 +209,7 @@ static const struct regmap_irq max14577_irqs[] = {
static const struct regmap_irq_chip max14577_irq_chip = {
.name = "max14577",
.status_base = MAX14577_REG_INT1,
- .mask_base = MAX14577_REG_INTMASK1,
- .mask_invert = true,
+ .unmask_base = MAX14577_REG_INTMASK1,
.num_regs = 3,
.irqs = max14577_irqs,
.num_irqs = ARRAY_SIZE(max14577_irqs),
@@ -239,8 +238,7 @@ static const struct regmap_irq max77836_muic_irqs[] = {
static const struct regmap_irq_chip max77836_muic_irq_chip = {
.name = "max77836-muic",
.status_base = MAX14577_REG_INT1,
- .mask_base = MAX14577_REG_INTMASK1,
- .mask_invert = true,
+ .unmask_base = MAX14577_REG_INTMASK1,
.num_regs = 3,
.irqs = max77836_muic_irqs,
.num_irqs = ARRAY_SIZE(max77836_muic_irqs),
@@ -255,7 +253,6 @@ static const struct regmap_irq_chip max77836_pmic_irq_chip = {
.name = "max77836-pmic",
.status_base = MAX77836_PMIC_REG_TOPSYS_INT,
.mask_base = MAX77836_PMIC_REG_TOPSYS_INT_MASK,
- .mask_invert = false,
.num_regs = 1,
.irqs = max77836_pmic_irqs,
.num_irqs = ARRAY_SIZE(max77836_pmic_irqs),
--
2.35.1
Replace the internal sub_irq_reg() function with a public callback
that drivers can use when they have more complex register layouts.
The default implementation is regmap_irq_get_irq_reg_linear(), used
if the chip doesn't provide its own callback.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/base/regmap/regmap-irq.c | 122 ++++++++++++++++++++-----------
include/linux/regmap.h | 15 +++-
2 files changed, 92 insertions(+), 45 deletions(-)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 7b5bd1d45fc0..acbd6e22b0cd 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -41,30 +41,12 @@ struct regmap_irq_chip_data {
unsigned int irq_reg_stride;
+ unsigned int (*get_irq_reg)(struct regmap_irq_chip_data *data,
+ unsigned int base, int index);
+
bool clear_status:1;
};
-static int sub_irq_reg(struct regmap_irq_chip_data *data,
- unsigned int base_reg, int i)
-{
- const struct regmap_irq_chip *chip = data->chip;
- struct regmap *map = data->map;
- struct regmap_irq_sub_irq_map *subreg;
- unsigned int offset;
- int reg = 0;
-
- if (!chip->sub_reg_offsets || !chip->not_fixed_stride) {
- /* Assume linear mapping */
- reg = base_reg + (i * map->reg_stride * data->irq_reg_stride);
- } else {
- subreg = &chip->sub_reg_offsets[i];
- offset = subreg->offset[0];
- reg = base_reg + offset;
- }
-
- return reg;
-}
-
static inline const
struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data,
int irq)
@@ -76,8 +58,14 @@ static bool regmap_irq_can_bulk_read_status(struct regmap_irq_chip_data *data)
{
struct regmap *map = data->map;
+ /*
+ * While possible that a user-defined get_irq_reg callback might be
+ * linear enough to support bulk reads, most of the time it won't.
+ * Therefore only allow them if the default callback is being used.
+ */
return !map->use_single_read && map->reg_stride == 1 &&
- data->irq_reg_stride == 1;
+ data->irq_reg_stride == 1 &&
+ data->get_irq_reg == regmap_irq_get_irq_reg_linear;
}
static void regmap_irq_lock(struct irq_data *data)
@@ -114,7 +102,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
if (d->clear_status) {
for (i = 0; i < d->chip->num_regs; i++) {
- reg = sub_irq_reg(d, d->chip->status_base, i);
+ reg = d->get_irq_reg(d, d->chip->status_base, i);
ret = regmap_read(map, reg, &val);
if (ret)
@@ -132,7 +120,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
*/
for (i = 0; i < d->chip->num_regs; i++) {
if (d->chip->mask_base) {
- reg = sub_irq_reg(d, d->chip->mask_base, i);
+ reg = d->get_irq_reg(d, d->chip->mask_base, i);
ret = regmap_irq_update_mask_bits(d, reg,
d->mask_buf_def[i], d->mask_buf[i]);
if (ret != 0)
@@ -141,7 +129,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
}
if (d->chip->unmask_base) {
- reg = sub_irq_reg(d, d->chip->unmask_base, i);
+ reg = d->get_irq_reg(d, d->chip->unmask_base, i);
ret = regmap_irq_update_mask_bits(d, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
if (ret != 0)
@@ -149,7 +137,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
reg);
}
- reg = sub_irq_reg(d, d->chip->wake_base, i);
+ reg = d->get_irq_reg(d, d->chip->wake_base, i);
if (d->wake_buf) {
if (d->chip->wake_invert)
ret = regmap_update_bits(d->map, reg,
@@ -173,7 +161,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
* it'll be ignored in irq handler, then may introduce irq storm
*/
if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) {
- reg = sub_irq_reg(d, d->chip->ack_base, i);
+ reg = d->get_irq_reg(d, d->chip->ack_base, i);
/* some chips ack by write 0 */
if (d->chip->ack_invert)
@@ -194,7 +182,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
for (i = 0; i < d->chip->num_config_bases; i++) {
for (j = 0; j < d->chip->num_config_regs; j++) {
- reg = sub_irq_reg(d, d->chip->config_base[i], j);
+ reg = d->get_irq_reg(d, d->chip->config_base[i], j);
ret = regmap_write(map, reg, d->config_buf[i][j]);
if (ret != 0)
dev_err(d->map->dev,
@@ -316,14 +304,17 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
const struct regmap_irq_chip *chip = data->chip;
struct regmap *map = data->map;
struct regmap_irq_sub_irq_map *subreg;
+ unsigned int reg;
int i, ret = 0;
if (!chip->sub_reg_offsets) {
- /* Assume linear mapping */
- ret = regmap_read(map, chip->status_base +
- (b * map->reg_stride * data->irq_reg_stride),
- &data->status_buf[b]);
+ reg = data->get_irq_reg(data, chip->status_base, b);
+ ret = regmap_read(map, reg, &data->status_buf[b]);
} else {
+ /*
+ * Note we can't use get_irq_reg() here because the offsets
+ * in 'subreg' are *not* interchangeable with indices.
+ */
subreg = &chip->sub_reg_offsets[b];
for (i = 0; i < subreg->num_regs; i++) {
unsigned int offset = subreg->offset[i];
@@ -389,10 +380,19 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
* sake of simplicity. and add bulk reads only if needed
*/
for (i = 0; i < chip->num_main_regs; i++) {
- ret = regmap_read(map, chip->main_status +
- (i * map->reg_stride
- * data->irq_reg_stride),
- &data->main_status_buf[i]);
+ /*
+ * For not_fixed_stride, don't use get_irq_reg().
+ * It would produce an incorrect result.
+ */
+ if (data->chip->not_fixed_stride)
+ reg = chip->main_status +
+ (i * map->reg_stride *
+ data->irq_reg_stride);
+ else
+ reg = data->get_irq_reg(data,
+ chip->main_status, i);
+
+ ret = regmap_read(map, reg, &data->main_status_buf[i]);
if (ret) {
dev_err(map->dev,
"Failed to read IRQ status %d\n",
@@ -457,7 +457,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
} else {
for (i = 0; i < data->chip->num_regs; i++) {
- unsigned int reg = sub_irq_reg(data,
+ unsigned int reg = data->get_irq_reg(data,
data->chip->status_base, i);
ret = regmap_read(map, reg, &data->status_buf[i]);
@@ -485,7 +485,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
data->status_buf[i] &= ~data->mask_buf[i];
if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) {
- reg = sub_irq_reg(data, data->chip->ack_base, i);
+ reg = data->get_irq_reg(data, data->chip->ack_base, i);
if (chip->ack_invert)
ret = regmap_write(map, reg,
@@ -545,6 +545,37 @@ static const struct irq_domain_ops regmap_domain_ops = {
.xlate = irq_domain_xlate_onetwocell,
};
+/**
+ * regmap_irq_get_irq_reg_linear() - Linear IRQ register mapping callback.
+ *
+ * @data: Data for the &struct regmap_irq_chip
+ * @base: Base register
+ * @index: Register index
+ *
+ * Returns the register address corresponding to the given @base and @index
+ * by the formula ``base + index * regmap_stride * irq_reg_stride``.
+ */
+unsigned int regmap_irq_get_irq_reg_linear(struct regmap_irq_chip_data *data,
+ unsigned int base, int index)
+{
+ const struct regmap_irq_chip *chip = data->chip;
+ struct regmap *map = data->map;
+
+ /*
+ * NOTE: This is for backward compatibility only and will be removed
+ * when not_fixed_stride is dropped (it's only used by qcom-pm8008).
+ */
+ if (chip->not_fixed_stride && chip->sub_reg_offsets) {
+ struct regmap_irq_sub_irq_map *subreg;
+
+ subreg = &chip->sub_reg_offsets[0];
+ return base + subreg->offset[0];
+ }
+
+ return base + index * (map->reg_stride * chip->irq_reg_stride);
+}
+EXPORT_SYMBOL_GPL(regmap_irq_get_irq_reg_linear);
+
/**
* regmap_irq_set_type_config_simple() - Simple IRQ type configuration callback.
*
@@ -730,6 +761,11 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
else
d->irq_reg_stride = 1;
+ if (chip->get_irq_reg)
+ d->get_irq_reg = chip->get_irq_reg;
+ else
+ d->get_irq_reg = regmap_irq_get_irq_reg_linear;
+
if (regmap_irq_can_bulk_read_status(d)) {
d->status_reg_buf = kmalloc_array(chip->num_regs,
map->format.val_bytes,
@@ -749,7 +785,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
d->mask_buf[i] = d->mask_buf_def[i];
if (d->chip->mask_base) {
- reg = sub_irq_reg(d, d->chip->mask_base, i);
+ reg = d->get_irq_reg(d, d->chip->mask_base, i);
ret = regmap_irq_update_mask_bits(d, reg,
d->mask_buf_def[i], d->mask_buf[i]);
if (ret != 0) {
@@ -760,7 +796,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
}
if (d->chip->unmask_base) {
- reg = sub_irq_reg(d, d->chip->unmask_base, i);
+ reg = d->get_irq_reg(d, d->chip->unmask_base, i);
ret = regmap_irq_update_mask_bits(d, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
if (ret != 0) {
@@ -774,7 +810,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
continue;
/* Ack masked but set interrupts */
- reg = sub_irq_reg(d, d->chip->status_base, i);
+ reg = d->get_irq_reg(d, d->chip->status_base, i);
ret = regmap_read(map, reg, &d->status_buf[i]);
if (ret != 0) {
dev_err(map->dev, "Failed to read IRQ status: %d\n",
@@ -786,7 +822,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
d->status_buf[i] = ~d->status_buf[i];
if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) {
- reg = sub_irq_reg(d, d->chip->ack_base, i);
+ reg = d->get_irq_reg(d, d->chip->ack_base, i);
if (chip->ack_invert)
ret = regmap_write(map, reg,
~(d->status_buf[i] & d->mask_buf[i]));
@@ -811,7 +847,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
if (d->wake_buf) {
for (i = 0; i < chip->num_regs; i++) {
d->wake_buf[i] = d->mask_buf_def[i];
- reg = sub_irq_reg(d, d->chip->wake_base, i);
+ reg = d->get_irq_reg(d, d->chip->wake_base, i);
if (chip->wake_invert)
ret = regmap_update_bits(d->map, reg,
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index bb625a1edef9..be51af0a2425 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -1424,6 +1424,8 @@ struct regmap_irq_sub_irq_map {
unsigned int *offset;
};
+struct regmap_irq_chip_data;
+
/**
* struct regmap_irq_chip - Description of a generic regmap irq_chip.
*
@@ -1489,6 +1491,13 @@ struct regmap_irq_sub_irq_map {
* @handle_post_irq: Driver specific callback to handle interrupt from device
* after handling the interrupts in regmap_irq_handler().
* @set_type_config: Callback used for configuring irq types.
+ * @get_irq_reg: Callback for mapping (base register, index) pairs to register
+ * addresses. The base register will be one of @status_base,
+ * @mask_base, etc., @main_status, or any of @config_base.
+ * The index will be in the range [0, num_main_regs[ for the
+ * main status base, [0, num_type_settings[ for any config
+ * register base, and [0, num_regs[ for any other base.
+ * If unspecified then regmap_irq_get_irq_reg_linear() is used.
* @irq_drv_data: Driver specific IRQ data which is passed as parameter when
* driver specific pre/post interrupt handler is called.
*
@@ -1535,11 +1544,13 @@ struct regmap_irq_chip {
int (*handle_post_irq)(void *irq_drv_data);
int (*set_type_config)(unsigned int **buf, unsigned int type,
const struct regmap_irq *irq_data, int idx);
+ unsigned int (*get_irq_reg)(struct regmap_irq_chip_data *data,
+ unsigned int base, int index);
void *irq_drv_data;
};
-struct regmap_irq_chip_data;
-
+unsigned int regmap_irq_get_irq_reg_linear(struct regmap_irq_chip_data *data,
+ unsigned int base, int index);
int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type,
const struct regmap_irq *irq_data, int idx);
--
2.35.1
An inverted mask register can be described more directly
as an unmask register.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/sprd-sc27xx-spi.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/mfd/sprd-sc27xx-spi.c b/drivers/mfd/sprd-sc27xx-spi.c
index d05a47c5187f..a4a9b81a952b 100644
--- a/drivers/mfd/sprd-sc27xx-spi.c
+++ b/drivers/mfd/sprd-sc27xx-spi.c
@@ -181,11 +181,10 @@ static int sprd_pmic_probe(struct spi_device *spi)
ddata->irq_chip.name = dev_name(&spi->dev);
ddata->irq_chip.status_base =
pdata->irq_base + SPRD_PMIC_INT_MASK_STATUS;
- ddata->irq_chip.mask_base = pdata->irq_base + SPRD_PMIC_INT_EN;
+ ddata->irq_chip.unmask_base = pdata->irq_base + SPRD_PMIC_INT_EN;
ddata->irq_chip.ack_base = 0;
ddata->irq_chip.num_regs = 1;
ddata->irq_chip.num_irqs = pdata->num_irqs;
- ddata->irq_chip.mask_invert = true;
ddata->irqs = devm_kcalloc(&spi->dev,
pdata->num_irqs, sizeof(struct regmap_irq),
--
2.35.1
An inverted mask register can be described more directly
as an unmask register.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/gateworks-gsc.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/mfd/gateworks-gsc.c b/drivers/mfd/gateworks-gsc.c
index d87876747b91..28ec167a9861 100644
--- a/drivers/mfd/gateworks-gsc.c
+++ b/drivers/mfd/gateworks-gsc.c
@@ -189,8 +189,7 @@ static const struct regmap_irq_chip gsc_irq_chip = {
.num_irqs = ARRAY_SIZE(gsc_irqs),
.num_regs = 1,
.status_base = GSC_IRQ_STATUS,
- .mask_base = GSC_IRQ_ENABLE,
- .mask_invert = true,
+ .unmask_base = GSC_IRQ_ENABLE,
.ack_base = GSC_IRQ_STATUS,
.ack_invert = true,
};
--
2.35.1
An inverted mask register can be described more directly
as an unmask register.
Also drop useless mask_invert flag from other irq chips.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/max77693.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c
index 4e6244e17559..fadea37b97cc 100644
--- a/drivers/mfd/max77693.c
+++ b/drivers/mfd/max77693.c
@@ -66,7 +66,6 @@ static const struct regmap_irq_chip max77693_led_irq_chip = {
.name = "max77693-led",
.status_base = MAX77693_LED_REG_FLASH_INT,
.mask_base = MAX77693_LED_REG_FLASH_INT_MASK,
- .mask_invert = false,
.num_regs = 1,
.irqs = max77693_led_irqs,
.num_irqs = ARRAY_SIZE(max77693_led_irqs),
@@ -82,7 +81,6 @@ static const struct regmap_irq_chip max77693_topsys_irq_chip = {
.name = "max77693-topsys",
.status_base = MAX77693_PMIC_REG_TOPSYS_INT,
.mask_base = MAX77693_PMIC_REG_TOPSYS_INT_MASK,
- .mask_invert = false,
.num_regs = 1,
.irqs = max77693_topsys_irqs,
.num_irqs = ARRAY_SIZE(max77693_topsys_irqs),
@@ -100,7 +98,6 @@ static const struct regmap_irq_chip max77693_charger_irq_chip = {
.name = "max77693-charger",
.status_base = MAX77693_CHG_REG_CHG_INT,
.mask_base = MAX77693_CHG_REG_CHG_INT_MASK,
- .mask_invert = false,
.num_regs = 1,
.irqs = max77693_charger_irqs,
.num_irqs = ARRAY_SIZE(max77693_charger_irqs),
@@ -136,8 +133,7 @@ static const struct regmap_irq max77693_muic_irqs[] = {
static const struct regmap_irq_chip max77693_muic_irq_chip = {
.name = "max77693-muic",
.status_base = MAX77693_MUIC_REG_INT1,
- .mask_base = MAX77693_MUIC_REG_INTMASK1,
- .mask_invert = true,
+ .unmask_base = MAX77693_MUIC_REG_INTMASK1,
.num_regs = 3,
.irqs = max77693_muic_irqs,
.num_irqs = ARRAY_SIZE(max77693_muic_irqs),
--
2.35.1
There are several conditions that must be satisfied to support
bulk read of status registers. Move the check into a function
to avoid duplicating it in two places.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/base/regmap/regmap-irq.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 0a8edaee064a..7b5bd1d45fc0 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -72,6 +72,14 @@ struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data,
return &data->chip->irqs[irq];
}
+static bool regmap_irq_can_bulk_read_status(struct regmap_irq_chip_data *data)
+{
+ struct regmap *map = data->map;
+
+ return !map->use_single_read && map->reg_stride == 1 &&
+ data->irq_reg_stride == 1;
+}
+
static void regmap_irq_lock(struct irq_data *data)
{
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
@@ -413,8 +421,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
}
}
- } else if (!map->use_single_read && map->reg_stride == 1 &&
- data->irq_reg_stride == 1) {
+ } else if (regmap_irq_can_bulk_read_status(data)) {
u8 *buf8 = data->status_reg_buf;
u16 *buf16 = data->status_reg_buf;
@@ -723,8 +730,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
else
d->irq_reg_stride = 1;
- if (!map->use_single_read && map->reg_stride == 1 &&
- d->irq_reg_stride == 1) {
+ if (regmap_irq_can_bulk_read_status(d)) {
d->status_reg_buf = kmalloc_array(chip->num_regs,
map->format.val_bytes,
GFP_KERNEL);
--
2.35.1
An inverted mask register can be described more directly
as an unmask register.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/axp20x.c | 21 +++++++--------------
1 file changed, 7 insertions(+), 14 deletions(-)
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index 8161a5dc68e8..3be0d0aa8b34 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -506,8 +506,7 @@ static const struct regmap_irq_chip axp152_regmap_irq_chip = {
.name = "axp152_irq_chip",
.status_base = AXP152_IRQ1_STATE,
.ack_base = AXP152_IRQ1_STATE,
- .mask_base = AXP152_IRQ1_EN,
- .mask_invert = true,
+ .unmask_base = AXP152_IRQ1_EN,
.init_ack_masked = true,
.irqs = axp152_regmap_irqs,
.num_irqs = ARRAY_SIZE(axp152_regmap_irqs),
@@ -518,8 +517,7 @@ static const struct regmap_irq_chip axp20x_regmap_irq_chip = {
.name = "axp20x_irq_chip",
.status_base = AXP20X_IRQ1_STATE,
.ack_base = AXP20X_IRQ1_STATE,
- .mask_base = AXP20X_IRQ1_EN,
- .mask_invert = true,
+ .unmask_base = AXP20X_IRQ1_EN,
.init_ack_masked = true,
.irqs = axp20x_regmap_irqs,
.num_irqs = ARRAY_SIZE(axp20x_regmap_irqs),
@@ -531,8 +529,7 @@ static const struct regmap_irq_chip axp22x_regmap_irq_chip = {
.name = "axp22x_irq_chip",
.status_base = AXP20X_IRQ1_STATE,
.ack_base = AXP20X_IRQ1_STATE,
- .mask_base = AXP20X_IRQ1_EN,
- .mask_invert = true,
+ .unmask_base = AXP20X_IRQ1_EN,
.init_ack_masked = true,
.irqs = axp22x_regmap_irqs,
.num_irqs = ARRAY_SIZE(axp22x_regmap_irqs),
@@ -543,8 +540,7 @@ static const struct regmap_irq_chip axp288_regmap_irq_chip = {
.name = "axp288_irq_chip",
.status_base = AXP20X_IRQ1_STATE,
.ack_base = AXP20X_IRQ1_STATE,
- .mask_base = AXP20X_IRQ1_EN,
- .mask_invert = true,
+ .unmask_base = AXP20X_IRQ1_EN,
.init_ack_masked = true,
.irqs = axp288_regmap_irqs,
.num_irqs = ARRAY_SIZE(axp288_regmap_irqs),
@@ -556,8 +552,7 @@ static const struct regmap_irq_chip axp803_regmap_irq_chip = {
.name = "axp803",
.status_base = AXP20X_IRQ1_STATE,
.ack_base = AXP20X_IRQ1_STATE,
- .mask_base = AXP20X_IRQ1_EN,
- .mask_invert = true,
+ .unmask_base = AXP20X_IRQ1_EN,
.init_ack_masked = true,
.irqs = axp803_regmap_irqs,
.num_irqs = ARRAY_SIZE(axp803_regmap_irqs),
@@ -568,8 +563,7 @@ static const struct regmap_irq_chip axp806_regmap_irq_chip = {
.name = "axp806",
.status_base = AXP20X_IRQ1_STATE,
.ack_base = AXP20X_IRQ1_STATE,
- .mask_base = AXP20X_IRQ1_EN,
- .mask_invert = true,
+ .unmask_base = AXP20X_IRQ1_EN,
.init_ack_masked = true,
.irqs = axp806_regmap_irqs,
.num_irqs = ARRAY_SIZE(axp806_regmap_irqs),
@@ -580,8 +574,7 @@ static const struct regmap_irq_chip axp809_regmap_irq_chip = {
.name = "axp809",
.status_base = AXP20X_IRQ1_STATE,
.ack_base = AXP20X_IRQ1_STATE,
- .mask_base = AXP20X_IRQ1_EN,
- .mask_invert = true,
+ .unmask_base = AXP20X_IRQ1_EN,
.init_ack_masked = true,
.irqs = axp809_regmap_irqs,
.num_irqs = ARRAY_SIZE(axp809_regmap_irqs),
--
2.35.1
An inverted mask register can be described more directly
as an unmask register.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/tps65090.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c
index bd6235308c6b..e474e1ca253a 100644
--- a/drivers/mfd/tps65090.c
+++ b/drivers/mfd/tps65090.c
@@ -127,8 +127,7 @@ static struct regmap_irq_chip tps65090_irq_chip = {
.num_irqs = ARRAY_SIZE(tps65090_irqs),
.num_regs = NUM_INT_REG,
.status_base = TPS65090_REG_INTR_STS,
- .mask_base = TPS65090_REG_INTR_MASK,
- .mask_invert = true,
+ .unmask_base = TPS65090_REG_INTR_MASK,
};
static bool is_volatile_reg(struct device *dev, unsigned int reg)
--
2.35.1
It appears that no chip ever required a nonzero type_reg_stride
and commit 1066cfbdfa3f ("regmap-irq: Extend sub-irq to support
non-fixed reg strides") broke support. Just remove the field.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/base/regmap/regmap-irq.c | 6 ------
include/linux/regmap.h | 3 ---
2 files changed, 9 deletions(-)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 85d7fd4e07d7..b24818ad36e6 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -40,7 +40,6 @@ struct regmap_irq_chip_data {
unsigned int **config_buf;
unsigned int irq_reg_stride;
- unsigned int type_reg_stride;
bool clear_status:1;
};
@@ -738,11 +737,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
else
d->irq_reg_stride = 1;
- if (chip->type_reg_stride)
- d->type_reg_stride = chip->type_reg_stride;
- else
- d->type_reg_stride = 1;
-
if (!map->use_single_read && map->reg_stride == 1 &&
d->irq_reg_stride == 1) {
d->status_reg_buf = kmalloc_array(chip->num_regs,
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 879afdc81526..1966ad4d0fa5 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -1483,8 +1483,6 @@ struct regmap_irq_sub_irq_map {
* @irqs: Descriptors for individual IRQs. Interrupt numbers are
* assigned based on the index in the array of the interrupt.
* @num_irqs: Number of descriptors.
- * @type_reg_stride: Stride to use for chips where type registers are not
- * contiguous.
* @num_config_bases: Number of config base registers.
* @num_config_regs: Number of config registers for each config base register.
* @handle_pre_irq: Driver specific callback to handle interrupt from device
@@ -1535,7 +1533,6 @@ struct regmap_irq_chip {
int num_config_bases;
int num_config_regs;
- unsigned int type_reg_stride;
int (*handle_pre_irq)(void *irq_drv_data);
int (*handle_post_irq)(void *irq_drv_data);
--
2.35.1
An inverted mask register can be described more directly
as an unmask register.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/mfd/atc260x-core.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/mfd/atc260x-core.c b/drivers/mfd/atc260x-core.c
index 7148ff5b05b1..7c5de3ae776e 100644
--- a/drivers/mfd/atc260x-core.c
+++ b/drivers/mfd/atc260x-core.c
@@ -100,8 +100,7 @@ static const struct regmap_irq_chip atc2603c_regmap_irq_chip = {
.num_irqs = ARRAY_SIZE(atc2603c_regmap_irqs),
.num_regs = 1,
.status_base = ATC2603C_INTS_PD,
- .mask_base = ATC2603C_INTS_MSK,
- .mask_invert = true,
+ .unmask_base = ATC2603C_INTS_MSK,
};
static const struct regmap_irq_chip atc2609a_regmap_irq_chip = {
@@ -110,8 +109,7 @@ static const struct regmap_irq_chip atc2609a_regmap_irq_chip = {
.num_irqs = ARRAY_SIZE(atc2609a_regmap_irqs),
.num_regs = 1,
.status_base = ATC2609A_INTS_PD,
- .mask_base = ATC2609A_INTS_MSK,
- .mask_invert = true,
+ .unmask_base = ATC2609A_INTS_MSK,
};
static const struct resource atc2603c_onkey_resources[] = {
--
2.35.1
To me "unmask" suggests that we write 1s to the register when
an interrupt is enabled. This also makes sense because it's the
opposite of what the "mask" register does (write 1s to disable
an interrupt).
But regmap-irq does the opposite: for a disabled interrupt, it
writes 1s to "unmask" and 0s to "mask". This is surprising and
deviates from the usual way mask registers are handled.
Additionally, mask_invert didn't interact with unmask registers
properly -- it caused them to be ignored entirely.
Fix this by making mask and unmask registers orthogonal, using
the following behavior:
* Mask registers are written with 1s for disabled interrupts.
* Unmask registers are written with 1s for enabled interrupts.
This behavior supports both normal or inverted mask registers
and separate set/clear registers via different combinations of
mask_base/unmask_base. The mask_invert flag is made redundant,
since an inverted mask register can be described more directly
as an unmask register.
To cope with existing drivers that rely on the old "backward"
behavior, check for the broken_mask_unmask flag and swap the
roles of mask/unmask registers. This is a compatibility measure
which can be dropped once the drivers are updated to use the
new, more consistent behavior.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/base/regmap/regmap-irq.c | 96 +++++++++++++++++---------------
include/linux/regmap.h | 7 ++-
2 files changed, 55 insertions(+), 48 deletions(-)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 875415fc3133..082a2981120c 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -30,6 +30,9 @@ struct regmap_irq_chip_data {
int irq;
int wake_count;
+ unsigned int mask_base;
+ unsigned int unmask_base;
+
void *status_reg_buf;
unsigned int *main_status_buf;
unsigned int *status_buf;
@@ -95,7 +98,6 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
struct regmap *map = d->map;
int i, j, ret;
u32 reg;
- u32 unmask_offset;
u32 val;
if (d->chip->runtime_pm) {
@@ -124,35 +126,23 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
* suppress pointless writes.
*/
for (i = 0; i < d->chip->num_regs; i++) {
- if (!d->chip->mask_base)
- continue;
-
- reg = sub_irq_reg(d, d->chip->mask_base, i);
- if (d->chip->mask_invert) {
+ if (d->mask_base) {
+ reg = sub_irq_reg(d, d->mask_base, i);
ret = regmap_irq_update_mask_bits(d, reg,
- d->mask_buf_def[i], ~d->mask_buf[i]);
- } else if (d->chip->unmask_base) {
- /* set mask with mask_base register */
+ d->mask_buf_def[i], d->mask_buf[i]);
+ if (ret != 0)
+ dev_err(d->map->dev, "Failed to sync masks in %x\n",
+ reg);
+ }
+
+ if (d->unmask_base) {
+ reg = sub_irq_reg(d, d->unmask_base, i);
ret = regmap_irq_update_mask_bits(d, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
- if (ret < 0)
- dev_err(d->map->dev,
- "Failed to sync unmasks in %x\n",
+ if (ret != 0)
+ dev_err(d->map->dev, "Failed to sync masks in %x\n",
reg);
- unmask_offset = d->chip->unmask_base -
- d->chip->mask_base;
- /* clear mask with unmask_base register */
- ret = regmap_irq_update_mask_bits(d,
- reg + unmask_offset,
- d->mask_buf_def[i],
- d->mask_buf[i]);
- } else {
- ret = regmap_irq_update_mask_bits(d, reg,
- d->mask_buf_def[i], d->mask_buf[i]);
}
- if (ret != 0)
- dev_err(d->map->dev, "Failed to sync masks in %x\n",
- reg);
reg = sub_irq_reg(d, d->chip->wake_base, i);
if (d->wake_buf) {
@@ -634,7 +624,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
int i;
int ret = -ENOMEM;
u32 reg;
- u32 unmask_offset;
if (chip->num_regs <= 0)
return -EINVAL;
@@ -732,6 +721,24 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
d->chip = chip;
d->irq_base = irq_base;
+ /*
+ * Swap role of mask_base and unmask_base if mask bits are inverted.
+ *
+ * Historically, chips that specify both mask_base and unmask_base
+ * got inverted mask behavior; this was arguably a bug in regmap-irq
+ * and there was no way to get the normal, non-inverted behavior.
+ * Those chips will set the broken_mask_unmask flag. They don't set
+ * mask_invert so there is no need to worry about interactions with
+ * that flag.
+ */
+ if (chip->mask_invert || chip->broken_mask_unmask) {
+ d->mask_base = chip->unmask_base;
+ d->unmask_base = chip->mask_base;
+ } else {
+ d->mask_base = chip->mask_base;
+ d->unmask_base = chip->unmask_base;
+ }
+
if (chip->irq_reg_stride)
d->irq_reg_stride = chip->irq_reg_stride;
else
@@ -755,28 +762,27 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
/* Mask all the interrupts by default */
for (i = 0; i < chip->num_regs; i++) {
d->mask_buf[i] = d->mask_buf_def[i];
- if (!chip->mask_base)
- continue;
- reg = sub_irq_reg(d, d->chip->mask_base, i);
-
- if (chip->mask_invert)
+ if (d->mask_base) {
+ reg = sub_irq_reg(d, d->mask_base, i);
ret = regmap_irq_update_mask_bits(d, reg,
- d->mask_buf[i], ~d->mask_buf[i]);
- else if (d->chip->unmask_base) {
- unmask_offset = d->chip->unmask_base -
- d->chip->mask_base;
- ret = regmap_irq_update_mask_bits(d,
- reg + unmask_offset,
- d->mask_buf[i],
- d->mask_buf[i]);
- } else
+ d->mask_buf_def[i], d->mask_buf[i]);
+ if (ret != 0) {
+ dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
+ reg, ret);
+ goto err_alloc;
+ }
+ }
+
+ if (d->unmask_base) {
+ reg = sub_irq_reg(d, d->unmask_base, i);
ret = regmap_irq_update_mask_bits(d, reg,
- d->mask_buf[i], d->mask_buf[i]);
- if (ret != 0) {
- dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
- reg, ret);
- goto err_alloc;
+ d->mask_buf_def[i], ~d->mask_buf[i]);
+ if (ret != 0) {
+ dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
+ reg, ret);
+ goto err_alloc;
+ }
}
if (!chip->init_ack_masked)
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 21a70fd99493..0cf3c4a66946 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -1451,10 +1451,11 @@ struct regmap_irq_sub_irq_map {
* main_status set.
*
* @status_base: Base status register address.
- * @mask_base: Base mask register address.
+ * @mask_base: Base mask register address. Mask bits are set to 1 when an
+ * interrupt is masked, 0 when unmasked.
* @mask_writeonly: Base mask register is write only.
- * @unmask_base: Base unmask register address. for chips who have
- * separate mask and unmask registers
+ * @unmask_base: Base unmask register address. Unmask bits are set to 1 when
+ * an interrupt is unmasked and 0 when masked.
* @ack_base: Base ack address. If zero then the chip is clear on read.
* Using zero value is possible with @use_ack bit.
* @wake_base: Base address for wake enables. If zero unsupported.
--
2.35.1
Check types_supported instead of checking type_rising/falling_val
when using type_in_mask interrupts. This makes the intent clearer
and allows a type_in_mask irq to support level or edge triggers,
rather than only edge triggers. Update the comment to reflect the
new behavior.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/base/regmap/regmap-irq.c | 19 ++++++++-----------
1 file changed, 8 insertions(+), 11 deletions(-)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index a6db605707b0..59cfd4000e63 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -253,22 +253,19 @@ static void regmap_irq_enable(struct irq_data *data)
struct regmap *map = d->map;
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
unsigned int reg = irq_data->reg_offset / map->reg_stride;
- unsigned int mask, type;
-
- type = irq_data->type.type_falling_val | irq_data->type.type_rising_val;
+ unsigned int 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.
+ * separate mask bits for each interrupt trigger type, but we want
+ * to have a single logical interrupt with a configurable type.
*
- * 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 the interrupt we're enabling defines any supported types
+ * 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)
+ if (d->chip->type_in_mask && irq_data->type.types_supported)
mask = d->type_buf[reg] & irq_data->mask;
else
mask = irq_data->mask;
--
2.35.1
Now that all users have been converted to use config registers
for setting IRQ types, the old type register handling code can
be removed. Also refactor the parts related to type_in_mask.
Signed-off-by: Aidan MacDonald <[email protected]>
---
drivers/base/regmap/regmap-irq.c | 102 +++++--------------------------
include/linux/regmap.h | 4 --
2 files changed, 14 insertions(+), 92 deletions(-)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 5a3e255816fd..85d7fd4e07d7 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -36,8 +36,7 @@ struct regmap_irq_chip_data {
unsigned int *mask_buf;
unsigned int *mask_buf_def;
unsigned int *wake_buf;
- unsigned int *type_buf;
- unsigned int *type_buf_def;
+ unsigned int *mask_type_buf;
unsigned int **config_buf;
unsigned int irq_reg_stride;
@@ -199,24 +198,6 @@ 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++) {
- if (!d->type_buf_def[i])
- continue;
- reg = sub_irq_reg(d, d->chip->type_base, i);
- if (d->chip->type_invert)
- ret = regmap_irq_update_bits(d, reg,
- d->type_buf_def[i], ~d->type_buf[i]);
- else
- ret = regmap_irq_update_bits(d, reg,
- d->type_buf_def[i], d->type_buf[i]);
- if (ret != 0)
- dev_err(d->map->dev, "Failed to sync type in %x\n",
- reg);
- }
- }
-
for (i = 0; i < d->chip->num_config_bases; i++) {
for (j = 0; j < d->chip->num_config_regs; j++) {
reg = sub_irq_reg(d, d->chip->config_base[i], j);
@@ -259,11 +240,11 @@ static void regmap_irq_enable(struct irq_data *data)
*
* If the interrupt we're enabling defines any supported types
* then instead of using the regular mask bits for this interrupt,
- * use the value previously written to the type buffer at the
+ * use the value previously written to the mask_type buffer at the
* corresponding offset in regmap_irq_set_type().
*/
if (d->chip->type_in_mask && irq_data->type.types_supported)
- mask = d->type_buf[reg] & irq_data->mask;
+ mask = d->mask_type_buf[reg] & irq_data->mask;
else
mask = irq_data->mask;
@@ -287,50 +268,21 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
struct regmap *map = d->map;
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
- int reg;
const struct regmap_irq_type *t = &irq_data->type;
+ unsigned int reg;
- if ((t->types_supported & type) != type)
+ if ((irq_data->type.types_supported & type) != type)
return 0;
reg = t->type_reg_offset / map->reg_stride;
+ if (d->chip->type_in_mask)
+ return regmap_irq_set_type_config_simple(&d->mask_type_buf,
+ type, irq_data, reg);
if (d->chip->set_type_config)
return d->chip->set_type_config(d->config_buf, type,
irq_data, reg);
- if (t->type_reg_mask)
- d->type_buf[reg] &= ~t->type_reg_mask;
- else
- d->type_buf[reg] &= ~(t->type_falling_val |
- t->type_rising_val |
- t->type_level_low_val |
- t->type_level_high_val);
- switch (type) {
- case IRQ_TYPE_EDGE_FALLING:
- d->type_buf[reg] |= t->type_falling_val;
- break;
-
- case IRQ_TYPE_EDGE_RISING:
- d->type_buf[reg] |= t->type_rising_val;
- break;
-
- case IRQ_TYPE_EDGE_BOTH:
- d->type_buf[reg] |= (t->type_falling_val |
- t->type_rising_val);
- break;
-
- case IRQ_TYPE_LEVEL_HIGH:
- d->type_buf[reg] |= t->type_level_high_val;
- break;
-
- case IRQ_TYPE_LEVEL_LOW:
- d->type_buf[reg] |= t->type_level_low_val;
- break;
- default:
- return -EINVAL;
- }
-
return 0;
}
@@ -682,7 +634,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
struct regmap_irq_chip_data *d;
int i;
int ret = -ENOMEM;
- int num_type_reg;
u32 reg;
u32 unmask_offset;
@@ -750,16 +701,10 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
goto err_alloc;
}
- num_type_reg = chip->type_in_mask ? chip->num_regs : chip->num_type_reg;
- if (num_type_reg) {
- d->type_buf_def = kcalloc(num_type_reg,
- sizeof(unsigned int), GFP_KERNEL);
- if (!d->type_buf_def)
- goto err_alloc;
-
- d->type_buf = kcalloc(num_type_reg, sizeof(unsigned int),
- GFP_KERNEL);
- if (!d->type_buf)
+ if (chip->type_in_mask) {
+ d->mask_type_buf = kcalloc(chip->num_regs,
+ sizeof(unsigned int), GFP_KERNEL);
+ if (!d->mask_type_buf)
goto err_alloc;
}
@@ -899,23 +844,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
}
}
- if (chip->num_type_reg && !chip->type_in_mask) {
- for (i = 0; i < chip->num_type_reg; ++i) {
- reg = sub_irq_reg(d, d->chip->type_base, i);
-
- ret = regmap_read(map, reg, &d->type_buf_def[i]);
-
- if (d->chip->type_invert)
- d->type_buf_def[i] = ~d->type_buf_def[i];
-
- if (ret) {
- dev_err(map->dev, "Failed to get type defaults at 0x%x: %d\n",
- reg, ret);
- goto err_alloc;
- }
- }
- }
-
if (irq_base)
d->domain = irq_domain_create_legacy(fwnode, chip->num_irqs,
irq_base, 0,
@@ -945,8 +873,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
err_domain:
/* Should really dispose of the domain but... */
err_alloc:
- kfree(d->type_buf);
- kfree(d->type_buf_def);
+ kfree(d->mask_type_buf);
kfree(d->wake_buf);
kfree(d->mask_buf_def);
kfree(d->mask_buf);
@@ -1020,8 +947,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
}
irq_domain_remove(d->domain);
- kfree(d->type_buf);
- kfree(d->type_buf_def);
+ kfree(d->mask_type_buf);
kfree(d->wake_buf);
kfree(d->mask_buf_def);
kfree(d->mask_buf);
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index bb8c89a83b51..879afdc81526 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -1458,7 +1458,6 @@ struct regmap_irq_sub_irq_map {
* @ack_base: Base ack address. If zero then the chip is clear on read.
* Using zero value is possible with @use_ack bit.
* @wake_base: Base address for wake enables. If zero unsupported.
- * @type_base: Base address for irq type. If zero unsupported.
* @config_base: Base address for IRQ type config regs. If null unsupported.
* @irq_reg_stride: Stride to use for chips where registers are not contiguous.
* @init_ack_masked: Ack all masked interrupts once during initalization.
@@ -1484,7 +1483,6 @@ struct regmap_irq_sub_irq_map {
* @irqs: Descriptors for individual IRQs. Interrupt numbers are
* assigned based on the index in the array of the interrupt.
* @num_irqs: Number of descriptors.
- * @num_type_reg: Number of type registers.
* @type_reg_stride: Stride to use for chips where type registers are not
* contiguous.
* @num_config_bases: Number of config base registers.
@@ -1514,7 +1512,6 @@ struct regmap_irq_chip {
unsigned int unmask_base;
unsigned int ack_base;
unsigned int wake_base;
- unsigned int type_base;
const unsigned int *config_base;
unsigned int irq_reg_stride;
bool mask_writeonly:1;
@@ -1536,7 +1533,6 @@ struct regmap_irq_chip {
const struct regmap_irq *irqs;
int num_irqs;
- int num_type_reg;
int num_config_bases;
int num_config_regs;
unsigned int type_reg_stride;
--
2.35.1
On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
<[email protected]> wrote:
>
> To me "unmask" suggests that we write 1s to the register when
> an interrupt is enabled. This also makes sense because it's the
> opposite of what the "mask" register does (write 1s to disable
> an interrupt).
>
> But regmap-irq does the opposite: for a disabled interrupt, it
> writes 1s to "unmask" and 0s to "mask". This is surprising and
> deviates from the usual way mask registers are handled.
>
> Additionally, mask_invert didn't interact with unmask registers
> properly -- it caused them to be ignored entirely.
>
> Fix this by making mask and unmask registers orthogonal, using
> the following behavior:
>
> * Mask registers are written with 1s for disabled interrupts.
> * Unmask registers are written with 1s for enabled interrupts.
>
> This behavior supports both normal or inverted mask registers
> and separate set/clear registers via different combinations of
> mask_base/unmask_base. The mask_invert flag is made redundant,
> since an inverted mask register can be described more directly
> as an unmask register.
>
> To cope with existing drivers that rely on the old "backward"
> behavior, check for the broken_mask_unmask flag and swap the
> roles of mask/unmask registers. This is a compatibility measure
> which can be dropped once the drivers are updated to use the
> new, more consistent behavior.
...
> + if (ret != 0)
if (ret)
> + dev_err(d->map->dev, "Failed to sync masks in %x\n",
> + reg);
...
> + if (ret != 0)
Ditto.
> + dev_err(d->map->dev, "Failed to sync masks in %x\n",
...
> + /*
> + * Swap role of mask_base and unmask_base if mask bits are inverted.
the roles
> + *
> + * Historically, chips that specify both mask_base and unmask_base
> + * got inverted mask behavior; this was arguably a bug in regmap-irq
> + * and there was no way to get the normal, non-inverted behavior.
> + * Those chips will set the broken_mask_unmask flag. They don't set
> + * mask_invert so there is no need to worry about interactions with
> + * that flag.
> + */
Reading this comment perhaps the code needs a validator part that will
issue a WARN_ON / dev_warn() / etc in case where the above is not
satisfied?
...
> + if (ret != 0) {
if (ret)
> + dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
> + reg, ret);
...
> + if (ret != 0) {
Ditto.
> + dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
> + reg, ret);
--
With Best Regards,
Andy Shevchenko
On Mon, Jun 20, 2022 at 10:10 PM Aidan MacDonald
<[email protected]> wrote:
>
> An inverted mask register can be described more directly
> as an unmask register.
Reviewed-by: Andy Shevchenko <[email protected]>
> Signed-off-by: Aidan MacDonald <[email protected]>
> ---
> drivers/gpio/gpio-sl28cpld.c | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/drivers/gpio/gpio-sl28cpld.c b/drivers/gpio/gpio-sl28cpld.c
> index 52404736ac86..2195f88c2048 100644
> --- a/drivers/gpio/gpio-sl28cpld.c
> +++ b/drivers/gpio/gpio-sl28cpld.c
> @@ -70,8 +70,7 @@ static int sl28cpld_gpio_irq_init(struct platform_device *pdev,
> irq_chip->num_irqs = ARRAY_SIZE(sl28cpld_gpio_irqs);
> irq_chip->num_regs = 1;
> irq_chip->status_base = base + GPIO_REG_IP;
> - irq_chip->mask_base = base + GPIO_REG_IE;
> - irq_chip->mask_invert = true;
> + irq_chip->unmask_base = base + GPIO_REG_IE;
> irq_chip->ack_base = base + GPIO_REG_IP;
>
> ret = devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(dev),
> --
> 2.35.1
>
--
With Best Regards,
Andy Shevchenko
On Mon, Jun 20, 2022 at 10:12 PM Aidan MacDonald
<[email protected]> wrote:
>
> Replace the internal sub_irq_reg() function with a public callback
> that drivers can use when they have more complex register layouts.
> The default implementation is regmap_irq_get_irq_reg_linear(), used
> if the chip doesn't provide its own callback.
...
> + /*
> + * While possible that a user-defined get_irq_reg callback might be
->get_irq_reg()
> + * linear enough to support bulk reads, most of the time it won't.
> + * Therefore only allow them if the default callback is being used.
> + */
> return !map->use_single_read && map->reg_stride == 1 &&
> - data->irq_reg_stride == 1;
> + data->irq_reg_stride == 1 &&
> + data->get_irq_reg == regmap_irq_get_irq_reg_linear;
If initially this had been as
return _reg_stride && irq_reg_stride &&
!map->use_single_read;
you would have done less changes by squeezing a new condition just in
between the other two. It will preserve the grouping of the checks as
well.
> }
...
> + /*
> + * Note we can't use get_irq_reg() here because the offsets
->get_irq_reg()
> + * in 'subreg' are *not* interchangeable with indices.
> + */
...
> + /*
> + * For not_fixed_stride, don't use get_irq_reg().
Ditto.
> + * It would produce an incorrect result.
> + */
...
> + reg = chip->main_status +
> + (i * map->reg_stride *
> + data->irq_reg_stride);
Parentheses are not necessary. And perhaps the last two can be put on
a single line.
...
> + /*
> + * NOTE: This is for backward compatibility only and will be removed
FIXME ?
> + * when not_fixed_stride is dropped (it's only used by qcom-pm8008).
> + */
--
With Best Regards,
Andy Shevchenko
On Mon, Jun 20, 2022 at 10:11 PM Aidan MacDonald
<[email protected]> wrote:
>
> There are several conditions that must be satisfied to support
> bulk read of status registers. Move the check into a function
> to avoid duplicating it in two places.
...
> - } else if (!map->use_single_read && map->reg_stride == 1 &&
> - data->irq_reg_stride == 1) {
> + } else if (regmap_irq_can_bulk_read_status(data)) {
>
While at it, you may drop this unneeded blank line.
> u8 *buf8 = data->status_reg_buf;
> u16 *buf16 = data->status_reg_buf;
--
With Best Regards,
Andy Shevchenko
On Mon, Jun 20, 2022 at 10:07 PM Aidan MacDonald
<[email protected]> wrote:
>
> Hi Mark,
>
> Here's a bunch of cleanups for regmap-irq focused on simplifying the API
> and generalizing it a bit. It's broken up into three refactors, focusing
> on one area at a time.
>
> * Patches 01 and 02 are straightforward bugfixes, independent of the
> rest of the series. Neither of the bugs are triggered by in-tree
> drivers but they might be worth picking up early anyhow.
>
> * Patches 03-13 clean up everything related to configuring IRQ types.
>
> * Patches 14-45 deal with mask/unmask registers. First, make unmask
> registers behave more intuitively and usefully, and get rid of the
> mask_invert flag in favor of describing inverted mask registers as
> unmask registers. Second, make the mask_writeonly flag more useful
> and enable it for two chips where it makes sense.
>
> * Patches 46-49 refactor sub_irq_reg() as a get_irq_reg() callback,
> and use that to eliminate the not_fixed_stride flag.
>
> The approach I used when refactoring is pretty simple: (1) introduce new
> functionality in regmap-irq, (2) convert the drivers, and (3) remove any
> old code. Nothing should break in the middle.
>
> The patches can be re-ordered to some extent if that's preferable, but
> it's best to add get_irq_reg() last to avoid having to think about how
> it interacts with features that'll be removed anyway.
>
> I can't test most of the devices affected by this series so a lot of the
> code is only build tested. I've tested on real hardware with my AXP192
> patchset[1], although it only provides limited code coverage.
>
> qcom-pm8008 in particular deserves careful testing - it used all of the
> features touched by the refactors and required the most changes. Other
> drivers only required trivial changes but there are three of them worth
> mentioning: wcd943x, wcd9335, and wcd938x. They have suspicious looking
> IRQ type definitions and I'm pretty sure aren't working properly, but
> I can't fix them myself. The refactor shouldn't affect their behavior
> so how / when / if they get fixed shouldn't be much of an issue.
>
> Oh, and I added the 'mask_writeonly' flag and volatile ranges to the
> stpmic1 driver based on its datasheet[2] as a small optimization. It's
> probably fine but testing would be a good idea.
>
> [1]: https://lore.kernel.org/linux-iio/20220618214009.2178567-1-aidanmacdonald.0x0@gmailcom/
> [2]: https://www.st.com/resource/en/datasheet/stpmic1.pdf
Cool series, thanks for cleaning this up!
--
With Best Regards,
Andy Shevchenko
On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
<[email protected]> wrote:
>
> No drivers currently use mask_writeonly, and in its current form
> it seems a bit misleading. When set, mask registers will be
> updated with regmap_write_bits() instead of regmap_update_bits(),
> but regmap_write_bits() still does a read-modify-write under the
> hood. It's not a write-only operation.
>
> Performing a simple regmap_write() is probably more useful, since
> it can be used for chips that have separate set & clear registers
> for controlling mask bits. Such registers are normally volatile
> and read as 0, so avoiding a register read minimizes bus traffic.
Reading your explanations and the code, I would rather think about
fixing the regmap_write_bits() to be writeonly op.
Otherwise it's unclear what's the difference between
regmap_write_bits() vs. regmap_update_bits().
...
> if (d->chip->mask_writeonly)
> - return regmap_write_bits(d->map, reg, mask, val);
> + return regmap_write(d->map, reg, val & mask);
> else
> return regmap_update_bits(d->map, reg, mask, val);
--
With Best Regards,
Andy Shevchenko
On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
<[email protected]> wrote:
>
> Config registers provide a more uniform approach to handling irq type
> registers. They are essentially an extension of the virtual registers
> used by the qcom-pm8008 driver.
>
> Config registers can be represented as a 2D array:
>
> config_base[0] reg0,0 reg0,1 reg0,2 reg0,3
> config_base[1] reg1,0 reg1,1 reg1,2 reg1,3
> config_base[2] reg2,0 reg2,1 reg2,2 reg2,3
>
> There are 'num_config_bases' base registers, each of which is used to
> address 'num_config_regs' registers. The addresses are calculated in
> the same way as for other bases. It is assumed that an irq's type is
> controlled by one column of registers; that column is identified by
> the irq's 'type_reg_offset'.
>
> The set_type_config() callback is responsible for updating the config
> register contents. It receives an array of buffers (each represents a
> row of registers) and the index of the column to update, along with
> the 'struct regmap_irq' description and requested irq type.
>
> Buffered values are written to registers in regmap_irq_sync_unlock().
> Note that the entire register contents are overwritten, which is a
> minor change in behavior from type registers via 'type_base'.
...
> + ret = regmap_write(map, reg, d->config_buf[i][j]);
> + if (ret != 0)
if (ret)
> + dev_err(d->map->dev,
> + "Failed to write config %x: %d\n",
> + reg, ret);
> + }
...
> + * regmap_irq_set_type_config_simple() - Simple IRQ type configuration callback.
> + *
Redundant line.
...
> + d->config_buf = kcalloc(chip->num_config_bases,
> + sizeof(*d->config_buf), GFP_KERNEL);
> + if (!d->config_buf)
> + goto err_alloc;
> +
> + for (i = 0; i < chip->num_config_regs; i++) {
> + d->config_buf[i] = kcalloc(chip->num_config_regs,
> + sizeof(unsigned int),
Can it be sizeof(**d->config_buf) ?
> + GFP_KERNEL);
> + if (!d->config_buf[i])
> + goto err_alloc;
> + }
--
With Best Regards,
Andy Shevchenko
On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
<[email protected]> wrote:
>
> The STPMIC1 has a normal "1 to disable" mask register with
> separate set and clear registers. It's relying on masks and
> unmasks being inverted from their intuitive meaning, so it
> needs the broken_mask_unmask flag.
Same comment as per previous patch and continues to all patches of a kind.
--
With Best Regards,
Andy Shevchenko
On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
<[email protected]> wrote:
>
> The qcom-pm8008 appears to use "1 to enable" convention for
> enabling interrupts, with separate set and clear registers.
> It's relying on masks and unmasks being inverted from their
It relies
> intuitive meaning, so it needs the broken_mask_unmask flag.
How has it worked until now?
--
With Best Regards,
Andy Shevchenko
On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
<[email protected]> wrote:
>
> This flag is necessary to prepare for fixing the behavior of unmask
> registers. Existing chips that set mask_base and unmask_base must
> set broken_mask_unmask=1 to declare that they expect the mask bits
Boolean should take true/false.
> will be inverted in both registers, contrary to the usual behavior
> of mask registers.
> diff --git a/include/linux/regmap.h b/include/linux/regmap.h
> index ee2567a0465c..21a70fd99493 100644
> --- a/include/linux/regmap.h
> +++ b/include/linux/regmap.h
> @@ -1523,6 +1523,7 @@ struct regmap_irq_chip {
> bool clear_on_unmask:1;
> bool not_fixed_stride:1;
> bool status_invert:1;
> + bool broken_mask_unmask:1;
Looking at the given context, I would group it with clean_on_unmask above.
The above is weird enough on its own. Can you prepare a precursor
patch that either drops the bit fields of booleans or moves them to
unsigned int?
Note, bit fields in C are beasts when it goes to concurrent access. It
would be nice to ensure these are not the cases of a such.
--
With Best Regards,
Andy Shevchenko
On Mon, Jun 20, 2022 at 09:05:55PM +0100, Aidan MacDonald wrote:
> Here's a bunch of cleanups for regmap-irq focused on simplifying the API
> and generalizing it a bit. It's broken up into three refactors, focusing
> on one area at a time.
This series is very large and the way it is interleaving patches for
several different subsystems adds to the difficulty managing it. As
you've identified there's several different subserieses in here, if you
need to resend any of this (I've not even started looking at the actual
patches yet) it would be easier to digest with some combination of
sending as separate serieses and reordering things so that all the
things for each subsystem are grouped together. That'd help with both
review and with merging, both large serieses and cross subsystem
dependencies tend to slow things down.
Andy Shevchenko <[email protected]> writes:
> On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
> <[email protected]> wrote:
>>
>> This flag is necessary to prepare for fixing the behavior of unmask
>> registers. Existing chips that set mask_base and unmask_base must
>> set broken_mask_unmask=1 to declare that they expect the mask bits
>
> Boolean should take true/false.
>
>> will be inverted in both registers, contrary to the usual behavior
>> of mask registers.
>
>> diff --git a/include/linux/regmap.h b/include/linux/regmap.h
>> index ee2567a0465c..21a70fd99493 100644
>> --- a/include/linux/regmap.h
>> +++ b/include/linux/regmap.h
>> @@ -1523,6 +1523,7 @@ struct regmap_irq_chip {
>> bool clear_on_unmask:1;
>> bool not_fixed_stride:1;
>> bool status_invert:1;
>> + bool broken_mask_unmask:1;
>
> Looking at the given context, I would group it with clean_on_unmask above.
>
> The above is weird enough on its own. Can you prepare a precursor
> patch that either drops the bit fields of booleans or moves them to
> unsigned int?
Sure.
> Note, bit fields in C are beasts when it goes to concurrent access. It
> would be nice to ensure these are not the cases of a such.
These are read-only so there's no danger here.
Andy Shevchenko <[email protected]> writes:
> On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
> <[email protected]> wrote:
>>
>> No drivers currently use mask_writeonly, and in its current form
>> it seems a bit misleading. When set, mask registers will be
>> updated with regmap_write_bits() instead of regmap_update_bits(),
>> but regmap_write_bits() still does a read-modify-write under the
>> hood. It's not a write-only operation.
>>
>> Performing a simple regmap_write() is probably more useful, since
>> it can be used for chips that have separate set & clear registers
>> for controlling mask bits. Such registers are normally volatile
>> and read as 0, so avoiding a register read minimizes bus traffic.
>
> Reading your explanations and the code, I would rather think about
> fixing the regmap_write_bits() to be writeonly op.
That's impossible without special hardware support.
> Otherwise it's unclear what's the difference between
> regmap_write_bits() vs. regmap_update_bits().
This was not obvious to me either. They're the same except in how they
issue the low-level write op -- regmap_update_bits() will only do the
write if the new value differs from the current one. regmap_write_bits()
will always do a write, even if the new value is the same.
I think the problem is lack of documentation. I only figured this out
by reading the implementation.
>> if (d->chip->mask_writeonly)
>> - return regmap_write_bits(d->map, reg, mask, val);
>> + return regmap_write(d->map, reg, val & mask);
>> else
>> return regmap_update_bits(d->map, reg, mask, val);
Mark Brown <[email protected]> writes:
> On Mon, Jun 20, 2022 at 09:05:55PM +0100, Aidan MacDonald wrote:
>
>> Here's a bunch of cleanups for regmap-irq focused on simplifying the API
>> and generalizing it a bit. It's broken up into three refactors, focusing
>> on one area at a time.
>
> This series is very large and the way it is interleaving patches for
> several different subsystems adds to the difficulty managing it. As
> you've identified there's several different subserieses in here, if you
> need to resend any of this (I've not even started looking at the actual
> patches yet) it would be easier to digest with some combination of
> sending as separate serieses and reordering things so that all the
> things for each subsystem are grouped together. That'd help with both
> review and with merging, both large serieses and cross subsystem
> dependencies tend to slow things down.
Thanks for the advice. After reading this and some of Andy's comments I
think I understand how to organize this better.
I'll send a trimmed down series including only regmap changes, and
instead of removing things right away I'll just mark them deprecated.
Then the driver patches can go by subsystem (one series per subsystem?)
and once everything is merged the deprecated stuff in regmap-irq can be
removed at a later date.
On Mon, 20 Jun 2022 21:05:55 +0100, Aidan MacDonald wrote:
> Here's a bunch of cleanups for regmap-irq focused on simplifying the API
> and generalizing it a bit. It's broken up into three refactors, focusing
> on one area at a time.
>
> * Patches 01 and 02 are straightforward bugfixes, independent of the
> rest of the series. Neither of the bugs are triggered by in-tree
> drivers but they might be worth picking up early anyhow.
>
> [...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git for-next
Thanks!
[01/49] regmap-irq: Fix a bug in regmap_irq_enable() for type_in_mask chips
commit: 485037ae9a095491beb7f893c909a76cc4f9d1e7
[02/49] regmap-irq: Fix offset/index mismatch in read_sub_irq_data()
commit: 3f05010f243be06478a9b11cfce0ce994f5a0890
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
Am 2022-06-20 22:06, schrieb Aidan MacDonald:
> An inverted mask register can be described more directly
> as an unmask register.
>
> Signed-off-by: Aidan MacDonald <[email protected]>
Reviewed-by: Michael Walle <[email protected]>
On 6/20/22 23:05, Aidan MacDonald wrote:
> We need to divide the sub-irq status register offset by register
> stride to get an index for the status buffer to avoid an out of
> bounds write when the register stride is greater than 1.
>
> Fixes: a2d21848d921 ("regmap: regmap-irq: Add main status register support")
> Signed-off-by: Aidan MacDonald <[email protected]>
> ---
> drivers/base/regmap/regmap-irq.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
> index 4f785bc7981c..a6db605707b0 100644
> --- a/drivers/base/regmap/regmap-irq.c
> +++ b/drivers/base/regmap/regmap-irq.c
> @@ -387,6 +387,7 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
> subreg = &chip->sub_reg_offsets[b];
> for (i = 0; i < subreg->num_regs; i++) {
> unsigned int offset = subreg->offset[i];
> + unsigned int index = offset / map->reg_stride;
>
> if (chip->not_fixed_stride)
> ret = regmap_read(map,
> @@ -395,7 +396,7 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
> else
> ret = regmap_read(map,
> chip->status_base + offset,
> - &data->status_buf[offset]);
> + &data->status_buf[index]);
>
> if (ret)
> break;
Reviewed-by: Matti Vaittinen <[email protected]>
--
Matti Vaittinen
Linux kernel developer at ROHM Semiconductors
Oulu Finland
~~ When things go utterly wrong vim users can always type :help! ~~
Discuss - Estimate - Plan - Report and finally accomplish this:
void do_work(int time) __attribute__ ((const));
On 6/20/22 23:06, Aidan MacDonald wrote:
> There's no need to set the flag explicitly to false, since that
> is the default value from zero initialization.
>
> Signed-off-by: Aidan MacDonald <[email protected]>
> ---
> drivers/mfd/rohm-bd718x7.c | 1 -
> 1 file changed, 1 deletion(-)
>
> diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c
> index bfd81f78beae..ad6c0971a997 100644
> --- a/drivers/mfd/rohm-bd718x7.c
> +++ b/drivers/mfd/rohm-bd718x7.c
> @@ -70,7 +70,6 @@ static struct regmap_irq_chip bd718xx_irq_chip = {
> .mask_base = BD718XX_REG_MIRQ,
> .ack_base = BD718XX_REG_IRQ,
> .init_ack_masked = true,
> - .mask_invert = false,
> };
>
> static const struct regmap_range pmic_status_range = {
Reviewed-by: Matti Vaittinen <[email protected]>
--
The Linux Kernel guy at ROHM Semiconductors
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND
~~ this year is the year of a signature writers block ~~
On Tue, Jun 21, 2022 at 10:04:59PM +0100, Aidan MacDonald wrote:
> Thanks for the advice. After reading this and some of Andy's comments I
> think I understand how to organize this better.
> I'll send a trimmed down series including only regmap changes, and
> instead of removing things right away I'll just mark them deprecated.
> Then the driver patches can go by subsystem (one series per subsystem?)
> and once everything is merged the deprecated stuff in regmap-irq can be
> removed at a later date.
That sounds like it should help a lot, thanks.
Andy Shevchenko <[email protected]> writes:
> On Tuesday, June 21, 2022, Aidan MacDonald <[email protected]> wrote:
>> Andy Shevchenko <[email protected]> writes:
>>
>> > On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
>> > <[email protected]> wrote:
>> >>
>> >> No drivers currently use mask_writeonly, and in its current form
>> >> it seems a bit misleading. When set, mask registers will be
>> >> updated with regmap_write_bits() instead of regmap_update_bits(),
>> >> but regmap_write_bits() still does a read-modify-write under the
>> >> hood. It's not a write-only operation.
>> >>
>> >> Performing a simple regmap_write() is probably more useful, since
>> >> it can be used for chips that have separate set & clear registers
>> >> for controlling mask bits. Such registers are normally volatile
>> >> and read as 0, so avoiding a register read minimizes bus traffic.
>> >
>> > Reading your explanations and the code, I would rather think about
>> > fixing the regmap_write_bits() to be writeonly op.
>>
>> That's impossible without special hardware support.
>>
>> > Otherwise it's unclear what's the difference between
>> > regmap_write_bits() vs. regmap_update_bits().
>>
>> This was not obvious to me either. They're the same except in how they
>> issue the low-level write op -- regmap_update_bits() will only do the
>> write if the new value differs from the current one. regmap_write_bits()
>> will always do a write, even if the new value is the same.
>
> Okay, it makes a lot of sense for W1C type of bits in the register.
> Also, “reading” might imply to restore last value from cache, no?
Maybe there needs to be some explanation of what the typical use case is
and why you'd choose write_bits() over update_bits(), because the more I
think about it the less clear it is. You're right that the read could be
served from a cache. But I'm not sure if a cache would be safe if even
one bit in the register is volatile, and I can't really see a use case
for write_bits() that doesn't involve volatile behavior of some sort.
In any event, I'm just going to drop this patch and the related driver
patches in favor of removing mask_writeonly entirely, since it looks
like it was never used, and after thinking about it I'm not sure what
I did helps much. If some driver needs write_bits() for mask registers
down the road it's not a big deal to add this back.
>> I think the problem is lack of documentation. I only figured this out
>> by reading the implementation.
>>
>> >> if (d->chip->mask_writeonly)
>> >> - return regmap_write_bits(d->map, reg, mask, val);
>> >> + return regmap_write(d->map, reg, val & mask);
>> >> else
>> >> return regmap_update_bits(d->map, reg, mask, val);
On Mon, Jun 20, 2022 at 09:05:57PM +0100, Aidan MacDonald wrote:
> We need to divide the sub-irq status register offset by register
> stride to get an index for the status buffer to avoid an out of
> bounds write when the register stride is greater than 1.
>
> Fixes: a2d21848d921 ("regmap: regmap-irq: Add main status register support")
> Signed-off-by: Aidan MacDonald <[email protected]>
Reviewed-by: Guru Das Srinagesh <[email protected]>
On Tue, Jun 21, 2022 at 11:35:09AM +0200, Andy Shevchenko wrote:
> On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
> <[email protected]> wrote:
> >
> > The qcom-pm8008 appears to use "1 to enable" convention for
> > enabling interrupts, with separate set and clear registers.
> > It's relying on masks and unmasks being inverted from their
>
> It relies
>
> > intuitive meaning, so it needs the broken_mask_unmask flag.
>
> How has it worked until now?
It is as Aidan rightly pointed out. When I was writing the pm8008 driver, I
found that the mask and unmask terminology used in the framework was inverted
when it came to the hardware, so I had to make do and swap them.
It works because in regmap_irq_sync_unlock(), the same mask is used to update
mask_reg and unmask_reg, except that it is inverted for updating the unmask
register. So, by just swapping which register gets updated with the plain mask
and which one gets updated with the inverted mask, I could use the framework to
accomplish the setting and clearing of the correct registers.
On Mon, Jun 20, 2022 at 09:06:15PM +0100, Aidan MacDonald wrote:
> To me "unmask" suggests that we write 1s to the register when
> an interrupt is enabled. This also makes sense because it's the
> opposite of what the "mask" register does (write 1s to disable
> an interrupt).
>
> But regmap-irq does the opposite: for a disabled interrupt, it
> writes 1s to "unmask" and 0s to "mask". This is surprising and
> deviates from the usual way mask registers are handled.
Thank you for fixing this.
>
> Additionally, mask_invert didn't interact with unmask registers
> properly -- it caused them to be ignored entirely.
>
> Fix this by making mask and unmask registers orthogonal, using
> the following behavior:
>
> * Mask registers are written with 1s for disabled interrupts.
> * Unmask registers are written with 1s for enabled interrupts.
This is more in line with my understanding of the semantics as well.
>
> This behavior supports both normal or inverted mask registers
> and separate set/clear registers via different combinations of
> mask_base/unmask_base. The mask_invert flag is made redundant,
> since an inverted mask register can be described more directly
> as an unmask register.
>
> To cope with existing drivers that rely on the old "backward"
> behavior, check for the broken_mask_unmask flag and swap the
> roles of mask/unmask registers. This is a compatibility measure
> which can be dropped once the drivers are updated to use the
> new, more consistent behavior.
>
> Signed-off-by: Aidan MacDonald <[email protected]>
> ---
> drivers/base/regmap/regmap-irq.c | 96 +++++++++++++++++---------------
> include/linux/regmap.h | 7 ++-
> 2 files changed, 55 insertions(+), 48 deletions(-)
>
> diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
> index 875415fc3133..082a2981120c 100644
> --- a/drivers/base/regmap/regmap-irq.c
> +++ b/drivers/base/regmap/regmap-irq.c
> @@ -30,6 +30,9 @@ struct regmap_irq_chip_data {
> int irq;
> int wake_count;
>
> + unsigned int mask_base;
> + unsigned int unmask_base;
> +
> void *status_reg_buf;
> unsigned int *main_status_buf;
> unsigned int *status_buf;
> @@ -95,7 +98,6 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
> struct regmap *map = d->map;
> int i, j, ret;
> u32 reg;
> - u32 unmask_offset;
> u32 val;
>
> if (d->chip->runtime_pm) {
> @@ -124,35 +126,23 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
> * suppress pointless writes.
> */
> for (i = 0; i < d->chip->num_regs; i++) {
> - if (!d->chip->mask_base)
> - continue;
> -
> - reg = sub_irq_reg(d, d->chip->mask_base, i);
> - if (d->chip->mask_invert) {
> + if (d->mask_base) {
> + reg = sub_irq_reg(d, d->mask_base, i);
> ret = regmap_irq_update_mask_bits(d, reg,
> - d->mask_buf_def[i], ~d->mask_buf[i]);
> - } else if (d->chip->unmask_base) {
> - /* set mask with mask_base register */
> + d->mask_buf_def[i], d->mask_buf[i]);
> + if (ret != 0)
> + dev_err(d->map->dev, "Failed to sync masks in %x\n",
> + reg);
> + }
> +
> + if (d->unmask_base) {
> + reg = sub_irq_reg(d, d->unmask_base, i);
> ret = regmap_irq_update_mask_bits(d, reg,
> d->mask_buf_def[i], ~d->mask_buf[i]);
> - if (ret < 0)
> - dev_err(d->map->dev,
> - "Failed to sync unmasks in %x\n",
> + if (ret != 0)
> + dev_err(d->map->dev, "Failed to sync masks in %x\n",
> reg);
> - unmask_offset = d->chip->unmask_base -
> - d->chip->mask_base;
> - /* clear mask with unmask_base register */
> - ret = regmap_irq_update_mask_bits(d,
> - reg + unmask_offset,
> - d->mask_buf_def[i],
> - d->mask_buf[i]);
> - } else {
> - ret = regmap_irq_update_mask_bits(d, reg,
> - d->mask_buf_def[i], d->mask_buf[i]);
> }
> - if (ret != 0)
> - dev_err(d->map->dev, "Failed to sync masks in %x\n",
> - reg);
>
> reg = sub_irq_reg(d, d->chip->wake_base, i);
> if (d->wake_buf) {
> @@ -634,7 +624,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
> int i;
> int ret = -ENOMEM;
> u32 reg;
> - u32 unmask_offset;
>
> if (chip->num_regs <= 0)
> return -EINVAL;
> @@ -732,6 +721,24 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
> d->chip = chip;
> d->irq_base = irq_base;
>
> + /*
> + * Swap role of mask_base and unmask_base if mask bits are inverted.
> + *
> + * Historically, chips that specify both mask_base and unmask_base
> + * got inverted mask behavior; this was arguably a bug in regmap-irq
> + * and there was no way to get the normal, non-inverted behavior.
> + * Those chips will set the broken_mask_unmask flag. They don't set
> + * mask_invert so there is no need to worry about interactions with
> + * that flag.
> + */
> + if (chip->mask_invert || chip->broken_mask_unmask) {
I'm not able to comment on whether mask_invert belongs here.
> + d->mask_base = chip->unmask_base;
> + d->unmask_base = chip->mask_base;
> + } else {
> + d->mask_base = chip->mask_base;
> + d->unmask_base = chip->unmask_base;
> + }
> +
> if (chip->irq_reg_stride)
> d->irq_reg_stride = chip->irq_reg_stride;
> else
> @@ -755,28 +762,27 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
> /* Mask all the interrupts by default */
> for (i = 0; i < chip->num_regs; i++) {
> d->mask_buf[i] = d->mask_buf_def[i];
> - if (!chip->mask_base)
> - continue;
>
> - reg = sub_irq_reg(d, d->chip->mask_base, i);
> -
> - if (chip->mask_invert)
> + if (d->mask_base) {
> + reg = sub_irq_reg(d, d->mask_base, i);
> ret = regmap_irq_update_mask_bits(d, reg,
> - d->mask_buf[i], ~d->mask_buf[i]);
> - else if (d->chip->unmask_base) {
> - unmask_offset = d->chip->unmask_base -
> - d->chip->mask_base;
> - ret = regmap_irq_update_mask_bits(d,
> - reg + unmask_offset,
> - d->mask_buf[i],
> - d->mask_buf[i]);
> - } else
> + d->mask_buf_def[i], d->mask_buf[i]);
> + if (ret != 0) {
> + dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
> + reg, ret);
> + goto err_alloc;
> + }
> + }
> +
> + if (d->unmask_base) {
This makes a lot of sense. unmask_base really needed to be handled separately
and not as an offset from mask_base.
> + reg = sub_irq_reg(d, d->unmask_base, i);
> ret = regmap_irq_update_mask_bits(d, reg,
> - d->mask_buf[i], d->mask_buf[i]);
> - if (ret != 0) {
> - dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
> - reg, ret);
> - goto err_alloc;
> + d->mask_buf_def[i], ~d->mask_buf[i]);
> + if (ret != 0) {
> + dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
> + reg, ret);
> + goto err_alloc;
> + }
> }
>
> if (!chip->init_ack_masked)
> diff --git a/include/linux/regmap.h b/include/linux/regmap.h
> index 21a70fd99493..0cf3c4a66946 100644
> --- a/include/linux/regmap.h
> +++ b/include/linux/regmap.h
> @@ -1451,10 +1451,11 @@ struct regmap_irq_sub_irq_map {
> * main_status set.
> *
> * @status_base: Base status register address.
> - * @mask_base: Base mask register address.
> + * @mask_base: Base mask register address. Mask bits are set to 1 when an
> + * interrupt is masked, 0 when unmasked.
> * @mask_writeonly: Base mask register is write only.
> - * @unmask_base: Base unmask register address. for chips who have
> - * separate mask and unmask registers
> + * @unmask_base: Base unmask register address. Unmask bits are set to 1 when
> + * an interrupt is unmasked and 0 when masked.
> * @ack_base: Base ack address. If zero then the chip is clear on read.
> * Using zero value is possible with @use_ack bit.
> * @wake_base: Base address for wake enables. If zero unsupported.
> --
> 2.35.1
>