diff mbox series

[v9,1/1] gpio: mpfs: add polarfire soc gpio support

Message ID 20241031-appease-purse-55145b5dfba4@spud
State New
Headers show
Series Polarfire SoC GPIO support | expand

Commit Message

Conor Dooley Oct. 31, 2024, 12:04 p.m. UTC
From: Lewis Hanly <lewis.hanly@microchip.com>

Add a driver to support the Polarfire SoC gpio controller. Interrupt
controller support is unavailable for now and will be added at a later
date.

Signed-off-by: Lewis Hanly <lewis.hanly@microchip.com>
Co-developed-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
---
v9:
- remove remove()
- drop OF_GPIO from kconfig
- add a prefix to MAX_NUM_GPIO

v8:
- drop interrupt support
- replace regular mmio acesses with regmap (nice complexity reduction)
- change mpfs_gpio_get() so that it can report non-zero when the line
  direction is output.
---
 drivers/gpio/Kconfig     |   6 ++
 drivers/gpio/Makefile    |   1 +
 drivers/gpio/gpio-mpfs.c | 170 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+)
 create mode 100644 drivers/gpio/gpio-mpfs.c

Comments

Bartosz Golaszewski Oct. 31, 2024, 1 p.m. UTC | #1
On Thu, Oct 31, 2024 at 1:04 PM Conor Dooley <conor@kernel.org> wrote:
>
> From: Lewis Hanly <lewis.hanly@microchip.com>
>
> Add a driver to support the Polarfire SoC gpio controller. Interrupt
> controller support is unavailable for now and will be added at a later
> date.
>
> Signed-off-by: Lewis Hanly <lewis.hanly@microchip.com>
> Co-developed-by: Conor Dooley <conor.dooley@microchip.com>
> Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
> ---
> v9:
> - remove remove()
> - drop OF_GPIO from kconfig
> - add a prefix to MAX_NUM_GPIO
>
> v8:
> - drop interrupt support
> - replace regular mmio acesses with regmap (nice complexity reduction)
> - change mpfs_gpio_get() so that it can report non-zero when the line
>   direction is output.
> ---
>  drivers/gpio/Kconfig     |   6 ++
>  drivers/gpio/Makefile    |   1 +
>  drivers/gpio/gpio-mpfs.c | 170 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 177 insertions(+)
>  create mode 100644 drivers/gpio/gpio-mpfs.c
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index d93cd4f722b40..811263b033c89 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -549,6 +549,12 @@ config GPIO_PL061
>         help
>           Say yes here to support the PrimeCell PL061 GPIO device.
>
> +config GPIO_POLARFIRE_SOC
> +       bool "Microchip FPGA GPIO support"
> +       select REGMAP_MMIO
> +       help
> +         Say yes here to support the GPIO controllers on Microchip FPGAs.
> +
>  config GPIO_PXA
>         bool "PXA GPIO support"
>         depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 1429e8c0229b9..fc66e6388c76c 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -133,6 +133,7 @@ obj-$(CONFIG_GPIO_PCI_IDIO_16)              += gpio-pci-idio-16.o
>  obj-$(CONFIG_GPIO_PISOSR)              += gpio-pisosr.o
>  obj-$(CONFIG_GPIO_PL061)               += gpio-pl061.o
>  obj-$(CONFIG_GPIO_PMIC_EIC_SPRD)       += gpio-pmic-eic-sprd.o
> +obj-$(CONFIG_GPIO_POLARFIRE_SOC)       += gpio-mpfs.o
>  obj-$(CONFIG_GPIO_PXA)                 += gpio-pxa.o
>  obj-$(CONFIG_GPIO_RASPBERRYPI_EXP)     += gpio-raspberrypi-exp.o
>  obj-$(CONFIG_GPIO_RC5T583)             += gpio-rc5t583.o
> diff --git a/drivers/gpio/gpio-mpfs.c b/drivers/gpio/gpio-mpfs.c
> new file mode 100644
> index 0000000000000..e5017307b0fe6
> --- /dev/null
> +++ b/drivers/gpio/gpio-mpfs.c
> @@ -0,0 +1,170 @@
> +// SPDX-License-Identifier: (GPL-2.0)
> +/*
> + * Microchip PolarFire SoC (MPFS) GPIO controller driver
> + *
> + * Copyright (c) 2018-2024 Microchip Technology Inc. and its subsidiaries
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/errno.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/init.h>
> +#include <linux/of.h>

Sorry for noticing it only now but you don't use any symbol from this
header so it can be dropped.

> +#include <linux/mod_devicetable.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/spinlock.h>
> +
> +#define MPFS_GPIO_CTRL(i)              (0x4 * (i))
> +#define MPFS_MAX_NUM_GPIO              32
> +#define MPFS_GPIO_EN_INT               3
> +#define MPFS_GPIO_EN_OUT_BUF           BIT(2)
> +#define MPFS_GPIO_EN_IN                        BIT(1)
> +#define MPFS_GPIO_EN_OUT               BIT(0)
> +#define MPFS_GPIO_DIR_MASK             GENMASK(2, 0)
> +
> +#define MPFS_GPIO_TYPE_INT_EDGE_BOTH   0x80
> +#define MPFS_GPIO_TYPE_INT_EDGE_NEG    0x60
> +#define MPFS_GPIO_TYPE_INT_EDGE_POS    0x40
> +#define MPFS_GPIO_TYPE_INT_LEVEL_LOW   0x20
> +#define MPFS_GPIO_TYPE_INT_LEVEL_HIGH  0x00
> +#define MPFS_GPIO_TYPE_INT_MASK                GENMASK(7, 5)
> +#define MPFS_IRQ_REG                   0x80
> +#define MPFS_INP_REG                   0x84
> +#define MPFS_OUTP_REG                  0x88
> +
> +struct mpfs_gpio_chip {
> +       struct clk *clk;

After the initial clk_get() you never reference it again so there's no
need to store the address.

> +       struct regmap *regs;
> +       struct gpio_chip gc;
> +};
> +
> +static const struct regmap_config mpfs_gpio_regmap_config = {
> +       .reg_bits = 32,
> +       .reg_stride = 4,
> +       .val_bits = 32,
> +};
> +
> +static int mpfs_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio_index)
> +{
> +       struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
> +
> +       regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index),
> +                          MPFS_GPIO_DIR_MASK, MPFS_GPIO_EN_IN);
> +
> +       return 0;
> +}
> +
> +static int mpfs_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio_index, int value)
> +{
> +       struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
> +
> +       regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index),
> +                          MPFS_GPIO_DIR_MASK, MPFS_GPIO_EN_IN);
> +       regmap_update_bits(mpfs_gpio->regs, MPFS_OUTP_REG, BIT(gpio_index),
> +                          value << gpio_index);
> +
> +       return 0;
> +}
> +
> +static int mpfs_gpio_get_direction(struct gpio_chip *gc,
> +                                  unsigned int gpio_index)
> +{
> +       struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
> +       unsigned int gpio_cfg;
> +
> +       regmap_read(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index), &gpio_cfg);
> +       if (gpio_cfg & MPFS_GPIO_EN_IN)
> +               return GPIO_LINE_DIRECTION_IN;
> +
> +       return GPIO_LINE_DIRECTION_OUT;
> +}
> +
> +static int mpfs_gpio_get(struct gpio_chip *gc, unsigned int gpio_index)
> +{
> +       struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
> +
> +       if (mpfs_gpio_get_direction(gc, gpio_index) == GPIO_LINE_DIRECTION_OUT)
> +               return regmap_test_bits(mpfs_gpio->regs, MPFS_OUTP_REG, BIT(gpio_index));
> +       else
> +               return regmap_test_bits(mpfs_gpio->regs, MPFS_INP_REG, BIT(gpio_index));
> +}
> +
> +static void mpfs_gpio_set(struct gpio_chip *gc, unsigned int gpio_index, int value)
> +{
> +       struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
> +
> +       mpfs_gpio_get(gc, gpio_index);
> +
> +       regmap_update_bits(mpfs_gpio->regs, MPFS_OUTP_REG, BIT(gpio_index),
> +                          value << gpio_index);
> +
> +       mpfs_gpio_get(gc, gpio_index);
> +}
> +
> +static int mpfs_gpio_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct mpfs_gpio_chip *mpfs_gpio;
> +       struct clk *clk;
> +       void __iomem *base;
> +       int ret, ngpios;
> +
> +       mpfs_gpio = devm_kzalloc(dev, sizeof(*mpfs_gpio), GFP_KERNEL);
> +       if (!mpfs_gpio)
> +               return -ENOMEM;
> +
> +       base = devm_platform_ioremap_resource(pdev, 0);
> +       if (IS_ERR(base))
> +               return dev_err_probe(dev, PTR_ERR(base), "failed to ioremap memory resource\n");
> +
> +       mpfs_gpio->regs = devm_regmap_init_mmio(dev, base, &mpfs_gpio_regmap_config);
> +       if (IS_ERR(mpfs_gpio->regs))
> +               return dev_err_probe(dev, PTR_ERR(mpfs_gpio->regs),
> +                                    "failed to initialise regmap\n");
> +
> +       clk = devm_clk_get_enabled(dev, NULL);
> +       if (IS_ERR(clk))
> +               return dev_err_probe(dev, PTR_ERR(clk), "failed to get and enable clock\n");
> +
> +       mpfs_gpio->clk = clk;
> +
> +       ngpios = MPFS_MAX_NUM_GPIO;
> +       device_property_read_u32(dev, "ngpios", &ngpios);
> +       if (ngpios > MPFS_MAX_NUM_GPIO)
> +               ngpios = MPFS_MAX_NUM_GPIO;
> +
> +       mpfs_gpio->gc.direction_input = mpfs_gpio_direction_input;
> +       mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output;
> +       mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction;
> +       mpfs_gpio->gc.get = mpfs_gpio_get;
> +       mpfs_gpio->gc.set = mpfs_gpio_set;
> +       mpfs_gpio->gc.base = -1;
> +       mpfs_gpio->gc.ngpio = ngpios;

The "ngpios" property will be parsed by GPIO core so no need to set it.

> +       mpfs_gpio->gc.label = dev_name(dev);
> +       mpfs_gpio->gc.parent = dev;
> +       mpfs_gpio->gc.owner = THIS_MODULE;
> +
> +       ret = devm_gpiochip_add_data(dev, &mpfs_gpio->gc, mpfs_gpio);
> +       if (ret)
> +               return ret;
> +
> +       platform_set_drvdata(pdev, mpfs_gpio);

There's no platform_get_drvdata() in the driver so you can drop this too.

Bart

> +
> +       return 0;
> +}
> +
> +static const struct of_device_id mpfs_gpio_of_ids[] = {
> +       { .compatible = "microchip,mpfs-gpio", },
> +       { /* end of list */ }
> +};
> +
> +static struct platform_driver mpfs_gpio_driver = {
> +       .probe = mpfs_gpio_probe,
> +       .driver = {
> +               .name = "microchip,mpfs-gpio",
> +               .of_match_table = mpfs_gpio_of_ids,
> +       },
> +};
> +builtin_platform_driver(mpfs_gpio_driver);
> --
> 2.45.2
>
Conor Dooley Nov. 1, 2024, 12:17 p.m. UTC | #2
On Thu, Oct 31, 2024 at 02:00:22PM +0100, Bartosz Golaszewski wrote:
> On Thu, Oct 31, 2024 at 1:04 PM Conor Dooley <conor@kernel.org> wrote:

> > +       mpfs_gpio->gc.direction_input = mpfs_gpio_direction_input;
> > +       mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output;
> > +       mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction;
> > +       mpfs_gpio->gc.get = mpfs_gpio_get;
> > +       mpfs_gpio->gc.set = mpfs_gpio_set;
> > +       mpfs_gpio->gc.base = -1;
> > +       mpfs_gpio->gc.ngpio = ngpios;
> 
> The "ngpios" property will be parsed by GPIO core so no need to set it.

Are you sure it'll work here? I tried dropping the ngpios parsing, but I
get:
gpiochip_add_data_with_key: GPIOs 0..-1 (20122000.gpio) failed to register, -22

That's coming from the device_property_read_u32(dev, "ngpios", &ngpios);
in gpiochip_get_ngpios(). Checking against the bluefield driver and the
code in gpiochip_add_data_with_key(), it's not immediately obvious what
I am missing.
Bartosz Golaszewski Nov. 1, 2024, 1:09 p.m. UTC | #3
On Fri, Nov 1, 2024 at 1:17 PM Conor Dooley <conor@kernel.org> wrote:
>
> On Thu, Oct 31, 2024 at 02:00:22PM +0100, Bartosz Golaszewski wrote:
> > On Thu, Oct 31, 2024 at 1:04 PM Conor Dooley <conor@kernel.org> wrote:
>
> > > +       mpfs_gpio->gc.direction_input = mpfs_gpio_direction_input;
> > > +       mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output;
> > > +       mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction;
> > > +       mpfs_gpio->gc.get = mpfs_gpio_get;
> > > +       mpfs_gpio->gc.set = mpfs_gpio_set;
> > > +       mpfs_gpio->gc.base = -1;
> > > +       mpfs_gpio->gc.ngpio = ngpios;
> >
> > The "ngpios" property will be parsed by GPIO core so no need to set it.
>
> Are you sure it'll work here? I tried dropping the ngpios parsing, but I
> get:
> gpiochip_add_data_with_key: GPIOs 0..-1 (20122000.gpio) failed to register, -22
>
> That's coming from the device_property_read_u32(dev, "ngpios", &ngpios);
> in gpiochip_get_ngpios(). Checking against the bluefield driver and the
> code in gpiochip_add_data_with_key(), it's not immediately obvious what
> I am missing.

Does dev have an fwnode correctly assigned? What does dev_fwnode(dev) return?

Bart
Conor Dooley Nov. 1, 2024, 1:37 p.m. UTC | #4
On Fri, Nov 01, 2024 at 02:09:06PM +0100, Bartosz Golaszewski wrote:
> On Fri, Nov 1, 2024 at 1:17 PM Conor Dooley <conor@kernel.org> wrote:
> >
> > On Thu, Oct 31, 2024 at 02:00:22PM +0100, Bartosz Golaszewski wrote:
> > > On Thu, Oct 31, 2024 at 1:04 PM Conor Dooley <conor@kernel.org> wrote:
> >
> > > > +       mpfs_gpio->gc.direction_input = mpfs_gpio_direction_input;
> > > > +       mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output;
> > > > +       mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction;
> > > > +       mpfs_gpio->gc.get = mpfs_gpio_get;
> > > > +       mpfs_gpio->gc.set = mpfs_gpio_set;
> > > > +       mpfs_gpio->gc.base = -1;
> > > > +       mpfs_gpio->gc.ngpio = ngpios;
> > >
> > > The "ngpios" property will be parsed by GPIO core so no need to set it.
> >
> > Are you sure it'll work here? I tried dropping the ngpios parsing, but I
> > get:
> > gpiochip_add_data_with_key: GPIOs 0..-1 (20122000.gpio) failed to register, -22
> >
> > That's coming from the device_property_read_u32(dev, "ngpios", &ngpios);
> > in gpiochip_get_ngpios(). Checking against the bluefield driver and the
> > code in gpiochip_add_data_with_key(), it's not immediately obvious what
> > I am missing.
> 
> Does dev have an fwnode correctly assigned? What does dev_fwnode(dev) return?

It's not a null pointer or something obviously wrong by virtue of being
null-adjacent, it is a virtual address but not one that %ps can identify.
Bartosz Golaszewski Nov. 1, 2024, 1:47 p.m. UTC | #5
On Fri, Nov 1, 2024 at 2:37 PM Conor Dooley <conor@kernel.org> wrote:
>
> On Fri, Nov 01, 2024 at 02:09:06PM +0100, Bartosz Golaszewski wrote:
> > On Fri, Nov 1, 2024 at 1:17 PM Conor Dooley <conor@kernel.org> wrote:
> > >
> > > On Thu, Oct 31, 2024 at 02:00:22PM +0100, Bartosz Golaszewski wrote:
> > > > On Thu, Oct 31, 2024 at 1:04 PM Conor Dooley <conor@kernel.org> wrote:
> > >
> > > > > +       mpfs_gpio->gc.direction_input = mpfs_gpio_direction_input;
> > > > > +       mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output;
> > > > > +       mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction;
> > > > > +       mpfs_gpio->gc.get = mpfs_gpio_get;
> > > > > +       mpfs_gpio->gc.set = mpfs_gpio_set;
> > > > > +       mpfs_gpio->gc.base = -1;
> > > > > +       mpfs_gpio->gc.ngpio = ngpios;
> > > >
> > > > The "ngpios" property will be parsed by GPIO core so no need to set it.
> > >
> > > Are you sure it'll work here? I tried dropping the ngpios parsing, but I
> > > get:
> > > gpiochip_add_data_with_key: GPIOs 0..-1 (20122000.gpio) failed to register, -22
> > >
> > > That's coming from the device_property_read_u32(dev, "ngpios", &ngpios);
> > > in gpiochip_get_ngpios(). Checking against the bluefield driver and the
> > > code in gpiochip_add_data_with_key(), it's not immediately obvious what
> > > I am missing.
> >
> > Does dev have an fwnode correctly assigned? What does dev_fwnode(dev) return?
>
> It's not a null pointer or something obviously wrong by virtue of being
> null-adjacent, it is a virtual address but not one that %ps can identify.

This is an OF system right? If you do dev_of_node(dev), does the
returned node->name show the OF node you expect? Does it have the
"ngpios" property? Can you read it with of_property_read_u32()?

Bart
Conor Dooley Nov. 1, 2024, 2:28 p.m. UTC | #6
On Fri, Nov 01, 2024 at 02:47:51PM +0100, Bartosz Golaszewski wrote:
> On Fri, Nov 1, 2024 at 2:37 PM Conor Dooley <conor@kernel.org> wrote:
> >
> > On Fri, Nov 01, 2024 at 02:09:06PM +0100, Bartosz Golaszewski wrote:
> > > On Fri, Nov 1, 2024 at 1:17 PM Conor Dooley <conor@kernel.org> wrote:
> > > >
> > > > On Thu, Oct 31, 2024 at 02:00:22PM +0100, Bartosz Golaszewski wrote:
> > > > > On Thu, Oct 31, 2024 at 1:04 PM Conor Dooley <conor@kernel.org> wrote:
> > > >
> > > > > > +       mpfs_gpio->gc.direction_input = mpfs_gpio_direction_input;
> > > > > > +       mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output;
> > > > > > +       mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction;
> > > > > > +       mpfs_gpio->gc.get = mpfs_gpio_get;
> > > > > > +       mpfs_gpio->gc.set = mpfs_gpio_set;
> > > > > > +       mpfs_gpio->gc.base = -1;
> > > > > > +       mpfs_gpio->gc.ngpio = ngpios;
> > > > >
> > > > > The "ngpios" property will be parsed by GPIO core so no need to set it.
> > > >
> > > > Are you sure it'll work here? I tried dropping the ngpios parsing, but I
> > > > get:
> > > > gpiochip_add_data_with_key: GPIOs 0..-1 (20122000.gpio) failed to register, -22
> > > >
> > > > That's coming from the device_property_read_u32(dev, "ngpios", &ngpios);
> > > > in gpiochip_get_ngpios(). Checking against the bluefield driver and the
> > > > code in gpiochip_add_data_with_key(), it's not immediately obvious what
> > > > I am missing.
> > >
> > > Does dev have an fwnode correctly assigned? What does dev_fwnode(dev) return?
> >
> > It's not a null pointer or something obviously wrong by virtue of being
> > null-adjacent, it is a virtual address but not one that %ps can identify.
> 
> This is an OF system right? If you do dev_of_node(dev), does the
> returned node->name show the OF node you expect?

Yes.

> Does it have the
> "ngpios" property? Can you read it with of_property_read_u32()?

No, EINVAL there too.
Bartosz Golaszewski Nov. 1, 2024, 2:32 p.m. UTC | #7
On Fri, Nov 1, 2024 at 3:28 PM Conor Dooley <conor@kernel.org> wrote:
>
> On Fri, Nov 01, 2024 at 02:47:51PM +0100, Bartosz Golaszewski wrote:
> > On Fri, Nov 1, 2024 at 2:37 PM Conor Dooley <conor@kernel.org> wrote:
> > >
> > > On Fri, Nov 01, 2024 at 02:09:06PM +0100, Bartosz Golaszewski wrote:
> > > > On Fri, Nov 1, 2024 at 1:17 PM Conor Dooley <conor@kernel.org> wrote:
> > > > >
> > > > > On Thu, Oct 31, 2024 at 02:00:22PM +0100, Bartosz Golaszewski wrote:
> > > > > > On Thu, Oct 31, 2024 at 1:04 PM Conor Dooley <conor@kernel.org> wrote:
> > > > >
> > > > > > > +       mpfs_gpio->gc.direction_input = mpfs_gpio_direction_input;
> > > > > > > +       mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output;
> > > > > > > +       mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction;
> > > > > > > +       mpfs_gpio->gc.get = mpfs_gpio_get;
> > > > > > > +       mpfs_gpio->gc.set = mpfs_gpio_set;
> > > > > > > +       mpfs_gpio->gc.base = -1;
> > > > > > > +       mpfs_gpio->gc.ngpio = ngpios;
> > > > > >
> > > > > > The "ngpios" property will be parsed by GPIO core so no need to set it.
> > > > >
> > > > > Are you sure it'll work here? I tried dropping the ngpios parsing, but I
> > > > > get:
> > > > > gpiochip_add_data_with_key: GPIOs 0..-1 (20122000.gpio) failed to register, -22
> > > > >
> > > > > That's coming from the device_property_read_u32(dev, "ngpios", &ngpios);
> > > > > in gpiochip_get_ngpios(). Checking against the bluefield driver and the
> > > > > code in gpiochip_add_data_with_key(), it's not immediately obvious what
> > > > > I am missing.
> > > >
> > > > Does dev have an fwnode correctly assigned? What does dev_fwnode(dev) return?
> > >
> > > It's not a null pointer or something obviously wrong by virtue of being
> > > null-adjacent, it is a virtual address but not one that %ps can identify.
> >
> > This is an OF system right? If you do dev_of_node(dev), does the
> > returned node->name show the OF node you expect?
>
> Yes.

I mean inside gpiochip_get_ngpios(), sorry for confusion.

>
> > Does it have the
> > "ngpios" property? Can you read it with of_property_read_u32()?
>
> No, EINVAL there too.

I assume the node is not assigned correctly. What if in your probe you
do: gc->fwnode = dev_fwnode(dev)?

Bart
Conor Dooley Nov. 1, 2024, 3:16 p.m. UTC | #8
On Fri, Nov 01, 2024 at 03:32:16PM +0100, Bartosz Golaszewski wrote:
> On Fri, Nov 1, 2024 at 3:28 PM Conor Dooley <conor@kernel.org> wrote:
> >
> > On Fri, Nov 01, 2024 at 02:47:51PM +0100, Bartosz Golaszewski wrote:
> > > On Fri, Nov 1, 2024 at 2:37 PM Conor Dooley <conor@kernel.org> wrote:
> > > >
> > > > On Fri, Nov 01, 2024 at 02:09:06PM +0100, Bartosz Golaszewski wrote:
> > > > > On Fri, Nov 1, 2024 at 1:17 PM Conor Dooley <conor@kernel.org> wrote:
> > > > > >
> > > > > > On Thu, Oct 31, 2024 at 02:00:22PM +0100, Bartosz Golaszewski wrote:
> > > > > > > On Thu, Oct 31, 2024 at 1:04 PM Conor Dooley <conor@kernel.org> wrote:
> > > > > >
> > > > > > > > +       mpfs_gpio->gc.direction_input = mpfs_gpio_direction_input;
> > > > > > > > +       mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output;
> > > > > > > > +       mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction;
> > > > > > > > +       mpfs_gpio->gc.get = mpfs_gpio_get;
> > > > > > > > +       mpfs_gpio->gc.set = mpfs_gpio_set;
> > > > > > > > +       mpfs_gpio->gc.base = -1;
> > > > > > > > +       mpfs_gpio->gc.ngpio = ngpios;
> > > > > > >
> > > > > > > The "ngpios" property will be parsed by GPIO core so no need to set it.
> > > > > >
> > > > > > Are you sure it'll work here? I tried dropping the ngpios parsing, but I
> > > > > > get:
> > > > > > gpiochip_add_data_with_key: GPIOs 0..-1 (20122000.gpio) failed to register, -22
> > > > > >
> > > > > > That's coming from the device_property_read_u32(dev, "ngpios", &ngpios);
> > > > > > in gpiochip_get_ngpios(). Checking against the bluefield driver and the
> > > > > > code in gpiochip_add_data_with_key(), it's not immediately obvious what
> > > > > > I am missing.
> > > > >
> > > > > Does dev have an fwnode correctly assigned? What does dev_fwnode(dev) return?
> > > >
> > > > It's not a null pointer or something obviously wrong by virtue of being
> > > > null-adjacent, it is a virtual address but not one that %ps can identify.
> > >
> > > This is an OF system right? If you do dev_of_node(dev), does the
> > > returned node->name show the OF node you expect?
> >
> > Yes.
> 
> I mean inside gpiochip_get_ngpios(), sorry for confusion.

That is what I checked actually, didn't think you'd ask me to check the
one in probe that works.

> > > Does it have the
> > > "ngpios" property? Can you read it with of_property_read_u32()?
> >
> > No, EINVAL there too.
> 
> I assume the node is not assigned correctly. What if in your probe you
> do: gc->fwnode = dev_fwnode(dev)?

Makes no difference, same probe failure as before...


...but I just realised something: ngpios isn't a required property for
this device as it has a default of 32 and 32 is how many gpios this
controller has. Simple oversight, hours wasted. That's always the way of
it I suppose. The core code can't be used here I suppose, since ngpios
is optional.
Bartosz Golaszewski Nov. 1, 2024, 8:50 p.m. UTC | #9
On Fri, Nov 1, 2024 at 4:16 PM Conor Dooley <conor@kernel.org> wrote:
>
> On Fri, Nov 01, 2024 at 03:32:16PM +0100, Bartosz Golaszewski wrote:
> > On Fri, Nov 1, 2024 at 3:28 PM Conor Dooley <conor@kernel.org> wrote:
> > >
> > > On Fri, Nov 01, 2024 at 02:47:51PM +0100, Bartosz Golaszewski wrote:
> > > > On Fri, Nov 1, 2024 at 2:37 PM Conor Dooley <conor@kernel.org> wrote:
> > > > >
> > > > > On Fri, Nov 01, 2024 at 02:09:06PM +0100, Bartosz Golaszewski wrote:
> > > > > > On Fri, Nov 1, 2024 at 1:17 PM Conor Dooley <conor@kernel.org> wrote:
> > > > > > >
> > > > > > > On Thu, Oct 31, 2024 at 02:00:22PM +0100, Bartosz Golaszewski wrote:
> > > > > > > > On Thu, Oct 31, 2024 at 1:04 PM Conor Dooley <conor@kernel.org> wrote:
> > > > > > >
> > > > > > > > > +       mpfs_gpio->gc.direction_input = mpfs_gpio_direction_input;
> > > > > > > > > +       mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output;
> > > > > > > > > +       mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction;
> > > > > > > > > +       mpfs_gpio->gc.get = mpfs_gpio_get;
> > > > > > > > > +       mpfs_gpio->gc.set = mpfs_gpio_set;
> > > > > > > > > +       mpfs_gpio->gc.base = -1;
> > > > > > > > > +       mpfs_gpio->gc.ngpio = ngpios;
> > > > > > > >
> > > > > > > > The "ngpios" property will be parsed by GPIO core so no need to set it.
> > > > > > >
> > > > > > > Are you sure it'll work here? I tried dropping the ngpios parsing, but I
> > > > > > > get:
> > > > > > > gpiochip_add_data_with_key: GPIOs 0..-1 (20122000.gpio) failed to register, -22
> > > > > > >
> > > > > > > That's coming from the device_property_read_u32(dev, "ngpios", &ngpios);
> > > > > > > in gpiochip_get_ngpios(). Checking against the bluefield driver and the
> > > > > > > code in gpiochip_add_data_with_key(), it's not immediately obvious what
> > > > > > > I am missing.
> > > > > >
> > > > > > Does dev have an fwnode correctly assigned? What does dev_fwnode(dev) return?
> > > > >
> > > > > It's not a null pointer or something obviously wrong by virtue of being
> > > > > null-adjacent, it is a virtual address but not one that %ps can identify.
> > > >
> > > > This is an OF system right? If you do dev_of_node(dev), does the
> > > > returned node->name show the OF node you expect?
> > >
> > > Yes.
> >
> > I mean inside gpiochip_get_ngpios(), sorry for confusion.
>
> That is what I checked actually, didn't think you'd ask me to check the
> one in probe that works.
>
> > > > Does it have the
> > > > "ngpios" property? Can you read it with of_property_read_u32()?
> > >
> > > No, EINVAL there too.
> >
> > I assume the node is not assigned correctly. What if in your probe you
> > do: gc->fwnode = dev_fwnode(dev)?
>
> Makes no difference, same probe failure as before...
>
>
> ...but I just realised something: ngpios isn't a required property for
> this device as it has a default of 32 and 32 is how many gpios this
> controller has. Simple oversight, hours wasted. That's always the way of
> it I suppose. The core code can't be used here I suppose, since ngpios
> is optional.

Ah right. If we get to gpiochip_get_ngpios() without a GPIO number
set, the property becomes mandatory.

Nevermind my comment from the review then.

Bart
diff mbox series

Patch

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index d93cd4f722b40..811263b033c89 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -549,6 +549,12 @@  config GPIO_PL061
 	help
 	  Say yes here to support the PrimeCell PL061 GPIO device.
 
+config GPIO_POLARFIRE_SOC
+	bool "Microchip FPGA GPIO support"
+	select REGMAP_MMIO
+	help
+	  Say yes here to support the GPIO controllers on Microchip FPGAs.
+
 config GPIO_PXA
 	bool "PXA GPIO support"
 	depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 1429e8c0229b9..fc66e6388c76c 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -133,6 +133,7 @@  obj-$(CONFIG_GPIO_PCI_IDIO_16)		+= gpio-pci-idio-16.o
 obj-$(CONFIG_GPIO_PISOSR)		+= gpio-pisosr.o
 obj-$(CONFIG_GPIO_PL061)		+= gpio-pl061.o
 obj-$(CONFIG_GPIO_PMIC_EIC_SPRD)	+= gpio-pmic-eic-sprd.o
+obj-$(CONFIG_GPIO_POLARFIRE_SOC)	+= gpio-mpfs.o
 obj-$(CONFIG_GPIO_PXA)			+= gpio-pxa.o
 obj-$(CONFIG_GPIO_RASPBERRYPI_EXP)	+= gpio-raspberrypi-exp.o
 obj-$(CONFIG_GPIO_RC5T583)		+= gpio-rc5t583.o
diff --git a/drivers/gpio/gpio-mpfs.c b/drivers/gpio/gpio-mpfs.c
new file mode 100644
index 0000000000000..e5017307b0fe6
--- /dev/null
+++ b/drivers/gpio/gpio-mpfs.c
@@ -0,0 +1,170 @@ 
+// SPDX-License-Identifier: (GPL-2.0)
+/*
+ * Microchip PolarFire SoC (MPFS) GPIO controller driver
+ *
+ * Copyright (c) 2018-2024 Microchip Technology Inc. and its subsidiaries
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+
+#define MPFS_GPIO_CTRL(i)		(0x4 * (i))
+#define MPFS_MAX_NUM_GPIO		32
+#define MPFS_GPIO_EN_INT		3
+#define MPFS_GPIO_EN_OUT_BUF		BIT(2)
+#define MPFS_GPIO_EN_IN			BIT(1)
+#define MPFS_GPIO_EN_OUT		BIT(0)
+#define MPFS_GPIO_DIR_MASK		GENMASK(2, 0)
+
+#define MPFS_GPIO_TYPE_INT_EDGE_BOTH	0x80
+#define MPFS_GPIO_TYPE_INT_EDGE_NEG	0x60
+#define MPFS_GPIO_TYPE_INT_EDGE_POS	0x40
+#define MPFS_GPIO_TYPE_INT_LEVEL_LOW	0x20
+#define MPFS_GPIO_TYPE_INT_LEVEL_HIGH	0x00
+#define MPFS_GPIO_TYPE_INT_MASK		GENMASK(7, 5)
+#define MPFS_IRQ_REG			0x80
+#define MPFS_INP_REG			0x84
+#define MPFS_OUTP_REG			0x88
+
+struct mpfs_gpio_chip {
+	struct clk *clk;
+	struct regmap *regs;
+	struct gpio_chip gc;
+};
+
+static const struct regmap_config mpfs_gpio_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+};
+
+static int mpfs_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio_index)
+{
+	struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
+
+	regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index),
+			   MPFS_GPIO_DIR_MASK, MPFS_GPIO_EN_IN);
+
+	return 0;
+}
+
+static int mpfs_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio_index, int value)
+{
+	struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
+
+	regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index),
+			   MPFS_GPIO_DIR_MASK, MPFS_GPIO_EN_IN);
+	regmap_update_bits(mpfs_gpio->regs, MPFS_OUTP_REG, BIT(gpio_index),
+			   value << gpio_index);
+
+	return 0;
+}
+
+static int mpfs_gpio_get_direction(struct gpio_chip *gc,
+				   unsigned int gpio_index)
+{
+	struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
+	unsigned int gpio_cfg;
+
+	regmap_read(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index), &gpio_cfg);
+	if (gpio_cfg & MPFS_GPIO_EN_IN)
+		return GPIO_LINE_DIRECTION_IN;
+
+	return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int mpfs_gpio_get(struct gpio_chip *gc, unsigned int gpio_index)
+{
+	struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
+
+	if (mpfs_gpio_get_direction(gc, gpio_index) == GPIO_LINE_DIRECTION_OUT)
+		return regmap_test_bits(mpfs_gpio->regs, MPFS_OUTP_REG, BIT(gpio_index));
+	else
+		return regmap_test_bits(mpfs_gpio->regs, MPFS_INP_REG, BIT(gpio_index));
+}
+
+static void mpfs_gpio_set(struct gpio_chip *gc, unsigned int gpio_index, int value)
+{
+	struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
+
+	mpfs_gpio_get(gc, gpio_index);
+
+	regmap_update_bits(mpfs_gpio->regs, MPFS_OUTP_REG, BIT(gpio_index),
+			   value << gpio_index);
+
+	mpfs_gpio_get(gc, gpio_index);
+}
+
+static int mpfs_gpio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mpfs_gpio_chip *mpfs_gpio;
+	struct clk *clk;
+	void __iomem *base;
+	int ret, ngpios;
+
+	mpfs_gpio = devm_kzalloc(dev, sizeof(*mpfs_gpio), GFP_KERNEL);
+	if (!mpfs_gpio)
+		return -ENOMEM;
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return dev_err_probe(dev, PTR_ERR(base), "failed to ioremap memory resource\n");
+
+	mpfs_gpio->regs = devm_regmap_init_mmio(dev, base, &mpfs_gpio_regmap_config);
+	if (IS_ERR(mpfs_gpio->regs))
+		return dev_err_probe(dev, PTR_ERR(mpfs_gpio->regs),
+				     "failed to initialise regmap\n");
+
+	clk = devm_clk_get_enabled(dev, NULL);
+	if (IS_ERR(clk))
+		return dev_err_probe(dev, PTR_ERR(clk), "failed to get and enable clock\n");
+
+	mpfs_gpio->clk = clk;
+
+	ngpios = MPFS_MAX_NUM_GPIO;
+	device_property_read_u32(dev, "ngpios", &ngpios);
+	if (ngpios > MPFS_MAX_NUM_GPIO)
+		ngpios = MPFS_MAX_NUM_GPIO;
+
+	mpfs_gpio->gc.direction_input = mpfs_gpio_direction_input;
+	mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output;
+	mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction;
+	mpfs_gpio->gc.get = mpfs_gpio_get;
+	mpfs_gpio->gc.set = mpfs_gpio_set;
+	mpfs_gpio->gc.base = -1;
+	mpfs_gpio->gc.ngpio = ngpios;
+	mpfs_gpio->gc.label = dev_name(dev);
+	mpfs_gpio->gc.parent = dev;
+	mpfs_gpio->gc.owner = THIS_MODULE;
+
+	ret = devm_gpiochip_add_data(dev, &mpfs_gpio->gc, mpfs_gpio);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, mpfs_gpio);
+
+	return 0;
+}
+
+static const struct of_device_id mpfs_gpio_of_ids[] = {
+	{ .compatible = "microchip,mpfs-gpio", },
+	{ /* end of list */ }
+};
+
+static struct platform_driver mpfs_gpio_driver = {
+	.probe = mpfs_gpio_probe,
+	.driver = {
+		.name = "microchip,mpfs-gpio",
+		.of_match_table = mpfs_gpio_of_ids,
+	},
+};
+builtin_platform_driver(mpfs_gpio_driver);