mbox series

[00/49] regmap-irq cleanups and refactoring

Message ID 20220620200644.1961936-1-aidanmacdonald.0x0@gmail.com
Headers show
Series regmap-irq cleanups and refactoring | expand

Message

Aidan MacDonald June 20, 2022, 8:05 p.m. UTC
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(-)

Comments

Andy Shevchenko June 21, 2022, 9:22 a.m. UTC | #1
On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
<aidanmacdonald.0x0@gmail.com> 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;
> +               }
Andy Shevchenko June 21, 2022, 9:29 a.m. UTC | #2
On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
<aidanmacdonald.0x0@gmail.com> 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);
Andy Shevchenko June 21, 2022, 9:35 a.m. UTC | #3
On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
<aidanmacdonald.0x0@gmail.com> 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.
Andy Shevchenko June 21, 2022, 9:42 a.m. UTC | #4
On Mon, Jun 20, 2022 at 10:10 PM Aidan MacDonald
<aidanmacdonald.0x0@gmail.com> wrote:
>
> An inverted mask register can be described more directly
> as an unmask register.

Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>

> Signed-off-by: Aidan MacDonald <aidanmacdonald.0x0@gmail.com>
> ---
>  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
>
Aidan MacDonald June 21, 2022, 9:04 p.m. UTC | #5
Mark Brown <broonie@kernel.org> 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.
Aidan MacDonald June 21, 2022, 9:13 p.m. UTC | #6
Andy Shevchenko <andy.shevchenko@gmail.com> writes:

> On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
> <aidanmacdonald.0x0@gmail.com> 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);
Andy Shevchenko June 21, 2022, 10:42 p.m. UTC | #7
On Tuesday, June 21, 2022, Aidan MacDonald <aidanmacdonald.0x0@gmail.com>
wrote:

>
> Andy Shevchenko <andy.shevchenko@gmail.com> writes:
>
> > On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
> > <aidanmacdonald.0x0@gmail.com> 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?


>
> 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);
>
Michael Walle June 23, 2022, 6:33 a.m. UTC | #8
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 <aidanmacdonald.0x0@gmail.com>

Reviewed-by: Michael Walle <michael@walle.cc>
Matti Vaittinen June 23, 2022, 9:03 a.m. UTC | #9
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 <aidanmacdonald.0x0@gmail.com>
> ---
>   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 <mazziesaccount@gmail.com>