[PATCHv3] gpio-generic: add support for device tree probing
Jamie Iles
jamie at jamieiles.com
Fri Jul 29 20:45:01 EST 2011
This patch adds support for gpio-generic controllers to be
instantiated from the device tree. The binding supports devices with
multiple banks.
v3:
- Remove extraneous of_node_{get,put}().
- Rename basic-mmio-gpio,reg-io-width to reg-io-width.
- Use vendor specific compatible values for now.
- Only add child banks, and allocate banks as an array.
- Remove bgpio_init_info() and populate bgpio_chip directly.
- Rename properties to gpio-generic,* to match the driver name.
v2:
- Added more detail to the binding.
- Added CONFIG_OF guards.
- Use regoffset-* properties for each register in each bank.
relative to the registers of the controller.
Cc: Grant Likely <grant.likely at secretlab.ca>
Cc: Anton Vorontsov <cbouatmailru at gmail.com>
Signed-off-by: Jamie Iles <jamie at jamieiles.com>
---
.../devicetree/bindings/gpio/gpio-generic.txt | 85 ++++++++++
drivers/gpio/gpio-generic.c | 174 ++++++++++++++++++--
2 files changed, 245 insertions(+), 14 deletions(-)
create mode 100644 Documentation/devicetree/bindings/gpio/gpio-generic.txt
diff --git a/Documentation/devicetree/bindings/gpio/gpio-generic.txt b/Documentation/devicetree/bindings/gpio/gpio-generic.txt
new file mode 100644
index 0000000..91c9441
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-generic.txt
@@ -0,0 +1,85 @@
+Generic MMIO GPIO controller
+
+This binding allows lots of common GPIO controllers to use a generic GPIO
+driver. The top level GPIO node describes the registers for the controller
+and high level properties such as endianness and register width. Each bank in
+the controller is represented as a child node.
+
+Required properties:
+- compatible : Must be one of:
+ - "snps,dw-apb-gpio" : Synopsys DesignWare APB GPIO controller
+- reg : The register window for the GPIO device. If the device has multiple
+ banks then this window should cover all of the banks.
+- gpio-generic,reg-io-width : The width of the registers in the controller
+ (in bytes).
+- #address-cells : should be set to 1.
+- #size-cells : should be set to 0. The addresses of the child nodes are the
+ bank numbers and the child nodes themselves represent the banks in the
+ controller.
+
+Optional properties:
+- gpio-generic,big-endian : the registers are in big-endian byte ordering.
+ If present then the first gpio in the controller occupies the MSB for
+ each register.
+
+Generic MMIO GPIO controller bank
+
+Required properties:
+- compatible : Must be one of:
+ - "snps,dw-apb-gpio-bank" : Synopsys DesignWare APB GPIO controller bank
+- gpio-controller : Marks the node as a GPIO controller.
+- #gpio-cells : Should be two. The first cell is the pin number and the
+ second cell encodes optional flags (currently unused).
+- gpio-generic,nr-gpio : The number of GPIO pins in the bank.
+- regoffset-dat : The offset from the beginning of the controller for the
+ "dat" register for this bank. This register is read to get the value of the
+ GPIO pins, and if there is no regoffset-set property then it is also used to
+ set the value of the pins.
+
+Optional properties:
+- regoffset-set : The offset from the beginning of the controller for the
+ "set" register for this bank. If present then the GPIO values are set
+ through this register (writing a 1 bit sets the GPIO high). If there is no
+ regoffset-clr property then writing a 0 bit to this register will set the
+ pin to a low value.
+- regoffset-clr : The offset from the beginning of the controller for the
+ "clr" register for this bank. Writing a 1 bit to this register will set the
+ GPIO to a low output value.
+- regoffset-dirout : The offset from the beginning of the controller for the
+ "dirout" register for this bank. Writing a 1 bit to this register sets the
+ pin to be an output pin, writing a zero sets the pin to be an input.
+- regoffset-dirin : The offset from the beginning of the controller for the
+ "dirin" register for this bank. Writing a 1 bit to this register sets the
+ pin to be an input pin, writing a zero sets the pin to be an output.
+
+Examples:
+
+gpio: gpio at 20000 {
+ compatible = "snps,dw-apb-gpio";
+ reg = <0x20000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ gpio-generic,reg-io-width = <4>;
+
+ banka: gpio-controller at 0 {
+ compatible = "snps,dw-apb-gpio";
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-generic,nr-gpio = <8>;
+
+ regoffset-dat = <0x50>;
+ regoffset-set = <0x00>;
+ regoffset-dirout = <0x04>;
+ };
+
+ bankb: gpio-controller at 1 {
+ compatible = "snps,dw-apb-gpio";
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-generic,nr-gpio = <16>;
+
+ regoffset-dat = <0x54>;
+ regoffset-set = <0x0c>;
+ regoffset-dirout = <0x10>;
+ };
+};
diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c
index 231714d..92ae38d 100644
--- a/drivers/gpio/gpio-generic.c
+++ b/drivers/gpio/gpio-generic.c
@@ -61,6 +61,9 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/basic_mmio_gpio.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
static void bgpio_write8(void __iomem *reg, unsigned long data)
{
@@ -444,7 +447,29 @@ static void __iomem *bgpio_map(struct platform_device *pdev,
return ret;
}
-static int __devinit bgpio_pdev_probe(struct platform_device *pdev)
+struct bgpio_drvdata {
+ struct bgpio_chip *banks;
+ unsigned int nr_banks;
+};
+
+static struct bgpio_drvdata *bgpio_alloc_banks(struct device *dev,
+ unsigned int nr_banks)
+{
+ struct bgpio_drvdata *banks;
+
+ banks = devm_kzalloc(dev, sizeof(*banks), GFP_KERNEL);
+ if (!banks)
+ return NULL;
+
+ banks->banks = devm_kzalloc(dev, sizeof(*banks->banks) * nr_banks,
+ GFP_KERNEL);
+ if (!banks->banks)
+ return NULL;
+
+ return banks;
+}
+
+static int bgpio_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *r;
@@ -456,8 +481,12 @@ static int __devinit bgpio_pdev_probe(struct platform_device *pdev)
unsigned long sz;
bool be;
int err;
- struct bgpio_chip *bgc;
- struct bgpio_pdata *pdata = dev_get_platdata(dev);
+ struct bgpio_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct bgpio_drvdata *banks = bgpio_alloc_banks(&pdev->dev, 1);
+
+ if (!banks)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, banks);
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
if (!r)
@@ -485,32 +514,148 @@ static int __devinit bgpio_pdev_probe(struct platform_device *pdev)
if (err)
return err;
- be = !strcmp(platform_get_device_id(pdev)->name, "basic-mmio-gpio-be");
-
- bgc = devm_kzalloc(&pdev->dev, sizeof(*bgc), GFP_KERNEL);
- if (!bgc)
- return -ENOMEM;
+ be = !strcmp(platform_get_device_id(pdev)->name,
+ "basic-mmio-gpio-be");
- err = bgpio_init(bgc, dev, sz, dat, set, clr, dirout, dirin, be);
+ banks->nr_banks = 1;
+ err = bgpio_init(&banks->banks[0], dev, sz, dat, set, clr, dirout,
+ dirin, be);
if (err)
return err;
if (pdata) {
- bgc->gc.base = pdata->base;
+ banks->banks[0].gc.base = pdata->base;
if (pdata->ngpio > 0)
- bgc->gc.ngpio = pdata->ngpio;
+ banks->banks[0].gc.ngpio = pdata->ngpio;
}
- platform_set_drvdata(pdev, bgc);
+ return gpiochip_add(&banks->banks[0].gc);
+}
+
+static void bgpio_remove_all_banks(struct platform_device *pdev)
+{
+ struct bgpio_drvdata *banks = platform_get_drvdata(pdev);
+ unsigned int m;
+
+ for (m = 0; m < banks->nr_banks; ++m)
+ bgpio_remove(&banks->banks[m]);
+}
+
+#ifdef CONFIG_OF
+static int bgpio_of_add_one_bank(struct platform_device *pdev,
+ struct bgpio_chip *bgc,
+ struct device_node *np, void __iomem *iobase,
+ size_t reg_width_bytes, bool be)
+{
+ void __iomem *dat = NULL;
+ void __iomem *set = NULL;
+ void __iomem *clr = NULL;
+ void __iomem *dirout = NULL;
+ void __iomem *dirin = NULL;
+ u32 val;
+ int err;
+
+ if (of_property_read_u32(np, "regoffset-dat", &val))
+ return -EINVAL;
+ dat = iobase + val;
+
+ if (!of_property_read_u32(np, "regoffset-set", &val))
+ set = iobase + val;
+ if (!of_property_read_u32(np, "regoffset-clr", &val))
+ clr = iobase + val;
+ if (!of_property_read_u32(np, "regoffset-dirout", &val))
+ dirout = iobase + val;
+ if (!of_property_read_u32(np, "regoffset-dirin", &val))
+ dirin = iobase + val;
+
+ err = bgpio_init(bgc, &pdev->dev, reg_width_bytes, dat, set, clr,
+ dirout, dirin, be);
+ if (err)
+ return err;
+
+ if (of_property_read_u32(np, "gpio-generic,nr-gpio", &val))
+ return -EINVAL;
+
+ bgc->gc.ngpio = val;
+ bgc->gc.of_node = np;
return gpiochip_add(&bgc->gc);
}
+static int bgpio_of_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ void __iomem *iobase = of_iomap(np, 0);
+ int err = 0;
+ u32 val;
+ size_t reg_width_bytes;
+ bool be;
+ int nr_banks = 0;
+ struct bgpio_drvdata *banks;
+
+ if (!iobase)
+ return -EIO;
+
+ if (of_property_read_u32(np, "reg-io-width", &val))
+ return -EINVAL;
+ reg_width_bytes = val;
+
+ be = of_get_property(np, "gpio-generic,big-endian", NULL) ?
+ true : false;
+
+ for_each_child_of_node(pdev->dev.of_node, np)
+ ++nr_banks;
+
+ banks = bgpio_alloc_banks(&pdev->dev, nr_banks);
+ if (!banks)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, banks);
+ banks->nr_banks = 0;
+
+ for_each_child_of_node(pdev->dev.of_node, np) {
+ err = bgpio_of_add_one_bank(pdev,
+ &banks->banks[banks->nr_banks],
+ np, iobase, reg_width_bytes, be);
+ if (err)
+ goto out_remove;
+ ++banks->nr_banks;
+ }
+
+ return 0;
+
+out_remove:
+ bgpio_remove_all_banks(pdev);
+
+ return err;
+}
+
+static const struct of_device_id bgpio_of_id_table[] = {
+ { .compatible = "snps,dw-apb-gpio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bgpio_of_id_table);
+#else /* CONFIG_OF */
+static inline int bgpio_of_probe(struct platform_device *pdev)
+{
+ return -ENODEV;
+}
+
+#define bgpio_of_id_table NULL
+#endif /* CONFIG_OF */
+
+static int __devinit bgpio_pdev_probe(struct platform_device *pdev)
+{
+ if (platform_get_device_id(pdev))
+ return bgpio_platform_probe(pdev);
+ else
+ return bgpio_of_probe(pdev);
+}
+
static int __devexit bgpio_pdev_remove(struct platform_device *pdev)
{
- struct bgpio_chip *bgc = platform_get_drvdata(pdev);
+ bgpio_remove_all_banks(pdev);
- return bgpio_remove(bgc);
+ return 0;
}
static const struct platform_device_id bgpio_id_table[] = {
@@ -523,6 +668,7 @@ MODULE_DEVICE_TABLE(platform, bgpio_id_table);
static struct platform_driver bgpio_driver = {
.driver = {
.name = "basic-mmio-gpio",
+ .of_match_table = bgpio_of_id_table,
},
.id_table = bgpio_id_table,
.probe = bgpio_pdev_probe,
--
1.7.4.1
More information about the devicetree-discuss
mailing list