diff mbox

[5/6,v14] gpio: Add device tree support to block GPIO API

Message ID 1358856404-8975-6-git-send-email-stigge@antcom.de (mailing list archive)
State New, archived
Headers show

Commit Message

Roland Stigge Jan. 22, 2013, 12:06 p.m. UTC
This patch adds device tree support to the block GPIO API.

Signed-off-by: Roland Stigge <stigge@antcom.de>

---
 Documentation/devicetree/bindings/gpio/gpio-block.txt |   36 ++++++
 drivers/gpio/Makefile                                 |    1 
 drivers/gpio/gpioblock-of.c                           |  100 ++++++++++++++++++
 3 files changed, 137 insertions(+)

Comments

Stijn Devriendt Jan. 27, 2013, 1:07 p.m. UTC | #1
On Tue, Jan 22, 2013 at 1:06 PM, Roland Stigge <stigge@antcom.de> wrote:
> This patch adds device tree support to the block GPIO API.
>
> Signed-off-by: Roland Stigge <stigge@antcom.de>
>
> ---
>  Documentation/devicetree/bindings/gpio/gpio-block.txt |   36 ++++++
>  drivers/gpio/Makefile                                 |    1
>  drivers/gpio/gpioblock-of.c                           |  100 ++++++++++++++++++
>  3 files changed, 137 insertions(+)
>
> --- /dev/null
> +++ linux-2.6/Documentation/devicetree/bindings/gpio/gpio-block.txt
> @@ -0,0 +1,36 @@
> +Block GPIO definition
> +=====================
> +
> +This binding specifies arbitrary blocks of gpios, combining gpios from one or
> +more GPIO controllers together, to form a word for r/w access.
> +
> +Required property:
> +- compatible: must be "linux,gpio-block"
> +
> +Required subnodes:
> +- the name will be the registered name of the block
> +- property "gpios" is a list of gpios for the respective block
> +
> +Example:
> +
> +        blockgpio {
> +                compatible = "linux,gpio-block";
> +
> +                block0 {
> +                        gpios = <&gpio 3 0 0>,
> +                                <&gpio 3 1 0>;
> +                };
> +                block1 {
> +                        gpios = <&gpio 4 1 0>,
> +                                <&gpio 4 3 0>,
> +                                <&gpio 4 2 0>,
> +                                <&gpio 4 4 0>,
> +                                <&gpio 4 5 0>,
> +                                <&gpio 4 6 0>,
> +                                <&gpio 4 7 0>,
> +                                <&gpio 4 8 0>,
> +                                <&gpio 4 9 0>,
> +                                <&gpio 4 10 0>,
> +                                <&gpio 4 19 0>;
> +                };
> +        };

How do you see bindings for other kinds of drivers?

In my patchset, it's possible for other drivers to use gpio-blocks.
One example we have is a power sequencer with 2 pins attached
to GPIO pins. These 2 pins form a 2bit word to select power margining.
These 2 pins need to be set synchronously (as otherwise when going
from profile 0 to profile 3 you pass either profile 1 or profile 2 which
could be bad for hardware)

In the device-tree this is specified as:

powr@0x20 {
   // other properties

  gpios = <&gpio 4 0
               &gpio 5 0>;
};

Is this kind of integration also possible?

Regards,
Stijn

> --- linux-2.6.orig/drivers/gpio/Makefile
> +++ linux-2.6/drivers/gpio/Makefile
> @@ -5,6 +5,7 @@ ccflags-$(CONFIG_DEBUG_GPIO)    += -DDEBUG
>  obj-$(CONFIG_GPIOLIB)          += gpiolib.o devres.o
>  obj-$(CONFIG_OF_GPIO)          += gpiolib-of.o
>  obj-$(CONFIG_GPIO_ACPI)                += gpiolib-acpi.o
> +obj-$(CONFIG_OF_GPIO)          += gpioblock-of.o
>
>  # Device drivers. Generally keep list sorted alphabetically
>  obj-$(CONFIG_GPIO_GENERIC)     += gpio-generic.o
> --- /dev/null
> +++ linux-2.6/drivers/gpio/gpioblock-of.c
> @@ -0,0 +1,100 @@
> +/*
> + * OF implementation for Block GPIO
> + *
> + * Copyright (C) 2012 Roland Stigge <stigge@antcom.de>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/gpio.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +
> +static int gpioblock_of_probe(struct platform_device *pdev)
> +{
> +       struct device_node *block;
> +       unsigned *gpios;
> +       int ngpio;
> +       int ret;
> +       struct gpio_block *gb;
> +
> +       for_each_available_child_of_node(pdev->dev.of_node, block) {
> +               int i;
> +
> +               ngpio = of_gpio_count(block);
> +               if (ngpio < 1) {
> +                       ret = -ENODEV;
> +                       goto err1;
> +               }
> +
> +               gpios = kzalloc(ngpio * sizeof(*gpios), GFP_KERNEL);
> +               if (!gpios) {
> +                       ret = -ENOMEM;
> +                       goto err1;
> +               }
> +
> +               for (i = 0; i < ngpio; i++) {
> +                       ret = of_get_gpio(block, i);
> +                       if (ret < 0)
> +                               goto err2; /* expect -EPROBE_DEFER */
> +                       gpios[i] = ret;
> +               }
> +               gb = gpio_block_create(gpios, ngpio, block->name);
> +               if (IS_ERR(gb)) {
> +                       dev_err(&pdev->dev,
> +                               "Error creating GPIO block from device tree\n");
> +                       ret = PTR_ERR(gb);
> +                       goto err2;
> +               }
> +               ret = gpio_block_register(gb);
> +               if (ret < 0)
> +                       goto err3;
> +
> +               kfree(gpios);
> +               dev_info(&pdev->dev, "Registered gpio block %s: %d gpios\n",
> +                        block->name, ngpio);
> +       }
> +       return 0;
> +
> +err3:
> +       gpio_block_free(gb);
> +err2:
> +       kfree(gpios);
> +err1:
> +       of_node_put(block);
> +       return ret;
> +}
> +
> +#ifdef CONFIG_OF
> +static struct of_device_id gpioblock_of_match[] = {
> +       { .compatible = "linux,gpio-block", },
> +       { },
> +};
> +MODULE_DEVICE_TABLE(of, gpioblock_of_match);
> +#endif
> +
> +static struct platform_driver gpioblock_of_driver = {
> +       .driver = {
> +               .name = "gpio-block",
> +               .owner = THIS_MODULE,
> +               .of_match_table = of_match_ptr(gpioblock_of_match),
> +
> +       },
> +       .probe  = gpioblock_of_probe,
> +};
> +
> +module_platform_driver(gpioblock_of_driver);
> +
> +MODULE_DESCRIPTION("GPIO Block driver");
> +MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:gpioblock-of");
Roland Stigge Jan. 27, 2013, 2:29 p.m. UTC | #2
On 27/01/13 14:07, Stijn Devriendt wrote:
>> +Example:
>> +
>> +        blockgpio {
>> +                compatible = "linux,gpio-block";
>> +
>> +                block0 {
>> +                        gpios = <&gpio 3 0 0>,
>> +                                <&gpio 3 1 0>;
>> +                };
>> +                block1 {
>> +                        gpios = <&gpio 4 1 0>,
>> +                                <&gpio 4 3 0>,
>> +                                <&gpio 4 2 0>,
>> +                                <&gpio 4 4 0>,
>> +                                <&gpio 4 5 0>,
>> +                                <&gpio 4 6 0>,
>> +                                <&gpio 4 7 0>,
>> +                                <&gpio 4 8 0>,
>> +                                <&gpio 4 9 0>,
>> +                                <&gpio 4 10 0>,
>> +                                <&gpio 4 19 0>;
>> +                };
>> +        };
> 
> How do you see bindings for other kinds of drivers?
> 
> In my patchset, it's possible for other drivers to use gpio-blocks.
> One example we have is a power sequencer with 2 pins attached
> to GPIO pins. These 2 pins form a 2bit word to select power margining.
> These 2 pins need to be set synchronously (as otherwise when going
> from profile 0 to profile 3 you pass either profile 1 or profile 2 which
> could be bad for hardware)
> 
> In the device-tree this is specified as:
> 
> powr@0x20 {
>    // other properties
> 
>   gpios = <&gpio 4 0
>                &gpio 5 0>;
> };
> 
> Is this kind of integration also possible?

You can reference the gpio block via a phandle, e.g.:

        blockgpio {
                compatible = "linux,gpio-block";

                selector1 {
                        gpios = <&gpio 4 0>,
                                <&gpio 5 0>;
                };
        };

        powr@0x20 {
                // ...

                gpios = <&selector1>;
        };


In the driver, you can get the gpio block like this:

block = gpio_block_find_by_name(of_parse_phandle(powr, "gpios", 0)->name);

(Simplified by removed error/NULL handling!)

If this turns out to be a common pattern, I can add a convenience "get"
function for this.

Roland
Stijn Devriendt Jan. 28, 2013, 11:39 a.m. UTC | #3
On Sun, Jan 27, 2013 at 3:29 PM, Roland Stigge <stigge@antcom.de> wrote:
> On 27/01/13 14:07, Stijn Devriendt wrote:
>>> +Example:
>>> +
>>> +        blockgpio {
>>> +                compatible = "linux,gpio-block";
>>> +
>>> +                block0 {
>>> +                        gpios = <&gpio 3 0 0>,
>>> +                                <&gpio 3 1 0>;
>>> +                };
>>> +                block1 {
>>> +                        gpios = <&gpio 4 1 0>,
>>> +                                <&gpio 4 3 0>,
>>> +                                <&gpio 4 2 0>,
>>> +                                <&gpio 4 4 0>,
>>> +                                <&gpio 4 5 0>,
>>> +                                <&gpio 4 6 0>,
>>> +                                <&gpio 4 7 0>,
>>> +                                <&gpio 4 8 0>,
>>> +                                <&gpio 4 9 0>,
>>> +                                <&gpio 4 10 0>,
>>> +                                <&gpio 4 19 0>;
>>> +                };
>>> +        };
>>
>> How do you see bindings for other kinds of drivers?
>>
>> In my patchset, it's possible for other drivers to use gpio-blocks.
>> One example we have is a power sequencer with 2 pins attached
>> to GPIO pins. These 2 pins form a 2bit word to select power margining.
>> These 2 pins need to be set synchronously (as otherwise when going
>> from profile 0 to profile 3 you pass either profile 1 or profile 2 which
>> could be bad for hardware)
>>
>> In the device-tree this is specified as:
>>
>> powr@0x20 {
>>    // other properties
>>
>>   gpios = <&gpio 4 0
>>                &gpio 5 0>;
>> };
>>
>> Is this kind of integration also possible?
>
> You can reference the gpio block via a phandle, e.g.:
>
>         blockgpio {
>                 compatible = "linux,gpio-block";
>
>                 selector1 {
>                         gpios = <&gpio 4 0>,
>                                 <&gpio 5 0>;
>                 };
>         };
>
>         powr@0x20 {
>                 // ...
>
>                 gpios = <&selector1>;
>         };
>
>
> In the driver, you can get the gpio block like this:
>
> block = gpio_block_find_by_name(of_parse_phandle(powr, "gpios", 0)->name);
>
> (Simplified by removed error/NULL handling!)
>
> If this turns out to be a common pattern, I can add a convenience "get"
> function for this.

Given the pick-up of device-tree in ARM and MIPS, I think this stands
a good chance
of becoming a common pattern. Do mind the "gpios" name; it's already used by the
normal GPIO request functions...

This is one of the things I liked about my patch, there's little
difference between
using a group of GPIOs versus multiple separate GPIOs. The device-tree
description
is the same, only the driver handles them differently.
You could ask the question whether the device-tree should make a
difference between
1 GPIO, 2 separate GPIOs or 3 GPIOs in a block. For a H/W description
language it's
all the same. It is an implementation detail of the OS/drivers whether
they handle them
as a block or as separate GPIOs.

Regards,
Stijn
Roland Stigge Jan. 28, 2013, 12:29 p.m. UTC | #4
On 01/28/2013 12:39 PM, Stijn Devriendt wrote:
>>> In the device-tree this is specified as:
>>>
>>> powr@0x20 {
>>>    // other properties
>>>
>>>   gpios = <&gpio 4 0
>>>                &gpio 5 0>;
>>> };
>>>
>>> Is this kind of integration also possible?
>>
>> You can reference the gpio block via a phandle, e.g.:
>>
>>         blockgpio {
>>                 compatible = "linux,gpio-block";
>>
>>                 selector1 {
>>                         gpios = <&gpio 4 0>,
>>                                 <&gpio 5 0>;
>>                 };
>>         };
>>
>>         powr@0x20 {
>>                 // ...
>>
>>                 gpios = <&selector1>;
>>         };
>>
>>
>> In the driver, you can get the gpio block like this:
>>
>> block = gpio_block_find_by_name(of_parse_phandle(powr, "gpios", 0)->name);
>>
>> (Simplified by removed error/NULL handling!)
>>
>> If this turns out to be a common pattern, I can add a convenience "get"
>> function for this.
> 
> Given the pick-up of device-tree in ARM and MIPS, I think this stands
> a good chance
> of becoming a common pattern. Do mind the "gpios" name; it's already used by the
> normal GPIO request functions...

Right, when providing the respective convenience function, I'll better
use sth. like "gpioblock" as property name.

Specifying a traditional list of GPIOs instead is easy, actually. The
respective driver just needs to allocate a gpio block explicitly
(insteady of implicitly via phandle as above). Unfortunately, gpio
blocks that should be exported and available to userspace can't be
specified this way. Therefore the strategy as above.

Roland
diff mbox

Patch

--- /dev/null
+++ linux-2.6/Documentation/devicetree/bindings/gpio/gpio-block.txt
@@ -0,0 +1,36 @@ 
+Block GPIO definition
+=====================
+
+This binding specifies arbitrary blocks of gpios, combining gpios from one or
+more GPIO controllers together, to form a word for r/w access.
+
+Required property:
+- compatible: must be "linux,gpio-block"
+
+Required subnodes:
+- the name will be the registered name of the block
+- property "gpios" is a list of gpios for the respective block
+
+Example:
+
+        blockgpio {
+                compatible = "linux,gpio-block";
+
+                block0 {
+                        gpios = <&gpio 3 0 0>,
+                                <&gpio 3 1 0>;
+                };
+                block1 {
+                        gpios = <&gpio 4 1 0>,
+                                <&gpio 4 3 0>,
+                                <&gpio 4 2 0>,
+                                <&gpio 4 4 0>,
+                                <&gpio 4 5 0>,
+                                <&gpio 4 6 0>,
+                                <&gpio 4 7 0>,
+                                <&gpio 4 8 0>,
+                                <&gpio 4 9 0>,
+                                <&gpio 4 10 0>,
+                                <&gpio 4 19 0>;
+                };
+        };
--- linux-2.6.orig/drivers/gpio/Makefile
+++ linux-2.6/drivers/gpio/Makefile
@@ -5,6 +5,7 @@  ccflags-$(CONFIG_DEBUG_GPIO)	+= -DDEBUG
 obj-$(CONFIG_GPIOLIB)		+= gpiolib.o devres.o
 obj-$(CONFIG_OF_GPIO)		+= gpiolib-of.o
 obj-$(CONFIG_GPIO_ACPI)		+= gpiolib-acpi.o
+obj-$(CONFIG_OF_GPIO)		+= gpioblock-of.o
 
 # Device drivers. Generally keep list sorted alphabetically
 obj-$(CONFIG_GPIO_GENERIC)	+= gpio-generic.o
--- /dev/null
+++ linux-2.6/drivers/gpio/gpioblock-of.c
@@ -0,0 +1,100 @@ 
+/*
+ * OF implementation for Block GPIO
+ *
+ * Copyright (C) 2012 Roland Stigge <stigge@antcom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+static int gpioblock_of_probe(struct platform_device *pdev)
+{
+	struct device_node *block;
+	unsigned *gpios;
+	int ngpio;
+	int ret;
+	struct gpio_block *gb;
+
+	for_each_available_child_of_node(pdev->dev.of_node, block) {
+		int i;
+
+		ngpio = of_gpio_count(block);
+		if (ngpio < 1) {
+			ret = -ENODEV;
+			goto err1;
+		}
+
+		gpios = kzalloc(ngpio * sizeof(*gpios), GFP_KERNEL);
+		if (!gpios) {
+			ret = -ENOMEM;
+			goto err1;
+		}
+
+		for (i = 0; i < ngpio; i++) {
+			ret = of_get_gpio(block, i);
+			if (ret < 0)
+				goto err2; /* expect -EPROBE_DEFER */
+			gpios[i] = ret;
+		}
+		gb = gpio_block_create(gpios, ngpio, block->name);
+		if (IS_ERR(gb)) {
+			dev_err(&pdev->dev,
+				"Error creating GPIO block from device tree\n");
+			ret = PTR_ERR(gb);
+			goto err2;
+		}
+		ret = gpio_block_register(gb);
+		if (ret < 0)
+			goto err3;
+
+		kfree(gpios);
+		dev_info(&pdev->dev, "Registered gpio block %s: %d gpios\n",
+			 block->name, ngpio);
+	}
+	return 0;
+
+err3:
+	gpio_block_free(gb);
+err2:
+	kfree(gpios);
+err1:
+	of_node_put(block);
+	return ret;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id gpioblock_of_match[] = {
+	{ .compatible = "linux,gpio-block", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, gpioblock_of_match);
+#endif
+
+static struct platform_driver gpioblock_of_driver = {
+	.driver	= {
+		.name = "gpio-block",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(gpioblock_of_match),
+
+	},
+	.probe	= gpioblock_of_probe,
+};
+
+module_platform_driver(gpioblock_of_driver);
+
+MODULE_DESCRIPTION("GPIO Block driver");
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpioblock-of");