diff mbox series

[RFC,PATH,2/2] gpio: starfive-jh7100: Add StarFive JH7100 GPIO driver

Message ID 20210701002037.912625-3-drew@beagleboard.org (mailing list archive)
State New, archived
Headers show
Series gpio: starfive-jh7100: Add StarFive JH7100 GPIO bindings and driver | expand

Commit Message

Drew Fustini July 1, 2021, 12:20 a.m. UTC
Add GPIO driver for the StarFive JH7100 SoC [1] used on the
BeagleV Starlight JH7100 board [2].

[1] https://github.com/starfive-tech/beaglev_doc/
[2] https://github.com/beagleboard/beaglev-starlight

Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
Signed-off-by: Drew Fustini <drew@beagleboard.org>
---
 MAINTAINERS                         |   8 +
 drivers/gpio/Kconfig                |   8 +
 drivers/gpio/Makefile               |   1 +
 drivers/gpio/gpio-starfive-jh7100.c | 425 ++++++++++++++++++++++++++++
 4 files changed, 442 insertions(+)
 create mode 100644 drivers/gpio/gpio-starfive-jh7100.c

Comments

Bin Meng July 1, 2021, 2:25 a.m. UTC | #1
On Thu, Jul 1, 2021 at 8:23 AM Drew Fustini <drew@beagleboard.org> wrote:
>
> Add GPIO driver for the StarFive JH7100 SoC [1] used on the
> BeagleV Starlight JH7100 board [2].
>
> [1] https://github.com/starfive-tech/beaglev_doc/
> [2] https://github.com/beagleboard/beaglev-starlight
>
> Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
> Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
> Signed-off-by: Drew Fustini <drew@beagleboard.org>
> ---
>  MAINTAINERS                         |   8 +
>  drivers/gpio/Kconfig                |   8 +
>  drivers/gpio/Makefile               |   1 +
>  drivers/gpio/gpio-starfive-jh7100.c | 425 ++++++++++++++++++++++++++++
>  4 files changed, 442 insertions(+)
>  create mode 100644 drivers/gpio/gpio-starfive-jh7100.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index bc0ceef87b73..04fccc2ceffa 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -17423,6 +17423,14 @@ S:     Supported
>  T:     git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
>  F:     drivers/staging/
>
> +SIFVE JH7100 SOC GPIO DRIVER

typo of SIFIVE, but it should be STARFIVE

> +M:     Drew Fustini <drew@beagleboard.org>
> +M:     Huan Feng <huan.feng@starfivetech.com>
> +L:     linux-riscv@lists.infradead.org
> +L:     linux-gpio@vger.kernel.org
> +F:     Documentation/devicetree/bindings/gpio/starfive,jh7100-gpio.yaml
> +F:     drivers/gpio/gpio-starfive-jh7100.c
> +

[snip]

Regards,
Bin
Michael Walle July 1, 2021, 6:39 a.m. UTC | #2
Hi Drew,

Am 2021-07-01 02:20, schrieb Drew Fustini:
> Add GPIO driver for the StarFive JH7100 SoC [1] used on the
> BeagleV Starlight JH7100 board [2].
> 
> [1] https://github.com/starfive-tech/beaglev_doc/
> [2] https://github.com/beagleboard/beaglev-starlight
> 
> Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
> Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
> Signed-off-by: Drew Fustini <drew@beagleboard.org>

Could this driver use GPIO_REGMAP and REGMAP_IRQ? See
drivers/gpio/gpio-sl28cpld.c for an example.

-michael
Drew Fustini July 1, 2021, 8:33 p.m. UTC | #3
On Thu, Jul 01, 2021 at 08:39:40AM +0200, Michael Walle wrote:
> Hi Drew,
> 
> Am 2021-07-01 02:20, schrieb Drew Fustini:
> > Add GPIO driver for the StarFive JH7100 SoC [1] used on the
> > BeagleV Starlight JH7100 board [2].
> > 
> > [1] https://github.com/starfive-tech/beaglev_doc/
> > [2] https://github.com/beagleboard/beaglev-starlight
> > 
> > Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
> > Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
> > Signed-off-by: Drew Fustini <drew@beagleboard.org>
> 
> Could this driver use GPIO_REGMAP and REGMAP_IRQ? See
> drivers/gpio/gpio-sl28cpld.c for an example.
> 
> -michael

Thank you for the suggestion.  I am not familiar with GPIO_REGMAP and
REGMAP_IRQ so I will read about it.  Is the advantage is that is helps
to reduce code duplication by using an abstraction?

I did notice that the gpio-sifive.c driver used regmap_update_bits() and
regmap_write().

I suppose that is better than writel_relaxed() and iowrite32() which
this RFC driver does?

thanks,
drew
Drew Fustini July 1, 2021, 8:44 p.m. UTC | #4
On Thu, Jul 01, 2021 at 10:25:12AM +0800, Bin Meng wrote:
> On Thu, Jul 1, 2021 at 8:23 AM Drew Fustini <drew@beagleboard.org> wrote:
> >
> > Add GPIO driver for the StarFive JH7100 SoC [1] used on the
> > BeagleV Starlight JH7100 board [2].
> >
> > [1] https://github.com/starfive-tech/beaglev_doc/
> > [2] https://github.com/beagleboard/beaglev-starlight
> >
> > Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
> > Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
> > Signed-off-by: Drew Fustini <drew@beagleboard.org>
> > ---
> >  MAINTAINERS                         |   8 +
> >  drivers/gpio/Kconfig                |   8 +
> >  drivers/gpio/Makefile               |   1 +
> >  drivers/gpio/gpio-starfive-jh7100.c | 425 ++++++++++++++++++++++++++++
> >  4 files changed, 442 insertions(+)
> >  create mode 100644 drivers/gpio/gpio-starfive-jh7100.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index bc0ceef87b73..04fccc2ceffa 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -17423,6 +17423,14 @@ S:     Supported
> >  T:     git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
> >  F:     drivers/staging/
> >
> > +SIFVE JH7100 SOC GPIO DRIVER
> 
> typo of SIFIVE, but it should be STARFIVE

Thank you!  My eyes should have caught that.

-Drew
Michael Walle July 2, 2021, 2:59 p.m. UTC | #5
Hi Drew,

Am 2021-07-01 22:33, schrieb Drew Fustini:
> On Thu, Jul 01, 2021 at 08:39:40AM +0200, Michael Walle wrote:
>> Hi Drew,
>> 
>> Am 2021-07-01 02:20, schrieb Drew Fustini:
>> > Add GPIO driver for the StarFive JH7100 SoC [1] used on the
>> > BeagleV Starlight JH7100 board [2].
>> >
>> > [1] https://github.com/starfive-tech/beaglev_doc/
>> > [2] https://github.com/beagleboard/beaglev-starlight
>> >
>> > Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
>> > Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
>> > Signed-off-by: Drew Fustini <drew@beagleboard.org>
>> 
>> Could this driver use GPIO_REGMAP and REGMAP_IRQ? See
>> drivers/gpio/gpio-sl28cpld.c for an example.
>> 
>> -michael
> 
> Thank you for the suggestion.  I am not familiar with GPIO_REGMAP and
> REGMAP_IRQ so I will read about it.  Is the advantage is that is helps
> to reduce code duplication by using an abstraction?

Yes, I've looked briefly at your patch and it seemed that GPIO_REGMAP
might fit here which will reduce code.

> I did notice that the gpio-sifive.c driver used regmap_update_bits() 
> and
> regmap_write().
> 
> I suppose that is better than writel_relaxed() and iowrite32() which
> this RFC driver does?

Its just another abstraction layer in between. For MMIO it will also
end up using some variant of the above (see regmap-mmio.c). But if you
use regmap, you can also use REGMAP_IRQ which might also be a fit
for your GPIO controller and thus don't have to implement your own
versions for the irq_chip ops.

-michael
Andy Shevchenko July 2, 2021, 4:03 p.m. UTC | #6
On Thu, Jul 1, 2021 at 3:23 AM Drew Fustini <drew@beagleboard.org> wrote:
>
> Add GPIO driver for the StarFive JH7100 SoC [1] used on the
> BeagleV Starlight JH7100 board [2].
>
> [1] https://github.com/starfive-tech/beaglev_doc/
> [2] https://github.com/beagleboard/beaglev-starlight

> Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
> Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
> Signed-off-by: Drew Fustini <drew@beagleboard.org>

Seems some Co-developed-by are missing.

Brief look into the code brings the Q. Can't you utilize gpio-regmap
here? Why not?
Drew Fustini July 2, 2021, 9 p.m. UTC | #7
On Thu, Jul 01, 2021 at 08:39:40AM +0200, Michael Walle wrote:
> Hi Drew,
> 
> Am 2021-07-01 02:20, schrieb Drew Fustini:
> > Add GPIO driver for the StarFive JH7100 SoC [1] used on the
> > BeagleV Starlight JH7100 board [2].
> > 
> > [1] https://github.com/starfive-tech/beaglev_doc/
> > [2] https://github.com/beagleboard/beaglev-starlight
> > 
> > Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
> > Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
> > Signed-off-by: Drew Fustini <drew@beagleboard.org>
> 
> Could this driver use GPIO_REGMAP and REGMAP_IRQ? See
> drivers/gpio/gpio-sl28cpld.c for an example.
> 
> -michael

I looked more at the example.  Do you have a suggestion of how to handle
different types of interrupts?  

This gpio controller can handle level triggered and edge triggered.
Edge triggered can be positve, negative or both. Level trigger can be
high or low.

Thanks,
Drew
Drew Fustini July 2, 2021, 9:06 p.m. UTC | #8
On Fri, Jul 02, 2021 at 07:03:19PM +0300, Andy Shevchenko wrote:
> On Thu, Jul 1, 2021 at 3:23 AM Drew Fustini <drew@beagleboard.org> wrote:
> >
> > Add GPIO driver for the StarFive JH7100 SoC [1] used on the
> > BeagleV Starlight JH7100 board [2].
> >
> > [1] https://github.com/starfive-tech/beaglev_doc/
> > [2] https://github.com/beagleboard/beaglev-starlight
> 
> > Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
> > Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
> > Signed-off-by: Drew Fustini <drew@beagleboard.org>
> 
> Seems some Co-developed-by are missing.

Thank you for suggesting this.  Huan Feng originally wrote the driver.
Emil and I have made some changes to reorganize and clean it up for
submission.

Do you think all three of us should list Co-developed-by: for our names
in addition to the SOB?

> Brief look into the code brings the Q. Can't you utilize gpio-regmap
> here? Why not?

Michael Walle asked about this yesterday and it was my first time
looking at regmap and gpio-regmap.  I've been reading the code and it
does look like I should try convert this driver over to using
gpio-regmap.

The open question in my mind is how to handle the interrupt type (edge
trigged on positive or negative, level triggered on high or low).
Hopefully I can find some other examples that can help me think about
how to do that correctly.

Thanks,
Drew
Michael Walle July 5, 2021, 1:29 p.m. UTC | #9
Hi Drew,

Am 2021-07-02 23:06, schrieb Drew Fustini:
> On Fri, Jul 02, 2021 at 07:03:19PM +0300, Andy Shevchenko wrote:
>> On Thu, Jul 1, 2021 at 3:23 AM Drew Fustini <drew@beagleboard.org> 
>> wrote:
>> >
>> > Add GPIO driver for the StarFive JH7100 SoC [1] used on the
>> > BeagleV Starlight JH7100 board [2].
>> >
>> > [1] https://github.com/starfive-tech/beaglev_doc/
>> > [2] https://github.com/beagleboard/beaglev-starlight
>> 
>> > Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
>> > Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
>> > Signed-off-by: Drew Fustini <drew@beagleboard.org>
>> 
>> Seems some Co-developed-by are missing.
> 
> Thank you for suggesting this.  Huan Feng originally wrote the driver.
> Emil and I have made some changes to reorganize and clean it up for
> submission.
> 
> Do you think all three of us should list Co-developed-by: for our names
> in addition to the SOB?
> 
>> Brief look into the code brings the Q. Can't you utilize gpio-regmap
>> here? Why not?
> 
> Michael Walle asked about this yesterday and it was my first time
> looking at regmap and gpio-regmap.  I've been reading the code and it
> does look like I should try convert this driver over to using
> gpio-regmap.
> 
> The open question in my mind is how to handle the interrupt type (edge
> trigged on positive or negative, level triggered on high or low).
> Hopefully I can find some other examples that can help me think about
> how to do that correctly.

Have a look at include/linux/regmap.h, there is "struct 
regmap_irq_type".
If you're lucky, you can just supply the corresponding values that fits
your hardware. If it doesn't match your hardware at all, then you can
keep your own functions, or if its slightly different, then maybe you
can add support for your quirk in regmap-irq. You don't necessarily have
to use regmap-irq together with gpio-regmap. You can also just use
regmap-irq or gpio-regmap independently.

A quick grep for "type_rising_" lists drivers/mfd/max77650.c and
drivers/mfd/rohm-bd70528.c for example.

-michael
Matti Vaittinen July 5, 2021, 2:33 p.m. UTC | #10
Hi deee Ho Drew, Michael, All

On Mon, 2021-07-05 at 15:29 +0200, Michael Walle wrote:
> Hi Drew,
> 
> Am 2021-07-02 23:06, schrieb Drew Fustini:
> > On Fri, Jul 02, 2021 at 07:03:19PM +0300, Andy Shevchenko wrote:
> > > On Thu, Jul 1, 2021 at 3:23 AM Drew Fustini <drew@beagleboard.org
> > > > 
> > > wrote:
> > > > Add GPIO driver for the StarFive JH7100 SoC [1] used on the
> > > > BeagleV Starlight JH7100 board [2].
> > > > 
> > > > [1] https://github.com/starfive-tech/beaglev_doc/
> > > > [2] https://github.com/beagleboard/beaglev-starlight
> > > > Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
> > > > Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
> > > > Signed-off-by: Drew Fustini <drew@beagleboard.org>
> > > 
> > > Seems some Co-developed-by are missing.
> > 
> > Thank you for suggesting this.  Huan Feng originally wrote the
> > driver.
> > Emil and I have made some changes to reorganize and clean it up for
> > submission.
> > 
> > Do you think all three of us should list Co-developed-by: for our
> > names
> > in addition to the SOB?
> > 
> > > Brief look into the code brings the Q. Can't you utilize gpio-
> > > regmap
> > > here? Why not?
> > 
> > Michael Walle asked about this yesterday and it was my first time
> > looking at regmap and gpio-regmap.  I've been reading the code and
> > it
> > does look like I should try convert this driver over to using
> > gpio-regmap.
> > 
> > The open question in my mind is how to handle the interrupt type
> > (edge
> > trigged on positive or negative, level triggered on high or low).
> > Hopefully I can find some other examples that can help me think
> > about
> > how to do that correctly.

> regmap_irq_type".
> If you're lucky, you can just supply the corresponding values that
> fits
> your hardware.

I added some level IRQ type-configuration support to regmap_irq back
when I wrote the BD70528 support. You should be able to just fill the
bit-mask indicating IRQ types supported by your GPIO controller
hardware, and then the corresponding type register values. As far as I
remember the supported types and values are given "per IRQ". If my
memory serves me right there was a limitation that the regmap-IRQ does
not distinguish setup where GPIO controller supports rising and falling
edges - but not both. That would have required adding another type
flag.

>  If it doesn't match your hardware at all, then you can
> keep your own functions, or if its slightly different, then maybe you
> can add support for your quirk in regmap-irq. You don't necessarily
> have
> to use regmap-irq together with gpio-regmap. You can also just use
> regmap-irq or gpio-regmap independently.
> 
> A quick grep for "type_rising_" lists drivers/mfd/max77650.c and
> drivers/mfd/rohm-bd70528.c for example.

The BD70528 has not been used too much and is scheduled for removal. It
may have received only limited testing but it *should* be functional
though.

Best Regards
	Matti Vaittinen
Ley Foon Tan July 15, 2021, 1:49 a.m. UTC | #11
Hi Drew


On Thu, Jul 1, 2021 at 8:25 AM Drew Fustini <drew@beagleboard.org> wrote:
>
> Add GPIO driver for the StarFive JH7100 SoC [1] used on the
> BeagleV Starlight JH7100 board [2].
>
> [1] https://github.com/starfive-tech/beaglev_doc/
> [2] https://github.com/beagleboard/beaglev-starlight
>
> Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
> Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
> Signed-off-by: Drew Fustini <drew@beagleboard.org>
> ---
>  MAINTAINERS                         |   8 +
>  drivers/gpio/Kconfig                |   8 +
>  drivers/gpio/Makefile               |   1 +
>  drivers/gpio/gpio-starfive-jh7100.c | 425 ++++++++++++++++++++++++++++
>  4 files changed, 442 insertions(+)
>  create mode 100644 drivers/gpio/gpio-starfive-jh7100.c

[...]

> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index d7c81e1611a4..939922eaf5f3 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -132,6 +132,7 @@ obj-$(CONFIG_GPIO_SAMA5D2_PIOBU)    += gpio-sama5d2-piobu.o
>  obj-$(CONFIG_GPIO_SCH311X)             += gpio-sch311x.o
>  obj-$(CONFIG_GPIO_SCH)                 += gpio-sch.o
>  obj-$(CONFIG_GPIO_SIFIVE)              += gpio-sifive.o
> +obj-$(CONFIG_GPIO_STARFIVE_JH7100)     += gpio-starfive-jh7100.o
Sort in alphabetical order.

>  obj-$(CONFIG_GPIO_SIOX)                        += gpio-siox.o
>  obj-$(CONFIG_GPIO_SL28CPLD)            += gpio-sl28cpld.o
>  obj-$(CONFIG_GPIO_SODAVILLE)           += gpio-sodaville.o
> diff --git a/drivers/gpio/gpio-starfive-jh7100.c b/drivers/gpio/gpio-starfive-jh7100.c
> new file mode 100644
> index 000000000000..b94ebfe9eaf7
> --- /dev/null
> +++ b/drivers/gpio/gpio-starfive-jh7100.c
> @@ -0,0 +1,425 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * GPIO driver for StarFive JH7100 SoC
> + *
> + * Copyright (C) 2020 Shanghai StarFive Technology Co., Ltd.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
Include files sort in alphabetical orde too.

> +
> +/*
> + * refer to Section 12. GPIO Registers in JH7100 datasheet:
> + * https://github.com/starfive-tech/beaglev_doc
> + */
> +
> +/* global enable */
> +#define GPIO_EN                0x0
> +
> +/* interrupt type */
> +#define GPIO_IS_LOW    0x10
> +#define GPIO_IS_HIGH   0x14
> +
> +/* edge trigger interrupt type */
> +#define GPIO_IBE_LOW   0x18
> +#define GPIO_IBE_HIGH  0x1c
> +
> +/* edge trigger interrupt polarity */
> +#define GPIO_IEV_LOW   0x20
> +#define GPIO_IEV_HIGH  0x24
> +
> +/* interrupt max */
> +#define GPIO_IE_LOW    0x28
> +#define GPIO_IE_HIGH   0x2c
> +
> +/* clear edge-triggered interrupt */
> +#define GPIO_IC_LOW    0x30
> +#define GPIO_IC_HIGH   0x34
> +
> +/* edge-triggered interrupt status (read-only) */
> +#define GPIO_RIS_LOW   0x38
> +#define GPIO_RIS_HIGH  0x3c
> +
> +/* interrupt status after masking (read-only) */
> +#define GPIO_MIS_LOW   0x40
> +#define GPIO_MIS_HIGH  0x44
> +
> +/* data value of gpio */
> +#define GPIO_DIN_LOW   0x48
> +#define GPIO_DIN_HIGH  0x4c
> +
> +/* GPIO0_DOUT_CFG is 0x50, GPIOn_DOUT_CFG is 0x50+(n*8) */
> +#define GPIO_DOUT_X_REG        0x50
> +
> +/* GPIO0_DOEN_CFG is 0x54, GPIOn_DOEN_CFG is 0x54+(n*8) */
> +#define GPIO_DOEN_X_REG        0x54
> +
> +#define MAX_GPIO        64
> +
> +struct starfive_gpio {
> +       raw_spinlock_t          lock;
> +       void __iomem            *base;
> +       struct gpio_chip        gc;
> +       unsigned long           enabled;
> +       unsigned int            trigger[MAX_GPIO];
> +       unsigned int            irq_parent[MAX_GPIO];
> +};
> +
> +static int starfive_direction_input(struct gpio_chip *gc, unsigned int offset)
> +{
> +       struct starfive_gpio *chip = gpiochip_get_data(gc);
> +       unsigned long flags;
> +
> +       if (offset >= gc->ngpio)
> +               return -EINVAL;
> +
> +       raw_spin_lock_irqsave(&chip->lock, flags);
> +       writel_relaxed(0x1, chip->base + GPIO_DOEN_X_REG + offset * 8);
> +       raw_spin_unlock_irqrestore(&chip->lock, flags);
> +
> +       return 0;
> +}
> +
> +static int starfive_direction_output(struct gpio_chip *gc, unsigned int offset, int value)
> +{
> +       struct starfive_gpio *chip = gpiochip_get_data(gc);
> +       unsigned long flags;
> +
> +       if (offset >= gc->ngpio)
> +               return -EINVAL;
> +
> +       raw_spin_lock_irqsave(&chip->lock, flags);
> +       writel_relaxed(0x0, chip->base + GPIO_DOEN_X_REG + offset * 8);
> +       writel_relaxed(value, chip->base + GPIO_DOUT_X_REG + offset * 8);
> +       raw_spin_unlock_irqrestore(&chip->lock, flags);
> +
> +       return 0;
> +}
> +
> +static int starfive_get_direction(struct gpio_chip *gc, unsigned int offset)
> +{
> +       struct starfive_gpio *chip = gpiochip_get_data(gc);
> +
> +       if (offset >= gc->ngpio)
> +               return -EINVAL;
> +
> +       return readl_relaxed(chip->base + GPIO_DOEN_X_REG + offset * 8) & 0x1;
> +}
> +
> +static int starfive_get_value(struct gpio_chip *gc, unsigned int offset)
> +{
> +       struct starfive_gpio *chip = gpiochip_get_data(gc);
> +       int value;
> +
> +       if (offset >= gc->ngpio)
> +               return -EINVAL;
> +
> +       if (offset < 32) {
> +               value = readl_relaxed(chip->base + GPIO_DIN_LOW);
> +               value = (value >> offset) & 0x1;
> +       } else {
> +               value = readl_relaxed(chip->base + GPIO_DIN_HIGH);
> +               value = (value >> (offset - 32)) & 0x1;
> +       }
> +
> +       return value;
> +}
> +
> +static void starfive_set_value(struct gpio_chip *gc, unsigned int offset, int value)
> +{
> +       struct starfive_gpio *chip = gpiochip_get_data(gc);
> +       unsigned long flags;
> +
> +       if (offset >= gc->ngpio)
> +               return;
> +
> +       raw_spin_lock_irqsave(&chip->lock, flags);
> +       writel_relaxed(value, chip->base + GPIO_DOUT_X_REG + offset * 8);
> +       raw_spin_unlock_irqrestore(&chip->lock, flags);
> +}
> +
> +static void starfive_set_ie(struct starfive_gpio *chip, int offset)
> +{
> +       unsigned long flags;
> +       int old_value, new_value;
> +       int reg_offset, index;
> +
> +       if (offset < 32) {
> +               reg_offset = 0;
> +               index = offset;
> +       } else {
> +               reg_offset = 4;
> +               index = offset - 32;
> +       }
Quite a number of places do this checking/calculation, can move this
to a helper function.

> +       raw_spin_lock_irqsave(&chip->lock, flags);
> +       old_value = readl_relaxed(chip->base + GPIO_IE_LOW + reg_offset);
> +       new_value = old_value | (1 << index);
> +       writel_relaxed(new_value, chip->base + GPIO_IE_LOW + reg_offset);
> +       raw_spin_unlock_irqrestore(&chip->lock, flags);
> +}
> +
> +static int starfive_irq_set_type(struct irq_data *d, unsigned int trigger)
> +{
> +       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> +       struct starfive_gpio *chip = gpiochip_get_data(gc);
> +       int offset = irqd_to_hwirq(d);
> +       unsigned int reg_is, reg_ibe, reg_iev;
> +       int reg_offset, index;
> +
> +       if (offset < 0 || offset >= gc->ngpio)
> +               return -EINVAL;
> +
> +       if (offset < 32) {
> +               reg_offset = 0;
> +               index = offset;
> +       } else {
> +               reg_offset = 4;
> +               index = offset - 32;
> +       }
> +
> +       reg_is = readl_relaxed(chip->base + GPIO_IS_LOW + reg_offset);
> +       reg_ibe = readl_relaxed(chip->base + GPIO_IBE_LOW + reg_offset);
> +       reg_iev = readl_relaxed(chip->base + GPIO_IEV_LOW + reg_offset);
> +
> +       switch (trigger) {
> +       case IRQ_TYPE_LEVEL_HIGH:
> +               reg_is  &= ~(1 << index);
> +               reg_ibe &= ~(1 << index);
> +               reg_iev |= (1 << index);
> +               break;
> +       case IRQ_TYPE_LEVEL_LOW:
> +               reg_is  &= ~(1 << index);
> +               reg_ibe &= ~(1 << index);
> +               reg_iev &= (1 << index);
> +               break;
> +       case IRQ_TYPE_EDGE_BOTH:
> +               reg_is  |= ~(1 << index);
> +               reg_ibe |= ~(1 << index);
> +               // no need to set edge type when both
Use /**/ comment style.

> +               break;
> +       case IRQ_TYPE_EDGE_RISING:
> +               reg_is  |= ~(1 << index);
> +               reg_ibe &= ~(1 << index);
> +               reg_iev |= (1 << index);
> +               break;
> +       case IRQ_TYPE_EDGE_FALLING:
> +               reg_is  |= ~(1 << index);
> +               reg_ibe &= ~(1 << index);
> +               reg_iev &= (1 << index);
> +               break;
> +       }
> +
> +       writel_relaxed(reg_is, chip->base + GPIO_IS_LOW + reg_offset);
> +       writel_relaxed(reg_ibe, chip->base + GPIO_IBE_LOW + reg_offset);
> +       writel_relaxed(reg_iev, chip->base + GPIO_IEV_LOW + reg_offset);
> +       chip->trigger[offset] = trigger;
> +       starfive_set_ie(chip, offset);
> +       return 0;
> +}
> +
> +/* chained_irq_{enter,exit} already mask the parent */
> +static void starfive_irq_mask(struct irq_data *d)
> +{
> +       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> +       struct starfive_gpio *chip = gpiochip_get_data(gc);
> +       unsigned int value;
> +       int offset = irqd_to_hwirq(d);
> +       int reg_offset, index;
> +
> +       if (offset < 0 || offset >= gc->ngpio)
> +               return;
> +
> +       if (offset < 32) {
> +               reg_offset = 0;
> +               index = offset;
> +       } else {
> +               reg_offset = 4;
> +               index = offset - 32;
> +       }
> +
> +       value = readl_relaxed(chip->base + GPIO_IE_LOW + reg_offset);
> +       value &= ~(0x1 << index);
> +       writel_relaxed(value, chip->base + GPIO_IE_LOW + reg_offset);
> +}
> +
> +static void starfive_irq_unmask(struct irq_data *d)
> +{
> +       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> +       struct starfive_gpio *chip = gpiochip_get_data(gc);
> +       unsigned int value;
> +       int offset = irqd_to_hwirq(d);
> +       int reg_offset, index;
> +
> +       if (offset < 0 || offset >= gc->ngpio)
> +               return;
> +
> +       if (offset < 32) {
> +               reg_offset = 0;
> +               index = offset;
> +       } else {
> +               reg_offset = 4;
> +               index = offset - 32;
> +       }
> +
> +       value = readl_relaxed(chip->base + GPIO_IE_LOW + reg_offset);
> +       value |= (0x1 << index);
> +       writel_relaxed(value, chip->base + GPIO_IE_LOW + reg_offset);
> +}
> +
> +static void starfive_irq_enable(struct irq_data *d)
> +{
> +       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> +       struct starfive_gpio *chip = gpiochip_get_data(gc);
> +       int offset = irqd_to_hwirq(d);
> +
> +       starfive_irq_unmask(d);
> +       assign_bit(offset, &chip->enabled, 1);
> +}
> +
> +static void starfive_irq_disable(struct irq_data *d)
> +{
> +       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> +       struct starfive_gpio *chip = gpiochip_get_data(gc);
> +       int offset = irqd_to_hwirq(d) % MAX_GPIO; // must not fail
> +
> +       assign_bit(offset, &chip->enabled, 0);
> +       starfive_set_ie(chip, offset);
> +}
> +
> +static struct irq_chip starfive_irqchip = {
> +       .name           = "starfive-jh7100-gpio",
> +       .irq_set_type   = starfive_irq_set_type,
> +       .irq_mask       = starfive_irq_mask,
> +       .irq_unmask     = starfive_irq_unmask,
> +       .irq_enable     = starfive_irq_enable,
> +       .irq_disable    = starfive_irq_disable,
> +};
> +
> +static irqreturn_t starfive_irq_handler(int irq, void *gc)
> +{
> +       int offset;
> +       int reg_offset, index;
> +       unsigned int value;
> +       unsigned long flags;
> +       struct starfive_gpio *chip = gc;
> +
> +       for (offset = 0; offset < MAX_GPIO; offset++) {
> +               if (offset < 32) {
> +                       reg_offset = 0;
> +                       index = offset;
> +               } else {
> +                       reg_offset = 4;
> +                       index = offset - 32;
> +               }
> +
> +               raw_spin_lock_irqsave(&chip->lock, flags);
> +               value = readl_relaxed(chip->base + GPIO_MIS_LOW + reg_offset);
> +               if (value & BIT(index))
> +                       writel_relaxed(BIT(index), chip->base + GPIO_IC_LOW +
> +                                       reg_offset);
> +               raw_spin_unlock_irqrestore(&chip->lock, flags);
> +       }
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int starfive_gpio_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct starfive_gpio *chip;
> +       struct gpio_irq_chip *girq;
> +       struct resource *res;
> +       int irq, ret, ngpio;
> +
> +       chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
> +       if (!chip)
> +               return -ENOMEM;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       chip->base = devm_ioremap_resource(dev, res);
Can use device managed function devm_
devm_platform_ioremap_resource(), then combile these 2 functions into
1.

> +       if (IS_ERR(chip->base)) {
> +               dev_err(dev, "failed to allocate device memory\n");
Perhaps change "allocate" to get or ioremap.

> +               return PTR_ERR(chip->base);
> +       }
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq < 0) {
> +               dev_err(dev, "Cannot get IRQ resource\n");
> +               return irq;
> +       }
> +
> +       raw_spin_lock_init(&chip->lock);
> +       chip->gc.direction_input = starfive_direction_input;
> +       chip->gc.direction_output = starfive_direction_output;
> +       chip->gc.get_direction = starfive_get_direction;
> +       chip->gc.get = starfive_get_value;
> +       chip->gc.set = starfive_set_value;
> +       chip->gc.base = 0;
> +       chip->gc.ngpio = MAX_GPIO;
> +       chip->gc.label = dev_name(dev);
> +       chip->gc.parent = dev;
> +       chip->gc.owner = THIS_MODULE;
> +
> +       girq = &chip->gc.irq;
> +       girq->chip = &starfive_irqchip;
> +       girq->parent_handler = NULL;
> +       girq->num_parents = 0;
> +       girq->parents = NULL;
> +       girq->default_type = IRQ_TYPE_NONE;
> +       girq->handler = handle_simple_irq;
> +
> +       ret = gpiochip_add_data(&chip->gc, chip);
Use devm_version, devm_gpiochip_add_data().

> +       if (ret) {
> +               dev_err(dev, "gpiochip_add_data ret=%d!\n", ret);
> +               return ret;
> +       }
> +
> +       /* Disable all GPIO interrupts before enabling parent interrupts */
Clear any pending interrupts as well when initialization.

> +       iowrite32(0, chip->base + GPIO_IE_HIGH);
> +       iowrite32(0, chip->base + GPIO_IE_LOW);
> +       chip->enabled = 0;
> +
> +       ret = devm_request_irq(dev, irq, starfive_irq_handler, IRQF_SHARED,
> +                              dev_name(dev), chip);
> +       if (ret) {
> +               dev_err(dev, "IRQ handler registering failed (%d)\n", ret);
> +               return ret;
> +       }
> +
> +       writel_relaxed(1, chip->base + GPIO_EN);
> +
> +       dev_info(dev, "StarFive GPIO chip registered %d GPIOs\n", ngpio);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id starfive_gpio_match[] = {
> +       { .compatible = "starfive,jh7100-gpio", },
> +       { },
> +};
> +
> +static struct platform_driver starfive_gpio_driver = {
> +       .probe  = starfive_gpio_probe,
> +       .driver = {
> +               .name = "gpio_starfive_jh7100",
> +               .of_match_table = of_match_ptr(starfive_gpio_match),
> +       },
> +};
> +
> +static int __init starfive_gpio_init(void)
> +{
> +       return platform_driver_register(&starfive_gpio_driver);
> +}
> +subsys_initcall(starfive_gpio_init);
> +
> +static void __exit starfive_gpio_exit(void)
> +{
> +       platform_driver_unregister(&starfive_gpio_driver);
Do you expect GPIO driver can be removed?
The driver needs proper removal, provides .remove callback.
Example, call gpiochip_remove() , disable interrupt when removing.

> +}
> +module_exit(starfive_gpio_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Huan Feng <huan.feng@starfivetech.com>");
> +MODULE_DESCRIPTION("StarFive JH7100 GPIO driver");
> --

Regards
Ley Foon
Linus Walleij July 23, 2021, 9:04 p.m. UTC | #12
On Thu, Jul 1, 2021 at 8:39 AM Michael Walle <michael@walle.cc> wrote:
> Am 2021-07-01 02:20, schrieb Drew Fustini:
> > Add GPIO driver for the StarFive JH7100 SoC [1] used on the
> > BeagleV Starlight JH7100 board [2].
> >
> > [1] https://github.com/starfive-tech/beaglev_doc/
> > [2] https://github.com/beagleboard/beaglev-starlight
> >
> > Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
> > Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
> > Signed-off-by: Drew Fustini <drew@beagleboard.org>
>
> Could this driver use GPIO_REGMAP and REGMAP_IRQ? See
> drivers/gpio/gpio-sl28cpld.c for an example.

To me it looks just memory-mapped?

Good old gpio-mmio.c (select GPIO_GENERIC) should
suffice I think.

Drew please look at drivers/gpio/gpio-ftgpio010.c for an example
of GPIO_GENERIC calling bgpio_init() in probe().

Yours,
Linus Walleij
Drew Fustini July 26, 2021, 7:11 a.m. UTC | #13
On Fri, Jul 23, 2021 at 11:04:41PM +0200, Linus Walleij wrote:
> On Thu, Jul 1, 2021 at 8:39 AM Michael Walle <michael@walle.cc> wrote:
> > Am 2021-07-01 02:20, schrieb Drew Fustini:
> > > Add GPIO driver for the StarFive JH7100 SoC [1] used on the
> > > BeagleV Starlight JH7100 board [2].
> > >
> > > [1] https://github.com/starfive-tech/beaglev_doc/
> > > [2] https://github.com/beagleboard/beaglev-starlight
> > >
> > > Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
> > > Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
> > > Signed-off-by: Drew Fustini <drew@beagleboard.org>
> >
> > Could this driver use GPIO_REGMAP and REGMAP_IRQ? See
> > drivers/gpio/gpio-sl28cpld.c for an example.
> 
> To me it looks just memory-mapped?
> 
> Good old gpio-mmio.c (select GPIO_GENERIC) should
> suffice I think.
> 
> Drew please look at drivers/gpio/gpio-ftgpio010.c for an example
> of GPIO_GENERIC calling bgpio_init() in probe().

Thank you for the suggestion. However, I am not sure that will work for
this SoC.

The GPIO registers are described in section 12 of JH7100 datasheet [1]
and I don't think they fit the expectation of gpio-mmio.c because there
is a seperate register for each GPIO line for output data value and
output enable.

There are 64 output data config registers which are 4 bytes wide. There
are 64 output enable config registers which are 4 bytes wide too. Output
data and output enable registers for a given GPIO pad are contiguous.
GPIO0_DOUT_CFG is 0x50 and GPIO0_DOEN_CFG is 0x54 while GPIO1_DOUT_CFG
is 0x58 and GPIO1_DOEN_CFG is 0x5C. The stride between GPIO pads is
effectively 8, which yields the formula: GPIOn_DOUT_CFG is 0x50+8n.
Similarly, GPIO0_DOEN_CFG is 0x54 and thus GPIOn_DOEN_CFG is 0x54+8n.

However, GPIO input data does use just one bit for each line. GPIODIN_0
at 0x48 covers GPIO[31:0] and GPIODIN_1 at 0x4c covers GPIO[63:32].

Thus the input could work with gpio-mmio but I am not sure how to
reconcile the register-per-gpio for the output value and output enable.

Is there way a way to adapt gpio-mmio for this situation?

Thanks,
Drew

[1] https://github.com/starfive-tech/beaglev_doc
Michael Walle July 26, 2021, 7:21 a.m. UTC | #14
Hi Drew, Hi Linus,

Am 2021-07-26 09:11, schrieb Drew Fustini:
> On Fri, Jul 23, 2021 at 11:04:41PM +0200, Linus Walleij wrote:
>> On Thu, Jul 1, 2021 at 8:39 AM Michael Walle <michael@walle.cc> wrote:
>> > Am 2021-07-01 02:20, schrieb Drew Fustini:
>> > > Add GPIO driver for the StarFive JH7100 SoC [1] used on the
>> > > BeagleV Starlight JH7100 board [2].
>> > >
>> > > [1] https://github.com/starfive-tech/beaglev_doc/
>> > > [2] https://github.com/beagleboard/beaglev-starlight
>> > >
>> > > Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
>> > > Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
>> > > Signed-off-by: Drew Fustini <drew@beagleboard.org>
>> >
>> > Could this driver use GPIO_REGMAP and REGMAP_IRQ? See
>> > drivers/gpio/gpio-sl28cpld.c for an example.
>> 
>> To me it looks just memory-mapped?
>> 
>> Good old gpio-mmio.c (select GPIO_GENERIC) should
>> suffice I think.

But that doesn't mean gpio-regmap can't be used, no? Or what are
the advantages of gpio-mmio?

>> Drew please look at drivers/gpio/gpio-ftgpio010.c for an example
>> of GPIO_GENERIC calling bgpio_init() in probe().
> 
> Thank you for the suggestion. However, I am not sure that will work for
> this SoC.
> 
> The GPIO registers are described in section 12 of JH7100 datasheet [1]
> and I don't think they fit the expectation of gpio-mmio.c because there
> is a seperate register for each GPIO line for output data value and
> output enable.
> 
> There are 64 output data config registers which are 4 bytes wide. There
> are 64 output enable config registers which are 4 bytes wide too. 
> Output
> data and output enable registers for a given GPIO pad are contiguous.
> GPIO0_DOUT_CFG is 0x50 and GPIO0_DOEN_CFG is 0x54 while GPIO1_DOUT_CFG
> is 0x58 and GPIO1_DOEN_CFG is 0x5C. The stride between GPIO pads is
> effectively 8, which yields the formula: GPIOn_DOUT_CFG is 0x50+8n.
> Similarly, GPIO0_DOEN_CFG is 0x54 and thus GPIOn_DOEN_CFG is 0x54+8n.
> 
> However, GPIO input data does use just one bit for each line. GPIODIN_0
> at 0x48 covers GPIO[31:0] and GPIODIN_1 at 0x4c covers GPIO[63:32].

I'd say, that should work with the .reg_mask_xlate of the gpio-regmap.

-michael

> Thus the input could work with gpio-mmio but I am not sure how to
> reconcile the register-per-gpio for the output value and output enable.
> 
> Is there way a way to adapt gpio-mmio for this situation?
> 
> Thanks,
> Drew
> 
> [1] https://github.com/starfive-tech/beaglev_doc
Drew Fustini July 27, 2021, 5:28 a.m. UTC | #15
On Mon, Jul 26, 2021 at 09:21:31AM +0200, Michael Walle wrote:
> Hi Drew, Hi Linus,
> 
> Am 2021-07-26 09:11, schrieb Drew Fustini:
> > On Fri, Jul 23, 2021 at 11:04:41PM +0200, Linus Walleij wrote:
> > > On Thu, Jul 1, 2021 at 8:39 AM Michael Walle <michael@walle.cc> wrote:
> > > > Am 2021-07-01 02:20, schrieb Drew Fustini:
> > > > > Add GPIO driver for the StarFive JH7100 SoC [1] used on the
> > > > > BeagleV Starlight JH7100 board [2].
> > > > >
> > > > > [1] https://github.com/starfive-tech/beaglev_doc/
> > > > > [2] https://github.com/beagleboard/beaglev-starlight
> > > > >
> > > > > Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
> > > > > Signed-off-by: Huan Feng <huan.feng@starfivetech.com>
> > > > > Signed-off-by: Drew Fustini <drew@beagleboard.org>
> > > >
> > > > Could this driver use GPIO_REGMAP and REGMAP_IRQ? See
> > > > drivers/gpio/gpio-sl28cpld.c for an example.
> > > 
> > > To me it looks just memory-mapped?
> > > 
> > > Good old gpio-mmio.c (select GPIO_GENERIC) should
> > > suffice I think.
> 
> But that doesn't mean gpio-regmap can't be used, no? Or what are
> the advantages of gpio-mmio?
> 
> > > Drew please look at drivers/gpio/gpio-ftgpio010.c for an example
> > > of GPIO_GENERIC calling bgpio_init() in probe().
> > 
> > Thank you for the suggestion. However, I am not sure that will work for
> > this SoC.
> > 
> > The GPIO registers are described in section 12 of JH7100 datasheet [1]
> > and I don't think they fit the expectation of gpio-mmio.c because there
> > is a seperate register for each GPIO line for output data value and
> > output enable.
> > 
> > There are 64 output data config registers which are 4 bytes wide. There
> > are 64 output enable config registers which are 4 bytes wide too. Output
> > data and output enable registers for a given GPIO pad are contiguous.
> > GPIO0_DOUT_CFG is 0x50 and GPIO0_DOEN_CFG is 0x54 while GPIO1_DOUT_CFG
> > is 0x58 and GPIO1_DOEN_CFG is 0x5C. The stride between GPIO pads is
> > effectively 8, which yields the formula: GPIOn_DOUT_CFG is 0x50+8n.
> > Similarly, GPIO0_DOEN_CFG is 0x54 and thus GPIOn_DOEN_CFG is 0x54+8n.
> > 
> > However, GPIO input data does use just one bit for each line. GPIODIN_0
> > at 0x48 covers GPIO[31:0] and GPIODIN_1 at 0x4c covers GPIO[63:32].
> 
> I'd say, that should work with the .reg_mask_xlate of the gpio-regmap.
> 
> -michael

Thanks, yes, I think trying to figure out how .reg_mask_xlate would need
to work this SoC.  I believe these are the only two implementations.

From drivers/gpio/gpio-regmap.c:

  static int gpio_regmap_simple_xlate(struct gpio_regmap *gpio,
				      unsigned int base, unsigned int offset,
				      unsigned int *reg, unsigned int *mask)
  {
	  unsigned int line = offset % gpio->ngpio_per_reg;
	  unsigned int stride = offset / gpio->ngpio_per_reg;

	  *reg = base + stride * gpio->reg_stride;
	  *mask = BIT(line);

	  return 0;
  }

From drivers/pinctrl/bcm/pinctrl-bcm63xx.c:

  static int bcm63xx_reg_mask_xlate(struct gpio_regmap *gpio,
				    unsigned int base, unsigned int offset,
				    unsigned int *reg, unsigned int *mask)
  {
	  unsigned int line = offset % BCM63XX_BANK_GPIOS;
	  unsigned int stride = offset / BCM63XX_BANK_GPIOS;

	  *reg = base - stride * BCM63XX_BANK_SIZE;
	  *mask = BIT(line);

	  return 0;
  }

Let's say a driver calls gpio_regmap_set(chip, 0, 5) to set line 5 to
value 1.

I believe this would result in call to:

  gpio->reg_mask_xlate(gpio, gpio->reg_set_base, 5, &reg, &mask)

Then this would be called to set the register:

  regmap_update_bits(gpio->regmap, reg, mask, mask);

From datasheet section 12 [1], there are 64 output data registers which
are 4 bytes wide. There are 64 output enable registers which are also 4
bytes wide too. Output data and output enable registers for a GPIO line
are contiguous. Thus GPIO0_DOUT_CFG is 0x50 and GPIO0_DOEN_CFG is 0x54.
The forumla is GPIOn_DOUT_CFG is 0x50+8n and GPIOn_DOEN_CFG is 0x54+8n.
Thus for GPIO line 5:

  GPIO5_DOUT_CFG is 0x50 + 0x28 = 0x78
  GPIO5_DOEN_CFG is 0x54 + 0x28 = 0x7C

Enable GPIO line 5 as output by writing 0x1 to 0x7C and set output value
to 1 by writing 1 to 0x7C.

Using gpio_regmap_simple_xlate() as a template, I am thinking through
xlate for this gpio controller:


static int gpio_regmap_starfive_xlate(struct gpio_regmap *gpio,
				      unsigned int base, unsigned int offset,
				      unsigned int *reg, unsigned int *mask)
{
	// reg_set_base is passed as base
	// let reg_set_base = 0x50 (GPIO0_DOUT_CFG)
	// let gpio->reg_stride = 8
	// let offest = 5 (for gpio line 5)

	*reg = base + offset * gpio->reg_stride;
	// *reg = base:0x50 + offset:0x5 * reg_stride:0x8
	// *reg = 0x50 + 0x28
	// *reg=  0x78

	// Each gpio line has a full register, not just a bit. To output
	// a digital 1, then GPIO5_DOUT_CFG would be 0x1. To output
	// digital 0, GPIO5_DOUT_CFG would be 0x0. Thus I think the mask
	// should be the least significant bit.
	*mask = BIT(1);

	return 0;
}

Let's walk through what would happen if gpio_regmap_set() was the
caller:

static void gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
			    int val)
{
	// for gpio line, offset = 5
	// if want to set line 5 high, then val = 1
	struct gpio_regmap *gpio = gpiochip_get_data(chip);

	// reg_set_base would be set to 0x50 (GPIO0_DOUT_CFG)
	unsigned int base = gpio_regmap_addr(gpio->reg_set_base);
	unsigned int reg, mask;

	gpio->reg_mask_xlate(gpio, base /* 0x50 */, offset /* 5 */, &reg, &mask);
	if (val) /* if val is 1 */
		regmap_update_bits(gpio->regmap, reg, mask, mask);
		// if mask returned was 0x1, then this would set the
		// bit 0 in GPIO5_DOUT_CFG
	else /* if val is 0 */
		regmap_update_bits(gpio->regmap, reg, mask, 0);
		// if mask returned was 0x1, then this would clear
		// bit 0 in GPIO5_DOUT_CFG
}

Now for the output enable register GPIO5_DOEN_CFG, the output driver is
active low so 0x0 is actually enables output where as 0x1 disables
output.  Thus maybe I need to add logic like:


static int gpio_regmap_starfive_xlate(struct gpio_regmap *gpio,
				      unsigned int base, unsigned int offset,
				      unsigned int *reg, unsigned int *mask)
{
	<snip>
	if (base == GPIO0_DOUT_CFG)
		*mask = 0x1U;
	else if (base == GPIO0_DOEN_CFG)
		*bit = ~(0x1U);

	return 0;
}

What do you think of that approach?

Are there any other examples of regmap xlate that I missed?

Thanks,
Drew

[1] https://github.com/starfive-tech/beaglev_doc/blob/main/JH7100%20Data%20Sheet%20V01.01.04-EN%20(4-21-2021).pdf
Michael Walle July 28, 2021, 9:49 a.m. UTC | #16
Hi Drew,

Am 2021-07-27 07:28, schrieb Drew Fustini:
[..]
>> > > Drew please look at drivers/gpio/gpio-ftgpio010.c for an example
>> > > of GPIO_GENERIC calling bgpio_init() in probe().
>> >
>> > Thank you for the suggestion. However, I am not sure that will work for
>> > this SoC.
>> >
>> > The GPIO registers are described in section 12 of JH7100 datasheet [1]
>> > and I don't think they fit the expectation of gpio-mmio.c because there
>> > is a seperate register for each GPIO line for output data value and
>> > output enable.
>> >
>> > There are 64 output data config registers which are 4 bytes wide. There
>> > are 64 output enable config registers which are 4 bytes wide too. Output
>> > data and output enable registers for a given GPIO pad are contiguous.
>> > GPIO0_DOUT_CFG is 0x50 and GPIO0_DOEN_CFG is 0x54 while GPIO1_DOUT_CFG
>> > is 0x58 and GPIO1_DOEN_CFG is 0x5C. The stride between GPIO pads is
>> > effectively 8, which yields the formula: GPIOn_DOUT_CFG is 0x50+8n.
>> > Similarly, GPIO0_DOEN_CFG is 0x54 and thus GPIOn_DOEN_CFG is 0x54+8n.
>> >
>> > However, GPIO input data does use just one bit for each line. GPIODIN_0
>> > at 0x48 covers GPIO[31:0] and GPIODIN_1 at 0x4c covers GPIO[63:32].

Mh, I'm not sure I'm understanding the datasheet/registers. _DOUT_CFG
and _DOEN_CFG seem to specify the pad where this GPIO is mapped to.
Shouldn't this be some kind of pinctrl then? Apparently you can map
any GPIO number to any output pad, no? Or at least to all pads
which are described in Table 11-2. What happens if two different GPIOs
are mapped to the same pad? Bit 31 in these _CFG seems to be an invert
bit, but what does it invert?

Similar, the input GPIOs are connected to an output pad by all the
GPI_*_CFG registers.

To me it seems, that there two multiplexers for each GPIO, where
you can connect any GPIOn to any input pad and output pad. Sound
like a huge overkill. I must be missing something here.

But what puzzles me the most, where do I set the actual GPIO output
value?

>> I'd say, that should work with the .reg_mask_xlate of the gpio-regmap.
>> 
>> -michael
> 
> Thanks, yes, I think trying to figure out how .reg_mask_xlate would 
> need
> to work this SoC.  I believe these are the only two implementations.
> 
> From drivers/gpio/gpio-regmap.c:
> 
>   static int gpio_regmap_simple_xlate(struct gpio_regmap *gpio,
> 				      unsigned int base, unsigned int offset,
> 				      unsigned int *reg, unsigned int *mask)
>   {
> 	  unsigned int line = offset % gpio->ngpio_per_reg;
> 	  unsigned int stride = offset / gpio->ngpio_per_reg;
> 
> 	  *reg = base + stride * gpio->reg_stride;
> 	  *mask = BIT(line);
> 
> 	  return 0;
>   }
> 
> From drivers/pinctrl/bcm/pinctrl-bcm63xx.c:
> 
>   static int bcm63xx_reg_mask_xlate(struct gpio_regmap *gpio,
> 				    unsigned int base, unsigned int offset,
> 				    unsigned int *reg, unsigned int *mask)
>   {
> 	  unsigned int line = offset % BCM63XX_BANK_GPIOS;
> 	  unsigned int stride = offset / BCM63XX_BANK_GPIOS;
> 
> 	  *reg = base - stride * BCM63XX_BANK_SIZE;
> 	  *mask = BIT(line);
> 
> 	  return 0;
>   }
> 
> Let's say a driver calls gpio_regmap_set(chip, 0, 5) to set line 5 to
> value 1.
> 
> I believe this would result in call to:
> 
>   gpio->reg_mask_xlate(gpio, gpio->reg_set_base, 5, &reg, &mask)
> 
> Then this would be called to set the register:
> 
>   regmap_update_bits(gpio->regmap, reg, mask, mask);
> 
> From datasheet section 12 [1], there are 64 output data registers which
> are 4 bytes wide. There are 64 output enable registers which are also 4
> bytes wide too. Output data and output enable registers for a GPIO line
> are contiguous. Thus GPIO0_DOUT_CFG is 0x50 and GPIO0_DOEN_CFG is 0x54.
> The forumla is GPIOn_DOUT_CFG is 0x50+8n and GPIOn_DOEN_CFG is 0x54+8n.
> Thus for GPIO line 5:
> 
>   GPIO5_DOUT_CFG is 0x50 + 0x28 = 0x78
>   GPIO5_DOEN_CFG is 0x54 + 0x28 = 0x7C
> 
> Enable GPIO line 5 as output by writing 0x1 to 0x7C and set output 
> value
> to 1 by writing 1 to 0x7C.
> 
> Using gpio_regmap_simple_xlate() as a template, I am thinking through
> xlate for this gpio controller:
> 
> 
> static int gpio_regmap_starfive_xlate(struct gpio_regmap *gpio,
> 				      unsigned int base, unsigned int offset,
> 				      unsigned int *reg, unsigned int *mask)
> {
> 	// reg_set_base is passed as base
> 	// let reg_set_base = 0x50 (GPIO0_DOUT_CFG)
> 	// let gpio->reg_stride = 8
> 	// let offest = 5 (for gpio line 5)
> 
> 	*reg = base + offset * gpio->reg_stride;
> 	// *reg = base:0x50 + offset:0x5 * reg_stride:0x8
> 	// *reg = 0x50 + 0x28
> 	// *reg=  0x78
> 
> 	// Each gpio line has a full register, not just a bit. To output
> 	// a digital 1, then GPIO5_DOUT_CFG would be 0x1. To output
> 	// digital 0, GPIO5_DOUT_CFG would be 0x0. Thus I think the mask
> 	// should be the least significant bit.
> 	*mask = BIT(1);
> 
> 	return 0;
> }
> 
> Let's walk through what would happen if gpio_regmap_set() was the
> caller:
> 
> static void gpio_regmap_set(struct gpio_chip *chip, unsigned int 
> offset,
> 			    int val)
> {
> 	// for gpio line, offset = 5
> 	// if want to set line 5 high, then val = 1
> 	struct gpio_regmap *gpio = gpiochip_get_data(chip);
> 
> 	// reg_set_base would be set to 0x50 (GPIO0_DOUT_CFG)
> 	unsigned int base = gpio_regmap_addr(gpio->reg_set_base);
> 	unsigned int reg, mask;
> 
> 	gpio->reg_mask_xlate(gpio, base /* 0x50 */, offset /* 5 */, &reg, 
> &mask);
> 	if (val) /* if val is 1 */
> 		regmap_update_bits(gpio->regmap, reg, mask, mask);
> 		// if mask returned was 0x1, then this would set the
> 		// bit 0 in GPIO5_DOUT_CFG
> 	else /* if val is 0 */
> 		regmap_update_bits(gpio->regmap, reg, mask, 0);
> 		// if mask returned was 0x1, then this would clear
> 		// bit 0 in GPIO5_DOUT_CFG
> }
> 
> Now for the output enable register GPIO5_DOEN_CFG, the output driver is
> active low so 0x0 is actually enables output where as 0x1 disables
> output.  Thus maybe I need to add logic like:
> 
> 
> static int gpio_regmap_starfive_xlate(struct gpio_regmap *gpio,
> 				      unsigned int base, unsigned int offset,
> 				      unsigned int *reg, unsigned int *mask)
> {
> 	<snip>
> 	if (base == GPIO0_DOUT_CFG)
> 		*mask = 0x1U;
> 	else if (base == GPIO0_DOEN_CFG)
> 		*bit = ~(0x1U);
> 
> 	return 0;
> }
> 
> What do you think of that approach?

I'm also not opposed to add a new flag to gpio-regmap which
invert the value itself.

But the idea was that you can differentiate in _xlate() by the
base register offset, like you already did:

static int gpio_regmap_starfive_xlate(struct gpio_regmap *gpio,
				      unsigned int base, unsigned int offset,
				      unsigned int *reg, unsigned int *mask)
{
	switch (base) {
	case GPIO0_DOUT_CFG:
		/* do some custom mapping just for DOUT_CFG */
	case GPIO0_DOEN_CFG:
		/* do some custom mapping just for DOEN_CFG */
	default:
		/* do normal mapping */
}

> Are there any other examples of regmap xlate that I missed?

No there aren't much yet. Usually the simple one is enough.

-michael

> [1] 
> https://github.com/starfive-tech/beaglev_doc/blob/main/JH7100%20Data%20Sheet%20V01.01.04-EN%20(4-21-2021).pdf
Emil Renner Berthing July 28, 2021, 10:59 a.m. UTC | #17
On Wed, 28 Jul 2021 at 11:49, Michael Walle <michael@walle.cc> wrote:
> Hi Drew,
> Am 2021-07-27 07:28, schrieb Drew Fustini:
> [..]
> >> > > Drew please look at drivers/gpio/gpio-ftgpio010.c for an example
> >> > > of GPIO_GENERIC calling bgpio_init() in probe().
> >> >
> >> > Thank you for the suggestion. However, I am not sure that will work for
> >> > this SoC.
> >> >
> >> > The GPIO registers are described in section 12 of JH7100 datasheet [1]
> >> > and I don't think they fit the expectation of gpio-mmio.c because there
> >> > is a seperate register for each GPIO line for output data value and
> >> > output enable.
> >> >
> >> > There are 64 output data config registers which are 4 bytes wide. There
> >> > are 64 output enable config registers which are 4 bytes wide too. Output
> >> > data and output enable registers for a given GPIO pad are contiguous.
> >> > GPIO0_DOUT_CFG is 0x50 and GPIO0_DOEN_CFG is 0x54 while GPIO1_DOUT_CFG
> >> > is 0x58 and GPIO1_DOEN_CFG is 0x5C. The stride between GPIO pads is
> >> > effectively 8, which yields the formula: GPIOn_DOUT_CFG is 0x50+8n.
> >> > Similarly, GPIO0_DOEN_CFG is 0x54 and thus GPIOn_DOEN_CFG is 0x54+8n.
> >> >
> >> > However, GPIO input data does use just one bit for each line. GPIODIN_0
> >> > at 0x48 covers GPIO[31:0] and GPIODIN_1 at 0x4c covers GPIO[63:32].
>
> Mh, I'm not sure I'm understanding the datasheet/registers. _DOUT_CFG
> and _DOEN_CFG seem to specify the pad where this GPIO is mapped to.
> Shouldn't this be some kind of pinctrl then? Apparently you can map
> any GPIO number to any output pad, no? Or at least to all pads
> which are described in Table 11-2. What happens if two different GPIOs
> are mapped to the same pad? Bit 31 in these _CFG seems to be an invert
> bit, but what does it invert?
>
> Similar, the input GPIOs are connected to an output pad by all the
> GPI_*_CFG registers.
>
> To me it seems, that there two multiplexers for each GPIO, where
> you can connect any GPIOn to any input pad and output pad. Sound
> like a huge overkill. I must be missing something here.
>
> But what puzzles me the most, where do I set the actual GPIO output
> value?

Yeah, it's a little confusing. The DOUT registers choose between a number of
signals from various peripherals to control the output value of the
pin. Similarly
the DOEN registers chose between a number of signals to control the output
enable of the pin. However, two of those signals are special in that they are
constant 0 or constant 1. This is how you control the output value and output
enable from software like a regular GPIO.

You're completely right though. This ought to be managed by a proper pinctrl
driver, and I'm working on one here:
https://github.com/esmil/linux/commits/beaglev-pinctrl

> >> I'd say, that should work with the .reg_mask_xlate of the gpio-regmap.
> >>
> >> -michael
> >
> > Thanks, yes, I think trying to figure out how .reg_mask_xlate would
> > need
> > to work this SoC.  I believe these are the only two implementations.
> >
> > From drivers/gpio/gpio-regmap.c:
> >
> >   static int gpio_regmap_simple_xlate(struct gpio_regmap *gpio,
> >                                     unsigned int base, unsigned int offset,
> >                                     unsigned int *reg, unsigned int *mask)
> >   {
> >         unsigned int line = offset % gpio->ngpio_per_reg;
> >         unsigned int stride = offset / gpio->ngpio_per_reg;
> >
> >         *reg = base + stride * gpio->reg_stride;
> >         *mask = BIT(line);
> >
> >         return 0;
> >   }
> >
> > From drivers/pinctrl/bcm/pinctrl-bcm63xx.c:
> >
> >   static int bcm63xx_reg_mask_xlate(struct gpio_regmap *gpio,
> >                                   unsigned int base, unsigned int offset,
> >                                   unsigned int *reg, unsigned int *mask)
> >   {
> >         unsigned int line = offset % BCM63XX_BANK_GPIOS;
> >         unsigned int stride = offset / BCM63XX_BANK_GPIOS;
> >
> >         *reg = base - stride * BCM63XX_BANK_SIZE;
> >         *mask = BIT(line);
> >
> >         return 0;
> >   }
> >
> > Let's say a driver calls gpio_regmap_set(chip, 0, 5) to set line 5 to
> > value 1.
> >
> > I believe this would result in call to:
> >
> >   gpio->reg_mask_xlate(gpio, gpio->reg_set_base, 5, &reg, &mask)
> >
> > Then this would be called to set the register:
> >
> >   regmap_update_bits(gpio->regmap, reg, mask, mask);
> >
> > From datasheet section 12 [1], there are 64 output data registers which
> > are 4 bytes wide. There are 64 output enable registers which are also 4
> > bytes wide too. Output data and output enable registers for a GPIO line
> > are contiguous. Thus GPIO0_DOUT_CFG is 0x50 and GPIO0_DOEN_CFG is 0x54.
> > The forumla is GPIOn_DOUT_CFG is 0x50+8n and GPIOn_DOEN_CFG is 0x54+8n.
> > Thus for GPIO line 5:
> >
> >   GPIO5_DOUT_CFG is 0x50 + 0x28 = 0x78
> >   GPIO5_DOEN_CFG is 0x54 + 0x28 = 0x7C
> >
> > Enable GPIO line 5 as output by writing 0x1 to 0x7C and set output
> > value
> > to 1 by writing 1 to 0x7C.
> >
> > Using gpio_regmap_simple_xlate() as a template, I am thinking through
> > xlate for this gpio controller:
> >
> >
> > static int gpio_regmap_starfive_xlate(struct gpio_regmap *gpio,
> >                                     unsigned int base, unsigned int offset,
> >                                     unsigned int *reg, unsigned int *mask)
> > {
> >       // reg_set_base is passed as base
> >       // let reg_set_base = 0x50 (GPIO0_DOUT_CFG)
> >       // let gpio->reg_stride = 8
> >       // let offest = 5 (for gpio line 5)
> >
> >       *reg = base + offset * gpio->reg_stride;
> >       // *reg = base:0x50 + offset:0x5 * reg_stride:0x8
> >       // *reg = 0x50 + 0x28
> >       // *reg=  0x78
> >
> >       // Each gpio line has a full register, not just a bit. To output
> >       // a digital 1, then GPIO5_DOUT_CFG would be 0x1. To output
> >       // digital 0, GPIO5_DOUT_CFG would be 0x0. Thus I think the mask
> >       // should be the least significant bit.
> >       *mask = BIT(1);
> >
> >       return 0;
> > }
> >
> > Let's walk through what would happen if gpio_regmap_set() was the
> > caller:
> >
> > static void gpio_regmap_set(struct gpio_chip *chip, unsigned int
> > offset,
> >                           int val)
> > {
> >       // for gpio line, offset = 5
> >       // if want to set line 5 high, then val = 1
> >       struct gpio_regmap *gpio = gpiochip_get_data(chip);
> >
> >       // reg_set_base would be set to 0x50 (GPIO0_DOUT_CFG)
> >       unsigned int base = gpio_regmap_addr(gpio->reg_set_base);
> >       unsigned int reg, mask;
> >
> >       gpio->reg_mask_xlate(gpio, base /* 0x50 */, offset /* 5 */, &reg,
> > &mask);
> >       if (val) /* if val is 1 */
> >               regmap_update_bits(gpio->regmap, reg, mask, mask);
> >               // if mask returned was 0x1, then this would set the
> >               // bit 0 in GPIO5_DOUT_CFG
> >       else /* if val is 0 */
> >               regmap_update_bits(gpio->regmap, reg, mask, 0);
> >               // if mask returned was 0x1, then this would clear
> >               // bit 0 in GPIO5_DOUT_CFG
> > }
> >
> > Now for the output enable register GPIO5_DOEN_CFG, the output driver is
> > active low so 0x0 is actually enables output where as 0x1 disables
> > output.  Thus maybe I need to add logic like:
> >
> >
> > static int gpio_regmap_starfive_xlate(struct gpio_regmap *gpio,
> >                                     unsigned int base, unsigned int offset,
> >                                     unsigned int *reg, unsigned int *mask)
> > {
> >       <snip>
> >       if (base == GPIO0_DOUT_CFG)
> >               *mask = 0x1U;
> >       else if (base == GPIO0_DOEN_CFG)
> >               *bit = ~(0x1U);
> >
> >       return 0;
> > }
> >
> > What do you think of that approach?
>
> I'm also not opposed to add a new flag to gpio-regmap which
> invert the value itself.
>
> But the idea was that you can differentiate in _xlate() by the
> base register offset, like you already did:
>
> static int gpio_regmap_starfive_xlate(struct gpio_regmap *gpio,
>                                       unsigned int base, unsigned int offset,
>                                       unsigned int *reg, unsigned int *mask)
> {
>         switch (base) {
>         case GPIO0_DOUT_CFG:
>                 /* do some custom mapping just for DOUT_CFG */
>         case GPIO0_DOEN_CFG:
>                 /* do some custom mapping just for DOEN_CFG */
>         default:
>                 /* do normal mapping */
> }
>
> > Are there any other examples of regmap xlate that I missed?
>
> No there aren't much yet. Usually the simple one is enough.
>
> -michael
>
> > [1]
> > https://github.com/starfive-tech/beaglev_doc/blob/main/JH7100%20Data%20Sheet%20V01.01.04-EN%20(4-21-2021).pdf
>
Michael Walle July 28, 2021, 11:19 a.m. UTC | #18
Am 2021-07-28 12:59, schrieb Emil Renner Berthing:
> On Wed, 28 Jul 2021 at 11:49, Michael Walle <michael@walle.cc> wrote:
>> Hi Drew,
>> Am 2021-07-27 07:28, schrieb Drew Fustini:
>> [..]
>> >> > > Drew please look at drivers/gpio/gpio-ftgpio010.c for an example
>> >> > > of GPIO_GENERIC calling bgpio_init() in probe().
>> >> >
>> >> > Thank you for the suggestion. However, I am not sure that will work for
>> >> > this SoC.
>> >> >
>> >> > The GPIO registers are described in section 12 of JH7100 datasheet [1]
>> >> > and I don't think they fit the expectation of gpio-mmio.c because there
>> >> > is a seperate register for each GPIO line for output data value and
>> >> > output enable.
>> >> >
>> >> > There are 64 output data config registers which are 4 bytes wide. There
>> >> > are 64 output enable config registers which are 4 bytes wide too. Output
>> >> > data and output enable registers for a given GPIO pad are contiguous.
>> >> > GPIO0_DOUT_CFG is 0x50 and GPIO0_DOEN_CFG is 0x54 while GPIO1_DOUT_CFG
>> >> > is 0x58 and GPIO1_DOEN_CFG is 0x5C. The stride between GPIO pads is
>> >> > effectively 8, which yields the formula: GPIOn_DOUT_CFG is 0x50+8n.
>> >> > Similarly, GPIO0_DOEN_CFG is 0x54 and thus GPIOn_DOEN_CFG is 0x54+8n.
>> >> >
>> >> > However, GPIO input data does use just one bit for each line. GPIODIN_0
>> >> > at 0x48 covers GPIO[31:0] and GPIODIN_1 at 0x4c covers GPIO[63:32].
>> 
>> Mh, I'm not sure I'm understanding the datasheet/registers. _DOUT_CFG
>> and _DOEN_CFG seem to specify the pad where this GPIO is mapped to.
>> Shouldn't this be some kind of pinctrl then? Apparently you can map
>> any GPIO number to any output pad, no? Or at least to all pads
>> which are described in Table 11-2. What happens if two different GPIOs
>> are mapped to the same pad? Bit 31 in these _CFG seems to be an invert
>> bit, but what does it invert?
>> 
>> Similar, the input GPIOs are connected to an output pad by all the
>> GPI_*_CFG registers.
>> 
>> To me it seems, that there two multiplexers for each GPIO, where
>> you can connect any GPIOn to any input pad and output pad. Sound
>> like a huge overkill. I must be missing something here.
>> 
>> But what puzzles me the most, where do I set the actual GPIO output
>> value?
> 
> Yeah, it's a little confusing. The DOUT registers choose between a 
> number of
> signals from various peripherals to control the output value of the
> pin. Similarly
> the DOEN registers chose between a number of signals to control the 
> output
> enable of the pin. However, two of those signals are special in that 
> they are
> constant 0 or constant 1. This is how you control the output value and 
> output
> enable from software like a regular GPIO.
> 
> You're completely right though. This ought to be managed by a proper 
> pinctrl
> driver, and I'm working on one here:
> https://github.com/esmil/linux/commits/beaglev-pinctrl

Ahh, I see. So for the non-gpio function you have to set a value other
than 0 or 1, correct?

And as an implementation detail you have to set the corresponding OE
pin if the non-gpio function will need a tristate pin (or whatever).

So, the _DOUT_CFG will actually be shared between the pinctrl and the
gpio driver, right? (I haven't done anything with pinctrl, so this might
be a stupid question).

-michael
Emil Renner Berthing July 28, 2021, 11:21 a.m. UTC | #19
On Wed, 28 Jul 2021 at 13:19, Michael Walle <michael@walle.cc> wrote:
> Am 2021-07-28 12:59, schrieb Emil Renner Berthing:
> > On Wed, 28 Jul 2021 at 11:49, Michael Walle <michael@walle.cc> wrote:
> >> Hi Drew,
> >> Am 2021-07-27 07:28, schrieb Drew Fustini:
> >> [..]
> >> >> > > Drew please look at drivers/gpio/gpio-ftgpio010.c for an example
> >> >> > > of GPIO_GENERIC calling bgpio_init() in probe().
> >> >> >
> >> >> > Thank you for the suggestion. However, I am not sure that will work for
> >> >> > this SoC.
> >> >> >
> >> >> > The GPIO registers are described in section 12 of JH7100 datasheet [1]
> >> >> > and I don't think they fit the expectation of gpio-mmio.c because there
> >> >> > is a seperate register for each GPIO line for output data value and
> >> >> > output enable.
> >> >> >
> >> >> > There are 64 output data config registers which are 4 bytes wide. There
> >> >> > are 64 output enable config registers which are 4 bytes wide too. Output
> >> >> > data and output enable registers for a given GPIO pad are contiguous.
> >> >> > GPIO0_DOUT_CFG is 0x50 and GPIO0_DOEN_CFG is 0x54 while GPIO1_DOUT_CFG
> >> >> > is 0x58 and GPIO1_DOEN_CFG is 0x5C. The stride between GPIO pads is
> >> >> > effectively 8, which yields the formula: GPIOn_DOUT_CFG is 0x50+8n.
> >> >> > Similarly, GPIO0_DOEN_CFG is 0x54 and thus GPIOn_DOEN_CFG is 0x54+8n.
> >> >> >
> >> >> > However, GPIO input data does use just one bit for each line. GPIODIN_0
> >> >> > at 0x48 covers GPIO[31:0] and GPIODIN_1 at 0x4c covers GPIO[63:32].
> >>
> >> Mh, I'm not sure I'm understanding the datasheet/registers. _DOUT_CFG
> >> and _DOEN_CFG seem to specify the pad where this GPIO is mapped to.
> >> Shouldn't this be some kind of pinctrl then? Apparently you can map
> >> any GPIO number to any output pad, no? Or at least to all pads
> >> which are described in Table 11-2. What happens if two different GPIOs
> >> are mapped to the same pad? Bit 31 in these _CFG seems to be an invert
> >> bit, but what does it invert?
> >>
> >> Similar, the input GPIOs are connected to an output pad by all the
> >> GPI_*_CFG registers.
> >>
> >> To me it seems, that there two multiplexers for each GPIO, where
> >> you can connect any GPIOn to any input pad and output pad. Sound
> >> like a huge overkill. I must be missing something here.
> >>
> >> But what puzzles me the most, where do I set the actual GPIO output
> >> value?
> >
> > Yeah, it's a little confusing. The DOUT registers choose between a
> > number of
> > signals from various peripherals to control the output value of the
> > pin. Similarly
> > the DOEN registers chose between a number of signals to control the
> > output
> > enable of the pin. However, two of those signals are special in that
> > they are
> > constant 0 or constant 1. This is how you control the output value and
> > output
> > enable from software like a regular GPIO.
> >
> > You're completely right though. This ought to be managed by a proper
> > pinctrl
> > driver, and I'm working on one here:
> > https://github.com/esmil/linux/commits/beaglev-pinctrl
>
> Ahh, I see. So for the non-gpio function you have to set a value other
> than 0 or 1, correct?
>
> And as an implementation detail you have to set the corresponding OE
> pin if the non-gpio function will need a tristate pin (or whatever).
>
> So, the _DOUT_CFG will actually be shared between the pinctrl and the
> gpio driver, right? (I haven't done anything with pinctrl, so this might
> be a stupid question).

No, not a stupid question. You've got that exactly right.

/Emil
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index bc0ceef87b73..04fccc2ceffa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17423,6 +17423,14 @@  S:	Supported
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
 F:	drivers/staging/
 
+SIFVE JH7100 SOC GPIO DRIVER
+M:	Drew Fustini <drew@beagleboard.org>
+M:	Huan Feng <huan.feng@starfivetech.com>
+L:	linux-riscv@lists.infradead.org
+L:	linux-gpio@vger.kernel.org
+F:	Documentation/devicetree/bindings/gpio/starfive,jh7100-gpio.yaml
+F:	drivers/gpio/gpio-starfive-jh7100.c
+
 STARFIRE/DURALAN NETWORK DRIVER
 M:	Ion Badulescu <ionut@badula.org>
 S:	Odd Fixes
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 1dd0ec6727fd..26630e4852c0 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -542,6 +542,14 @@  config GPIO_SIFIVE
 	help
 	  Say yes here to support the GPIO device on SiFive SoCs.
 
+config GPIO_STARFIVE_JH7100
+	bool "StarFive JH7100 GPIO support"
+	depends on OF_GPIO
+	select GPIOLIB_IRQCHIP
+	default y if SOC_STARFIVE_VIC7100
+	help
+	  Say yes here to support the GPIO device on StarFive JH7100 SoC.
+
 config GPIO_SIOX
 	tristate "SIOX GPIO support"
 	depends on SIOX
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index d7c81e1611a4..939922eaf5f3 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -132,6 +132,7 @@  obj-$(CONFIG_GPIO_SAMA5D2_PIOBU)	+= gpio-sama5d2-piobu.o
 obj-$(CONFIG_GPIO_SCH311X)		+= gpio-sch311x.o
 obj-$(CONFIG_GPIO_SCH)			+= gpio-sch.o
 obj-$(CONFIG_GPIO_SIFIVE)		+= gpio-sifive.o
+obj-$(CONFIG_GPIO_STARFIVE_JH7100)	+= gpio-starfive-jh7100.o
 obj-$(CONFIG_GPIO_SIOX)			+= gpio-siox.o
 obj-$(CONFIG_GPIO_SL28CPLD)		+= gpio-sl28cpld.o
 obj-$(CONFIG_GPIO_SODAVILLE)		+= gpio-sodaville.o
diff --git a/drivers/gpio/gpio-starfive-jh7100.c b/drivers/gpio/gpio-starfive-jh7100.c
new file mode 100644
index 000000000000..b94ebfe9eaf7
--- /dev/null
+++ b/drivers/gpio/gpio-starfive-jh7100.c
@@ -0,0 +1,425 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * GPIO driver for StarFive JH7100 SoC
+ *
+ * Copyright (C) 2020 Shanghai StarFive Technology Co., Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio/driver.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+/*
+ * refer to Section 12. GPIO Registers in JH7100 datasheet:
+ * https://github.com/starfive-tech/beaglev_doc
+ */
+
+/* global enable */
+#define GPIO_EN		0x0
+
+/* interrupt type */
+#define GPIO_IS_LOW	0x10
+#define GPIO_IS_HIGH	0x14
+
+/* edge trigger interrupt type */
+#define GPIO_IBE_LOW	0x18
+#define GPIO_IBE_HIGH	0x1c
+
+/* edge trigger interrupt polarity */
+#define GPIO_IEV_LOW	0x20
+#define GPIO_IEV_HIGH	0x24
+
+/* interrupt max */
+#define GPIO_IE_LOW	0x28
+#define GPIO_IE_HIGH	0x2c
+
+/* clear edge-triggered interrupt */
+#define GPIO_IC_LOW	0x30
+#define GPIO_IC_HIGH	0x34
+
+/* edge-triggered interrupt status (read-only) */
+#define GPIO_RIS_LOW	0x38
+#define GPIO_RIS_HIGH	0x3c
+
+/* interrupt status after masking (read-only) */
+#define GPIO_MIS_LOW	0x40
+#define GPIO_MIS_HIGH	0x44
+
+/* data value of gpio */
+#define GPIO_DIN_LOW	0x48
+#define GPIO_DIN_HIGH	0x4c
+
+/* GPIO0_DOUT_CFG is 0x50, GPIOn_DOUT_CFG is 0x50+(n*8) */
+#define GPIO_DOUT_X_REG	0x50
+
+/* GPIO0_DOEN_CFG is 0x54, GPIOn_DOEN_CFG is 0x54+(n*8) */
+#define GPIO_DOEN_X_REG	0x54
+
+#define MAX_GPIO	 64
+
+struct starfive_gpio {
+	raw_spinlock_t		lock;
+	void __iomem		*base;
+	struct gpio_chip	gc;
+	unsigned long		enabled;
+	unsigned int		trigger[MAX_GPIO];
+	unsigned int		irq_parent[MAX_GPIO];
+};
+
+static int starfive_direction_input(struct gpio_chip *gc, unsigned int offset)
+{
+	struct starfive_gpio *chip = gpiochip_get_data(gc);
+	unsigned long flags;
+
+	if (offset >= gc->ngpio)
+		return -EINVAL;
+
+	raw_spin_lock_irqsave(&chip->lock, flags);
+	writel_relaxed(0x1, chip->base + GPIO_DOEN_X_REG + offset * 8);
+	raw_spin_unlock_irqrestore(&chip->lock, flags);
+
+	return 0;
+}
+
+static int starfive_direction_output(struct gpio_chip *gc, unsigned int offset, int value)
+{
+	struct starfive_gpio *chip = gpiochip_get_data(gc);
+	unsigned long flags;
+
+	if (offset >= gc->ngpio)
+		return -EINVAL;
+
+	raw_spin_lock_irqsave(&chip->lock, flags);
+	writel_relaxed(0x0, chip->base + GPIO_DOEN_X_REG + offset * 8);
+	writel_relaxed(value, chip->base + GPIO_DOUT_X_REG + offset * 8);
+	raw_spin_unlock_irqrestore(&chip->lock, flags);
+
+	return 0;
+}
+
+static int starfive_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+	struct starfive_gpio *chip = gpiochip_get_data(gc);
+
+	if (offset >= gc->ngpio)
+		return -EINVAL;
+
+	return readl_relaxed(chip->base + GPIO_DOEN_X_REG + offset * 8) & 0x1;
+}
+
+static int starfive_get_value(struct gpio_chip *gc, unsigned int offset)
+{
+	struct starfive_gpio *chip = gpiochip_get_data(gc);
+	int value;
+
+	if (offset >= gc->ngpio)
+		return -EINVAL;
+
+	if (offset < 32) {
+		value = readl_relaxed(chip->base + GPIO_DIN_LOW);
+		value = (value >> offset) & 0x1;
+	} else {
+		value = readl_relaxed(chip->base + GPIO_DIN_HIGH);
+		value = (value >> (offset - 32)) & 0x1;
+	}
+
+	return value;
+}
+
+static void starfive_set_value(struct gpio_chip *gc, unsigned int offset, int value)
+{
+	struct starfive_gpio *chip = gpiochip_get_data(gc);
+	unsigned long flags;
+
+	if (offset >= gc->ngpio)
+		return;
+
+	raw_spin_lock_irqsave(&chip->lock, flags);
+	writel_relaxed(value, chip->base + GPIO_DOUT_X_REG + offset * 8);
+	raw_spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static void starfive_set_ie(struct starfive_gpio *chip, int offset)
+{
+	unsigned long flags;
+	int old_value, new_value;
+	int reg_offset, index;
+
+	if (offset < 32) {
+		reg_offset = 0;
+		index = offset;
+	} else {
+		reg_offset = 4;
+		index = offset - 32;
+	}
+	raw_spin_lock_irqsave(&chip->lock, flags);
+	old_value = readl_relaxed(chip->base + GPIO_IE_LOW + reg_offset);
+	new_value = old_value | (1 << index);
+	writel_relaxed(new_value, chip->base + GPIO_IE_LOW + reg_offset);
+	raw_spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int starfive_irq_set_type(struct irq_data *d, unsigned int trigger)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct starfive_gpio *chip = gpiochip_get_data(gc);
+	int offset = irqd_to_hwirq(d);
+	unsigned int reg_is, reg_ibe, reg_iev;
+	int reg_offset, index;
+
+	if (offset < 0 || offset >= gc->ngpio)
+		return -EINVAL;
+
+	if (offset < 32) {
+		reg_offset = 0;
+		index = offset;
+	} else {
+		reg_offset = 4;
+		index = offset - 32;
+	}
+
+	reg_is = readl_relaxed(chip->base + GPIO_IS_LOW + reg_offset);
+	reg_ibe = readl_relaxed(chip->base + GPIO_IBE_LOW + reg_offset);
+	reg_iev = readl_relaxed(chip->base + GPIO_IEV_LOW + reg_offset);
+
+	switch (trigger) {
+	case IRQ_TYPE_LEVEL_HIGH:
+		reg_is  &= ~(1 << index);
+		reg_ibe &= ~(1 << index);
+		reg_iev |= (1 << index);
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		reg_is  &= ~(1 << index);
+		reg_ibe &= ~(1 << index);
+		reg_iev &= (1 << index);
+		break;
+	case IRQ_TYPE_EDGE_BOTH:
+		reg_is  |= ~(1 << index);
+		reg_ibe |= ~(1 << index);
+		// no need to set edge type when both
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		reg_is  |= ~(1 << index);
+		reg_ibe &= ~(1 << index);
+		reg_iev |= (1 << index);
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		reg_is  |= ~(1 << index);
+		reg_ibe &= ~(1 << index);
+		reg_iev &= (1 << index);
+		break;
+	}
+
+	writel_relaxed(reg_is, chip->base + GPIO_IS_LOW + reg_offset);
+	writel_relaxed(reg_ibe, chip->base + GPIO_IBE_LOW + reg_offset);
+	writel_relaxed(reg_iev, chip->base + GPIO_IEV_LOW + reg_offset);
+	chip->trigger[offset] = trigger;
+	starfive_set_ie(chip, offset);
+	return 0;
+}
+
+/* chained_irq_{enter,exit} already mask the parent */
+static void starfive_irq_mask(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct starfive_gpio *chip = gpiochip_get_data(gc);
+	unsigned int value;
+	int offset = irqd_to_hwirq(d);
+	int reg_offset, index;
+
+	if (offset < 0 || offset >= gc->ngpio)
+		return;
+
+	if (offset < 32) {
+		reg_offset = 0;
+		index = offset;
+	} else {
+		reg_offset = 4;
+		index = offset - 32;
+	}
+
+	value = readl_relaxed(chip->base + GPIO_IE_LOW + reg_offset);
+	value &= ~(0x1 << index);
+	writel_relaxed(value, chip->base + GPIO_IE_LOW + reg_offset);
+}
+
+static void starfive_irq_unmask(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct starfive_gpio *chip = gpiochip_get_data(gc);
+	unsigned int value;
+	int offset = irqd_to_hwirq(d);
+	int reg_offset, index;
+
+	if (offset < 0 || offset >= gc->ngpio)
+		return;
+
+	if (offset < 32) {
+		reg_offset = 0;
+		index = offset;
+	} else {
+		reg_offset = 4;
+		index = offset - 32;
+	}
+
+	value = readl_relaxed(chip->base + GPIO_IE_LOW + reg_offset);
+	value |= (0x1 << index);
+	writel_relaxed(value, chip->base + GPIO_IE_LOW + reg_offset);
+}
+
+static void starfive_irq_enable(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct starfive_gpio *chip = gpiochip_get_data(gc);
+	int offset = irqd_to_hwirq(d);
+
+	starfive_irq_unmask(d);
+	assign_bit(offset, &chip->enabled, 1);
+}
+
+static void starfive_irq_disable(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct starfive_gpio *chip = gpiochip_get_data(gc);
+	int offset = irqd_to_hwirq(d) % MAX_GPIO; // must not fail
+
+	assign_bit(offset, &chip->enabled, 0);
+	starfive_set_ie(chip, offset);
+}
+
+static struct irq_chip starfive_irqchip = {
+	.name		= "starfive-jh7100-gpio",
+	.irq_set_type	= starfive_irq_set_type,
+	.irq_mask	= starfive_irq_mask,
+	.irq_unmask	= starfive_irq_unmask,
+	.irq_enable	= starfive_irq_enable,
+	.irq_disable	= starfive_irq_disable,
+};
+
+static irqreturn_t starfive_irq_handler(int irq, void *gc)
+{
+	int offset;
+	int reg_offset, index;
+	unsigned int value;
+	unsigned long flags;
+	struct starfive_gpio *chip = gc;
+
+	for (offset = 0; offset < MAX_GPIO; offset++) {
+		if (offset < 32) {
+			reg_offset = 0;
+			index = offset;
+		} else {
+			reg_offset = 4;
+			index = offset - 32;
+		}
+
+		raw_spin_lock_irqsave(&chip->lock, flags);
+		value = readl_relaxed(chip->base + GPIO_MIS_LOW + reg_offset);
+		if (value & BIT(index))
+			writel_relaxed(BIT(index), chip->base + GPIO_IC_LOW +
+					reg_offset);
+		raw_spin_unlock_irqrestore(&chip->lock, flags);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int starfive_gpio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct starfive_gpio *chip;
+	struct gpio_irq_chip *girq;
+	struct resource *res;
+	int irq, ret, ngpio;
+
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	chip->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(chip->base)) {
+		dev_err(dev, "failed to allocate device memory\n");
+		return PTR_ERR(chip->base);
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "Cannot get IRQ resource\n");
+		return irq;
+	}
+
+	raw_spin_lock_init(&chip->lock);
+	chip->gc.direction_input = starfive_direction_input;
+	chip->gc.direction_output = starfive_direction_output;
+	chip->gc.get_direction = starfive_get_direction;
+	chip->gc.get = starfive_get_value;
+	chip->gc.set = starfive_set_value;
+	chip->gc.base = 0;
+	chip->gc.ngpio = MAX_GPIO;
+	chip->gc.label = dev_name(dev);
+	chip->gc.parent = dev;
+	chip->gc.owner = THIS_MODULE;
+
+	girq = &chip->gc.irq;
+	girq->chip = &starfive_irqchip;
+	girq->parent_handler = NULL;
+	girq->num_parents = 0;
+	girq->parents = NULL;
+	girq->default_type = IRQ_TYPE_NONE;
+	girq->handler = handle_simple_irq;
+
+	ret = gpiochip_add_data(&chip->gc, chip);
+	if (ret) {
+		dev_err(dev, "gpiochip_add_data ret=%d!\n", ret);
+		return ret;
+	}
+
+	/* Disable all GPIO interrupts before enabling parent interrupts */
+	iowrite32(0, chip->base + GPIO_IE_HIGH);
+	iowrite32(0, chip->base + GPIO_IE_LOW);
+	chip->enabled = 0;
+
+	ret = devm_request_irq(dev, irq, starfive_irq_handler, IRQF_SHARED,
+			       dev_name(dev), chip);
+	if (ret) {
+		dev_err(dev, "IRQ handler registering failed (%d)\n", ret);
+		return ret;
+	}
+
+	writel_relaxed(1, chip->base + GPIO_EN);
+
+	dev_info(dev, "StarFive GPIO chip registered %d GPIOs\n", ngpio);
+
+	return 0;
+}
+
+static const struct of_device_id starfive_gpio_match[] = {
+	{ .compatible = "starfive,jh7100-gpio", },
+	{ },
+};
+
+static struct platform_driver starfive_gpio_driver = {
+	.probe	= starfive_gpio_probe,
+	.driver	= {
+		.name = "gpio_starfive_jh7100",
+		.of_match_table = of_match_ptr(starfive_gpio_match),
+	},
+};
+
+static int __init starfive_gpio_init(void)
+{
+	return platform_driver_register(&starfive_gpio_driver);
+}
+subsys_initcall(starfive_gpio_init);
+
+static void __exit starfive_gpio_exit(void)
+{
+	platform_driver_unregister(&starfive_gpio_driver);
+}
+module_exit(starfive_gpio_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Huan Feng <huan.feng@starfivetech.com>");
+MODULE_DESCRIPTION("StarFive JH7100 GPIO driver");