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:25 a.m. UTC | #1
On Mon, Jun 20, 2022 at 10:07 PM Aidan MacDonald
<aidanmacdonald.0x0@gmail.com> 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!
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:33 a.m. UTC | #3
On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
<aidanmacdonald.0x0@gmail.com> 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.
Andy Shevchenko June 21, 2022, 9:35 a.m. UTC | #4
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 | #5
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
>
Mark Brown June 21, 2022, 5:08 p.m. UTC | #6
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.
Aidan MacDonald June 21, 2022, 9:04 p.m. UTC | #7
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:07 p.m. UTC | #8
Andy Shevchenko <andy.shevchenko@gmail.com> writes:

> On Mon, Jun 20, 2022 at 10:08 PM Aidan MacDonald
> <aidanmacdonald.0x0@gmail.com> 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.
Aidan MacDonald June 21, 2022, 9:13 p.m. UTC | #9
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);
Michael Walle June 23, 2022, 6:33 a.m. UTC | #10
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>
Mark Brown June 23, 2022, 1:18 p.m. UTC | #11
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.
Aidan MacDonald June 23, 2022, 8:54 p.m. UTC | #12
Andy Shevchenko <andy.shevchenko@gmail.com> writes:

> 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?

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);