diff mbox

[v9,2/2] gpio: move clps711x, moxart, ts4800 and gpio-ge into gpio-mmio

Message ID 85c74586dbd0023306f012764b85d6aa168b4ae7.1462911225.git.chunkeey@googlemail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Christian Lamparter May 11, 2016, 9:34 a.m. UTC
This patch integrates the GPIO drivers for the following
boards, SoCs, etc. into gpio-mmio:
 - CLPS711X SoCs
 - MOXA ART SoC
 - TS-4800 FPGA DIO blocks and compatibles
 - GPIO controllers found on some GE Single Board Computers

Cc: Alexander Shiyan <shc_work@mail.ru>
Cc: Julien Grossholtz <julien.grossholtz@savoirfairelinux.com>
Cc: Martyn Welch <martyn.welch@ge.com>
Cc: Jonas Jensen <jonas.jensen@gmail.com>
Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
---
 drivers/gpio/Kconfig         |  16 +++-
 drivers/gpio/Makefile        |   4 -
 drivers/gpio/gpio-clps711x.c |  91 -------------------
 drivers/gpio/gpio-ge.c       | 114 -----------------------
 drivers/gpio/gpio-mmio.c     | 212 +++++++++++++++++++++++++++++++++++++++++++
 drivers/gpio/gpio-moxart.c   |  84 -----------------
 drivers/gpio/gpio-ts4800.c   |  81 -----------------
 7 files changed, 224 insertions(+), 378 deletions(-)
 delete mode 100644 drivers/gpio/gpio-clps711x.c
 delete mode 100644 drivers/gpio/gpio-ge.c
 delete mode 100644 drivers/gpio/gpio-moxart.c
 delete mode 100644 drivers/gpio/gpio-ts4800.c

Comments

Andy Shevchenko May 11, 2016, 12:19 p.m. UTC | #1
On Wed, May 11, 2016 at 12:34 PM, Christian Lamparter
<chunkeey@googlemail.com> wrote:
> This patch integrates the GPIO drivers for the following
> boards, SoCs, etc. into gpio-mmio:
>  - CLPS711X SoCs
>  - MOXA ART SoC
>  - TS-4800 FPGA DIO blocks and compatibles
>  - GPIO controllers found on some GE Single Board Computers
>
> Cc: Alexander Shiyan <shc_work@mail.ru>
> Cc: Julien Grossholtz <julien.grossholtz@savoirfairelinux.com>
> Cc: Martyn Welch <martyn.welch@ge.com>
> Cc: Jonas Jensen <jonas.jensen@gmail.com>
> Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>

FWIW:
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>


> ---
>  drivers/gpio/Kconfig         |  16 +++-
>  drivers/gpio/Makefile        |   4 -
>  drivers/gpio/gpio-clps711x.c |  91 -------------------
>  drivers/gpio/gpio-ge.c       | 114 -----------------------
>  drivers/gpio/gpio-mmio.c     | 212 +++++++++++++++++++++++++++++++++++++++++++
>  drivers/gpio/gpio-moxart.c   |  84 -----------------
>  drivers/gpio/gpio-ts4800.c   |  81 -----------------
>  7 files changed, 224 insertions(+), 378 deletions(-)
>  delete mode 100644 drivers/gpio/gpio-clps711x.c
>  delete mode 100644 drivers/gpio/gpio-ge.c
>  delete mode 100644 drivers/gpio/gpio-moxart.c
>  delete mode 100644 drivers/gpio/gpio-ts4800.c
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index a68d838..e4d1065 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -154,7 +154,7 @@ config GPIO_BRCMSTB
>  config GPIO_CLPS711X
>         tristate "CLPS711X GPIO support"
>         depends on ARCH_CLPS711X || COMPILE_TEST
> -       select GPIO_GENERIC
> +       select GPIO_GENERIC_PLATFORM
>         help
>           Say yes here to support GPIO on CLPS711X SoCs.
>
> @@ -196,7 +196,7 @@ config GPIO_ETRAXFS
>  config GPIO_GE_FPGA
>         bool "GE FPGA based GPIO"
>         depends on GE_FPGA
> -       select GPIO_GENERIC
> +       select GPIO_GENERIC_PLATFORM
>         help
>           Support for common GPIO functionality provided on some GE Single Board
>           Computers.
> @@ -209,6 +209,14 @@ config GPIO_GENERIC_PLATFORM
>         tristate "Generic memory-mapped GPIO controller support (MMIO platform device)"
>         select GPIO_GENERIC
>         help
> +         Select this to support many generic memory-mapped GPIO controllers.
> +
> +         This driver also includes support for the following GPIOs:
> +           CLPS711X SoCs
> +           MOXA ART SoC
> +           TS-4800 FPGA DIO blocks and compatibles.
> +           GPIOs found on some GE Single Board Computers.
> +
>           Say yes here to support basic platform_device memory-mapped GPIO controllers.
>
>  config GPIO_GRGPIO
> @@ -288,7 +296,7 @@ config GPIO_MM_LANTIQ
>  config GPIO_MOXART
>         bool "MOXART GPIO support"
>         depends on ARCH_MOXART || COMPILE_TEST
> -       select GPIO_GENERIC
> +       select GPIO_GENERIC_PLATFORM
>         help
>           Select this option to enable GPIO driver for
>           MOXA ART SoC devices.
> @@ -408,7 +416,7 @@ config GPIO_TS4800
>         tristate "TS-4800 DIO blocks and compatibles"
>         depends on OF_GPIO
>         depends on SOC_IMX51 || COMPILE_TEST
> -       select GPIO_GENERIC
> +       select GPIO_GENERIC_PLATFORM
>         help
>           This driver support TS-4800 FPGA GPIO controllers.
>
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 991598e..d8d63ae 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -31,7 +31,6 @@ obj-$(CONFIG_GPIO_ATH79)      += gpio-ath79.o
>  obj-$(CONFIG_GPIO_BCM_KONA)    += gpio-bcm-kona.o
>  obj-$(CONFIG_GPIO_BRCMSTB)     += gpio-brcmstb.o
>  obj-$(CONFIG_GPIO_BT8XX)       += gpio-bt8xx.o
> -obj-$(CONFIG_GPIO_CLPS711X)    += gpio-clps711x.o
>  obj-$(CONFIG_GPIO_CS5535)      += gpio-cs5535.o
>  obj-$(CONFIG_GPIO_CRYSTAL_COVE)        += gpio-crystalcove.o
>  obj-$(CONFIG_GPIO_DA9052)      += gpio-da9052.o
> @@ -43,7 +42,6 @@ obj-$(CONFIG_GPIO_EM)         += gpio-em.o
>  obj-$(CONFIG_GPIO_EP93XX)      += gpio-ep93xx.o
>  obj-$(CONFIG_GPIO_ETRAXFS)     += gpio-etraxfs.o
>  obj-$(CONFIG_GPIO_F7188X)      += gpio-f7188x.o
> -obj-$(CONFIG_GPIO_GE_FPGA)     += gpio-ge.o
>  obj-$(CONFIG_GPIO_GRGPIO)      += gpio-grgpio.o
>  obj-$(CONFIG_GPIO_ICH)         += gpio-ich.o
>  obj-$(CONFIG_GPIO_IOP)         += gpio-iop.o
> @@ -68,7 +66,6 @@ obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o
>  obj-$(CONFIG_GPIO_MCP23S08)    += gpio-mcp23s08.o
>  obj-$(CONFIG_GPIO_ML_IOH)      += gpio-ml-ioh.o
>  obj-$(CONFIG_GPIO_MM_LANTIQ)   += gpio-mm-lantiq.o
> -obj-$(CONFIG_GPIO_MOXART)      += gpio-moxart.o
>  obj-$(CONFIG_GPIO_MPC5200)     += gpio-mpc5200.o
>  obj-$(CONFIG_GPIO_MPC8XXX)     += gpio-mpc8xxx.o
>  obj-$(CONFIG_GPIO_MSIC)                += gpio-msic.o
> @@ -107,7 +104,6 @@ obj-$(CONFIG_GPIO_TPS65218) += gpio-tps65218.o
>  obj-$(CONFIG_GPIO_TPS6586X)    += gpio-tps6586x.o
>  obj-$(CONFIG_GPIO_TPS65910)    += gpio-tps65910.o
>  obj-$(CONFIG_GPIO_TPS65912)    += gpio-tps65912.o
> -obj-$(CONFIG_GPIO_TS4800)      += gpio-ts4800.o
>  obj-$(CONFIG_GPIO_TS5500)      += gpio-ts5500.o
>  obj-$(CONFIG_GPIO_TWL4030)     += gpio-twl4030.o
>  obj-$(CONFIG_GPIO_TWL6040)     += gpio-twl6040.o
> diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c
> deleted file mode 100644
> index 5a69025..0000000
> --- a/drivers/gpio/gpio-clps711x.c
> +++ /dev/null
> @@ -1,91 +0,0 @@
> -/*
> - *  CLPS711X GPIO driver
> - *
> - *  Copyright (C) 2012,2013 Alexander Shiyan <shc_work@mail.ru>
> - *
> - * 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/err.h>
> -#include <linux/module.h>
> -#include <linux/gpio/driver.h>
> -#include <linux/platform_device.h>
> -
> -static int clps711x_gpio_probe(struct platform_device *pdev)
> -{
> -       struct device_node *np = pdev->dev.of_node;
> -       void __iomem *dat, *dir;
> -       struct gpio_chip *gc;
> -       struct resource *res;
> -       int err, id = np ? of_alias_get_id(np, "gpio") : pdev->id;
> -
> -       if ((id < 0) || (id > 4))
> -               return -ENODEV;
> -
> -       gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
> -       if (!gc)
> -               return -ENOMEM;
> -
> -       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> -       dat = devm_ioremap_resource(&pdev->dev, res);
> -       if (IS_ERR(dat))
> -               return PTR_ERR(dat);
> -
> -       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> -       dir = devm_ioremap_resource(&pdev->dev, res);
> -       if (IS_ERR(dir))
> -               return PTR_ERR(dir);
> -
> -       switch (id) {
> -       case 3:
> -               /* PORTD is inverted logic for direction register */
> -               err = bgpio_init(gc, &pdev->dev, 1, dat, NULL, NULL,
> -                                NULL, dir, 0);
> -               break;
> -       default:
> -               err = bgpio_init(gc, &pdev->dev, 1, dat, NULL, NULL,
> -                                dir, NULL, 0);
> -               break;
> -       }
> -
> -       if (err)
> -               return err;
> -
> -       switch (id) {
> -       case 4:
> -               /* PORTE is 3 lines only */
> -               gc->ngpio = 3;
> -               break;
> -       default:
> -               break;
> -       }
> -
> -       gc->base = id * 8;
> -       gc->owner = THIS_MODULE;
> -       platform_set_drvdata(pdev, gc);
> -
> -       return devm_gpiochip_add_data(&pdev->dev, gc, NULL);
> -}
> -
> -static const struct of_device_id __maybe_unused clps711x_gpio_ids[] = {
> -       { .compatible = "cirrus,clps711x-gpio" },
> -       { }
> -};
> -MODULE_DEVICE_TABLE(of, clps711x_gpio_ids);
> -
> -static struct platform_driver clps711x_gpio_driver = {
> -       .driver = {
> -               .name           = "clps711x-gpio",
> -               .of_match_table = of_match_ptr(clps711x_gpio_ids),
> -       },
> -       .probe  = clps711x_gpio_probe,
> -};
> -module_platform_driver(clps711x_gpio_driver);
> -
> -MODULE_LICENSE("GPL");
> -MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
> -MODULE_DESCRIPTION("CLPS711X GPIO driver");
> -MODULE_ALIAS("platform:clps711x-gpio");
> diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c
> deleted file mode 100644
> index 8650b29..0000000
> --- a/drivers/gpio/gpio-ge.c
> +++ /dev/null
> @@ -1,114 +0,0 @@
> -/*
> - * Driver for GE FPGA based GPIO
> - *
> - * Author: Martyn Welch <martyn.welch@ge.com>
> - *
> - * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc.
> - *
> - * This file is licensed under the terms of the GNU General Public License
> - * version 2.  This program is licensed "as is" without any warranty of any
> - * kind, whether express or implied.
> - */
> -
> -/* TODO
> - *
> - * Configuration of output modes (totem-pole/open-drain)
> - * Interrupt configuration - interrupts are always generated the FPGA relies on
> - * the I/O interrupt controllers mask to stop them propergating
> - */
> -
> -#include <linux/kernel.h>
> -#include <linux/io.h>
> -#include <linux/slab.h>
> -#include <linux/of_device.h>
> -#include <linux/of_gpio.h>
> -#include <linux/of_address.h>
> -#include <linux/module.h>
> -#include <linux/gpio/driver.h>
> -
> -#define GEF_GPIO_DIRECT                0x00
> -#define GEF_GPIO_IN            0x04
> -#define GEF_GPIO_OUT           0x08
> -#define GEF_GPIO_TRIG          0x0C
> -#define GEF_GPIO_POLAR_A       0x10
> -#define GEF_GPIO_POLAR_B       0x14
> -#define GEF_GPIO_INT_STAT      0x18
> -#define GEF_GPIO_OVERRUN       0x1C
> -#define GEF_GPIO_MODE          0x20
> -
> -static const struct of_device_id gef_gpio_ids[] = {
> -       {
> -               .compatible     = "gef,sbc610-gpio",
> -               .data           = (void *)19,
> -       }, {
> -               .compatible     = "gef,sbc310-gpio",
> -               .data           = (void *)6,
> -       }, {
> -               .compatible     = "ge,imp3a-gpio",
> -               .data           = (void *)16,
> -       },
> -       { }
> -};
> -MODULE_DEVICE_TABLE(of, gef_gpio_ids);
> -
> -static int __init gef_gpio_probe(struct platform_device *pdev)
> -{
> -       const struct of_device_id *of_id =
> -               of_match_device(gef_gpio_ids, &pdev->dev);
> -       struct gpio_chip *gc;
> -       void __iomem *regs;
> -       int ret;
> -
> -       gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
> -       if (!gc)
> -               return -ENOMEM;
> -
> -       regs = of_iomap(pdev->dev.of_node, 0);
> -       if (!regs)
> -               return -ENOMEM;
> -
> -       ret = bgpio_init(gc, &pdev->dev, 4, regs + GEF_GPIO_IN,
> -                        regs + GEF_GPIO_OUT, NULL, NULL,
> -                        regs + GEF_GPIO_DIRECT, BGPIOF_BIG_ENDIAN_BYTE_ORDER);
> -       if (ret) {
> -               dev_err(&pdev->dev, "bgpio_init failed\n");
> -               goto err0;
> -       }
> -
> -       /* Setup pointers to chip functions */
> -       gc->label = devm_kstrdup(&pdev->dev, pdev->dev.of_node->full_name,
> -                                    GFP_KERNEL);
> -       if (!gc->label) {
> -               ret = -ENOMEM;
> -               goto err0;
> -       }
> -
> -       gc->base = -1;
> -       gc->ngpio = (u16)(uintptr_t)of_id->data;
> -       gc->of_gpio_n_cells = 2;
> -       gc->of_node = pdev->dev.of_node;
> -
> -       /* This function adds a memory mapped GPIO chip */
> -       ret = devm_gpiochip_add_data(&pdev->dev, gc, NULL);
> -       if (ret)
> -               goto err0;
> -
> -       return 0;
> -err0:
> -       iounmap(regs);
> -       pr_err("%s: GPIO chip registration failed\n",
> -                       pdev->dev.of_node->full_name);
> -       return ret;
> -};
> -
> -static struct platform_driver gef_gpio_driver = {
> -       .driver = {
> -               .name           = "gef-gpio",
> -               .of_match_table = gef_gpio_ids,
> -       },
> -};
> -module_platform_driver_probe(gef_gpio_driver, gef_gpio_probe);
> -
> -MODULE_DESCRIPTION("GE I/O FPGA GPIO driver");
> -MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com");
> -MODULE_LICENSE("GPL");
> diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c
> index f72e40e..1de9172 100644
> --- a/drivers/gpio/gpio-mmio.c
> +++ b/drivers/gpio/gpio-mmio.c
> @@ -586,8 +586,220 @@ static int bgpio_basic_mmio_parse_dt(struct platform_device *pdev,
>         return 0;
>  }
>
> +static int clps711x_parse_dt(struct platform_device *pdev,
> +                            struct bgpio_pdata *pdata,
> +                            unsigned long *flags)
> +{
> +       struct device_node *np = pdev->dev.of_node;
> +       struct resource *res;
> +       const char *dir_reg_name;
> +       int id = np ? of_alias_get_id(np, "gpio") : pdev->id;
> +
> +       switch (id) {
> +       case 0:
> +       case 1:
> +       case 2:
> +               pdata->ngpio = 0; /* determined by register width */
> +               dir_reg_name = "dirout";
> +               break;
> +       case 3:
> +               pdata->ngpio = 0; /* determined by register width */
> +               /* PORTD is inverted logic for direction register */
> +               dir_reg_name = "dirin";
> +               break;
> +       case 4:
> +               pdata->ngpio = 3; /* PORTE is 3 lines only */
> +               dir_reg_name = "dirout";
> +               break;
> +       default:
> +               return -ENODEV;
> +       }
> +
> +       pdata->base = id * 8;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (!res)
> +               return -EINVAL;
> +       if (!res->name || strcmp("dat", res->name))
> +               res->name = devm_kstrdup(&pdev->dev, "dat", GFP_KERNEL);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +       if (!res)
> +               return -EINVAL;
> +       if (!res->name || strcmp(dir_reg_name, res->name))
> +               res->name = devm_kstrdup(&pdev->dev, dir_reg_name, GFP_KERNEL);
> +
> +       return 0;
> +}
> +
> +static int ge_dt_cb(struct platform_device *pdev,
> +                   struct bgpio_pdata *pdata,
> +                   unsigned long *flags)
> +{
> +       struct device_node *np = pdev->dev.of_node;
> +
> +       pdata->label = devm_kstrdup(&pdev->dev, np->full_name, GFP_KERNEL);
> +       if (!pdata->label)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +static int moxart_dt_cb(struct platform_device *pdev,
> +                       struct bgpio_pdata *pdata,
> +                       unsigned long *flags)
> +{
> +       pdata->base = 0;
> +       pdata->label = "moxart-gpio";
> +       return 0;
> +}
> +
> +
> +static int ts4800_dt_cb(struct platform_device *pdev,
> +                       struct bgpio_pdata *pdata,
> +                       unsigned long *flags)
> +{
> +       int err;
> +
> +       err = of_property_read_u32(pdev->dev.of_node, "ngpios", &pdata->ngpio);
> +       if (err == -EINVAL) {
> +               pdata->ngpio = 16;
> +               return 0;
> +       }
> +
> +       return err;
> +}
> +
> +/*
> + * bgpio_init() needs up to five named mmio register resources.
> + * These currently are dat, set, clr, [dirout | dirin]. There
> + * is no particular order or predefined place for the entries.
> + * However, dirout and dirin are mutually exclusive.
> + */
> +#define __NUM_COMPAT_OVERRIDE_MMIO_RESOURCES 4
> +
> +struct compat_gpio_device_data {
> +       unsigned int expected_resource_size;
> +       unsigned int ngpio;
> +       resource_size_t register_width;
> +       unsigned long flags;
> +       int (*call_back)(struct platform_device *pdev,
> +                        struct bgpio_pdata *pdata,
> +                        unsigned long *flags);
> +       struct resource_replacement {
> +               resource_size_t start_offset;
> +               const char *name;
> +       } resources[__NUM_COMPAT_OVERRIDE_MMIO_RESOURCES];
> +};
> +#undef __NUM_COMPAT_OVERRIDE_MMIO_RESOURCES
> +
> +#define ADD_COMPAT_REGISTER(_name, _offset)    \
> +       { .name = (_name), .start_offset = (_offset) }
> +
> +#define ADD_COMPAT_GPIO(_comp, _sz, _ngpio, _width, _cb, _f, _res...)  \
> +       {                                                               \
> +         .compatible = (_comp),                                        \
> +         .data = &(struct compat_gpio_device_data) {                   \
> +               .expected_resource_size = (_sz),                        \
> +               .ngpio = (_ngpio),                                      \
> +               .register_width = (_width),                             \
> +               .flags = (_f),                                          \
> +               .call_back = (_cb),                                     \
> +               .resources = { _res },                                  \
> +       }                                                               \
> +}
> +
> +#define ADD_COMPAT_GE_GPIO(_name, _ngpio)                              \
> +       ADD_COMPAT_GPIO(_name, 0x24, _ngpio, 0x4, ge_dt_cb,             \
> +                BGPIOF_BIG_ENDIAN_BYTE_ORDER,                          \
> +                ADD_COMPAT_REGISTER("dat", 0x04),                      \
> +                ADD_COMPAT_REGISTER("set", 0x08),                      \
> +                ADD_COMPAT_REGISTER("dirin", 0x00))                    \
> +
> +static const struct of_device_id compat_gpio_devices[] = {
> +       ADD_COMPAT_GE_GPIO("ge,imp3a-gpio", 16),
> +       ADD_COMPAT_GE_GPIO("gef,sbc310-gpio", 6),
> +       ADD_COMPAT_GE_GPIO("gef,sbc610-gpio", 19),
> +       ADD_COMPAT_GPIO("moxa,moxart-gpio", 0xc, 0, 0x4, moxart_dt_cb,
> +                BGPIOF_READ_OUTPUT_REG_SET,
> +                ADD_COMPAT_REGISTER("dat", 0x04),
> +                ADD_COMPAT_REGISTER("set", 0x00),
> +                ADD_COMPAT_REGISTER("dirout", 0x08)),
> +       ADD_COMPAT_GPIO("technologic,ts4800-gpio", 0x6, 16, 0x2, ts4800_dt_cb,
> +                0, ADD_COMPAT_REGISTER("dat", 0x00),
> +                ADD_COMPAT_REGISTER("set", 0x02),
> +                ADD_COMPAT_REGISTER("dirout", 0x04)),
> +};
> +
> +#undef ADD_COMPAT_GE_GPIO
> +#undef ADD_COMPAT_GPIO
> +#undef ADD_COMPAT_REGISTER
> +
> +static int compat_parse_dt(struct platform_device *pdev,
> +                          struct bgpio_pdata *pdata,
> +                          unsigned long *flags)
> +{
> +       const struct device_node *node = pdev->dev.of_node;
> +       const struct compat_gpio_device_data *entry;
> +       const struct of_device_id *of_id;
> +       struct resource *res;
> +       int err;
> +
> +       of_id = of_match_node(compat_gpio_devices, node);
> +       if (!of_id)
> +               return -ENODEV;
> +
> +       entry = of_id->data;
> +       if (!entry || !entry->resources[0].name)
> +               return -EINVAL;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (!res)
> +               return -EINVAL;
> +
> +       if (!res->name || strcmp(entry->resources[0].name, res->name)) {
> +               struct resource nres[ARRAY_SIZE(entry->resources)];
> +               size_t i;
> +
> +               if (resource_size(res) != entry->expected_resource_size)
> +                       return -EINVAL;
> +
> +               for (i = 0; i < ARRAY_SIZE(entry->resources); i++) {
> +                       if (!entry->resources[i].name)
> +                               continue;
> +
> +                       nres[i].name = devm_kstrdup(&pdev->dev,
> +                               entry->resources[i].name, GFP_KERNEL);
> +                       nres[i].start = res->start +
> +                               entry->resources[i].start_offset;
> +                       nres[i].end = nres[i].start +
> +                               entry->register_width - 1;
> +                       nres[i].flags = IORESOURCE_MEM;
> +               }
> +
> +               err = platform_device_add_resources(pdev, nres, i);
> +               if (err)
> +                       return err;
> +       }
> +
> +       pdata->base = -1;
> +       pdata->ngpio = entry->ngpio;
> +       *flags = entry->flags;
> +
> +       if (entry->call_back)
> +               err = entry->call_back(pdev, pdata, flags);
> +
> +       return err;
> +}
> +
>  static const struct of_device_id bgpio_of_match[] = {
>         { .compatible = "wd,mbl-gpio", .data = bgpio_basic_mmio_parse_dt },
> +       { .compatible = "cirrus,clps711x-gpio", .data = clps711x_parse_dt },
> +       { .compatible = "ge,imp3a-gpio", .data = compat_parse_dt },
> +       { .compatible = "gef,sbc310-gpio", .data = compat_parse_dt },
> +       { .compatible = "gef,sbc610-gpio", .data = compat_parse_dt },
> +       { .compatible = "moxa,moxart-gpio", .data = compat_parse_dt },
> +       { .compatible = "technologic,ts4800-gpio", .data = compat_parse_dt },
>         { }
>  };
>  MODULE_DEVICE_TABLE(of, bgpio_of_match);
> diff --git a/drivers/gpio/gpio-moxart.c b/drivers/gpio/gpio-moxart.c
> deleted file mode 100644
> index d58d389..0000000
> --- a/drivers/gpio/gpio-moxart.c
> +++ /dev/null
> @@ -1,84 +0,0 @@
> -/*
> - * MOXA ART SoCs GPIO driver.
> - *
> - * Copyright (C) 2013 Jonas Jensen
> - *
> - * Jonas Jensen <jonas.jensen@gmail.com>
> - *
> - * This file is licensed under the terms of the GNU General Public
> - * License version 2.  This program is licensed "as is" without any
> - * warranty of any kind, whether express or implied.
> - */
> -
> -#include <linux/err.h>
> -#include <linux/init.h>
> -#include <linux/irq.h>
> -#include <linux/io.h>
> -#include <linux/platform_device.h>
> -#include <linux/of_address.h>
> -#include <linux/of_gpio.h>
> -#include <linux/pinctrl/consumer.h>
> -#include <linux/delay.h>
> -#include <linux/timer.h>
> -#include <linux/bitops.h>
> -#include <linux/gpio/driver.h>
> -
> -#define GPIO_DATA_OUT          0x00
> -#define GPIO_DATA_IN           0x04
> -#define GPIO_PIN_DIRECTION     0x08
> -
> -static int moxart_gpio_probe(struct platform_device *pdev)
> -{
> -       struct device *dev = &pdev->dev;
> -       struct resource *res;
> -       struct gpio_chip *gc;
> -       void __iomem *base;
> -       int ret;
> -
> -       gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
> -       if (!gc)
> -               return -ENOMEM;
> -
> -       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> -       base = devm_ioremap_resource(dev, res);
> -       if (IS_ERR(base))
> -               return PTR_ERR(base);
> -
> -       ret = bgpio_init(gc, dev, 4, base + GPIO_DATA_IN,
> -                        base + GPIO_DATA_OUT, NULL,
> -                        base + GPIO_PIN_DIRECTION, NULL,
> -                        BGPIOF_READ_OUTPUT_REG_SET);
> -       if (ret) {
> -               dev_err(&pdev->dev, "bgpio_init failed\n");
> -               return ret;
> -       }
> -
> -       gc->label = "moxart-gpio";
> -       gc->request = gpiochip_generic_request;
> -       gc->free = gpiochip_generic_free;
> -       gc->base = 0;
> -       gc->owner = THIS_MODULE;
> -
> -       ret = devm_gpiochip_add_data(dev, gc, NULL);
> -       if (ret) {
> -               dev_err(dev, "%s: gpiochip_add failed\n",
> -                       dev->of_node->full_name);
> -               return ret;
> -       }
> -
> -       return ret;
> -}
> -
> -static const struct of_device_id moxart_gpio_match[] = {
> -       { .compatible = "moxa,moxart-gpio" },
> -       { }
> -};
> -
> -static struct platform_driver moxart_gpio_driver = {
> -       .driver = {
> -               .name           = "moxart-gpio",
> -               .of_match_table = moxart_gpio_match,
> -       },
> -       .probe  = moxart_gpio_probe,
> -};
> -builtin_platform_driver(moxart_gpio_driver);
> diff --git a/drivers/gpio/gpio-ts4800.c b/drivers/gpio/gpio-ts4800.c
> deleted file mode 100644
> index 0c144a7..0000000
> --- a/drivers/gpio/gpio-ts4800.c
> +++ /dev/null
> @@ -1,81 +0,0 @@
> -/*
> - * GPIO driver for the TS-4800 board
> - *
> - * Copyright (c) 2016 - Savoir-faire Linux
> - *
> - * This file is licensed under the terms of the GNU General Public
> - * License version 2. This program is licensed "as is" without any
> - * warranty of any kind, whether express or implied.
> - */
> -
> -#include <linux/gpio/driver.h>
> -#include <linux/of_address.h>
> -#include <linux/of_device.h>
> -#include <linux/platform_device.h>
> -
> -#define DEFAULT_PIN_NUMBER      16
> -#define INPUT_REG_OFFSET        0x00
> -#define OUTPUT_REG_OFFSET       0x02
> -#define DIRECTION_REG_OFFSET    0x04
> -
> -static int ts4800_gpio_probe(struct platform_device *pdev)
> -{
> -       struct device_node *node;
> -       struct gpio_chip *chip;
> -       struct resource *res;
> -       void __iomem *base_addr;
> -       int retval;
> -       u32 ngpios;
> -
> -       chip = devm_kzalloc(&pdev->dev, sizeof(struct gpio_chip), GFP_KERNEL);
> -       if (!chip)
> -               return -ENOMEM;
> -
> -       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> -       base_addr = devm_ioremap_resource(&pdev->dev, res);
> -       if (IS_ERR(base_addr))
> -               return PTR_ERR(base_addr);
> -
> -       node = pdev->dev.of_node;
> -       if (!node)
> -               return -EINVAL;
> -
> -       retval = of_property_read_u32(node, "ngpios", &ngpios);
> -       if (retval == -EINVAL)
> -               ngpios = DEFAULT_PIN_NUMBER;
> -       else if (retval)
> -               return retval;
> -
> -       retval = bgpio_init(chip, &pdev->dev, 2, base_addr + INPUT_REG_OFFSET,
> -                           base_addr + OUTPUT_REG_OFFSET, NULL,
> -                           base_addr + DIRECTION_REG_OFFSET, NULL, 0);
> -       if (retval) {
> -               dev_err(&pdev->dev, "bgpio_init failed\n");
> -               return retval;
> -       }
> -
> -       chip->ngpio = ngpios;
> -
> -       platform_set_drvdata(pdev, chip);
> -
> -       return devm_gpiochip_add_data(&pdev->dev, chip, NULL);
> -}
> -
> -static const struct of_device_id ts4800_gpio_of_match[] = {
> -       { .compatible = "technologic,ts4800-gpio", },
> -       {},
> -};
> -
> -static struct platform_driver ts4800_gpio_driver = {
> -       .driver = {
> -                  .name = "ts4800-gpio",
> -                  .of_match_table = ts4800_gpio_of_match,
> -                  },
> -       .probe = ts4800_gpio_probe,
> -};
> -
> -module_platform_driver_probe(ts4800_gpio_driver, ts4800_gpio_probe);
> -
> -MODULE_AUTHOR("Julien Grossholtz <julien.grossholtz@savoirfairelinux.com>");
> -MODULE_DESCRIPTION("TS4800 FPGA GPIO driver");
> -MODULE_LICENSE("GPL v2");
> --
> 2.8.1
>
Alexandre Courbot May 12, 2016, 10:30 a.m. UTC | #2
On Wednesday, May 11, 2016 6:34:35 PM JST, Christian Lamparter wrote:
> This patch integrates the GPIO drivers for the following
> boards, SoCs, etc. into gpio-mmio:
>  - CLPS711X SoCs
>  - MOXA ART SoC
>  - TS-4800 FPGA DIO blocks and compatibles
>  - GPIO controllers found on some GE Single Board Computers
>
> Cc: Alexander Shiyan <shc_work@mail.ru>
> Cc: Julien Grossholtz <julien.grossholtz@savoirfairelinux.com>
> Cc: Martyn Welch <martyn.welch@ge.com>
> Cc: Jonas Jensen <jonas.jensen@gmail.com>
> Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
> ---
>  drivers/gpio/Kconfig         |  16 +++-
>  drivers/gpio/Makefile        |   4 -
>  drivers/gpio/gpio-clps711x.c |  91 -------------------
>  drivers/gpio/gpio-ge.c       | 114 -----------------------
>  drivers/gpio/gpio-mmio.c     | 212 
> +++++++++++++++++++++++++++++++++++++++++++
>  drivers/gpio/gpio-moxart.c   |  84 -----------------
>  drivers/gpio/gpio-ts4800.c   |  81 -----------------
>  7 files changed, 224 insertions(+), 378 deletions(-)
>  delete mode 100644 drivers/gpio/gpio-clps711x.c
>  delete mode 100644 drivers/gpio/gpio-ge.c
>  delete mode 100644 drivers/gpio/gpio-moxart.c
>  delete mode 100644 drivers/gpio/gpio-ts4800.c
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index a68d838..e4d1065 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -154,7 +154,7 @@ config GPIO_BRCMSTB
>  config GPIO_CLPS711X
>  	tristate "CLPS711X GPIO support"
>  	depends on ARCH_CLPS711X || COMPILE_TEST
> -	select GPIO_GENERIC
> +	select GPIO_GENERIC_PLATFORM
>  	help
>  	  Say yes here to support GPIO on CLPS711X SoCs.
>  
> @@ -196,7 +196,7 @@ config GPIO_ETRAXFS
>  config GPIO_GE_FPGA
>  	bool "GE FPGA based GPIO"
>  	depends on GE_FPGA
> -	select GPIO_GENERIC
> +	select GPIO_GENERIC_PLATFORM
>  	help
>  	  Support for common GPIO functionality provided on some GE Single Board
>  	  Computers.
> @@ -209,6 +209,14 @@ config GPIO_GENERIC_PLATFORM
>  	tristate "Generic memory-mapped GPIO controller support (MMIO 
> platform device)"
>  	select GPIO_GENERIC
>  	help
> +	  Select this to support many generic memory-mapped GPIO controllers.
> +
> +	  This driver also includes support for the following GPIOs:
> +	    CLPS711X SoCs
> +	    MOXA ART SoC
> +	    TS-4800 FPGA DIO blocks and compatibles.
> +	    GPIOs found on some GE Single Board Computers.
> +
>  	  Say yes here to support basic platform_device memory-mapped 
> GPIO controllers.
>  
>  config GPIO_GRGPIO
> @@ -288,7 +296,7 @@ config GPIO_MM_LANTIQ
>  config GPIO_MOXART
>  	bool "MOXART GPIO support"
>  	depends on ARCH_MOXART || COMPILE_TEST
> -	select GPIO_GENERIC
> +	select GPIO_GENERIC_PLATFORM
>  	help
>  	  Select this option to enable GPIO driver for
>  	  MOXA ART SoC devices.
> @@ -408,7 +416,7 @@ config GPIO_TS4800
>  	tristate "TS-4800 DIO blocks and compatibles"
>  	depends on OF_GPIO
>  	depends on SOC_IMX51 || COMPILE_TEST
> -	select GPIO_GENERIC
> +	select GPIO_GENERIC_PLATFORM
>  	help
>  	  This driver support TS-4800 FPGA GPIO controllers.
>  
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 991598e..d8d63ae 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -31,7 +31,6 @@ obj-$(CONFIG_GPIO_ATH79)	+= gpio-ath79.o
>  obj-$(CONFIG_GPIO_BCM_KONA)	+= gpio-bcm-kona.o
>  obj-$(CONFIG_GPIO_BRCMSTB)	+= gpio-brcmstb.o
>  obj-$(CONFIG_GPIO_BT8XX)	+= gpio-bt8xx.o
> -obj-$(CONFIG_GPIO_CLPS711X)	+= gpio-clps711x.o
>  obj-$(CONFIG_GPIO_CS5535)	+= gpio-cs5535.o
>  obj-$(CONFIG_GPIO_CRYSTAL_COVE)	+= gpio-crystalcove.o
>  obj-$(CONFIG_GPIO_DA9052)	+= gpio-da9052.o
> @@ -43,7 +42,6 @@ obj-$(CONFIG_GPIO_EM)		+= gpio-em.o
>  obj-$(CONFIG_GPIO_EP93XX)	+= gpio-ep93xx.o
>  obj-$(CONFIG_GPIO_ETRAXFS)	+= gpio-etraxfs.o
>  obj-$(CONFIG_GPIO_F7188X)	+= gpio-f7188x.o
> -obj-$(CONFIG_GPIO_GE_FPGA)	+= gpio-ge.o
>  obj-$(CONFIG_GPIO_GRGPIO)	+= gpio-grgpio.o
>  obj-$(CONFIG_GPIO_ICH)		+= gpio-ich.o
>  obj-$(CONFIG_GPIO_IOP)		+= gpio-iop.o
> @@ -68,7 +66,6 @@ obj-$(CONFIG_GPIO_MC9S08DZ60)	+= gpio-mc9s08dz60.o
>  obj-$(CONFIG_GPIO_MCP23S08)	+= gpio-mcp23s08.o
>  obj-$(CONFIG_GPIO_ML_IOH)	+= gpio-ml-ioh.o
>  obj-$(CONFIG_GPIO_MM_LANTIQ)	+= gpio-mm-lantiq.o
> -obj-$(CONFIG_GPIO_MOXART)	+= gpio-moxart.o
>  obj-$(CONFIG_GPIO_MPC5200)	+= gpio-mpc5200.o
>  obj-$(CONFIG_GPIO_MPC8XXX)	+= gpio-mpc8xxx.o
>  obj-$(CONFIG_GPIO_MSIC)		+= gpio-msic.o
> @@ -107,7 +104,6 @@ obj-$(CONFIG_GPIO_TPS65218)	+= gpio-tps65218.o
>  obj-$(CONFIG_GPIO_TPS6586X)	+= gpio-tps6586x.o
>  obj-$(CONFIG_GPIO_TPS65910)	+= gpio-tps65910.o
>  obj-$(CONFIG_GPIO_TPS65912)	+= gpio-tps65912.o
> -obj-$(CONFIG_GPIO_TS4800)	+= gpio-ts4800.o
>  obj-$(CONFIG_GPIO_TS5500)	+= gpio-ts5500.o
>  obj-$(CONFIG_GPIO_TWL4030)	+= gpio-twl4030.o
>  obj-$(CONFIG_GPIO_TWL6040)	+= gpio-twl6040.o
> diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c
> deleted file mode 100644
> index 5a69025..0000000
> --- a/drivers/gpio/gpio-clps711x.c
> +++ /dev/null
> @@ -1,91 +0,0 @@
> -/*
> - *  CLPS711X GPIO driver
> - *
> - *  Copyright (C) 2012,2013 Alexander Shiyan <shc_work@mail.ru>
> - *
> - * 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/err.h>
> -#include <linux/module.h>
> -#include <linux/gpio/driver.h>
> -#include <linux/platform_device.h>
> -
> -static int clps711x_gpio_probe(struct platform_device *pdev)
> -{
> -	struct device_node *np = pdev->dev.of_node;
> -	void __iomem *dat, *dir;
> -	struct gpio_chip *gc;
> -	struct resource *res;
> -	int err, id = np ? of_alias_get_id(np, "gpio") : pdev->id;
> -
> -	if ((id < 0) || (id > 4))
> -		return -ENODEV;
> -
> -	gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
> -	if (!gc)
> -		return -ENOMEM;
> -
> -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> -	dat = devm_ioremap_resource(&pdev->dev, res);
> -	if (IS_ERR(dat))
> -		return PTR_ERR(dat);
> -
> -	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> -	dir = devm_ioremap_resource(&pdev->dev, res);
> -	if (IS_ERR(dir))
> -		return PTR_ERR(dir);
> -
> -	switch (id) {
> -	case 3:
> -		/* PORTD is inverted logic for direction register */
> -		err = bgpio_init(gc, &pdev->dev, 1, dat, NULL, NULL,
> -				 NULL, dir, 0);
> -		break;
> -	default:
> -		err = bgpio_init(gc, &pdev->dev, 1, dat, NULL, NULL,
> -				 dir, NULL, 0);
> -		break;
> -	}
> -
> -	if (err)
> -		return err;
> -
> -	switch (id) {
> -	case 4:
> -		/* PORTE is 3 lines only */
> -		gc->ngpio = 3;
> -		break;
> -	default:
> -		break;
> -	}
> -
> -	gc->base = id * 8;
> -	gc->owner = THIS_MODULE;
> -	platform_set_drvdata(pdev, gc);
> -
> -	return devm_gpiochip_add_data(&pdev->dev, gc, NULL);
> -}
> -
> -static const struct of_device_id __maybe_unused clps711x_gpio_ids[] = {
> -	{ .compatible = "cirrus,clps711x-gpio" },
> -	{ }
> -};
> -MODULE_DEVICE_TABLE(of, clps711x_gpio_ids);
> -
> -static struct platform_driver clps711x_gpio_driver = {
> -	.driver	= {
> -		.name		= "clps711x-gpio",
> -		.of_match_table	= of_match_ptr(clps711x_gpio_ids),
> -	},
> -	.probe	= clps711x_gpio_probe,
> -};
> -module_platform_driver(clps711x_gpio_driver);
> -
> -MODULE_LICENSE("GPL");
> -MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
> -MODULE_DESCRIPTION("CLPS711X GPIO driver");
> -MODULE_ALIAS("platform:clps711x-gpio");
> diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c
> deleted file mode 100644
> index 8650b29..0000000
> --- a/drivers/gpio/gpio-ge.c
> +++ /dev/null
> @@ -1,114 +0,0 @@
> -/*
> - * Driver for GE FPGA based GPIO
> - *
> - * Author: Martyn Welch <martyn.welch@ge.com>
> - *
> - * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc.
> - *
> - * This file is licensed under the terms of the GNU General Public License
> - * version 2.  This program is licensed "as is" without any warranty of any
> - * kind, whether express or implied.
> - */
> -
> -/* TODO
> - *
> - * Configuration of output modes (totem-pole/open-drain)
> - * Interrupt configuration - interrupts are always generated 
> the FPGA relies on
> - * the I/O interrupt controllers mask to stop them propergating
> - */
> -
> -#include <linux/kernel.h>
> -#include <linux/io.h>
> -#include <linux/slab.h>
> -#include <linux/of_device.h>
> -#include <linux/of_gpio.h>
> -#include <linux/of_address.h>
> -#include <linux/module.h>
> -#include <linux/gpio/driver.h>
> -
> -#define GEF_GPIO_DIRECT		0x00
> -#define GEF_GPIO_IN		0x04
> -#define GEF_GPIO_OUT		0x08
> -#define GEF_GPIO_TRIG		0x0C
> -#define GEF_GPIO_POLAR_A	0x10
> -#define GEF_GPIO_POLAR_B	0x14
> -#define GEF_GPIO_INT_STAT	0x18
> -#define GEF_GPIO_OVERRUN	0x1C
> -#define GEF_GPIO_MODE		0x20
> -
> -static const struct of_device_id gef_gpio_ids[] = {
> -	{
> -		.compatible	= "gef,sbc610-gpio",
> -		.data		= (void *)19,
> -	}, {
> -		.compatible	= "gef,sbc310-gpio",
> -		.data		= (void *)6,
> -	}, {
> -		.compatible	= "ge,imp3a-gpio",
> -		.data		= (void *)16,
> -	},
> -	{ }
> -};
> -MODULE_DEVICE_TABLE(of, gef_gpio_ids);
> -
> -static int __init gef_gpio_probe(struct platform_device *pdev)
> -{
> -	const struct of_device_id *of_id =
> -		of_match_device(gef_gpio_ids, &pdev->dev);
> -	struct gpio_chip *gc;
> -	void __iomem *regs;
> -	int ret;
> -
> -	gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
> -	if (!gc)
> -		return -ENOMEM;
> -
> -	regs = of_iomap(pdev->dev.of_node, 0);
> -	if (!regs)
> -		return -ENOMEM;
> -
> -	ret = bgpio_init(gc, &pdev->dev, 4, regs + GEF_GPIO_IN,
> -			 regs + GEF_GPIO_OUT, NULL, NULL,
> -			 regs + GEF_GPIO_DIRECT, BGPIOF_BIG_ENDIAN_BYTE_ORDER);
> -	if (ret) {
> -		dev_err(&pdev->dev, "bgpio_init failed\n");
> -		goto err0;
> -	}
> -
> -	/* Setup pointers to chip functions */
> -	gc->label = devm_kstrdup(&pdev->dev, pdev->dev.of_node->full_name,
> -				     GFP_KERNEL);
> -	if (!gc->label) {
> -		ret = -ENOMEM;
> -		goto err0;
> -	}
> -
> -	gc->base = -1;
> -	gc->ngpio = (u16)(uintptr_t)of_id->data;
> -	gc->of_gpio_n_cells = 2;
> -	gc->of_node = pdev->dev.of_node;
> -
> -	/* This function adds a memory mapped GPIO chip */
> -	ret = devm_gpiochip_add_data(&pdev->dev, gc, NULL);
> -	if (ret)
> -		goto err0;
> -
> -	return 0;
> -err0:
> -	iounmap(regs);
> -	pr_err("%s: GPIO chip registration failed\n",
> -			pdev->dev.of_node->full_name);
> -	return ret;
> -};
> -
> -static struct platform_driver gef_gpio_driver = {
> -	.driver = {
> -		.name		= "gef-gpio",
> -		.of_match_table	= gef_gpio_ids,
> -	},
> -};
> -module_platform_driver_probe(gef_gpio_driver, gef_gpio_probe);
> -
> -MODULE_DESCRIPTION("GE I/O FPGA GPIO driver");
> -MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com");
> -MODULE_LICENSE("GPL");
> diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c
> index f72e40e..1de9172 100644
> --- a/drivers/gpio/gpio-mmio.c
> +++ b/drivers/gpio/gpio-mmio.c
> @@ -586,8 +586,220 @@ static int 
> bgpio_basic_mmio_parse_dt(struct platform_device *pdev,
>  	return 0;
>  }
>  
> +static int clps711x_parse_dt(struct platform_device *pdev,
> +			     struct bgpio_pdata *pdata,
> +			     unsigned long *flags)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct resource *res;
> +	const char *dir_reg_name;
> +	int id = np ? of_alias_get_id(np, "gpio") : pdev->id;
> +
> +	switch (id) {
> +	case 0:
> +	case 1:
> +	case 2:
> +		pdata->ngpio = 0; /* determined by register width */
> +		dir_reg_name = "dirout";
> +		break;
> +	case 3:
> +		pdata->ngpio = 0; /* determined by register width */
> +		/* PORTD is inverted logic for direction register */
> +		dir_reg_name = "dirin";
> +		break;
> +	case 4:
> +		pdata->ngpio = 3; /* PORTE is 3 lines only */
> +		dir_reg_name = "dirout";
> +		break;
> +	default:
> +		return -ENODEV;
> +	}
> +
> +	pdata->base = id * 8;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res)
> +		return -EINVAL;
> +	if (!res->name || strcmp("dat", res->name))
> +		res->name = devm_kstrdup(&pdev->dev, "dat", GFP_KERNEL);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	if (!res)
> +		return -EINVAL;
> +	if (!res->name || strcmp(dir_reg_name, res->name))
> +		res->name = devm_kstrdup(&pdev->dev, dir_reg_name, GFP_KERNEL);

Ouch, I don't like this. Also the probability that the "if (!res->name || 
...)" condition is not met is so small that I would not bother with it and 
do the kstrdup unconditionally. But maybe we can remove this altogether - 
see below...

> +
> +	return 0;
> +}
> +
> +static int ge_dt_cb(struct platform_device *pdev,
> +		    struct bgpio_pdata *pdata,
> +		    unsigned long *flags)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +
> +	pdata->label = devm_kstrdup(&pdev->dev, np->full_name, GFP_KERNEL);
> +	if (!pdata->label)
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +
> +static int moxart_dt_cb(struct platform_device *pdev,
> +			struct bgpio_pdata *pdata,
> +			unsigned long *flags)
> +{
> +	pdata->base = 0;
> +	pdata->label = "moxart-gpio";
> +	return 0;
> +}
> +
> +
> +static int ts4800_dt_cb(struct platform_device *pdev,
> +			struct bgpio_pdata *pdata,
> +			unsigned long *flags)
> +{
> +	int err;
> +
> +	err = of_property_read_u32(pdev->dev.of_node, "ngpios", &pdata->ngpio);
> +	if (err == -EINVAL) {
> +		pdata->ngpio = 16;
> +		return 0;
> +	}
> +
> +	return err;
> +}
> +
> +/*
> + * bgpio_init() needs up to five named mmio register resources.
> + * These currently are dat, set, clr, [dirout | dirin]. There
> + * is no particular order or predefined place for the entries.
> + * However, dirout and dirin are mutually exclusive.
> + */
> +#define __NUM_COMPAT_OVERRIDE_MMIO_RESOURCES 4
> +
> +struct compat_gpio_device_data {
> +	unsigned int expected_resource_size;
> +	unsigned int ngpio;
> +	resource_size_t register_width;
> +	unsigned long flags;
> +	int (*call_back)(struct platform_device *pdev,
> +			 struct bgpio_pdata *pdata,
> +			 unsigned long *flags);
> +	struct resource_replacement {
> +		resource_size_t start_offset;
> +		const char *name;
> +	} resources[__NUM_COMPAT_OVERRIDE_MMIO_RESOURCES];
> +};
> +#undef __NUM_COMPAT_OVERRIDE_MMIO_RESOURCES
> +
> +#define ADD_COMPAT_REGISTER(_name, _offset)	\
> +	{ .name = (_name), .start_offset = (_offset) }
> +
> +#define ADD_COMPAT_GPIO(_comp, _sz, _ngpio, _width, _cb, _f, _res...)	\
> +	{								\
> +	  .compatible = (_comp),					\
> +	  .data = &(struct compat_gpio_device_data) {			\
> +		.expected_resource_size = (_sz),			\
> +		.ngpio = (_ngpio),					\
> +		.register_width = (_width),				\
> +		.flags = (_f),						\
> +		.call_back = (_cb),					\
> +		.resources = { _res },					\
> +	}								\
> +}
> +
> +#define ADD_COMPAT_GE_GPIO(_name, _ngpio)				\
> +	ADD_COMPAT_GPIO(_name, 0x24, _ngpio, 0x4, ge_dt_cb,		\
> +		 BGPIOF_BIG_ENDIAN_BYTE_ORDER,				\
> +		 ADD_COMPAT_REGISTER("dat", 0x04),			\
> +		 ADD_COMPAT_REGISTER("set", 0x08),			\
> +		 ADD_COMPAT_REGISTER("dirin", 0x00))			\
> +
> +static const struct of_device_id compat_gpio_devices[] = {
> +	ADD_COMPAT_GE_GPIO("ge,imp3a-gpio", 16),
> +	ADD_COMPAT_GE_GPIO("gef,sbc310-gpio", 6),
> +	ADD_COMPAT_GE_GPIO("gef,sbc610-gpio", 19),
> +	ADD_COMPAT_GPIO("moxa,moxart-gpio", 0xc, 0, 0x4, moxart_dt_cb,
> +		 BGPIOF_READ_OUTPUT_REG_SET,
> +		 ADD_COMPAT_REGISTER("dat", 0x04),
> +		 ADD_COMPAT_REGISTER("set", 0x00),
> +		 ADD_COMPAT_REGISTER("dirout", 0x08)),
> +	ADD_COMPAT_GPIO("technologic,ts4800-gpio", 0x6, 16, 0x2, ts4800_dt_cb,
> +		 0, ADD_COMPAT_REGISTER("dat", 0x00),
> +		 ADD_COMPAT_REGISTER("set", 0x02),
> +		 ADD_COMPAT_REGISTER("dirout", 0x04)),
> +};
> +
> +#undef ADD_COMPAT_GE_GPIO
> +#undef ADD_COMPAT_GPIO
> +#undef ADD_COMPAT_REGISTER
> +
> +static int compat_parse_dt(struct platform_device *pdev,
> +			   struct bgpio_pdata *pdata,
> +			   unsigned long *flags)
> +{
> +	const struct device_node *node = pdev->dev.of_node;
> +	const struct compat_gpio_device_data *entry;
> +	const struct of_device_id *of_id;
> +	struct resource *res;
> +	int err;
> +
> +	of_id = of_match_node(compat_gpio_devices, node);
> +	if (!of_id)
> +		return -ENODEV;
> +
> +	entry = of_id->data;
> +	if (!entry || !entry->resources[0].name)
> +		return -EINVAL;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res)
> +		return -EINVAL;
> +
> +	if (!res->name || strcmp(entry->resources[0].name, res->name)) {
> +		struct resource nres[ARRAY_SIZE(entry->resources)];
> +		size_t i;
> +
> +		if (resource_size(res) != entry->expected_resource_size)
> +			return -EINVAL;
> +
> +		for (i = 0; i < ARRAY_SIZE(entry->resources); i++) {
> +			if (!entry->resources[i].name)
> +				continue;
> +
> +			nres[i].name = devm_kstrdup(&pdev->dev,
> +				entry->resources[i].name, GFP_KERNEL);
> +			nres[i].start = res->start +
> +				entry->resources[i].start_offset;
> +			nres[i].end = nres[i].start +
> +				entry->register_width - 1;
> +			nres[i].flags = IORESOURCE_MEM;
> +		}
> +
> +		err = platform_device_add_resources(pdev, nres, i);
> +		if (err)
> +			return err;
> +	}
> +
> +	pdata->base = -1;
> +	pdata->ngpio = entry->ngpio;
> +	*flags = entry->flags;
> +
> +	if (entry->call_back)
> +		err = entry->call_back(pdev, pdata, flags);
> +
> +	return err;
> +}

Ok, so this is getting quite complex, with two of_device_id tables and two 
levels of hooks, mainly because you want to use the bgpio_pdev_probe() 
function which relies on named memory resources.

This function is here for basic platform devices, and you are not obliged 
to rely on it for DT. Feel free to write your own function (or rather split 
bgpio_pdev_probe() in two code paths) if it can reduce the amount of black 
magic you need to do (especially the renaming of resources on-the-fly).

At the end of the day what you want is a function that calls bgpio_init() 
with the right parameters. These parameters should be inferred from the 
compatible property whenever possible (which is most of the time, and why I 
am suspiscious of using "reg" properties for these devices since I don't 
think it can change much?), and passed as-is to bgpio_init(). Your 
compat_gpio_device_data struct basically encodes this, so this will take 
care of all the "compat" devices.

For the other devices, you should be able to fill in such a structure from 
the DT properties. Then you just use it for the parameters of bgpio_init(). 
It will be more elegant that adding resources, and probably shorter as 
well.

> +
>  static const struct of_device_id bgpio_of_match[] = {
>  	{ .compatible = "wd,mbl-gpio", .data = bgpio_basic_mmio_parse_dt },
> +	{ .compatible = "cirrus,clps711x-gpio", .data = clps711x_parse_dt },
> +	{ .compatible = "ge,imp3a-gpio", .data = compat_parse_dt },
> +	{ .compatible = "gef,sbc310-gpio", .data = compat_parse_dt },
> +	{ .compatible = "gef,sbc610-gpio", .data = compat_parse_dt },
> +	{ .compatible = "moxa,moxart-gpio", .data = compat_parse_dt },
> +	{ .compatible = "technologic,ts4800-gpio", .data = compat_parse_dt },

If you implement my idea, the function referred to by .data should return 
that structure I was talking about, and bgpio_init() can be called directly 
on it after that. The current path of bgpio_pdev_probe() should only be 
taken if bgpio_parse_dt() returned NULL.

I believe this will reduce the number of lines in your patch some more, and 
it will also make the code easier to read.

Also this should be split into several patches, like the previous one: the 
first one adds the required infrastructure to gpio-mmio, and the subsequent 
patches each move one device into gpio-mmio.
Christian Lamparter May 12, 2016, 1:07 p.m. UTC | #3
On Thursday, May 12, 2016 07:30:05 PM Alexandre Courbot wrote:
> On Wednesday, May 11, 2016 6:34:35 PM JST, Christian Lamparter wrote:
> > This patch integrates the GPIO drivers for the following
> > boards, SoCs, etc. into gpio-mmio:
> >  - CLPS711X SoCs
> >  - MOXA ART SoC
> >  - TS-4800 FPGA DIO blocks and compatibles
> >  - GPIO controllers found on some GE Single Board Computers
> >
> > Cc: Alexander Shiyan <shc_work@mail.ru>
> > Cc: Julien Grossholtz <julien.grossholtz@savoirfairelinux.com>
> > Cc: Martyn Welch <martyn.welch@ge.com>
> > Cc: Jonas Jensen <jonas.jensen@gmail.com>
> > Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
> > ---
[...]
> > diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c
> > index f72e40e..1de9172 100644
> > --- a/drivers/gpio/gpio-mmio.c
> > +++ b/drivers/gpio/gpio-mmio.c
> > @@ -586,8 +586,220 @@ static int 
> > bgpio_basic_mmio_parse_dt(struct platform_device *pdev,
> >  	return 0;
> >  }
> >  
> > +static int clps711x_parse_dt(struct platform_device *pdev,
> > +			     struct bgpio_pdata *pdata,
> > +			     unsigned long *flags)
> > +{
> > +	struct device_node *np = pdev->dev.of_node;
> > +	struct resource *res;
> > +	const char *dir_reg_name;
> > +	int id = np ? of_alias_get_id(np, "gpio") : pdev->id;
> > +
> > +	switch (id) {
> > +	case 0:
> > +	case 1:
> > +	case 2:
> > +		pdata->ngpio = 0; /* determined by register width */
> > +		dir_reg_name = "dirout";
> > +		break;
> > +	case 3:
> > +		pdata->ngpio = 0; /* determined by register width */
> > +		/* PORTD is inverted logic for direction register */
> > +		dir_reg_name = "dirin";
> > +		break;
> > +	case 4:
> > +		pdata->ngpio = 3; /* PORTE is 3 lines only */
> > +		dir_reg_name = "dirout";
> > +		break;
> > +	default:
> > +		return -ENODEV;
> > +	}
> > +
> > +	pdata->base = id * 8;
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	if (!res)
> > +		return -EINVAL;
> > +	if (!res->name || strcmp("dat", res->name))
> > +		res->name = devm_kstrdup(&pdev->dev, "dat", GFP_KERNEL);
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> > +	if (!res)
> > +		return -EINVAL;
> > +	if (!res->name || strcmp(dir_reg_name, res->name))
> > +		res->name = devm_kstrdup(&pdev->dev, dir_reg_name, GFP_KERNEL);
> 
> Ouch, I don't like this. Also the probability that the "if (!res->name || 
> ...)" condition is not met is so small that I would not bother with it and 
> do the kstrdup unconditionally. But maybe we can remove this altogether - 
> see below...

The !res->name check is simply there because the kernel's strcmp doesn't
check for NULL... the res->name will be coming from the device tree.
Yes it should always be initialized but again: it's not within the functions 
control so it better be prepared for it. The "dir_reg_name" is part of the
function so we can guarantee that it's not NULL or a invalid string. 

About the leakage: I use devm_kstrdup unlike the dump strdup, it does bind
the dupped string to the device. This way the resource is not leaked even
if the module is loaded and unloaded, it stays with the device. (Until
it is destroyed).
 
Note: The strcmp is there to check if resource name was already updated
so even if the module was loaded multiple times it will dup the string
just once.

Doing a direct assignment like I did in the earlier versions would lead
to issues since the device resources are part of the the device and not
with the driver module.

> > +/*
> > + * bgpio_init() needs up to five named mmio register resources.
> > + * These currently are dat, set, clr, [dirout | dirin]. There
> > + * is no particular order or predefined place for the entries.
> > + * However, dirout and dirin are mutually exclusive.
> > + */
> > +#define __NUM_COMPAT_OVERRIDE_MMIO_RESOURCES 4
> > +
> > +struct compat_gpio_device_data {
> > +	unsigned int expected_resource_size;
> > +	unsigned int ngpio;
> > +	resource_size_t register_width;
> > +	unsigned long flags;
> > +	int (*call_back)(struct platform_device *pdev,
> > +			 struct bgpio_pdata *pdata,
> > +			 unsigned long *flags);
> > +	struct resource_replacement {
> > +		resource_size_t start_offset;
> > +		const char *name;
> > +	} resources[__NUM_COMPAT_OVERRIDE_MMIO_RESOURCES];
> > +};
> > +#undef __NUM_COMPAT_OVERRIDE_MMIO_RESOURCES
> > +
> > +#define ADD_COMPAT_REGISTER(_name, _offset)	\
> > +	{ .name = (_name), .start_offset = (_offset) }
> > +
> > +#define ADD_COMPAT_GPIO(_comp, _sz, _ngpio, _width, _cb, _f, _res...)	\
> > +	{								\
> > +	  .compatible = (_comp),					\
> > +	  .data = &(struct compat_gpio_device_data) {			\
> > +		.expected_resource_size = (_sz),			\
> > +		.ngpio = (_ngpio),					\
> > +		.register_width = (_width),				\
> > +		.flags = (_f),						\
> > +		.call_back = (_cb),					\
> > +		.resources = { _res },					\
> > +	}								\
> > +}
> > +
> > +#define ADD_COMPAT_GE_GPIO(_name, _ngpio)				\
> > +	ADD_COMPAT_GPIO(_name, 0x24, _ngpio, 0x4, ge_dt_cb,		\
> > +		 BGPIOF_BIG_ENDIAN_BYTE_ORDER,				\
> > +		 ADD_COMPAT_REGISTER("dat", 0x04),			\
> > +		 ADD_COMPAT_REGISTER("set", 0x08),			\
> > +		 ADD_COMPAT_REGISTER("dirin", 0x00))			\
> > +
> > +static const struct of_device_id compat_gpio_devices[] = {
> > +	ADD_COMPAT_GE_GPIO("ge,imp3a-gpio", 16),
> > +	ADD_COMPAT_GE_GPIO("gef,sbc310-gpio", 6),
> > +	ADD_COMPAT_GE_GPIO("gef,sbc610-gpio", 19),
> > +	ADD_COMPAT_GPIO("moxa,moxart-gpio", 0xc, 0, 0x4, moxart_dt_cb,
> > +		 BGPIOF_READ_OUTPUT_REG_SET,
> > +		 ADD_COMPAT_REGISTER("dat", 0x04),
> > +		 ADD_COMPAT_REGISTER("set", 0x00),
> > +		 ADD_COMPAT_REGISTER("dirout", 0x08)),
> > +	ADD_COMPAT_GPIO("technologic,ts4800-gpio", 0x6, 16, 0x2, ts4800_dt_cb,
> > +		 0, ADD_COMPAT_REGISTER("dat", 0x00),
> > +		 ADD_COMPAT_REGISTER("set", 0x02),
> > +		 ADD_COMPAT_REGISTER("dirout", 0x04)),
> > +};
> > +
> > +#undef ADD_COMPAT_GE_GPIO
> > +#undef ADD_COMPAT_GPIO
> > +#undef ADD_COMPAT_REGISTER
> > +
> > +static int compat_parse_dt(struct platform_device *pdev,
> > +			   struct bgpio_pdata *pdata,
> > +			   unsigned long *flags)
> > +{
> > +	const struct device_node *node = pdev->dev.of_node;
> > +	const struct compat_gpio_device_data *entry;
> > +	const struct of_device_id *of_id;
> > +	struct resource *res;
> > +	int err;
> > +
> > +	of_id = of_match_node(compat_gpio_devices, node);
> > +	if (!of_id)
> > +		return -ENODEV;
> > +
> > +	entry = of_id->data;
> > +	if (!entry || !entry->resources[0].name)
> > +		return -EINVAL;
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	if (!res)
> > +		return -EINVAL;
> > +
> > +	if (!res->name || strcmp(entry->resources[0].name, res->name)) {
> > +		struct resource nres[ARRAY_SIZE(entry->resources)];
> > +		size_t i;
> > +
> > +		if (resource_size(res) != entry->expected_resource_size)
> > +			return -EINVAL;
> > +
> > +		for (i = 0; i < ARRAY_SIZE(entry->resources); i++) {
> > +			if (!entry->resources[i].name)
> > +				continue;
> > +
> > +			nres[i].name = devm_kstrdup(&pdev->dev,
> > +				entry->resources[i].name, GFP_KERNEL);
> > +			nres[i].start = res->start +
> > +				entry->resources[i].start_offset;
> > +			nres[i].end = nres[i].start +
> > +				entry->register_width - 1;
> > +			nres[i].flags = IORESOURCE_MEM;
> > +		}
> > +
> > +		err = platform_device_add_resources(pdev, nres, i);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	pdata->base = -1;
> > +	pdata->ngpio = entry->ngpio;
> > +	*flags = entry->flags;
> > +
> > +	if (entry->call_back)
> > +		err = entry->call_back(pdev, pdata, flags);
> > +
> > +	return err;
> > +}
> 
> Ok, so this is getting quite complex, with two of_device_id tables and two 
> levels of hooks, mainly because you want to use the bgpio_pdev_probe() 
> function which relies on named memory resources.
I think I need more here. The bgpio_pdev_probe() also sets up the bgpio_maps()
(there are 6 and I'm using 4 at the moment for all compat chips). 
The bgpio_pdev_probe() also deals with devm_gpiochip_add_data() and allocates
the memory for the gpiochip structure... as well as  and the error handling.
etc...

That's why I reused the existing code as much as possible.
(But let's get to the main issue here:)
> This function is here for basic platform devices, and you are not obliged 
> to rely on it for DT. Feel free to write your own function (or rather split 
> bgpio_pdev_probe() in two code paths) if it can reduce the amount of black 
> magic you need to do (especially the renaming of resources on-the-fly).
> 
> At the end of the day what you want is a function that calls bgpio_init() 
> with the right parameters. These parameters should be inferred from the 
> compatible property whenever possible (which is most of the time, and why I 
> am suspiscious of using "reg" properties for these devices since I don't 
> think it can change much?), and passed as-is to bgpio_init(). Your 
> compat_gpio_device_data struct basically encodes this, so this will take 
> care of all the "compat" devices.
> 
> For the other devices, you should be able to fill in such a structure from 
> the DT properties. Then you just use it for the parameters of bgpio_init(). 
> It will be more elegant that adding resources, and probably shorter as 
> well.
> 
> > +
> >  static const struct of_device_id bgpio_of_match[] = {
> >  	{ .compatible = "wd,mbl-gpio", .data = bgpio_basic_mmio_parse_dt },
> > +	{ .compatible = "cirrus,clps711x-gpio", .data = clps711x_parse_dt },
> > +	{ .compatible = "ge,imp3a-gpio", .data = compat_parse_dt },
> > +	{ .compatible = "gef,sbc310-gpio", .data = compat_parse_dt },
> > +	{ .compatible = "gef,sbc610-gpio", .data = compat_parse_dt },
> > +	{ .compatible = "moxa,moxart-gpio", .data = compat_parse_dt },
> > +	{ .compatible = "technologic,ts4800-gpio", .data = compat_parse_dt },
> 
> If you implement my idea, the function referred to by .data should return 
> that structure I was talking about, and bgpio_init() can be called directly 
> on it after that. The current path of bgpio_pdev_probe() should only be 
> taken if bgpio_parse_dt() returned NULL.
> 
> I believe this will reduce the number of lines in your patch some more, and 
> it will also make the code easier to read.
> 
> Also this should be split into several patches, like the previous one: the 
> first one adds the required infrastructure to gpio-mmio, and the subsequent 
> patches each move one device into gpio-mmio.

The main issue why this got so complicated was keeping compatibility with
the existing drivers.

If you look at "clps711x_parse_dt". You will see that it supports 5 GPIOs.
The thing that makes it really ugly is that it uses the dt alias as a way
to identify each port (A-E). The Port is then used to encode the number of
gpios the controller has and whenever it is dirin or dirout... 
I argued that this information should be put in the dts and I made patch
for it. But updating the dts was not the way to go. as Rob explained
in <http://www.spinics.net/lists/devicetree/msg124538.html>.

This means that unless you can make a single structure that would encode
everything these compat drivers do, (look at the call_back functions of
the other drivers, each of the compat drivers also has a little bit of
extra code associated to setup stuff like a label or have an extra ngpios
property parser, etc...). I cannot see how you could make this any better
without breaking compatibility. This was the sole reason why there are
these individual parsers. (In RFC v4? I had that parser in their original
files - I don't know if this would have worked any better, but feedback
indicated that everything should be moved to the gpio-mmio.c). 

So, what's your take on it?

Note: This patch was added to this series as a request of Rob to demonstrate
that having a device tree binding for the gpio-mmio.c driver is something we
want. It isn't necessary to apply it right away as it also hinges on the ACKs
of people who use these devices. And so far, nobody replied. We can add this
individual patch at a later time. 

Regards,
Christian
Alexandre Courbot May 16, 2016, 8:34 a.m. UTC | #4
On Thursday, May 12, 2016 10:07:06 PM JST, Christian Lamparter wrote:
> On Thursday, May 12, 2016 07:30:05 PM Alexandre Courbot wrote:
>> On Wednesday, May 11, 2016 6:34:35 PM JST, Christian Lamparter wrote: ...
> [...]
>>> ...
>> Ouch, I don't like this. Also the probability that the "if (!res->name || 
>> ...)" condition is not met is so small that I would not bother 
>> with it and 
>> do the kstrdup unconditionally. But maybe we can remove this altogether - 
>> see below... ...
>
> The !res->name check is simply there because the kernel's strcmp doesn't
> check for NULL... the res->name will be coming from the device tree.
> Yes it should always be initialized but again: it's not within 
> the functions 
> control so it better be prepared for it. The "dir_reg_name" is part of the
> function so we can guarantee that it's not NULL or a invalid string. 
>
> About the leakage: I use devm_kstrdup unlike the dump strdup, it does bind
> the dupped string to the device. This way the resource is not leaked even
> if the module is loaded and unloaded, it stays with the device. (Until
> it is destroyed).

What I wanted to point out is that it is probably ok to just duplicate the 
string at all times, since the probability that res->name will not be NULL 
*and* the string will already match what we expect is very low (and could 
only come from a device tree that is aware of this patch).

Leakage is not a concern thanks to devm, as you have pointed out.

>  
> Note: The strcmp is there to check if resource name was already updated
> so even if the module was loaded multiple times it will dup the string
> just once.

Mmm, is this how things really work? My understanding is that 
devm-allocated resources will be freed after the driver's remove function 
is called (see slides 11/12 of 
http://haifux.org/lectures/323/haifux-devres.pdf). I.e. when the module is 
unloaded, your string will be destroyed. So unless I am missing something 
you would still need to duplicate it the next time the device is probed.

So here it seems to me that to be on the safe side you either need to 
restore the previous value of res->name on device destruction, or always 
duplicate the string when probing it. But with this later solution there is 
still the risk that if you then load *another* driver to probe this device, 
which will then find a valid pointer on res->name, only to find invalid 
content when following it... So in the end I'd recommend against messing 
with resources like that - it just creates potential for invalid states.

Hopefully is my following suggestion is valid, you won't need to.

>
> Doing a direct assignment like I did in the earlier versions would lead
> to issues since the device resources are part of the the device and not
> with the driver module.
>
>>> ...
>> Ok, so this is getting quite complex, with two of_device_id 
>> tables and two 
>> levels of hooks, mainly because you want to use the bgpio_pdev_probe() 
>> function which relies on named memory resources. ...
> I think I need more here. The bgpio_pdev_probe() also sets up 
> the bgpio_maps()
> (there are 6 and I'm using 4 at the moment for all compat chips). 
> The bgpio_pdev_probe() also deals with devm_gpiochip_add_data() 
> and allocates
> the memory for the gpiochip structure... as well as  and the error handling.
> etc...
>
> That's why I reused the existing code as much as possible.
> (But let's get to the main issue here:)
>> This function is here for basic platform devices, and you are not obliged 
>> to rely on it for DT. Feel free to write your own function (or 
>> rather split 
>> bgpio_pdev_probe() in two code paths) if it can reduce the 
>> amount of black 
>> magic you need to do (especially the renaming of resources on-the-fly).
>> 
>> At the end of the day what you want is a function that calls 
>> bgpio_init()  ...
>
> The main issue why this got so complicated was keeping compatibility with
> the existing drivers.
>
> If you look at "clps711x_parse_dt". You will see that it supports 5 GPIOs.
> The thing that makes it really ugly is that it uses the dt alias as a way
> to identify each port (A-E). The Port is then used to encode the number of
> gpios the controller has and whenever it is dirin or dirout... 
> I argued that this information should be put in the dts and I made patch
> for it. But updating the dts was not the way to go. as Rob explained
> in <http://www.spinics.net/lists/devicetree/msg124538.html>.
>
> This means that unless you can make a single structure that would encode
> everything these compat drivers do, (look at the call_back functions of
> the other drivers, each of the compat drivers also has a little bit of
> extra code associated to setup stuff like a label or have an extra ngpios
> property parser, etc...). I cannot see how you could make this any better
> without breaking compatibility. This was the sole reason why there are
> these individual parsers. (In RFC v4? I had that parser in their original
> files - I don't know if this would have worked any better, but feedback
> indicated that everything should be moved to the gpio-mmio.c). 
>
> So, what's your take on it?

I think your problem can be summarized as follows: for each device, you 
need to call bgpio_init() with the proper parameters. Your goal is to infer 
the right parameters for a variety a devices that make a different use of 
the device tree.

Because of this, you *will* need device-specific DT-parsing functions, and 
your current patch already contains them (like clps711x_parse_dt()). My 
suggestion is to not rely on bgpio_pdev_probe() for these specific devices, 
as this function expects resources, and forces you to so some unclear 
hacking. Instead, how about something like the following (warning, this is 
dirty and just to let you get the idea):

/* Params to bgpio_init()  */
struct bgpio_params {
    unsigned long sz;
    void __iomem *dat;
    void __iomem *set;
    void __iomem *clr;
    void __iomem *dirout;
    void __iomem *dirin;
    unsigned long flags;

    struct bgpio_pdata pdata;
};

static int clps711x_parse_dt(struct platform_device *pdev,
		struct bgpio_params *params)
{
	struct device_node *np = pdev->dev.of_node;
	struct resource *res;
	void __iomem *mem;
	int id = np ? of_alias_get_id(np, "gpio") : pdev->id;

	params->pdata.base = id * 8;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res)
		return -EINVAL;
	params->dat = devm_ioremap_resource(&pdev->dev, res);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
	if (!res)
		return -EINVAL;
	mem = devm_ioremap_resource(&pdev->dev, res);

	switch (id) {
	case 0:
	case 1:
	case 2:
		pdata->ngpio = 0; /* determined by register width */
		params->dirout = mem;
		break;
	case 3:
		pdata->ngpio = 0; /* determined by register width */
		/* PORTD is inverted logic for direction register */
		params->dirin = mem;
		break;
	case 4:
		pdata->ngpio = 3; /* PORTE is 3 lines only */
		params->dirout = mem;
		break;
	default:
		return -ENODEV;
	}

	return 0;
}

static int bgpio_pdev_probe(struct platform_device *pdev)
{
    /* clear params out */
    struct bgpio_params params = { 0 };
    const int (*parse_dt)(struct platform_device *,
                          struct bgpio_params *);

    parse_dt = of_device_get_match_data(&pdev->dev);
    /* We are not instanciated from the DT, do regular probe using 
resources */
    if (!parse_dt) {
        struct resource *r;

        r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
        if (!r)
                return -EINVAL;

        params.sz = resource_size(r);

        params.dat = bgpio_map(pdev, "dat", sz);
        if (IS_ERR(params.dat))
                return PTR_ERR(params.dat);

        /* etc, fill in params by doing what bgpio_pdev_probe initially did 
*/
    } else {
        /* instanciated from DT, fill in params with the device-specific 
function */
        parse_dt(pdev, &params);
        pdata = &params.pdata;
    }

    ....
 
    err = bgpio_init(gc, dev, params.sz, params.dat, params.set, 
params.clr, params.dirout, params.dirin, params.flags);
    ....
}

... or something like that, it is probably possible to make this cleaner by 
refactoring the code a bit (bgpio_pdata and my new params structure can 
probably be merged, for instance).

You get the same result, but without messing with resources in dangerous 
ways, and also removing one level of callback (and some hard-to-follow 
macros). Of course parse_dt functions can be shared across devices when 
they are compatible. Feel also free to adopt another approach (like 
returning the struct resource in bgpio_params instead of the mapped 
resource so you can call a modified bgpio_map() on them in 
bgpio_pdev_probe()) if you find this more elegant. The idea I want to 
convey is that you don't need to rely on the named resources at all times.

Hope this clarifies!

Alex.
diff mbox

Patch

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index a68d838..e4d1065 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -154,7 +154,7 @@  config GPIO_BRCMSTB
 config GPIO_CLPS711X
 	tristate "CLPS711X GPIO support"
 	depends on ARCH_CLPS711X || COMPILE_TEST
-	select GPIO_GENERIC
+	select GPIO_GENERIC_PLATFORM
 	help
 	  Say yes here to support GPIO on CLPS711X SoCs.
 
@@ -196,7 +196,7 @@  config GPIO_ETRAXFS
 config GPIO_GE_FPGA
 	bool "GE FPGA based GPIO"
 	depends on GE_FPGA
-	select GPIO_GENERIC
+	select GPIO_GENERIC_PLATFORM
 	help
 	  Support for common GPIO functionality provided on some GE Single Board
 	  Computers.
@@ -209,6 +209,14 @@  config GPIO_GENERIC_PLATFORM
 	tristate "Generic memory-mapped GPIO controller support (MMIO platform device)"
 	select GPIO_GENERIC
 	help
+	  Select this to support many generic memory-mapped GPIO controllers.
+
+	  This driver also includes support for the following GPIOs:
+	    CLPS711X SoCs
+	    MOXA ART SoC
+	    TS-4800 FPGA DIO blocks and compatibles.
+	    GPIOs found on some GE Single Board Computers.
+
 	  Say yes here to support basic platform_device memory-mapped GPIO controllers.
 
 config GPIO_GRGPIO
@@ -288,7 +296,7 @@  config GPIO_MM_LANTIQ
 config GPIO_MOXART
 	bool "MOXART GPIO support"
 	depends on ARCH_MOXART || COMPILE_TEST
-	select GPIO_GENERIC
+	select GPIO_GENERIC_PLATFORM
 	help
 	  Select this option to enable GPIO driver for
 	  MOXA ART SoC devices.
@@ -408,7 +416,7 @@  config GPIO_TS4800
 	tristate "TS-4800 DIO blocks and compatibles"
 	depends on OF_GPIO
 	depends on SOC_IMX51 || COMPILE_TEST
-	select GPIO_GENERIC
+	select GPIO_GENERIC_PLATFORM
 	help
 	  This driver support TS-4800 FPGA GPIO controllers.
 
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 991598e..d8d63ae 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -31,7 +31,6 @@  obj-$(CONFIG_GPIO_ATH79)	+= gpio-ath79.o
 obj-$(CONFIG_GPIO_BCM_KONA)	+= gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BRCMSTB)	+= gpio-brcmstb.o
 obj-$(CONFIG_GPIO_BT8XX)	+= gpio-bt8xx.o
-obj-$(CONFIG_GPIO_CLPS711X)	+= gpio-clps711x.o
 obj-$(CONFIG_GPIO_CS5535)	+= gpio-cs5535.o
 obj-$(CONFIG_GPIO_CRYSTAL_COVE)	+= gpio-crystalcove.o
 obj-$(CONFIG_GPIO_DA9052)	+= gpio-da9052.o
@@ -43,7 +42,6 @@  obj-$(CONFIG_GPIO_EM)		+= gpio-em.o
 obj-$(CONFIG_GPIO_EP93XX)	+= gpio-ep93xx.o
 obj-$(CONFIG_GPIO_ETRAXFS)	+= gpio-etraxfs.o
 obj-$(CONFIG_GPIO_F7188X)	+= gpio-f7188x.o
-obj-$(CONFIG_GPIO_GE_FPGA)	+= gpio-ge.o
 obj-$(CONFIG_GPIO_GRGPIO)	+= gpio-grgpio.o
 obj-$(CONFIG_GPIO_ICH)		+= gpio-ich.o
 obj-$(CONFIG_GPIO_IOP)		+= gpio-iop.o
@@ -68,7 +66,6 @@  obj-$(CONFIG_GPIO_MC9S08DZ60)	+= gpio-mc9s08dz60.o
 obj-$(CONFIG_GPIO_MCP23S08)	+= gpio-mcp23s08.o
 obj-$(CONFIG_GPIO_ML_IOH)	+= gpio-ml-ioh.o
 obj-$(CONFIG_GPIO_MM_LANTIQ)	+= gpio-mm-lantiq.o
-obj-$(CONFIG_GPIO_MOXART)	+= gpio-moxart.o
 obj-$(CONFIG_GPIO_MPC5200)	+= gpio-mpc5200.o
 obj-$(CONFIG_GPIO_MPC8XXX)	+= gpio-mpc8xxx.o
 obj-$(CONFIG_GPIO_MSIC)		+= gpio-msic.o
@@ -107,7 +104,6 @@  obj-$(CONFIG_GPIO_TPS65218)	+= gpio-tps65218.o
 obj-$(CONFIG_GPIO_TPS6586X)	+= gpio-tps6586x.o
 obj-$(CONFIG_GPIO_TPS65910)	+= gpio-tps65910.o
 obj-$(CONFIG_GPIO_TPS65912)	+= gpio-tps65912.o
-obj-$(CONFIG_GPIO_TS4800)	+= gpio-ts4800.o
 obj-$(CONFIG_GPIO_TS5500)	+= gpio-ts5500.o
 obj-$(CONFIG_GPIO_TWL4030)	+= gpio-twl4030.o
 obj-$(CONFIG_GPIO_TWL6040)	+= gpio-twl6040.o
diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c
deleted file mode 100644
index 5a69025..0000000
--- a/drivers/gpio/gpio-clps711x.c
+++ /dev/null
@@ -1,91 +0,0 @@ 
-/*
- *  CLPS711X GPIO driver
- *
- *  Copyright (C) 2012,2013 Alexander Shiyan <shc_work@mail.ru>
- *
- * 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/err.h>
-#include <linux/module.h>
-#include <linux/gpio/driver.h>
-#include <linux/platform_device.h>
-
-static int clps711x_gpio_probe(struct platform_device *pdev)
-{
-	struct device_node *np = pdev->dev.of_node;
-	void __iomem *dat, *dir;
-	struct gpio_chip *gc;
-	struct resource *res;
-	int err, id = np ? of_alias_get_id(np, "gpio") : pdev->id;
-
-	if ((id < 0) || (id > 4))
-		return -ENODEV;
-
-	gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
-	if (!gc)
-		return -ENOMEM;
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	dat = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(dat))
-		return PTR_ERR(dat);
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-	dir = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(dir))
-		return PTR_ERR(dir);
-
-	switch (id) {
-	case 3:
-		/* PORTD is inverted logic for direction register */
-		err = bgpio_init(gc, &pdev->dev, 1, dat, NULL, NULL,
-				 NULL, dir, 0);
-		break;
-	default:
-		err = bgpio_init(gc, &pdev->dev, 1, dat, NULL, NULL,
-				 dir, NULL, 0);
-		break;
-	}
-
-	if (err)
-		return err;
-
-	switch (id) {
-	case 4:
-		/* PORTE is 3 lines only */
-		gc->ngpio = 3;
-		break;
-	default:
-		break;
-	}
-
-	gc->base = id * 8;
-	gc->owner = THIS_MODULE;
-	platform_set_drvdata(pdev, gc);
-
-	return devm_gpiochip_add_data(&pdev->dev, gc, NULL);
-}
-
-static const struct of_device_id __maybe_unused clps711x_gpio_ids[] = {
-	{ .compatible = "cirrus,clps711x-gpio" },
-	{ }
-};
-MODULE_DEVICE_TABLE(of, clps711x_gpio_ids);
-
-static struct platform_driver clps711x_gpio_driver = {
-	.driver	= {
-		.name		= "clps711x-gpio",
-		.of_match_table	= of_match_ptr(clps711x_gpio_ids),
-	},
-	.probe	= clps711x_gpio_probe,
-};
-module_platform_driver(clps711x_gpio_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
-MODULE_DESCRIPTION("CLPS711X GPIO driver");
-MODULE_ALIAS("platform:clps711x-gpio");
diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c
deleted file mode 100644
index 8650b29..0000000
--- a/drivers/gpio/gpio-ge.c
+++ /dev/null
@@ -1,114 +0,0 @@ 
-/*
- * Driver for GE FPGA based GPIO
- *
- * Author: Martyn Welch <martyn.welch@ge.com>
- *
- * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc.
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2.  This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-/* TODO
- *
- * Configuration of output modes (totem-pole/open-drain)
- * Interrupt configuration - interrupts are always generated the FPGA relies on
- * the I/O interrupt controllers mask to stop them propergating
- */
-
-#include <linux/kernel.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
-#include <linux/of_address.h>
-#include <linux/module.h>
-#include <linux/gpio/driver.h>
-
-#define GEF_GPIO_DIRECT		0x00
-#define GEF_GPIO_IN		0x04
-#define GEF_GPIO_OUT		0x08
-#define GEF_GPIO_TRIG		0x0C
-#define GEF_GPIO_POLAR_A	0x10
-#define GEF_GPIO_POLAR_B	0x14
-#define GEF_GPIO_INT_STAT	0x18
-#define GEF_GPIO_OVERRUN	0x1C
-#define GEF_GPIO_MODE		0x20
-
-static const struct of_device_id gef_gpio_ids[] = {
-	{
-		.compatible	= "gef,sbc610-gpio",
-		.data		= (void *)19,
-	}, {
-		.compatible	= "gef,sbc310-gpio",
-		.data		= (void *)6,
-	}, {
-		.compatible	= "ge,imp3a-gpio",
-		.data		= (void *)16,
-	},
-	{ }
-};
-MODULE_DEVICE_TABLE(of, gef_gpio_ids);
-
-static int __init gef_gpio_probe(struct platform_device *pdev)
-{
-	const struct of_device_id *of_id =
-		of_match_device(gef_gpio_ids, &pdev->dev);
-	struct gpio_chip *gc;
-	void __iomem *regs;
-	int ret;
-
-	gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
-	if (!gc)
-		return -ENOMEM;
-
-	regs = of_iomap(pdev->dev.of_node, 0);
-	if (!regs)
-		return -ENOMEM;
-
-	ret = bgpio_init(gc, &pdev->dev, 4, regs + GEF_GPIO_IN,
-			 regs + GEF_GPIO_OUT, NULL, NULL,
-			 regs + GEF_GPIO_DIRECT, BGPIOF_BIG_ENDIAN_BYTE_ORDER);
-	if (ret) {
-		dev_err(&pdev->dev, "bgpio_init failed\n");
-		goto err0;
-	}
-
-	/* Setup pointers to chip functions */
-	gc->label = devm_kstrdup(&pdev->dev, pdev->dev.of_node->full_name,
-				     GFP_KERNEL);
-	if (!gc->label) {
-		ret = -ENOMEM;
-		goto err0;
-	}
-
-	gc->base = -1;
-	gc->ngpio = (u16)(uintptr_t)of_id->data;
-	gc->of_gpio_n_cells = 2;
-	gc->of_node = pdev->dev.of_node;
-
-	/* This function adds a memory mapped GPIO chip */
-	ret = devm_gpiochip_add_data(&pdev->dev, gc, NULL);
-	if (ret)
-		goto err0;
-
-	return 0;
-err0:
-	iounmap(regs);
-	pr_err("%s: GPIO chip registration failed\n",
-			pdev->dev.of_node->full_name);
-	return ret;
-};
-
-static struct platform_driver gef_gpio_driver = {
-	.driver = {
-		.name		= "gef-gpio",
-		.of_match_table	= gef_gpio_ids,
-	},
-};
-module_platform_driver_probe(gef_gpio_driver, gef_gpio_probe);
-
-MODULE_DESCRIPTION("GE I/O FPGA GPIO driver");
-MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c
index f72e40e..1de9172 100644
--- a/drivers/gpio/gpio-mmio.c
+++ b/drivers/gpio/gpio-mmio.c
@@ -586,8 +586,220 @@  static int bgpio_basic_mmio_parse_dt(struct platform_device *pdev,
 	return 0;
 }
 
+static int clps711x_parse_dt(struct platform_device *pdev,
+			     struct bgpio_pdata *pdata,
+			     unsigned long *flags)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct resource *res;
+	const char *dir_reg_name;
+	int id = np ? of_alias_get_id(np, "gpio") : pdev->id;
+
+	switch (id) {
+	case 0:
+	case 1:
+	case 2:
+		pdata->ngpio = 0; /* determined by register width */
+		dir_reg_name = "dirout";
+		break;
+	case 3:
+		pdata->ngpio = 0; /* determined by register width */
+		/* PORTD is inverted logic for direction register */
+		dir_reg_name = "dirin";
+		break;
+	case 4:
+		pdata->ngpio = 3; /* PORTE is 3 lines only */
+		dir_reg_name = "dirout";
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	pdata->base = id * 8;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -EINVAL;
+	if (!res->name || strcmp("dat", res->name))
+		res->name = devm_kstrdup(&pdev->dev, "dat", GFP_KERNEL);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res)
+		return -EINVAL;
+	if (!res->name || strcmp(dir_reg_name, res->name))
+		res->name = devm_kstrdup(&pdev->dev, dir_reg_name, GFP_KERNEL);
+
+	return 0;
+}
+
+static int ge_dt_cb(struct platform_device *pdev,
+		    struct bgpio_pdata *pdata,
+		    unsigned long *flags)
+{
+	struct device_node *np = pdev->dev.of_node;
+
+	pdata->label = devm_kstrdup(&pdev->dev, np->full_name, GFP_KERNEL);
+	if (!pdata->label)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int moxart_dt_cb(struct platform_device *pdev,
+			struct bgpio_pdata *pdata,
+			unsigned long *flags)
+{
+	pdata->base = 0;
+	pdata->label = "moxart-gpio";
+	return 0;
+}
+
+
+static int ts4800_dt_cb(struct platform_device *pdev,
+			struct bgpio_pdata *pdata,
+			unsigned long *flags)
+{
+	int err;
+
+	err = of_property_read_u32(pdev->dev.of_node, "ngpios", &pdata->ngpio);
+	if (err == -EINVAL) {
+		pdata->ngpio = 16;
+		return 0;
+	}
+
+	return err;
+}
+
+/*
+ * bgpio_init() needs up to five named mmio register resources.
+ * These currently are dat, set, clr, [dirout | dirin]. There
+ * is no particular order or predefined place for the entries.
+ * However, dirout and dirin are mutually exclusive.
+ */
+#define __NUM_COMPAT_OVERRIDE_MMIO_RESOURCES 4
+
+struct compat_gpio_device_data {
+	unsigned int expected_resource_size;
+	unsigned int ngpio;
+	resource_size_t register_width;
+	unsigned long flags;
+	int (*call_back)(struct platform_device *pdev,
+			 struct bgpio_pdata *pdata,
+			 unsigned long *flags);
+	struct resource_replacement {
+		resource_size_t start_offset;
+		const char *name;
+	} resources[__NUM_COMPAT_OVERRIDE_MMIO_RESOURCES];
+};
+#undef __NUM_COMPAT_OVERRIDE_MMIO_RESOURCES
+
+#define ADD_COMPAT_REGISTER(_name, _offset)	\
+	{ .name = (_name), .start_offset = (_offset) }
+
+#define ADD_COMPAT_GPIO(_comp, _sz, _ngpio, _width, _cb, _f, _res...)	\
+	{								\
+	  .compatible = (_comp),					\
+	  .data = &(struct compat_gpio_device_data) {			\
+		.expected_resource_size = (_sz),			\
+		.ngpio = (_ngpio),					\
+		.register_width = (_width),				\
+		.flags = (_f),						\
+		.call_back = (_cb),					\
+		.resources = { _res },					\
+	}								\
+}
+
+#define ADD_COMPAT_GE_GPIO(_name, _ngpio)				\
+	ADD_COMPAT_GPIO(_name, 0x24, _ngpio, 0x4, ge_dt_cb,		\
+		 BGPIOF_BIG_ENDIAN_BYTE_ORDER,				\
+		 ADD_COMPAT_REGISTER("dat", 0x04),			\
+		 ADD_COMPAT_REGISTER("set", 0x08),			\
+		 ADD_COMPAT_REGISTER("dirin", 0x00))			\
+
+static const struct of_device_id compat_gpio_devices[] = {
+	ADD_COMPAT_GE_GPIO("ge,imp3a-gpio", 16),
+	ADD_COMPAT_GE_GPIO("gef,sbc310-gpio", 6),
+	ADD_COMPAT_GE_GPIO("gef,sbc610-gpio", 19),
+	ADD_COMPAT_GPIO("moxa,moxart-gpio", 0xc, 0, 0x4, moxart_dt_cb,
+		 BGPIOF_READ_OUTPUT_REG_SET,
+		 ADD_COMPAT_REGISTER("dat", 0x04),
+		 ADD_COMPAT_REGISTER("set", 0x00),
+		 ADD_COMPAT_REGISTER("dirout", 0x08)),
+	ADD_COMPAT_GPIO("technologic,ts4800-gpio", 0x6, 16, 0x2, ts4800_dt_cb,
+		 0, ADD_COMPAT_REGISTER("dat", 0x00),
+		 ADD_COMPAT_REGISTER("set", 0x02),
+		 ADD_COMPAT_REGISTER("dirout", 0x04)),
+};
+
+#undef ADD_COMPAT_GE_GPIO
+#undef ADD_COMPAT_GPIO
+#undef ADD_COMPAT_REGISTER
+
+static int compat_parse_dt(struct platform_device *pdev,
+			   struct bgpio_pdata *pdata,
+			   unsigned long *flags)
+{
+	const struct device_node *node = pdev->dev.of_node;
+	const struct compat_gpio_device_data *entry;
+	const struct of_device_id *of_id;
+	struct resource *res;
+	int err;
+
+	of_id = of_match_node(compat_gpio_devices, node);
+	if (!of_id)
+		return -ENODEV;
+
+	entry = of_id->data;
+	if (!entry || !entry->resources[0].name)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -EINVAL;
+
+	if (!res->name || strcmp(entry->resources[0].name, res->name)) {
+		struct resource nres[ARRAY_SIZE(entry->resources)];
+		size_t i;
+
+		if (resource_size(res) != entry->expected_resource_size)
+			return -EINVAL;
+
+		for (i = 0; i < ARRAY_SIZE(entry->resources); i++) {
+			if (!entry->resources[i].name)
+				continue;
+
+			nres[i].name = devm_kstrdup(&pdev->dev,
+				entry->resources[i].name, GFP_KERNEL);
+			nres[i].start = res->start +
+				entry->resources[i].start_offset;
+			nres[i].end = nres[i].start +
+				entry->register_width - 1;
+			nres[i].flags = IORESOURCE_MEM;
+		}
+
+		err = platform_device_add_resources(pdev, nres, i);
+		if (err)
+			return err;
+	}
+
+	pdata->base = -1;
+	pdata->ngpio = entry->ngpio;
+	*flags = entry->flags;
+
+	if (entry->call_back)
+		err = entry->call_back(pdev, pdata, flags);
+
+	return err;
+}
+
 static const struct of_device_id bgpio_of_match[] = {
 	{ .compatible = "wd,mbl-gpio", .data = bgpio_basic_mmio_parse_dt },
+	{ .compatible = "cirrus,clps711x-gpio", .data = clps711x_parse_dt },
+	{ .compatible = "ge,imp3a-gpio", .data = compat_parse_dt },
+	{ .compatible = "gef,sbc310-gpio", .data = compat_parse_dt },
+	{ .compatible = "gef,sbc610-gpio", .data = compat_parse_dt },
+	{ .compatible = "moxa,moxart-gpio", .data = compat_parse_dt },
+	{ .compatible = "technologic,ts4800-gpio", .data = compat_parse_dt },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, bgpio_of_match);
diff --git a/drivers/gpio/gpio-moxart.c b/drivers/gpio/gpio-moxart.c
deleted file mode 100644
index d58d389..0000000
--- a/drivers/gpio/gpio-moxart.c
+++ /dev/null
@@ -1,84 +0,0 @@ 
-/*
- * MOXA ART SoCs GPIO driver.
- *
- * Copyright (C) 2013 Jonas Jensen
- *
- * Jonas Jensen <jonas.jensen@gmail.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2.  This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/irq.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
-#include <linux/of_address.h>
-#include <linux/of_gpio.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/bitops.h>
-#include <linux/gpio/driver.h>
-
-#define GPIO_DATA_OUT		0x00
-#define GPIO_DATA_IN		0x04
-#define GPIO_PIN_DIRECTION	0x08
-
-static int moxart_gpio_probe(struct platform_device *pdev)
-{
-	struct device *dev = &pdev->dev;
-	struct resource *res;
-	struct gpio_chip *gc;
-	void __iomem *base;
-	int ret;
-
-	gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
-	if (!gc)
-		return -ENOMEM;
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	base = devm_ioremap_resource(dev, res);
-	if (IS_ERR(base))
-		return PTR_ERR(base);
-
-	ret = bgpio_init(gc, dev, 4, base + GPIO_DATA_IN,
-			 base + GPIO_DATA_OUT, NULL,
-			 base + GPIO_PIN_DIRECTION, NULL,
-			 BGPIOF_READ_OUTPUT_REG_SET);
-	if (ret) {
-		dev_err(&pdev->dev, "bgpio_init failed\n");
-		return ret;
-	}
-
-	gc->label = "moxart-gpio";
-	gc->request = gpiochip_generic_request;
-	gc->free = gpiochip_generic_free;
-	gc->base = 0;
-	gc->owner = THIS_MODULE;
-
-	ret = devm_gpiochip_add_data(dev, gc, NULL);
-	if (ret) {
-		dev_err(dev, "%s: gpiochip_add failed\n",
-			dev->of_node->full_name);
-		return ret;
-	}
-
-	return ret;
-}
-
-static const struct of_device_id moxart_gpio_match[] = {
-	{ .compatible = "moxa,moxart-gpio" },
-	{ }
-};
-
-static struct platform_driver moxart_gpio_driver = {
-	.driver	= {
-		.name		= "moxart-gpio",
-		.of_match_table	= moxart_gpio_match,
-	},
-	.probe	= moxart_gpio_probe,
-};
-builtin_platform_driver(moxart_gpio_driver);
diff --git a/drivers/gpio/gpio-ts4800.c b/drivers/gpio/gpio-ts4800.c
deleted file mode 100644
index 0c144a7..0000000
--- a/drivers/gpio/gpio-ts4800.c
+++ /dev/null
@@ -1,81 +0,0 @@ 
-/*
- * GPIO driver for the TS-4800 board
- *
- * Copyright (c) 2016 - Savoir-faire Linux
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#include <linux/gpio/driver.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-
-#define DEFAULT_PIN_NUMBER      16
-#define INPUT_REG_OFFSET        0x00
-#define OUTPUT_REG_OFFSET       0x02
-#define DIRECTION_REG_OFFSET    0x04
-
-static int ts4800_gpio_probe(struct platform_device *pdev)
-{
-	struct device_node *node;
-	struct gpio_chip *chip;
-	struct resource *res;
-	void __iomem *base_addr;
-	int retval;
-	u32 ngpios;
-
-	chip = devm_kzalloc(&pdev->dev, sizeof(struct gpio_chip), GFP_KERNEL);
-	if (!chip)
-		return -ENOMEM;
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	base_addr = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(base_addr))
-		return PTR_ERR(base_addr);
-
-	node = pdev->dev.of_node;
-	if (!node)
-		return -EINVAL;
-
-	retval = of_property_read_u32(node, "ngpios", &ngpios);
-	if (retval == -EINVAL)
-		ngpios = DEFAULT_PIN_NUMBER;
-	else if (retval)
-		return retval;
-
-	retval = bgpio_init(chip, &pdev->dev, 2, base_addr + INPUT_REG_OFFSET,
-			    base_addr + OUTPUT_REG_OFFSET, NULL,
-			    base_addr + DIRECTION_REG_OFFSET, NULL, 0);
-	if (retval) {
-		dev_err(&pdev->dev, "bgpio_init failed\n");
-		return retval;
-	}
-
-	chip->ngpio = ngpios;
-
-	platform_set_drvdata(pdev, chip);
-
-	return devm_gpiochip_add_data(&pdev->dev, chip, NULL);
-}
-
-static const struct of_device_id ts4800_gpio_of_match[] = {
-	{ .compatible = "technologic,ts4800-gpio", },
-	{},
-};
-
-static struct platform_driver ts4800_gpio_driver = {
-	.driver = {
-		   .name = "ts4800-gpio",
-		   .of_match_table = ts4800_gpio_of_match,
-		   },
-	.probe = ts4800_gpio_probe,
-};
-
-module_platform_driver_probe(ts4800_gpio_driver, ts4800_gpio_probe);
-
-MODULE_AUTHOR("Julien Grossholtz <julien.grossholtz@savoirfairelinux.com>");
-MODULE_DESCRIPTION("TS4800 FPGA GPIO driver");
-MODULE_LICENSE("GPL v2");