diff mbox series

Add a GPIO driver for Altera FPGA Manager Fabric I/O

Message ID AM5PR0701MB2657A33A85F675C4A0175CE0E4670@AM5PR0701MB2657.eurprd07.prod.outlook.com
State New
Headers show
Series Add a GPIO driver for Altera FPGA Manager Fabric I/O | expand

Commit Message

Bernd Edlinger Sept. 22, 2017, 6:41 p.m. UTC
This is an internal 32-bit input and 32-bit output port to the FPGA logic.

Instantiate this in the device tree as:

   gpio3: gpio@ff706010 {
    #address-cells = <1>;
    #size-cells = <0>;
    compatible = "altr,fpgamgr-gpio";
    reg = <0xff706010 0x8>;
    status = "okay";

    portd: gpio-controller@0 {
     compatible = "altr,fpgamgr-gpio-output";
     gpio-controller;
     #gpio-cells = <2>;
     reg = <0>;
    };

    porte: gpio-controller@1 {
     compatible = "altr,fpgamgr-gpio-input";
     gpio-controller;
     #gpio-cells = <2>;
     reg = <1>;
    };
   };

Signed-off-by: Bernd Edlinger <bernd.edlinger@hotmail.de>

---
  drivers/gpio/Kconfig        |   6 ++
  drivers/gpio/Makefile       |   1 +
  drivers/gpio/gpio-fpgamgr.c | 256 
++++++++++++++++++++++++++++++++++++++++++++
  3 files changed, 263 insertions(+)
  create mode 100644 drivers/gpio/gpio-fpgamgr.c

+MODULE_DESCRIPTION("Altera fpgamgr GPIO driver");
-- 
2.7.4

Comments

Linus Walleij Sept. 27, 2017, 12:20 a.m. UTC | #1
On Fri, Sep 22, 2017 at 8:41 PM, Bernd Edlinger
<bernd.edlinger@hotmail.de> wrote:

> This is an internal 32-bit input and 32-bit output port to the FPGA logic.

What does "internal" mean in this context? On the chip?

I hope it still qualifies to be called "general purpose input/output".

Otherwise it's not GPIO...

> Instantiate this in the device tree as:
>
>    gpio3: gpio@ff706010 {
>     #address-cells = <1>;
>     #size-cells = <0>;
>     compatible = "altr,fpgamgr-gpio";
>     reg = <0xff706010 0x8>;
>     status = "okay";
>
>     portd: gpio-controller@0 {
>      compatible = "altr,fpgamgr-gpio-output";
>      gpio-controller;
>      #gpio-cells = <2>;
>      reg = <0>;
>     };
>
>     porte: gpio-controller@1 {
>      compatible = "altr,fpgamgr-gpio-input";
>      gpio-controller;
>      #gpio-cells = <2>;
>      reg = <1>;
>     };

So one port is output-only and one is input-only?

Fair enough.

> +config GPIO_FPGAMGR

Call it GPIO_ALTERA_FPGAMGR or something.

"FPGAMGR" is too generic. There must be a plethora
of FPGA managers in the world.

> +obj-$(CONFIG_GPIO_FPGAMGR)     += gpio-fpgamgr.o

So call that file gpio-altera-fpgamgr.o as well.

> +/*

Add a blurb here at the top describing the driver and what it is for.

> + * Copyright (c) 2015 Softing Industrial Automation GmbH

> +static int fpgamgr_gpio_add_port(struct fpgamgr_gpio *gpio,
> +                                struct fpgamgr_port_property *pp,
> +                                unsigned int offs)
> +{
> +       struct fpgamgr_gpio_port *port;
> +       void __iomem *dat;
> +       int err;
> +
> +       port = &gpio->ports[offs];
> +       port->gpio = gpio;
> +       port->idx = pp->idx;
> +
> +       dat = gpio->regs + (pp->idx * 4);
> +
> +       err = bgpio_init(&port->bgc, gpio->dev, 4, dat, NULL, NULL,
> +                        NULL, NULL, 0);

Nice reuse of MMIO GPIO.

> +#ifdef CONFIG_OF_GPIO
> +       port->bgc.of_node = pp->node;
> +#endif

Drop the #ifdef.

The Kconfig already depends on OF_GPIO so this is always true.

The concept of "port" is the same as "bank".

Please read Thierry's proposed changes that I will merge as soon as
he respins them, he creates a "bank" abstraction for gpiolib:
https://marc.info/?l=linux-gpio&m=150429237121695&w=2

I strongly feel you should use this infrastructure, even if Thierry's work
is much centered around being able to have per-bank IRQs.

> +static void fpgamgr_gpio_unregister(struct fpgamgr_gpio *gpio)
> +{
> +       unsigned int m;
> +
> +       for (m = 0; m < gpio->nr_ports; ++m)
> +               if (gpio->ports[m].is_registered)
> +                       gpiochip_remove(&gpio->ports[m].bgc);
> +}

Why can't you simply use devm_gpiochip_add_data() and get rid
of the unregister business? (data can be NULL)

> +static struct fpgamgr_platform_data *
> +fpgamgr_gpio_get_pdata_of(struct device *dev)
> +{
> +       struct device_node *node, *port_np;

Rename "node" to just "np" (node pointer) this is the convention
we usually employ.

Just declare it like this:

struct device_node *np = dev->of_node;

So you don't need to assign it later.

> +       struct fpgamgr_platform_data *pdata;
> +       struct fpgamgr_port_property *pp;
> +       int nports;
> +       int i;
> +
> +       node = dev->of_node;
> +       if (!IS_ENABLED(CONFIG_OF_GPIO) || !node)
> +               return ERR_PTR(-ENODEV);

Drop this, as stated above it is always enabled when compiling
and probing this code.

> +       nports = of_get_child_count(node);
> +       if (nports == 0)
> +               return ERR_PTR(-ENODEV);
> +
> +       pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
> +       if (!pdata)
> +               return ERR_PTR(-ENOMEM);
> +
> +       pdata->properties = kcalloc(nports, sizeof(*pp), GFP_KERNEL);
> +       if (!pdata->properties) {
> +               kfree(pdata);
> +               return ERR_PTR(-ENOMEM);
> +       }
> +
> +       pdata->nports = nports;

Again important to use Thierry's infrastructure for ports/banks.

> +static inline void fpgamgr_free_pdata_of(struct fpgamgr_platform_data
> *pdata)
> +{
> +       if (!IS_ENABLED(CONFIG_OF_GPIO) || !pdata)
> +               return;

Drop that.

> +static int fpgamgr_gpio_probe(struct platform_device *pdev)
> +{

Since you use the struct device * pointer a lot here, introduce a local
variable like this:


struct device *dev = &pdev->dev;

Then substitute &pdev->dev for just dev below. Make it so much
easier to read.

> +       unsigned int i;
> +       struct resource *res;
> +       struct fpgamgr_gpio *gpio;

gpio is a bit ambigous here don't you think?

At least call it "fgpio" or something.

> +static const struct of_device_id fpgamgr_of_match[] = {
> +       { .compatible = "altr,fpgamgr-gpio" },
> +       { /* Sentinel */ }
> +};

Are these device tree bindings already merged? Else they need
a separate patch with Cc to devicetree@vger.kernel.org.

> +#ifdef CONFIG_PM_SLEEP
> +static int fpgamgr_gpio_suspend(struct device *dev)
> +{
> +       return 0;
> +}
> +
> +static int fpgamgr_gpio_resume(struct device *dev)
> +{
> +       return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(fpgamgr_gpio_pm_ops, fpgamgr_gpio_suspend,
> +                        fpgamgr_gpio_resume);

Either implement the suspend resume for real or drop this entire thing.
Just skeleton functions are not going to help anyone.

> +static struct platform_driver fpgamgr_gpio_driver = {
> +       .driver         = {
> +               .name   = "gpio-fpgamgr",

gpio-altera-fpgamgr

> +               .owner  = THIS_MODULE,
> +               .pm     = &fpgamgr_gpio_pm_ops,

Drop that too unless you implement it.

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bernd Edlinger Sept. 30, 2017, 1:15 p.m. UTC | #2
On 09/27/17 02:20, Linus Walleij wrote:
> On Fri, Sep 22, 2017 at 8:41 PM, Bernd Edlinger
> <bernd.edlinger@hotmail.de> wrote:
> 
>> This is an internal 32-bit input and 32-bit output port to the FPGA logic.
> 
> What does "internal" mean in this context? On the chip?
> 

Yes, these are simple interconnects between the two major parts on the
chip.  One is called HPS and it consists of two ARM cores.
And the other is a large programmable logic area called FPGA Fabric.

> I hope it still qualifies to be called "general purpose input/output".
> 
> Otherwise it's not GPIO...
> 

Absolutely it looks and feels like a general-purpose I/O facility.
It can be used to control arbitrary logic functions.

>> Instantiate this in the device tree as:
>>
>>     gpio3: gpio@ff706010 {
>>      #address-cells = <1>;
>>      #size-cells = <0>;
>>      compatible = "altr,fpgamgr-gpio";
>>      reg = <0xff706010 0x8>;
>>      status = "okay";
>>
>>      portd: gpio-controller@0 {
>>       compatible = "altr,fpgamgr-gpio-output";
>>       gpio-controller;
>>       #gpio-cells = <2>;
>>       reg = <0>;
>>      };
>>
>>      porte: gpio-controller@1 {
>>       compatible = "altr,fpgamgr-gpio-input";
>>       gpio-controller;
>>       #gpio-cells = <2>;
>>       reg = <1>;
>>      };
> 
> So one port is output-only and one is input-only?
> 
> Fair enough.
> 
>> +config GPIO_FPGAMGR
> 
> Call it GPIO_ALTERA_FPGAMGR or something.
> 

Done.

> "FPGAMGR" is too generic. There must be a plethora
> of FPGA managers in the world.
> 
>> +obj-$(CONFIG_GPIO_FPGAMGR)     += gpio-fpgamgr.o
> 
> So call that file gpio-altera-fpgamgr.o as well.
> 

Done.

>> +/*
> 
> Add a blurb here at the top describing the driver and what it is for.
> 

Done.

>> + * Copyright (c) 2015 Softing Industrial Automation GmbH
> 
>> +static int fpgamgr_gpio_add_port(struct fpgamgr_gpio *gpio,
>> +                                struct fpgamgr_port_property *pp,
>> +                                unsigned int offs)
>> +{
>> +       struct fpgamgr_gpio_port *port;
>> +       void __iomem *dat;
>> +       int err;
>> +
>> +       port = &gpio->ports[offs];
>> +       port->gpio = gpio;
>> +       port->idx = pp->idx;
>> +
>> +       dat = gpio->regs + (pp->idx * 4);
>> +
>> +       err = bgpio_init(&port->bgc, gpio->dev, 4, dat, NULL, NULL,
>> +                        NULL, NULL, 0);
> 
> Nice reuse of MMIO GPIO.
> 

Thanks.
Though I must admit, that I have copied most of this code verbatim
from gpio-dwapb.c ;)

>> +#ifdef CONFIG_OF_GPIO
>> +       port->bgc.of_node = pp->node;
>> +#endif
> 
> Drop the #ifdef.
> 

Done.

> The Kconfig already depends on OF_GPIO so this is always true.
> 
> The concept of "port" is the same as "bank".
> 
> Please read Thierry's proposed changes that I will merge as soon as
> he respins them, he creates a "bank" abstraction for gpiolib:
> https://marc.info/?l=linux-gpio&m=150429237121695&w=2
> 
> I strongly feel you should use this infrastructure, even if Thierry's work
> is much centered around being able to have per-bank IRQs.
> 

I hope that does not mean both ports have to share the same
interrupt spin lock.  While it is necessary to synchronize write
accesses to the same port, this driver has no need to synchronize
with the other port.

Interesting point is that this patch does not touch gpio-dwapb.c
at all.  Although gpio-dwapb.c has even irq support.

>> +static void fpgamgr_gpio_unregister(struct fpgamgr_gpio *gpio)
>> +{
>> +       unsigned int m;
>> +
>> +       for (m = 0; m < gpio->nr_ports; ++m)
>> +               if (gpio->ports[m].is_registered)
>> +                       gpiochip_remove(&gpio->ports[m].bgc);
>> +}
> 
> Why can't you simply use devm_gpiochip_add_data() and get rid
> of the unregister business? (data can be NULL)
> 

Done.  Note, that funny unregister code is originally from gpio-dwapb.c,
I have probably never tested the unregister code path at all.

>> +static struct fpgamgr_platform_data *
>> +fpgamgr_gpio_get_pdata_of(struct device *dev)
>> +{
>> +       struct device_node *node, *port_np;
> 
> Rename "node" to just "np" (node pointer) this is the convention
> we usually employ.
> 
> Just declare it like this:
> 
> struct device_node *np = dev->of_node;
> 
> So you don't need to assign it later.
> 

Done.

>> +       struct fpgamgr_platform_data *pdata;
>> +       struct fpgamgr_port_property *pp;
>> +       int nports;
>> +       int i;
>> +
>> +       node = dev->of_node;
>> +       if (!IS_ENABLED(CONFIG_OF_GPIO) || !node)
>> +               return ERR_PTR(-ENODEV);
> 
> Drop this, as stated above it is always enabled when compiling
> and probing this code.
> 

Done.

>> +       nports = of_get_child_count(node);
>> +       if (nports == 0)
>> +               return ERR_PTR(-ENODEV);
>> +
>> +       pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
>> +       if (!pdata)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       pdata->properties = kcalloc(nports, sizeof(*pp), GFP_KERNEL);
>> +       if (!pdata->properties) {
>> +               kfree(pdata);
>> +               return ERR_PTR(-ENOMEM);
>> +       }
>> +
>> +       pdata->nports = nports;
> 
> Again important to use Thierry's infrastructure for ports/banks.
> 

I do not yet fully understood what that means for this driver.
However I would like to do that as a separate patch, in order to allow
back-ports of this driver.

>> +static inline void fpgamgr_free_pdata_of(struct fpgamgr_platform_data
>> *pdata)
>> +{
>> +       if (!IS_ENABLED(CONFIG_OF_GPIO) || !pdata)
>> +               return;
> 
> Drop that.
> 

Done.

>> +static int fpgamgr_gpio_probe(struct platform_device *pdev)
>> +{
> 
> Since you use the struct device * pointer a lot here, introduce a local
> variable like this:
> 
> 
> struct device *dev = &pdev->dev;
> 
> Then substitute &pdev->dev for just dev below. Make it so much
> easier to read.
> 

Done.

>> +       unsigned int i;
>> +       struct resource *res;
>> +       struct fpgamgr_gpio *gpio;
> 
> gpio is a bit ambigous here don't you think?
> 
> At least call it "fgpio" or something.
> 

Done.

>> +static const struct of_device_id fpgamgr_of_match[] = {
>> +       { .compatible = "altr,fpgamgr-gpio" },
>> +       { /* Sentinel */ }
>> +};
> 
> Are these device tree bindings already merged? Else they need
> a separate patch with Cc to devicetree@vger.kernel.org.
> 

You mean a text file under Documentation/devicetree/bindings/gpio
with a few words how the device tree has to look like?

I have not yet done that, but I will prepare a separate patch.

>> +#ifdef CONFIG_PM_SLEEP
>> +static int fpgamgr_gpio_suspend(struct device *dev)
>> +{
>> +       return 0;
>> +}
>> +
>> +static int fpgamgr_gpio_resume(struct device *dev)
>> +{
>> +       return 0;
>> +}
>> +#endif
>> +
>> +static SIMPLE_DEV_PM_OPS(fpgamgr_gpio_pm_ops, fpgamgr_gpio_suspend,
>> +                        fpgamgr_gpio_resume);
> 
> Either implement the suspend resume for real or drop this entire thing.
> Just skeleton functions are not going to help anyone.
> 

Dropped.

>> +static struct platform_driver fpgamgr_gpio_driver = {
>> +       .driver         = {
>> +               .name   = "gpio-fpgamgr",
> 
> gpio-altera-fpgamgr
> 

Done.

>> +               .owner  = THIS_MODULE,
>> +               .pm     = &fpgamgr_gpio_pm_ops,
> 
> Drop that too unless you implement it.
> 

Done.

Thanks
Bernd.

From 223be945bf0a7d7d4bc979eccd77075b0a447ece Mon Sep 17 00:00:00 2001
From: Bernd Edlinger <bernd.edlinger@hotmail.de>
Date: Thu, 21 Sep 2017 15:50:36 +0200
Subject: [PATCH] Add a GPIO driver for Altera FPGA Manager Fabric I/O.
 This is an internal 32-bit input and 32-bit output port to the FPGA logic.

Instantiate this in the device tree as:

  gpio3: gpio@ff706010 {
   #address-cells = <1>;
   #size-cells = <0>;
   compatible = "altr,fpgamgr-gpio";
   reg = <0xff706010 0x8>;
   status = "okay";

   portd: gpio-controller@0 {
    compatible = "altr,fpgamgr-gpio-output";
    gpio-controller;
    #gpio-cells = <2>;
    reg = <0>;
   };

   porte: gpio-controller@1 {
    compatible = "altr,fpgamgr-gpio-input";
    gpio-controller;
    #gpio-cells = <2>;
    reg = <1>;
   };
  };

Signed-off-by: Bernd Edlinger <bernd.edlinger@hotmail.de>
---
 drivers/gpio/Kconfig               |   6 +
 drivers/gpio/Makefile              |   1 +
 drivers/gpio/gpio-altera-fpgamgr.c | 219 +++++++++++++++++++++++++++++++++++++
 3 files changed, 226 insertions(+)
 create mode 100644 drivers/gpio/gpio-altera-fpgamgr.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 3388d54..0bec903 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -97,6 +97,12 @@ config GPIO_ALTERA
 
 	  If driver is built as a module it will be called gpio-altera.
 
+config GPIO_ALTERA_FPGAMGR
+	tristate "Altera FPGAMGR GPIO"
+	depends on OF_GPIO
+	help
+	  Say yes here to support the Altera FPGAMGR GPIO device.
+
 config GPIO_AMDPT
 	tristate "AMD Promontory GPIO support"
 	depends on ACPI
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index aeb70e9d..3eb73d4 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_GPIO_ADP5520)	+= gpio-adp5520.o
 obj-$(CONFIG_GPIO_ADP5588)	+= gpio-adp5588.o
 obj-$(CONFIG_GPIO_ALTERA)  	+= gpio-altera.o
 obj-$(CONFIG_GPIO_ALTERA_A10SR)	+= gpio-altera-a10sr.o
+obj-$(CONFIG_GPIO_ALTERA_FPGAMGR)	+= gpio-altera-fpgamgr.o
 obj-$(CONFIG_GPIO_AMD8111)	+= gpio-amd8111.o
 obj-$(CONFIG_GPIO_AMDPT)	+= gpio-amdpt.o
 obj-$(CONFIG_GPIO_ARIZONA)	+= gpio-arizona.o
diff --git a/drivers/gpio/gpio-altera-fpgamgr.c b/drivers/gpio/gpio-altera-fpgamgr.c
new file mode 100644
index 0000000..76eff81
--- /dev/null
+++ b/drivers/gpio/gpio-altera-fpgamgr.c
@@ -0,0 +1,219 @@
+/*
+ * This is a GPIO driver for the internal FPGA Manager I/O ports
+ * connecting the HPS to the FPGA logic on certain Altera parts.
+ *
+ * Copyright (c) 2015 Softing Industrial Automation GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/gpio/driver.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+struct fpgamgr_port_property {
+	struct device_node		*node;
+	const char			*name;
+	unsigned int			idx;
+};
+
+struct fpgamgr_platform_data {
+	struct fpgamgr_port_property	*properties;
+	unsigned int			nports;
+};
+
+struct fpgamgr_gpio_port {
+	struct gpio_chip		bgc;
+	struct fpgamgr_gpio		*gpio;
+	unsigned int			idx;
+};
+
+struct fpgamgr_gpio {
+	struct	device			*dev;
+	void __iomem			*regs;
+	struct fpgamgr_gpio_port	*ports;
+	unsigned int			nr_ports;
+};
+
+static int fpgamgr_gpio_add_port(struct fpgamgr_gpio *gpio,
+				 struct fpgamgr_port_property *pp,
+				 unsigned int offs)
+{
+	struct fpgamgr_gpio_port *port;
+	void __iomem *dat;
+	int err;
+
+	port = &gpio->ports[offs];
+	port->gpio = gpio;
+	port->idx = pp->idx;
+
+	dat = gpio->regs + (pp->idx * 4);
+
+	err = bgpio_init(&port->bgc, gpio->dev, 4, dat, NULL, NULL,
+			 NULL, NULL, 0);
+	if (err) {
+		dev_err(gpio->dev, "failed to init gpio chip for %s\n",
+			pp->name);
+		return err;
+	}
+
+	port->bgc.of_node = pp->node;
+
+	err = devm_gpiochip_add_data(gpio->dev, &port->bgc, NULL);
+	if (err)
+		dev_err(gpio->dev, "failed to register gpiochip for %s\n",
+			pp->name);
+
+	return err;
+}
+
+static struct fpgamgr_platform_data *
+fpgamgr_gpio_get_pdata_of(struct device *dev)
+{
+	struct device_node *np = dev->of_node, *port_np;
+	struct fpgamgr_platform_data *pdata;
+	struct fpgamgr_port_property *pp;
+	int nports;
+	int i;
+
+	if (!np)
+		return ERR_PTR(-ENODEV);
+
+	nports = of_get_child_count(np);
+	if (nports == 0)
+		return ERR_PTR(-ENODEV);
+
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return ERR_PTR(-ENOMEM);
+
+	pdata->properties = kcalloc(nports, sizeof(*pp), GFP_KERNEL);
+	if (!pdata->properties) {
+		kfree(pdata);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	pdata->nports = nports;
+
+	i = 0;
+	for_each_child_of_node(np, port_np) {
+		pp = &pdata->properties[i++];
+		pp->node = port_np;
+
+		if (of_property_read_u32(port_np, "reg", &pp->idx) ||
+		    pp->idx > 1) {
+			dev_err(dev, "missing/invalid port index for %s\n",
+				port_np->full_name);
+			kfree(pdata->properties);
+			kfree(pdata);
+			return ERR_PTR(-EINVAL);
+		}
+
+		pp->name = port_np->full_name;
+	}
+
+	return pdata;
+}
+
+static inline void fpgamgr_free_pdata_of(struct fpgamgr_platform_data *pdata)
+{
+	if (!pdata)
+		return;
+
+	kfree(pdata->properties);
+	kfree(pdata);
+}
+
+static int fpgamgr_gpio_probe(struct platform_device *pdev)
+{
+	unsigned int i;
+	struct resource *res;
+	struct fpgamgr_gpio *fgpio;
+	int err;
+	struct device *dev = &pdev->dev;
+	struct fpgamgr_platform_data *pdata = dev_get_platdata(dev);
+	bool is_pdata_alloc = !pdata;
+
+	if (is_pdata_alloc) {
+		pdata = fpgamgr_gpio_get_pdata_of(dev);
+		if (IS_ERR(pdata))
+			return PTR_ERR(pdata);
+	}
+
+	if (!pdata->nports) {
+		err = -ENODEV;
+		goto out_err;
+	}
+
+	fgpio = devm_kzalloc(dev, sizeof(*fgpio), GFP_KERNEL);
+	if (!fgpio) {
+		err = -ENOMEM;
+		goto out_err;
+	}
+	fgpio->dev = dev;
+	fgpio->nr_ports = pdata->nports;
+
+	fgpio->ports = devm_kcalloc(dev, fgpio->nr_ports,
+				   sizeof(*fgpio->ports), GFP_KERNEL);
+	if (!fgpio->ports) {
+		err = -ENOMEM;
+		goto out_err;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	fgpio->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(fgpio->regs)) {
+		err = PTR_ERR(fgpio->regs);
+		goto out_err;
+	}
+
+	for (i = 0; i < fgpio->nr_ports; i++) {
+		err = fpgamgr_gpio_add_port(fgpio, &pdata->properties[i], i);
+		if (err)
+			goto out_unregister;
+	}
+	platform_set_drvdata(pdev, fgpio);
+
+	goto out_err;
+
+out_unregister:
+	while (i > 0)
+		devm_gpiochip_remove(dev, &fgpio->ports[--i].bgc);
+
+out_err:
+	if (is_pdata_alloc)
+		fpgamgr_free_pdata_of(pdata);
+
+	return err;
+}
+
+static const struct of_device_id fpgamgr_of_match[] = {
+	{ .compatible = "altr,fpgamgr-gpio" },
+	{ /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fpgamgr_of_match);
+
+static struct platform_driver fpgamgr_gpio_driver = {
+	.driver		= {
+		.name	= "gpio-altera-fpgamgr",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(fpgamgr_of_match),
+	},
+	.probe		= fpgamgr_gpio_probe,
+};
+
+module_platform_driver(fpgamgr_gpio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Bernd Edlinger");
+MODULE_DESCRIPTION("Altera fpgamgr GPIO driver");
Christian Lamparter Sept. 30, 2017, 4:02 p.m. UTC | #3
Hello,

On Saturday, September 30, 2017 1:15:38 PM CEST Bernd Edlinger wrote:
> Absolutely it looks and feels like a general-purpose I/O facility.
> It can be used to control arbitrary logic functions.
Hm, Is it possible to differenciate it from a "syscon" device?

"System controller node represents a register region containing a set
of miscellaneous registers. The registers are not cohesive enough to
represent as any specific type of device. The typical use-case is for
some other node's driver, or platform-specific code, to acquire a
reference to the syscon node (e.g. by phandle, node path, or search
using a specific compatible value), interrogate the node (or associated
OS driver) to determine the location of the registers, and access the
registers directly."

<http://elixir.free-electrons.com/linux/latest/source/Documentation/devicetree/bindings/mfd/syscon.txt>

> >> Instantiate this in the device tree as:
> >>
> >>     gpio3: gpio@ff706010 {
> >>      #address-cells = <1>;
> >>      #size-cells = <0>;
> >>      compatible = "altr,fpgamgr-gpio";
> >>      reg = <0xff706010 0x8>;
> >>      status = "okay";
> >>
> >>      portd: gpio-controller@0 {
> >>       compatible = "altr,fpgamgr-gpio-output";
> >>       gpio-controller;
> >>       #gpio-cells = <2>;
> >>       reg = <0>;
> >>      };
> >>
> >>      porte: gpio-controller@1 {
> >>       compatible = "altr,fpgamgr-gpio-input";
> >>       gpio-controller;
> >>       #gpio-cells = <2>;
> >>       reg = <1>;
> >>      };
> > 
> > So one port is output-only and one is input-only?
> > 
> > Fair enough.

The driver seemd fairly simple. So, You could actually get
away with just adding the "altr,fpgamgr-gpio" compatible string
to gpio-mmio.c's bgpio_of_match struct at [0] and change the dt
to something like this:

	portd: gpio@ff706010 {
		compatible = "altr,fpgamgr-gpio";
		reg = <0xff706010 0x4>;
		reg-names = "dat";
		gpio-controller;
		#gpio-cells = <2>;
   }

   	porte: gpio@ff706014 {
		compatible = "altr,fpgamgr-gpio";
		reg = <0xff706014 0x4>;
		reg-names = "dat";
		gpio-controller;
		#gpio-cells = <2>;
		no-output;
   }

I added "no-output" property to the "porte" gpio definition.
This property will force the direction of all of porte gpios
to "in". (Which based on the "input", is what you want?)

Note: If you need any insperation for the missing Device-tree
binding document, you can look at:

wd,mbl-gpio.txt [1] and ni,169445-nand-gpio.txt [2].

Regards,
Christian

[0] <https://github.com/torvalds/linux/blob/master/drivers/gpio/gpio-mmio.c#L575>
[1] <https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/gpio/wd%2Cmbl-gpio.txt
[2] <https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/gpio/ni%2C169445-nand-gpio.txt>


--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Linus Walleij Oct. 7, 2017, 10:39 a.m. UTC | #4
On Sat, Sep 30, 2017 at 3:15 PM, Bernd Edlinger
<bernd.edlinger@hotmail.de> wrote:
> On 09/27/17 02:20, Linus Walleij wrote:
>> On Fri, Sep 22, 2017 at 8:41 PM, Bernd Edlinger

> Though I must admit, that I have copied most of this code verbatim
> from gpio-dwapb.c ;)

All right, so then we need to keep this in mind in the future, we are going
to see more of these kind of layouts.

>> Please read Thierry's proposed changes that I will merge as soon as
>> he respins them, he creates a "bank" abstraction for gpiolib:
>> https://marc.info/?l=linux-gpio&m=150429237121695&w=2
>>
>> I strongly feel you should use this infrastructure, even if Thierry's work
>> is much centered around being able to have per-bank IRQs.
>>
>
> I hope that does not mean both ports have to share the same
> interrupt spin lock.  While it is necessary to synchronize write
> accesses to the same port, this driver has no need to synchronize
> with the other port.
>
> Interesting point is that this patch does not touch gpio-dwapb.c
> at all.  Although gpio-dwapb.c has even irq support.

It came up, and it is pretty evident that when we add a port/bank abstraction
it should be possible to reuse with dwapb as well, we need to cover a
few basic cases like that.

> I have probably never tested the unregister code path at all.

You can do this in sysfs using the "bind" and "unbind"
files. Echo the bus ID into these to unbind/bind the driver.

> From 223be945bf0a7d7d4bc979eccd77075b0a447ece Mon Sep 17 00:00:00 2001
> From: Bernd Edlinger <bernd.edlinger@hotmail.de>
> Date: Thu, 21 Sep 2017 15:50:36 +0200
> Subject: [PATCH] Add a GPIO driver for Altera FPGA Manager Fabric I/O.

Just resend the patch with a v2 suffix after PATCH.

Else I get confused!

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bernd Edlinger Oct. 7, 2017, 5:39 p.m. UTC | #5
T24gMTAvMDcvMTcgMTI6MzksIExpbnVzIFdhbGxlaWogd3JvdGU6DQo+IEp1c3QgcmVzZW5kIHRo
ZSBwYXRjaCB3aXRoIGEgdjIgc3VmZml4IGFmdGVyIFBBVENILg0KPiANCj4gRWxzZSBJIGdldCBj
b25mdXNlZCENCg0KU3VyZSwgcmVzZW50IHBhdGNoIGZvbGxvd3MuDQpJIHdpbGwgc2VuZCB0aGUg
ZGV2aWNlIHRyZWUgYmluZGluZ3MgcGF0Y2ggaW4gYSBzZXBhcmF0ZSBtYWlsLg0KDQoNCiBGcm9t
IDIyM2JlOTQ1YmYwYTdkN2Q0YmM5NzllY2NkNzcwNzViMGE0NDdlY2UgTW9uIFNlcCAxNyAwMDow
MDowMCAyMDAxDQpGcm9tOiBCZXJuZCBFZGxpbmdlciA8YmVybmQuZWRsaW5nZXJAaG90bWFpbC5k
ZT4NCkRhdGU6IFRodSwgMjEgU2VwIDIwMTcgMTU6NTA6MzYgKzAyMDANClN1YmplY3Q6IFtQQVRD
SHYyXSBBZGQgYSBHUElPIGRyaXZlciBmb3IgQWx0ZXJhIEZQR0EgTWFuYWdlciBGYWJyaWMgSS9P
Lg0KDQpUaGlzIGlzIGFuIGludGVybmFsIDMyLWJpdCBpbnB1dCBhbmQgMzItYml0IG91dHB1dCBw
b3J0IHRvIHRoZSBGUEdBIGxvZ2ljLg0KDQpJbnN0YW50aWF0ZSB0aGlzIGluIHRoZSBkZXZpY2Ug
dHJlZSBhczoNCg0KICAgZ3BpbzM6IGdwaW9AZmY3MDYwMTAgew0KICAgICNhZGRyZXNzLWNlbGxz
ID0gPDE+Ow0KICAgICNzaXplLWNlbGxzID0gPDA+Ow0KICAgIGNvbXBhdGlibGUgPSAiYWx0cixm
cGdhbWdyLWdwaW8iOw0KICAgIHJlZyA9IDwweGZmNzA2MDEwIDB4OD47DQogICAgc3RhdHVzID0g
Im9rYXkiOw0KDQogICAgcG9ydGQ6IGdwaW8tY29udHJvbGxlckAwIHsNCiAgICAgY29tcGF0aWJs
ZSA9ICJhbHRyLGZwZ2FtZ3ItZ3Bpby1vdXRwdXQiOw0KICAgICBncGlvLWNvbnRyb2xsZXI7DQog
ICAgICNncGlvLWNlbGxzID0gPDI+Ow0KICAgICByZWcgPSA8MD47DQogICAgfTsNCg0KICAgIHBv
cnRlOiBncGlvLWNvbnRyb2xsZXJAMSB7DQogICAgIGNvbXBhdGlibGUgPSAiYWx0cixmcGdhbWdy
LWdwaW8taW5wdXQiOw0KICAgICBncGlvLWNvbnRyb2xsZXI7DQogICAgICNncGlvLWNlbGxzID0g
PDI+Ow0KICAgICByZWcgPSA8MT47DQogICAgfTsNCiAgIH07DQoNClNpZ25lZC1vZmYtYnk6IEJl
cm5kIEVkbGluZ2VyIDxiZXJuZC5lZGxpbmdlckBob3RtYWlsLmRlPg0KLS0tDQogIGRyaXZlcnMv
Z3Bpby9LY29uZmlnICAgICAgICAgICAgICAgfCAgIDYgKw0KICBkcml2ZXJzL2dwaW8vTWFrZWZp
bGUgICAgICAgICAgICAgIHwgICAxICsNCiAgZHJpdmVycy9ncGlvL2dwaW8tYWx0ZXJhLWZwZ2Ft
Z3IuYyB8IDIxOSANCisrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysNCiAgMyBm
aWxlcyBjaGFuZ2VkLCAyMjYgaW5zZXJ0aW9ucygrKQ0KICBjcmVhdGUgbW9kZSAxMDA2NDQgZHJp
dmVycy9ncGlvL2dwaW8tYWx0ZXJhLWZwZ2FtZ3IuYw0KDQpkaWZmIC0tZ2l0IGEvZHJpdmVycy9n
cGlvL0tjb25maWcgYi9kcml2ZXJzL2dwaW8vS2NvbmZpZw0KaW5kZXggMzM4OGQ1NC4uMGJlYzkw
MyAxMDA2NDQNCi0tLSBhL2RyaXZlcnMvZ3Bpby9LY29uZmlnDQorKysgYi9kcml2ZXJzL2dwaW8v
S2NvbmZpZw0KQEAgLTk3LDYgKzk3LDEyIEBAIGNvbmZpZyBHUElPX0FMVEVSQQ0KDQogIAkgIElm
IGRyaXZlciBpcyBidWlsdCBhcyBhIG1vZHVsZSBpdCB3aWxsIGJlIGNhbGxlZCBncGlvLWFsdGVy
YS4NCg0KK2NvbmZpZyBHUElPX0FMVEVSQV9GUEdBTUdSDQorCXRyaXN0YXRlICJBbHRlcmEgRlBH
QU1HUiBHUElPIg0KKwlkZXBlbmRzIG9uIE9GX0dQSU8NCisJaGVscA0KKwkgIFNheSB5ZXMgaGVy
ZSB0byBzdXBwb3J0IHRoZSBBbHRlcmEgRlBHQU1HUiBHUElPIGRldmljZS4NCisNCiAgY29uZmln
IEdQSU9fQU1EUFQNCiAgCXRyaXN0YXRlICJBTUQgUHJvbW9udG9yeSBHUElPIHN1cHBvcnQiDQog
IAlkZXBlbmRzIG9uIEFDUEkNCmRpZmYgLS1naXQgYS9kcml2ZXJzL2dwaW8vTWFrZWZpbGUgYi9k
cml2ZXJzL2dwaW8vTWFrZWZpbGUNCmluZGV4IGFlYjcwZTlkLi4zZWI3M2Q0IDEwMDY0NA0KLS0t
IGEvZHJpdmVycy9ncGlvL01ha2VmaWxlDQorKysgYi9kcml2ZXJzL2dwaW8vTWFrZWZpbGUNCkBA
IC0yNiw2ICsyNiw3IEBAIG9iai0kKENPTkZJR19HUElPX0FEUDU1MjApCSs9IGdwaW8tYWRwNTUy
MC5vDQogIG9iai0kKENPTkZJR19HUElPX0FEUDU1ODgpCSs9IGdwaW8tYWRwNTU4OC5vDQogIG9i
ai0kKENPTkZJR19HUElPX0FMVEVSQSkgIAkrPSBncGlvLWFsdGVyYS5vDQogIG9iai0kKENPTkZJ
R19HUElPX0FMVEVSQV9BMTBTUikJKz0gZ3Bpby1hbHRlcmEtYTEwc3Iubw0KK29iai0kKENPTkZJ
R19HUElPX0FMVEVSQV9GUEdBTUdSKQkrPSBncGlvLWFsdGVyYS1mcGdhbWdyLm8NCiAgb2JqLSQo
Q09ORklHX0dQSU9fQU1EODExMSkJKz0gZ3Bpby1hbWQ4MTExLm8NCiAgb2JqLSQoQ09ORklHX0dQ
SU9fQU1EUFQpCSs9IGdwaW8tYW1kcHQubw0KICBvYmotJChDT05GSUdfR1BJT19BUklaT05BKQkr
PSBncGlvLWFyaXpvbmEubw0KZGlmZiAtLWdpdCBhL2RyaXZlcnMvZ3Bpby9ncGlvLWFsdGVyYS1m
cGdhbWdyLmMgDQpiL2RyaXZlcnMvZ3Bpby9ncGlvLWFsdGVyYS1mcGdhbWdyLmMNCm5ldyBmaWxl
IG1vZGUgMTAwNjQ0DQppbmRleCAwMDAwMDAwLi43NmVmZjgxDQotLS0gL2Rldi9udWxsDQorKysg
Yi9kcml2ZXJzL2dwaW8vZ3Bpby1hbHRlcmEtZnBnYW1nci5jDQpAQCAtMCwwICsxLDIxOSBAQA0K
Ky8qDQorICogVGhpcyBpcyBhIEdQSU8gZHJpdmVyIGZvciB0aGUgaW50ZXJuYWwgRlBHQSBNYW5h
Z2VyIEkvTyBwb3J0cw0KKyAqIGNvbm5lY3RpbmcgdGhlIEhQUyB0byB0aGUgRlBHQSBsb2dpYyBv
biBjZXJ0YWluIEFsdGVyYSBwYXJ0cy4NCisgKg0KKyAqIENvcHlyaWdodCAoYykgMjAxNSBTb2Z0
aW5nIEluZHVzdHJpYWwgQXV0b21hdGlvbiBHbWJIDQorICoNCisgKiBUaGlzIHByb2dyYW0gaXMg
ZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeQ0KKyAq
IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgdmVy
c2lvbiAyIGFzDQorICogcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24u
DQorICoNCisgKi8NCisjaW5jbHVkZSA8bGludXgvZ3Bpby9kcml2ZXIuaD4NCisjaW5jbHVkZSA8
bGludXgvZXJyLmg+DQorI2luY2x1ZGUgPGxpbnV4L2luaXQuaD4NCisjaW5jbHVkZSA8bGludXgv
aW8uaD4NCisjaW5jbHVkZSA8bGludXgvaW9wb3J0Lmg+DQorI2luY2x1ZGUgPGxpbnV4L21vZHVs
ZS5oPg0KKyNpbmNsdWRlIDxsaW51eC9vZi5oPg0KKyNpbmNsdWRlIDxsaW51eC9vZl9hZGRyZXNz
Lmg+DQorI2luY2x1ZGUgPGxpbnV4L3BsYXRmb3JtX2RldmljZS5oPg0KKyNpbmNsdWRlIDxsaW51
eC9zcGlubG9jay5oPg0KKyNpbmNsdWRlIDxsaW51eC9zbGFiLmg+DQorDQorc3RydWN0IGZwZ2Ft
Z3JfcG9ydF9wcm9wZXJ0eSB7DQorCXN0cnVjdCBkZXZpY2Vfbm9kZQkJKm5vZGU7DQorCWNvbnN0
IGNoYXIJCQkqbmFtZTsNCisJdW5zaWduZWQgaW50CQkJaWR4Ow0KK307DQorDQorc3RydWN0IGZw
Z2FtZ3JfcGxhdGZvcm1fZGF0YSB7DQorCXN0cnVjdCBmcGdhbWdyX3BvcnRfcHJvcGVydHkJKnBy
b3BlcnRpZXM7DQorCXVuc2lnbmVkIGludAkJCW5wb3J0czsNCit9Ow0KKw0KK3N0cnVjdCBmcGdh
bWdyX2dwaW9fcG9ydCB7DQorCXN0cnVjdCBncGlvX2NoaXAJCWJnYzsNCisJc3RydWN0IGZwZ2Ft
Z3JfZ3BpbwkJKmdwaW87DQorCXVuc2lnbmVkIGludAkJCWlkeDsNCit9Ow0KKw0KK3N0cnVjdCBm
cGdhbWdyX2dwaW8gew0KKwlzdHJ1Y3QJZGV2aWNlCQkJKmRldjsNCisJdm9pZCBfX2lvbWVtCQkJ
KnJlZ3M7DQorCXN0cnVjdCBmcGdhbWdyX2dwaW9fcG9ydAkqcG9ydHM7DQorCXVuc2lnbmVkIGlu
dAkJCW5yX3BvcnRzOw0KK307DQorDQorc3RhdGljIGludCBmcGdhbWdyX2dwaW9fYWRkX3BvcnQo
c3RydWN0IGZwZ2FtZ3JfZ3BpbyAqZ3BpbywNCisJCQkJIHN0cnVjdCBmcGdhbWdyX3BvcnRfcHJv
cGVydHkgKnBwLA0KKwkJCQkgdW5zaWduZWQgaW50IG9mZnMpDQorew0KKwlzdHJ1Y3QgZnBnYW1n
cl9ncGlvX3BvcnQgKnBvcnQ7DQorCXZvaWQgX19pb21lbSAqZGF0Ow0KKwlpbnQgZXJyOw0KKw0K
Kwlwb3J0ID0gJmdwaW8tPnBvcnRzW29mZnNdOw0KKwlwb3J0LT5ncGlvID0gZ3BpbzsNCisJcG9y
dC0+aWR4ID0gcHAtPmlkeDsNCisNCisJZGF0ID0gZ3Bpby0+cmVncyArIChwcC0+aWR4ICogNCk7
DQorDQorCWVyciA9IGJncGlvX2luaXQoJnBvcnQtPmJnYywgZ3Bpby0+ZGV2LCA0LCBkYXQsIE5V
TEwsIE5VTEwsDQorCQkJIE5VTEwsIE5VTEwsIDApOw0KKwlpZiAoZXJyKSB7DQorCQlkZXZfZXJy
KGdwaW8tPmRldiwgImZhaWxlZCB0byBpbml0IGdwaW8gY2hpcCBmb3IgJXNcbiIsDQorCQkJcHAt
Pm5hbWUpOw0KKwkJcmV0dXJuIGVycjsNCisJfQ0KKw0KKwlwb3J0LT5iZ2Mub2Zfbm9kZSA9IHBw
LT5ub2RlOw0KKw0KKwllcnIgPSBkZXZtX2dwaW9jaGlwX2FkZF9kYXRhKGdwaW8tPmRldiwgJnBv
cnQtPmJnYywgTlVMTCk7DQorCWlmIChlcnIpDQorCQlkZXZfZXJyKGdwaW8tPmRldiwgImZhaWxl
ZCB0byByZWdpc3RlciBncGlvY2hpcCBmb3IgJXNcbiIsDQorCQkJcHAtPm5hbWUpOw0KKw0KKwly
ZXR1cm4gZXJyOw0KK30NCisNCitzdGF0aWMgc3RydWN0IGZwZ2FtZ3JfcGxhdGZvcm1fZGF0YSAq
DQorZnBnYW1ncl9ncGlvX2dldF9wZGF0YV9vZihzdHJ1Y3QgZGV2aWNlICpkZXYpDQorew0KKwlz
dHJ1Y3QgZGV2aWNlX25vZGUgKm5wID0gZGV2LT5vZl9ub2RlLCAqcG9ydF9ucDsNCisJc3RydWN0
IGZwZ2FtZ3JfcGxhdGZvcm1fZGF0YSAqcGRhdGE7DQorCXN0cnVjdCBmcGdhbWdyX3BvcnRfcHJv
cGVydHkgKnBwOw0KKwlpbnQgbnBvcnRzOw0KKwlpbnQgaTsNCisNCisJaWYgKCFucCkNCisJCXJl
dHVybiBFUlJfUFRSKC1FTk9ERVYpOw0KKw0KKwlucG9ydHMgPSBvZl9nZXRfY2hpbGRfY291bnQo
bnApOw0KKwlpZiAobnBvcnRzID09IDApDQorCQlyZXR1cm4gRVJSX1BUUigtRU5PREVWKTsNCisN
CisJcGRhdGEgPSBremFsbG9jKHNpemVvZigqcGRhdGEpLCBHRlBfS0VSTkVMKTsNCisJaWYgKCFw
ZGF0YSkNCisJCXJldHVybiBFUlJfUFRSKC1FTk9NRU0pOw0KKw0KKwlwZGF0YS0+cHJvcGVydGll
cyA9IGtjYWxsb2MobnBvcnRzLCBzaXplb2YoKnBwKSwgR0ZQX0tFUk5FTCk7DQorCWlmICghcGRh
dGEtPnByb3BlcnRpZXMpIHsNCisJCWtmcmVlKHBkYXRhKTsNCisJCXJldHVybiBFUlJfUFRSKC1F
Tk9NRU0pOw0KKwl9DQorDQorCXBkYXRhLT5ucG9ydHMgPSBucG9ydHM7DQorDQorCWkgPSAwOw0K
Kwlmb3JfZWFjaF9jaGlsZF9vZl9ub2RlKG5wLCBwb3J0X25wKSB7DQorCQlwcCA9ICZwZGF0YS0+
cHJvcGVydGllc1tpKytdOw0KKwkJcHAtPm5vZGUgPSBwb3J0X25wOw0KKw0KKwkJaWYgKG9mX3By
b3BlcnR5X3JlYWRfdTMyKHBvcnRfbnAsICJyZWciLCAmcHAtPmlkeCkgfHwNCisJCSAgICBwcC0+
aWR4ID4gMSkgew0KKwkJCWRldl9lcnIoZGV2LCAibWlzc2luZy9pbnZhbGlkIHBvcnQgaW5kZXgg
Zm9yICVzXG4iLA0KKwkJCQlwb3J0X25wLT5mdWxsX25hbWUpOw0KKwkJCWtmcmVlKHBkYXRhLT5w
cm9wZXJ0aWVzKTsNCisJCQlrZnJlZShwZGF0YSk7DQorCQkJcmV0dXJuIEVSUl9QVFIoLUVJTlZB
TCk7DQorCQl9DQorDQorCQlwcC0+bmFtZSA9IHBvcnRfbnAtPmZ1bGxfbmFtZTsNCisJfQ0KKw0K
KwlyZXR1cm4gcGRhdGE7DQorfQ0KKw0KK3N0YXRpYyBpbmxpbmUgdm9pZCBmcGdhbWdyX2ZyZWVf
cGRhdGFfb2Yoc3RydWN0IGZwZ2FtZ3JfcGxhdGZvcm1fZGF0YSANCipwZGF0YSkNCit7DQorCWlm
ICghcGRhdGEpDQorCQlyZXR1cm47DQorDQorCWtmcmVlKHBkYXRhLT5wcm9wZXJ0aWVzKTsNCisJ
a2ZyZWUocGRhdGEpOw0KK30NCisNCitzdGF0aWMgaW50IGZwZ2FtZ3JfZ3Bpb19wcm9iZShzdHJ1
Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2KQ0KK3sNCisJdW5zaWduZWQgaW50IGk7DQorCXN0cnVj
dCByZXNvdXJjZSAqcmVzOw0KKwlzdHJ1Y3QgZnBnYW1ncl9ncGlvICpmZ3BpbzsNCisJaW50IGVy
cjsNCisJc3RydWN0IGRldmljZSAqZGV2ID0gJnBkZXYtPmRldjsNCisJc3RydWN0IGZwZ2FtZ3Jf
cGxhdGZvcm1fZGF0YSAqcGRhdGEgPSBkZXZfZ2V0X3BsYXRkYXRhKGRldik7DQorCWJvb2wgaXNf
cGRhdGFfYWxsb2MgPSAhcGRhdGE7DQorDQorCWlmIChpc19wZGF0YV9hbGxvYykgew0KKwkJcGRh
dGEgPSBmcGdhbWdyX2dwaW9fZ2V0X3BkYXRhX29mKGRldik7DQorCQlpZiAoSVNfRVJSKHBkYXRh
KSkNCisJCQlyZXR1cm4gUFRSX0VSUihwZGF0YSk7DQorCX0NCisNCisJaWYgKCFwZGF0YS0+bnBv
cnRzKSB7DQorCQllcnIgPSAtRU5PREVWOw0KKwkJZ290byBvdXRfZXJyOw0KKwl9DQorDQorCWZn
cGlvID0gZGV2bV9remFsbG9jKGRldiwgc2l6ZW9mKCpmZ3BpbyksIEdGUF9LRVJORUwpOw0KKwlp
ZiAoIWZncGlvKSB7DQorCQllcnIgPSAtRU5PTUVNOw0KKwkJZ290byBvdXRfZXJyOw0KKwl9DQor
CWZncGlvLT5kZXYgPSBkZXY7DQorCWZncGlvLT5ucl9wb3J0cyA9IHBkYXRhLT5ucG9ydHM7DQor
DQorCWZncGlvLT5wb3J0cyA9IGRldm1fa2NhbGxvYyhkZXYsIGZncGlvLT5ucl9wb3J0cywNCisJ
CQkJICAgc2l6ZW9mKCpmZ3Bpby0+cG9ydHMpLCBHRlBfS0VSTkVMKTsNCisJaWYgKCFmZ3Bpby0+
cG9ydHMpIHsNCisJCWVyciA9IC1FTk9NRU07DQorCQlnb3RvIG91dF9lcnI7DQorCX0NCisNCisJ
cmVzID0gcGxhdGZvcm1fZ2V0X3Jlc291cmNlKHBkZXYsIElPUkVTT1VSQ0VfTUVNLCAwKTsNCisJ
ZmdwaW8tPnJlZ3MgPSBkZXZtX2lvcmVtYXBfcmVzb3VyY2UoZGV2LCByZXMpOw0KKwlpZiAoSVNf
RVJSKGZncGlvLT5yZWdzKSkgew0KKwkJZXJyID0gUFRSX0VSUihmZ3Bpby0+cmVncyk7DQorCQln
b3RvIG91dF9lcnI7DQorCX0NCisNCisJZm9yIChpID0gMDsgaSA8IGZncGlvLT5ucl9wb3J0czsg
aSsrKSB7DQorCQllcnIgPSBmcGdhbWdyX2dwaW9fYWRkX3BvcnQoZmdwaW8sICZwZGF0YS0+cHJv
cGVydGllc1tpXSwgaSk7DQorCQlpZiAoZXJyKQ0KKwkJCWdvdG8gb3V0X3VucmVnaXN0ZXI7DQor
CX0NCisJcGxhdGZvcm1fc2V0X2RydmRhdGEocGRldiwgZmdwaW8pOw0KKw0KKwlnb3RvIG91dF9l
cnI7DQorDQorb3V0X3VucmVnaXN0ZXI6DQorCXdoaWxlIChpID4gMCkNCisJCWRldm1fZ3Bpb2No
aXBfcmVtb3ZlKGRldiwgJmZncGlvLT5wb3J0c1stLWldLmJnYyk7DQorDQorb3V0X2VycjoNCisJ
aWYgKGlzX3BkYXRhX2FsbG9jKQ0KKwkJZnBnYW1ncl9mcmVlX3BkYXRhX29mKHBkYXRhKTsNCisN
CisJcmV0dXJuIGVycjsNCit9DQorDQorc3RhdGljIGNvbnN0IHN0cnVjdCBvZl9kZXZpY2VfaWQg
ZnBnYW1ncl9vZl9tYXRjaFtdID0gew0KKwl7IC5jb21wYXRpYmxlID0gImFsdHIsZnBnYW1nci1n
cGlvIiB9LA0KKwl7IC8qIFNlbnRpbmVsICovIH0NCit9Ow0KK01PRFVMRV9ERVZJQ0VfVEFCTEUo
b2YsIGZwZ2FtZ3Jfb2ZfbWF0Y2gpOw0KKw0KK3N0YXRpYyBzdHJ1Y3QgcGxhdGZvcm1fZHJpdmVy
IGZwZ2FtZ3JfZ3Bpb19kcml2ZXIgPSB7DQorCS5kcml2ZXIJCT0gew0KKwkJLm5hbWUJPSAiZ3Bp
by1hbHRlcmEtZnBnYW1nciIsDQorCQkub3duZXIJPSBUSElTX01PRFVMRSwNCisJCS5vZl9tYXRj
aF90YWJsZSA9IG9mX21hdGNoX3B0cihmcGdhbWdyX29mX21hdGNoKSwNCisJfSwNCisJLnByb2Jl
CQk9IGZwZ2FtZ3JfZ3Bpb19wcm9iZSwNCit9Ow0KKw0KK21vZHVsZV9wbGF0Zm9ybV9kcml2ZXIo
ZnBnYW1ncl9ncGlvX2RyaXZlcik7DQorDQorTU9EVUxFX0xJQ0VOU0UoIkdQTCIpOw0KK01PRFVM
RV9BVVRIT1IoIkJlcm5kIEVkbGluZ2VyIik7DQorTU9EVUxFX0RFU0NSSVBUSU9OKCJBbHRlcmEg
ZnBnYW1nciBHUElPIGRyaXZlciIpOw0KLS0gDQoyLjcuNA0K
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Linus Walleij Oct. 7, 2017, 11:37 p.m. UTC | #6
On Sat, Oct 7, 2017 at 7:39 PM, Bernd Edlinger
<bernd.edlinger@hotmail.de> wrote:

> On 10/07/17 12:39, Linus Walleij wrote:
>> Just resend the patch with a v2 suffix after PATCH.
>>
>> Else I get confused!
>
> Sure, resent patch follows.
> I will send the device tree bindings patch in a separate mail.

The bindings must be reviewed first before I can apply them
and then the driver.

Please keep the patches together in a series 1/2, 2/2.

Please send patches with git send-email as separate mails,
not inline.

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox series

Patch

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 3388d54..e0c5a35 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -192,6 +192,12 @@  config GPIO_EXAR
  	  Selecting this option will enable handling of GPIO pins present
  	  on Exar XR17V352/354/358 chips.

+config GPIO_FPGAMGR
+	tristate "Altera FPGAMGR GPIO"
+	depends on OF_GPIO
+	help
+	  Say yes here to support the Altera FPGAMGR GPIO device.
+
  config GPIO_GE_FPGA
  	bool "GE FPGA based GPIO"
  	depends on GE_FPGA
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index aeb70e9d..88a32ab 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -50,6 +50,7 @@  obj-$(CONFIG_GPIO_ETRAXFS)	+= gpio-etraxfs.o
  obj-$(CONFIG_GPIO_EXAR)		+= gpio-exar.o
  obj-$(CONFIG_GPIO_F7188X)	+= gpio-f7188x.o
  obj-$(CONFIG_GPIO_FTGPIO010)	+= gpio-ftgpio010.o
+obj-$(CONFIG_GPIO_FPGAMGR)	+= gpio-fpgamgr.o
  obj-$(CONFIG_GPIO_GE_FPGA)	+= gpio-ge.o
  obj-$(CONFIG_GPIO_GPIO_MM)	+= gpio-gpio-mm.o
  obj-$(CONFIG_GPIO_GRGPIO)	+= gpio-grgpio.o
diff --git a/drivers/gpio/gpio-fpgamgr.c b/drivers/gpio/gpio-fpgamgr.c
new file mode 100644
index 0000000..8aa86e7
--- /dev/null
+++ b/drivers/gpio/gpio-fpgamgr.c
@@ -0,0 +1,256 @@ 
+/*
+ * Copyright (c) 2015 Softing Industrial Automation GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/gpio/driver.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+struct fpgamgr_port_property {
+	struct device_node		*node;
+	const char			*name;
+	unsigned int			idx;
+};
+
+struct fpgamgr_platform_data {
+	struct fpgamgr_port_property	*properties;
+	unsigned int			nports;
+};
+
+struct fpgamgr_gpio_port {
+	struct gpio_chip		bgc;
+	bool				is_registered;
+	struct fpgamgr_gpio		*gpio;
+	unsigned int			idx;
+};
+
+struct fpgamgr_gpio {
+	struct	device			*dev;
+	void __iomem			*regs;
+	struct fpgamgr_gpio_port	*ports;
+	unsigned int			nr_ports;
+};
+
+static int fpgamgr_gpio_add_port(struct fpgamgr_gpio *gpio,
+				 struct fpgamgr_port_property *pp,
+				 unsigned int offs)
+{
+	struct fpgamgr_gpio_port *port;
+	void __iomem *dat;
+	int err;
+
+	port = &gpio->ports[offs];
+	port->gpio = gpio;
+	port->idx = pp->idx;
+
+	dat = gpio->regs + (pp->idx * 4);
+
+	err = bgpio_init(&port->bgc, gpio->dev, 4, dat, NULL, NULL,
+			 NULL, NULL, 0);
+	if (err) {
+		dev_err(gpio->dev, "failed to init gpio chip for %s\n",
+			pp->name);
+		return err;
+	}
+
+#ifdef CONFIG_OF_GPIO
+	port->bgc.of_node = pp->node;
+#endif
+
+	err = gpiochip_add(&port->bgc);
+	if (err)
+		dev_err(gpio->dev, "failed to register gpiochip for %s\n",
+			pp->name);
+	else
+		port->is_registered = true;
+
+	return err;
+}
+
+static void fpgamgr_gpio_unregister(struct fpgamgr_gpio *gpio)
+{
+	unsigned int m;
+
+	for (m = 0; m < gpio->nr_ports; ++m)
+		if (gpio->ports[m].is_registered)
+			gpiochip_remove(&gpio->ports[m].bgc);
+}
+
+static struct fpgamgr_platform_data *
+fpgamgr_gpio_get_pdata_of(struct device *dev)
+{
+	struct device_node *node, *port_np;
+	struct fpgamgr_platform_data *pdata;
+	struct fpgamgr_port_property *pp;
+	int nports;
+	int i;
+
+	node = dev->of_node;
+	if (!IS_ENABLED(CONFIG_OF_GPIO) || !node)
+		return ERR_PTR(-ENODEV);
+
+	nports = of_get_child_count(node);
+	if (nports == 0)
+		return ERR_PTR(-ENODEV);
+
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return ERR_PTR(-ENOMEM);
+
+	pdata->properties = kcalloc(nports, sizeof(*pp), GFP_KERNEL);
+	if (!pdata->properties) {
+		kfree(pdata);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	pdata->nports = nports;
+
+	i = 0;
+	for_each_child_of_node(node, port_np) {
+		pp = &pdata->properties[i++];
+		pp->node = port_np;
+
+		if (of_property_read_u32(port_np, "reg", &pp->idx) ||
+		    pp->idx > 1) {
+			dev_err(dev, "missing/invalid port index for %s\n",
+				port_np->full_name);
+			kfree(pdata->properties);
+			kfree(pdata);
+			return ERR_PTR(-EINVAL);
+		}
+
+		pp->name = port_np->full_name;
+	}
+
+	return pdata;
+}
+
+static inline void fpgamgr_free_pdata_of(struct fpgamgr_platform_data 
*pdata)
+{
+	if (!IS_ENABLED(CONFIG_OF_GPIO) || !pdata)
+		return;
+
+	kfree(pdata->properties);
+	kfree(pdata);
+}
+
+static int fpgamgr_gpio_probe(struct platform_device *pdev)
+{
+	unsigned int i;
+	struct resource *res;
+	struct fpgamgr_gpio *gpio;
+	int err;
+	struct device *dev = &pdev->dev;
+	struct fpgamgr_platform_data *pdata = dev_get_platdata(dev);
+	bool is_pdata_alloc = !pdata;
+
+	if (is_pdata_alloc) {
+		pdata = fpgamgr_gpio_get_pdata_of(dev);
+		if (IS_ERR(pdata))
+			return PTR_ERR(pdata);
+	}
+
+	if (!pdata->nports) {
+		err = -ENODEV;
+		goto out_err;
+	}
+
+	gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+	if (!gpio) {
+		err = -ENOMEM;
+		goto out_err;
+	}
+	gpio->dev = &pdev->dev;
+	gpio->nr_ports = pdata->nports;
+
+	gpio->ports = devm_kcalloc(&pdev->dev, gpio->nr_ports,
+				   sizeof(*gpio->ports), GFP_KERNEL);
+	if (!gpio->ports) {
+		err = -ENOMEM;
+		goto out_err;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	gpio->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(gpio->regs)) {
+		err = PTR_ERR(gpio->regs);
+		goto out_err;
+	}
+
+	for (i = 0; i < gpio->nr_ports; i++) {
+		err = fpgamgr_gpio_add_port(gpio, &pdata->properties[i], i);
+		if (err)
+			goto out_unregister;
+	}
+	platform_set_drvdata(pdev, gpio);
+
+	goto out_err;
+
+out_unregister:
+	fpgamgr_gpio_unregister(gpio);
+
+out_err:
+	if (is_pdata_alloc)
+		fpgamgr_free_pdata_of(pdata);
+
+	return err;
+}
+
+static int fpgamgr_gpio_remove(struct platform_device *pdev)
+{
+	struct fpgamgr_gpio *gpio = platform_get_drvdata(pdev);
+
+	fpgamgr_gpio_unregister(gpio);
+
+	return 0;
+}
+
+static const struct of_device_id fpgamgr_of_match[] = {
+	{ .compatible = "altr,fpgamgr-gpio" },
+	{ /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fpgamgr_of_match);
+
+#ifdef CONFIG_PM_SLEEP
+static int fpgamgr_gpio_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int fpgamgr_gpio_resume(struct device *dev)
+{
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(fpgamgr_gpio_pm_ops, fpgamgr_gpio_suspend,
+			 fpgamgr_gpio_resume);
+
+static struct platform_driver fpgamgr_gpio_driver = {
+	.driver		= {
+		.name	= "gpio-fpgamgr",
+		.owner	= THIS_MODULE,
+		.pm	= &fpgamgr_gpio_pm_ops,
+		.of_match_table = of_match_ptr(fpgamgr_of_match),
+	},
+	.probe		= fpgamgr_gpio_probe,
+	.remove		= fpgamgr_gpio_remove,
+};
+
+module_platform_driver(fpgamgr_gpio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Bernd Edlinger");