diff mbox series

gpio: mmio: do not calculate bgpio_bits via "ngpios"

Message ID TYCP286MB089577B47D70F0AB25ABA6F5BCD52@TYCP286MB0895.JPNP286.PROD.OUTLOOK.COM
State New
Headers show
Series gpio: mmio: do not calculate bgpio_bits via "ngpios" | expand

Commit Message

Shiji Yang June 25, 2024, 1:19 a.m. UTC
bgpio_bits must be aligned with the data bus width. For example, on a
32 bit big endian system and we only have 16 GPIOs. If we only assume
bgpio_bits=16 we can never control the GPIO because the base address
is the lowest address.

low address                          high address
-------------------------------------------------
|   byte3   |   byte2   |   byte1   |   byte0   |
-------------------------------------------------
|    NaN    |    NaN    |  gpio8-15 |  gpio0-7  |
-------------------------------------------------

Fixes: 55b2395e4e92 ("gpio: mmio: handle "ngpios" properly in bgpio_init()")
Fixes: https://github.com/openwrt/openwrt/issues/15739
Reported-by: Mark Mentovai <mark@mentovai.com>
Signed-off-by: Shiji Yang <yangshiji66@outlook.com>
Suggested-By: Mark Mentovai <mark@mentovai.com>
Reviewed-by: Jonas Gorski <jonas.gorski@gmail.com>
Tested-by: Lóránd Horváth <lorand.horvath82@gmail.com>
---
 drivers/gpio/gpio-mmio.c | 2 --
 1 file changed, 2 deletions(-)

Comments

Linus Walleij June 26, 2024, 12:06 p.m. UTC | #1
Hi Shiji,

thanks for your patch!

On Tue, Jun 25, 2024 at 3:22 AM Shiji Yang <yangshiji66@outlook.com> wrote:

> bgpio_bits must be aligned with the data bus width. For example, on a
> 32 bit big endian system and we only have 16 GPIOs. If we only assume
> bgpio_bits=16 we can never control the GPIO because the base address
> is the lowest address.
>
> low address                          high address
> -------------------------------------------------
> |   byte3   |   byte2   |   byte1   |   byte0   |
> -------------------------------------------------
> |    NaN    |    NaN    |  gpio8-15 |  gpio0-7  |
> -------------------------------------------------
>
> Fixes: 55b2395e4e92 ("gpio: mmio: handle "ngpios" properly in bgpio_init()")
> Fixes: https://github.com/openwrt/openwrt/issues/15739
> Reported-by: Mark Mentovai <mark@mentovai.com>
> Signed-off-by: Shiji Yang <yangshiji66@outlook.com>
> Suggested-By: Mark Mentovai <mark@mentovai.com>
> Reviewed-by: Jonas Gorski <jonas.gorski@gmail.com>
> Tested-by: Lóránd Horváth <lorand.horvath82@gmail.com>

Commit  55b2395e4e92 also contains this:

@@ -614,10 +616,15 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev,
        gc->parent = dev;
        gc->label = dev_name(dev);
        gc->base = -1;
-       gc->ngpio = gc->bgpio_bits;
        gc->request = bgpio_request;

After this patch gc->ngpio will be unset for any GPIO chip that
provides a ngpios property, so restore the above line too.

But maybe a better fix is:

+ #include <linux/types.h>
(...)
+  else
+               gc->bgpio_bits = round_up(gc->ngpio, sizeof(phys_addr_t) * 8);

?

Yours,
Linus Walleij
Jonas Gorski June 26, 2024, 2:46 p.m. UTC | #2
Hi Linus,

On Wed, 26 Jun 2024 at 14:06, Linus Walleij <linus.walleij@linaro.org> wrote:
>
> Hi Shiji,
>
> thanks for your patch!
>
> On Tue, Jun 25, 2024 at 3:22 AM Shiji Yang <yangshiji66@outlook.com> wrote:
>
> > bgpio_bits must be aligned with the data bus width. For example, on a
> > 32 bit big endian system and we only have 16 GPIOs. If we only assume
> > bgpio_bits=16 we can never control the GPIO because the base address
> > is the lowest address.
> >
> > low address                          high address
> > -------------------------------------------------
> > |   byte3   |   byte2   |   byte1   |   byte0   |
> > -------------------------------------------------
> > |    NaN    |    NaN    |  gpio8-15 |  gpio0-7  |
> > -------------------------------------------------
> >
> > Fixes: 55b2395e4e92 ("gpio: mmio: handle "ngpios" properly in bgpio_init()")
> > Fixes: https://github.com/openwrt/openwrt/issues/15739
> > Reported-by: Mark Mentovai <mark@mentovai.com>
> > Signed-off-by: Shiji Yang <yangshiji66@outlook.com>
> > Suggested-By: Mark Mentovai <mark@mentovai.com>
> > Reviewed-by: Jonas Gorski <jonas.gorski@gmail.com>
> > Tested-by: Lóránd Horváth <lorand.horvath82@gmail.com>
>
> Commit  55b2395e4e92 also contains this:
>
> @@ -614,10 +616,15 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev,
>         gc->parent = dev;
>         gc->label = dev_name(dev);
>         gc->base = -1;
> -       gc->ngpio = gc->bgpio_bits;
>         gc->request = bgpio_request;
>
> After this patch gc->ngpio will be unset for any GPIO chip that
> provides a ngpios property, so restore the above line too.

The patch only removes a line changing gc->bgpio_bits, not gc->ngpio.
gc->ngpio is untouched.

gc->ngpio will still be set by gpiochip_get_ngpios() if there is a
ngpio property. See the context of the patch:

> @@ -619,8 +619,6 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev,
>         ret = gpiochip_get_ngpios(gc, dev); <--
>         if (ret)
>                 gc->ngpio = gc->bgpio_bits; <- and if it fails, fallback to bgpio_bits

>
> But maybe a better fix is:
>
> + #include <linux/types.h>
> (...)
> +  else
> +               gc->bgpio_bits = round_up(gc->ngpio, sizeof(phys_addr_t) * 8);
>
> ?

bgpio only supports a single register worth of gpios, so the limit is
1 * sizeof(phys_addr_t) * 8. So this would force force bgpio_bits to
sizeof(phys_addr_t) * 8. And this will break any bgpio users where the
gpio registers are less than phys_addr_t wide. Like 32 bit registers
on a 64 bit system, or 16 bit registers on 32 bit.

Therefore I think the most sane thing is to keep gc->bgpio_bits at sz * 8.

The only potentially reasonable thing that could be added here is a
check that gc->ngpio is at most bgpio_bits. But that would be an
additional check, not a fix per se.

Best Regards,
Jonas
Asmaa Mnebhi June 26, 2024, 2:59 p.m. UTC | #3
I am not sure this change is needed?
When I initially submitted " gpio: mmio: handle "ngpios" properly in bgpio_init() ", It was specifically because I have a 32bit reg access but only 16 gpios. Initially, I did not add the else and so Andy suggested to add it with the roundup_pow_of_two to stay backward compatible. If your system is a 32 bit arch and you only use 16 Gpio bits, why don't you configure that in your DTS?

Thanks.
Asmaa

>  drivers/gpio/gpio-mmio.c | 2 --
>  1 file changed, 2 deletions(-)
> 
> diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index
> 71e1af7c2184..d89e78f0ead3 100644
> --- a/drivers/gpio/gpio-mmio.c
> +++ b/drivers/gpio/gpio-mmio.c
> @@ -619,8 +619,6 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev,
>  	ret = gpiochip_get_ngpios(gc, dev);
>  	if (ret)
>  		gc->ngpio = gc->bgpio_bits;
> -	else
> -		gc->bgpio_bits = roundup_pow_of_two(round_up(gc->ngpio,
> 8));
> 
>  	ret = bgpio_setup_io(gc, dat, set, clr, flags);
>  	if (ret)
> --
> 2.45.1
Jonas Gorski June 26, 2024, 3:20 p.m. UTC | #4
On Wed, 26 Jun 2024 at 17:00, Asmaa Mnebhi <asmaa@nvidia.com> wrote:
>
> I am not sure this change is needed?
> When I initially submitted " gpio: mmio: handle "ngpios" properly in bgpio_init() ", It was specifically because I have a 32bit reg access but only 16 gpios. Initially, I did not add the else and so Andy suggested to add it with the roundup_pow_of_two to stay backward compatible. If your system is a 32 bit arch and you only use 16 Gpio bits, why don't you configure that in your DTS?

Because the registers in the datasheet are specified as 32 bit wide,
so defining them as 32 bit in the dts(i) is the most natural way of
defining them, even if they may use less than half of the register for
gpios. And on big endian systems you cannot just use smaller
accessors, you also must shift the register offsets. So this change
broke existing devicetrees.

And as other theoretical arguments against doing that, less than 32
bit accesses may be inefficient, or the bus where the registers are
may require 32 bit accesses. And finally, the caller explicitly passed
a register width via the sz argument, so we should listen to the
caller and use that, and not trying to be clever by changing the
access width based on the number of gpios. At least not unless the
caller explicitly requested that. Like e.g. make 0 a special value for
automatically calculating the number of bits based on the number of
gpios.

If you only use 16 bits of the 32 bit registers and you want to use 16
bit accessors, IMHO it's up to you to pass appropriate values to
bgpio_init(), and not hope that bgpio_init() will fix this magically
up for you.

Best Regards,
Jonas
Asmaa Mnebhi June 26, 2024, 7:59 p.m. UTC | #5
> -----Original Message-----
> From: Jonas Gorski <jonas.gorski@gmail.com>
> Sent: Wednesday, June 26, 2024 11:21 AM
> To: Asmaa Mnebhi <asmaa@nvidia.com>
> Cc: Shiji Yang <yangshiji66@outlook.com>; linux-gpio@vger.kernel.org; Linus
> Walleij <linus.walleij@linaro.org>; Bartosz Golaszewski <brgl@bgdev.pl>;
> Andy Shevchenko <andy.shevchenko@gmail.com>; linux-
> kernel@vger.kernel.org; Mark Mentovai <mark@mentovai.com>; Lóránd
> Horváth <lorand.horvath82@gmail.com>
> Subject: Re: [PATCH] gpio: mmio: do not calculate bgpio_bits via "ngpios"
> Importance: High
> 
> On Wed, 26 Jun 2024 at 17:00, Asmaa Mnebhi <asmaa@nvidia.com> wrote:
> >
> > I am not sure this change is needed?
> > When I initially submitted " gpio: mmio: handle "ngpios" properly in
> bgpio_init() ", It was specifically because I have a 32bit reg access but only 16
> gpios. Initially, I did not add the else and so Andy suggested to add it with the
> roundup_pow_of_two to stay backward compatible. If your system is a 32 bit
> arch and you only use 16 Gpio bits, why don't you configure that in your DTS?
> 
> Because the registers in the datasheet are specified as 32 bit wide, so defining
> them as 32 bit in the dts(i) is the most natural way of defining them, even if
> they may use less than half of the register for gpios. And on big endian
> systems you cannot just use smaller accessors, you also must shift the register
> offsets. So this change broke existing devicetrees.
> 
> And as other theoretical arguments against doing that, less than 32 bit
> accesses may be inefficient, or the bus where the registers are may require 32
> bit accesses. And finally, the caller explicitly passed a register width via the sz
> argument, so we should listen to the caller and use that, and not trying to be
> clever by changing the access width based on the number of gpios. At least
> not unless the caller explicitly requested that. Like e.g. make 0 a special value
> for automatically calculating the number of bits based on the number of
> gpios.
> 
> If you only use 16 bits of the 32 bit registers and you want to use 16 bit
> accessors, IMHO it's up to you to pass appropriate values to bgpio_init(), and
> not hope that bgpio_init() will fix this magically up for you.
> 

It was definitely not my intention to change/hack a 32 bits reg access to 16.
I agree with you that bgpio_bits should just not be changed. Maybe the else statement introduces a bug/hack in the case where ngpio is already a power of 2 such as 16 while the register access is a 32 bit access. In this case bgpio_bits would be 16 when it should be 32.

However, Shiji's has a bug and will break other code. My very first patch for "gpio: mmio: handle "ngpios" properly in bgpio_init()" was meant to fix the following problem (that shiji's code will reintroduce):
" bgpio_init uses "sz" argument to populate ngpio, which is not accurate.
Instead, read the "ngpios" property from the DT and if it doesn't 
exist, use the "sz" argument. With this change, drivers no longer need 
to overwrite the ngpio variable after calling bgpio_init."

My v1->v3 patches were only changing ngpio, not bgpio_bit. Please check my v3 patch: https://lore.kernel.org/lkml/202303050354.HH9DhJsr-lkp@intel.com/t/

After v3, Andy asked me to also change bgpio_bits. Please see the whole thread:

> > > >+       ret = gpiochip_get_ngpios(gc, dev);
> > > >+       if (ret)
> > >> +               gc->ngpio = gc->bgpio_bits;
> >>
> > >But this doesn't update bgpio_bits in the success case. Can you explain why
> >> it's not a problem (should be at least in the code as a comment).
>>
>> In the success rate, the bgpio_bits would also be equal to "sz * 8" anyways.
>> The argument " unsigned long sz" passed in bgpio_init is specifically for this purpose. That tells the gpio library the gpio register access size.
> >if (!is_power_of_2(sz))
>>                  return -EINVAL;
> > gc->bgpio_bits = sz * 8;
>>
> >If in the success case, we make it dependent on the ngpio value, we would need to round it up anyways to the closest (power of 2 && multiple of 8) which is the same as "sz * 8"
>> I will add a comment in the code in my next patch.

>I believe we should use only a single source for what we need. If we
> rely on ngpios, the bgpio_bits should be recalculated based on it. The
>expression doing this is very simple. Something like round_up(ngpios,
>8);

 Now, if we want to not modify bgpio_bits, we could go back to my v3 patch.
ngpio is the number of gpio pins supported while bgpio_bits is the register access bit type. These are 2 different entities.
Jonas Gorski June 26, 2024, 8:25 p.m. UTC | #6
On Wed, 26 Jun 2024 at 21:59, Asmaa Mnebhi <asmaa@nvidia.com> wrote:
>
>
>
> > -----Original Message-----
> > From: Jonas Gorski <jonas.gorski@gmail.com>
> > Sent: Wednesday, June 26, 2024 11:21 AM
> > To: Asmaa Mnebhi <asmaa@nvidia.com>
> > Cc: Shiji Yang <yangshiji66@outlook.com>; linux-gpio@vger.kernel.org; Linus
> > Walleij <linus.walleij@linaro.org>; Bartosz Golaszewski <brgl@bgdev.pl>;
> > Andy Shevchenko <andy.shevchenko@gmail.com>; linux-
> > kernel@vger.kernel.org; Mark Mentovai <mark@mentovai.com>; Lóránd
> > Horváth <lorand.horvath82@gmail.com>
> > Subject: Re: [PATCH] gpio: mmio: do not calculate bgpio_bits via "ngpios"
> > Importance: High
> >
> > On Wed, 26 Jun 2024 at 17:00, Asmaa Mnebhi <asmaa@nvidia.com> wrote:
> > >
> > > I am not sure this change is needed?
> > > When I initially submitted " gpio: mmio: handle "ngpios" properly in
> > bgpio_init() ", It was specifically because I have a 32bit reg access but only 16
> > gpios. Initially, I did not add the else and so Andy suggested to add it with the
> > roundup_pow_of_two to stay backward compatible. If your system is a 32 bit
> > arch and you only use 16 Gpio bits, why don't you configure that in your DTS?
> >
> > Because the registers in the datasheet are specified as 32 bit wide, so defining
> > them as 32 bit in the dts(i) is the most natural way of defining them, even if
> > they may use less than half of the register for gpios. And on big endian
> > systems you cannot just use smaller accessors, you also must shift the register
> > offsets. So this change broke existing devicetrees.
> >
> > And as other theoretical arguments against doing that, less than 32 bit
> > accesses may be inefficient, or the bus where the registers are may require 32
> > bit accesses. And finally, the caller explicitly passed a register width via the sz
> > argument, so we should listen to the caller and use that, and not trying to be
> > clever by changing the access width based on the number of gpios. At least
> > not unless the caller explicitly requested that. Like e.g. make 0 a special value
> > for automatically calculating the number of bits based on the number of
> > gpios.
> >
> > If you only use 16 bits of the 32 bit registers and you want to use 16 bit
> > accessors, IMHO it's up to you to pass appropriate values to bgpio_init(), and
> > not hope that bgpio_init() will fix this magically up for you.
> >
>
> It was definitely not my intention to change/hack a 32 bits reg access to 16.
> I agree with you that bgpio_bits should just not be changed. Maybe the else statement introduces a bug/hack in the case where ngpio is already a power of 2 such as 16 while the register access is a 32 bit access. In this case bgpio_bits would be 16 when it should be 32.

It definitely does, as currently ngpio 1 - 8 will cause bgpio_bits
will be set to 8, leading to 8 bit accesses, 9 - 16 will be 16 bit
accesses etc.

>
> However, Shiji's has a bug and will break other code. My very first patch for "gpio: mmio: handle "ngpios" properly in bgpio_init()" was meant to fix the following problem (that shiji's code will reintroduce):
> " bgpio_init uses "sz" argument to populate ngpio, which is not accurate.
> Instead, read the "ngpios" property from the DT and if it doesn't
> exist, use the "sz" argument. With this change, drivers no longer need
> to overwrite the ngpio variable after calling bgpio_init."

And this is a very nice thing to have! No objections here.

>
> My v1->v3 patches were only changing ngpio, not bgpio_bit. Please check my v3 patch: https://lore.kernel.org/lkml/202303050354.HH9DhJsr-lkp@intel.com/t/
>
> After v3, Andy asked me to also change bgpio_bits. Please see the whole thread:
>
> > > > >+       ret = gpiochip_get_ngpios(gc, dev);
> > > > >+       if (ret)
> > > >> +               gc->ngpio = gc->bgpio_bits;
> > >>
> > > >But this doesn't update bgpio_bits in the success case. Can you explain why
> > >> it's not a problem (should be at least in the code as a comment).
> >>
> >> In the success rate, the bgpio_bits would also be equal to "sz * 8" anyways.
> >> The argument " unsigned long sz" passed in bgpio_init is specifically for this purpose. That tells the gpio library the gpio register access size.
> > >if (!is_power_of_2(sz))
> >>                  return -EINVAL;
> > > gc->bgpio_bits = sz * 8;
> >>
> > >If in the success case, we make it dependent on the ngpio value, we would need to round it up anyways to the closest (power of 2 && multiple of 8) which is the same as "sz * 8"
> >> I will add a comment in the code in my next patch.
>
> >I believe we should use only a single source for what we need. If we
> > rely on ngpios, the bgpio_bits should be recalculated based on it. The
> >expression doing this is very simple. Something like round_up(ngpios,
> >8);
>

Right, mistakes happen. Also I don't want to blame anyone, because it
happens to work fine on little endian systems, and big endian systems
are so rare it's easy to forget that they exist ;-)

>  Now, if we want to not modify bgpio_bits, we could go back to my v3 patch.
> ngpio is the number of gpio pins supported while bgpio_bits is the register access bit type. These are 2 different entities.

Exactly. And AFAICT this patch does exactly this, restoring the code
to the state of v3.

Best Regards,
Jonas
Asmaa Mnebhi June 26, 2024, 9:45 p.m. UTC | #7
> > >I believe we should use only a single source for what we need. If we
> > >rely on ngpios, the bgpio_bits should be recalculated based on it.
> > >The expression doing this is very simple. Something like
> > >round_up(ngpios, 8);
> >
> 
> Right, mistakes happen. Also I don't want to blame anyone, because it
> happens to work fine on little endian systems, and big endian systems are so
> rare it's easy to forget that they exist ;-)
> 
> >  Now, if we want to not modify bgpio_bits, we could go back to my v3 patch.
> > ngpio is the number of gpio pins supported while bgpio_bits is the register
> access bit type. These are 2 different entities.
> 
> Exactly. And AFAICT this patch does exactly this, restoring the code to the
> state of v3.
> 
LGTM then 😊. Thanks!
Linus Walleij July 3, 2024, 12:38 p.m. UTC | #8
On Tue, Jun 25, 2024 at 3:22 AM Shiji Yang <yangshiji66@outlook.com> wrote:

> bgpio_bits must be aligned with the data bus width. For example, on a
> 32 bit big endian system and we only have 16 GPIOs. If we only assume
> bgpio_bits=16 we can never control the GPIO because the base address
> is the lowest address.
>
> low address                          high address
> -------------------------------------------------
> |   byte3   |   byte2   |   byte1   |   byte0   |
> -------------------------------------------------
> |    NaN    |    NaN    |  gpio8-15 |  gpio0-7  |
> -------------------------------------------------
>
> Fixes: 55b2395e4e92 ("gpio: mmio: handle "ngpios" properly in bgpio_init()")
> Fixes: https://github.com/openwrt/openwrt/issues/15739
> Reported-by: Mark Mentovai <mark@mentovai.com>
> Signed-off-by: Shiji Yang <yangshiji66@outlook.com>
> Suggested-By: Mark Mentovai <mark@mentovai.com>
> Reviewed-by: Jonas Gorski <jonas.gorski@gmail.com>
> Tested-by: Lóránd Horváth <lorand.horvath82@gmail.com>

I'm convinced this is the right thing to do. (By Jonas Gorski.)
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>

Bart, can you apply it for fixes?
(Or for -next if you wanna be really careful.)

Yours,
Linus Walleij
Bartosz Golaszewski July 3, 2024, 1:28 p.m. UTC | #9
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>


On Tue, 25 Jun 2024 09:19:49 +0800, Shiji Yang wrote:
> bgpio_bits must be aligned with the data bus width. For example, on a
> 32 bit big endian system and we only have 16 GPIOs. If we only assume
> bgpio_bits=16 we can never control the GPIO because the base address
> is the lowest address.
> 
> low address                          high address
> -------------------------------------------------
> |   byte3   |   byte2   |   byte1   |   byte0   |
> -------------------------------------------------
> |    NaN    |    NaN    |  gpio8-15 |  gpio0-7  |
> -------------------------------------------------
> 
> [...]

Applied, thanks!

[1/1] gpio: mmio: do not calculate bgpio_bits via "ngpios"
      commit: f07798d7bb9c46d17d80103fb772fd2c75d47919

Best regards,
diff mbox series

Patch

diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c
index 71e1af7c2184..d89e78f0ead3 100644
--- a/drivers/gpio/gpio-mmio.c
+++ b/drivers/gpio/gpio-mmio.c
@@ -619,8 +619,6 @@  int bgpio_init(struct gpio_chip *gc, struct device *dev,
 	ret = gpiochip_get_ngpios(gc, dev);
 	if (ret)
 		gc->ngpio = gc->bgpio_bits;
-	else
-		gc->bgpio_bits = roundup_pow_of_two(round_up(gc->ngpio, 8));
 
 	ret = bgpio_setup_io(gc, dat, set, clr, flags);
 	if (ret)