[PATCH 2/6] gpio: Add support for PTX1K CBC FPGA spare GPIOs

From: Pantelis Antoniou
Date: Fri Oct 07 2016 - 11:35:55 EST


From: Georgi Vlaev <gvlaev@xxxxxxxxxxx>

Add support for the GPIO block in Juniper's CBC FPGA.

A number of GPIOs exported by different kind of boards
is supported.

Signed-off-by: Georgi Vlaev <gvlaev@xxxxxxxxxxx>
Signed-off-by: Guenter Roeck <groeck@xxxxxxxxxxx>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@xxxxxxxxxxxx>
---
drivers/gpio/Kconfig | 11 +++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-cbc.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 248 insertions(+)
create mode 100644 drivers/gpio/gpio-cbc.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 281029b..b29f521 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -147,6 +147,17 @@ config GPIO_BRCMSTB
help
Say yes here to enable GPIO support for Broadcom STB (BCM7XXX) SoCs.

+config GPIO_CBC
+ tristate "Juniper Networks PTX1K CBC GPIO support"
+ depends on MFD_JUNIPER_CBC
+ default y if MFD_JUNIPER_CBC
+ help
+ This driver supports the spare GPIO interfaces on the Juniper
+ PTX1K CBC.
+
+ This driver can also be built as a module. If so, the module
+ will be called gpio-cbc.
+
config GPIO_CLPS711X
tristate "CLPS711X GPIO support"
depends on ARCH_CLPS711X || COMPILE_TEST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ec890c7..78dd0d4 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_GPIO_AXP209) += gpio-axp209.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_CBC) += gpio-cbc.o
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o
obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o
diff --git a/drivers/gpio/gpio-cbc.c b/drivers/gpio/gpio-cbc.c
new file mode 100644
index 0000000..d698f00
--- /dev/null
+++ b/drivers/gpio/gpio-cbc.c
@@ -0,0 +1,236 @@
+/*
+ * Polaris CBC 8614, 8112, SIB, FPC, FTC Spare GPIO driver
+ *
+ * Copyright 2014 Juniper Networks
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/mfd/cbc-core.h>
+
+#define CBC_GPIO_DIR 0x00
+#define CBC_GPIO_OUTPUT 0x04
+#define CBC_GPIO_INPUT 0x08
+
+struct cbc_gpio {
+ u32 reg; /* start register offset */
+ u32 ngpio; /* number of GPIOs */
+ u32 offset; /* start offset of the fisrt GPIO */
+};
+
+#define CBC_GPIO(r, n, o) { .reg = r, .ngpio = n, .offset = o }
+
+static struct cbc_gpio cbc_gpios[] = {
+ CBC_GPIO(CBC_TOP_REGS_GPIO_CTRL, 12, 0), /* GPIO_8614-GPIO_8112 */
+ CBC_GPIO(CBC_TOP_REGS_SIB_SPARE_OUTPUTENABLE, 18, 0), /* SIB_SPARE */
+ CBC_GPIO(CBC_TOP_REGS_FPC_SPARE_OUTPUTENABLE, 32, 0), /* FPC_SPARE */
+ CBC_GPIO(CBC_TOP_REGS_OTHER_SPARE_OUTPUTENABLE, 10, 1) /* OTHER_SPARE */
+};
+
+/*
+ * struct cbc_gpio_chip
+ */
+struct cbc_gpio_chip {
+ void __iomem *base;
+ struct device *dev;
+ struct gpio_chip chip;
+ u32 gpio_state;
+ u32 gpio_dir;
+ u32 offset;
+ spinlock_t gpio_lock;
+};
+
+/*
+ * cbc_gpio_get - Read the specified signal of the GPIO device.
+ */
+static int cbc_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct cbc_gpio_chip *chip =
+ container_of(gc, struct cbc_gpio_chip, chip);
+
+ return !!(ioread32(chip->base + CBC_GPIO_INPUT) &
+ BIT(gpio));
+}
+
+/*
+ * cbc_gpio_set - Write the specified signal of the GPIO device.
+ */
+static void cbc_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ unsigned long flags;
+ struct cbc_gpio_chip *chip =
+ container_of(gc, struct cbc_gpio_chip, chip);
+
+ spin_lock_irqsave(&chip->gpio_lock, flags);
+
+ /* Write to GPIO signal and set its direction to output */
+ if (val)
+ chip->gpio_state |= BIT(gpio);
+ else
+ chip->gpio_state &= ~BIT(gpio);
+
+ iowrite32(chip->gpio_state, chip->base + CBC_GPIO_OUTPUT);
+
+ spin_unlock_irqrestore(&chip->gpio_lock, flags);
+}
+
+/*
+ * cbc_gpio_dir_in - Set the direction of the specified GPIO signal as input.
+ */
+static int cbc_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ unsigned long flags;
+ struct cbc_gpio_chip *chip =
+ container_of(gc, struct cbc_gpio_chip, chip);
+
+ spin_lock_irqsave(&chip->gpio_lock, flags);
+
+ chip->gpio_dir &= ~BIT(gpio);
+ iowrite32(chip->gpio_dir, chip->base + CBC_GPIO_DIR);
+
+ spin_unlock_irqrestore(&chip->gpio_lock, flags);
+
+ return 0;
+}
+
+/*
+ * cbc_gpio_dir_out - Set the direction of the specified GPIO signal as output.
+ */
+static int cbc_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ unsigned long flags;
+ struct cbc_gpio_chip *chip =
+ container_of(gc, struct cbc_gpio_chip, chip);
+
+ spin_lock_irqsave(&chip->gpio_lock, flags);
+
+ chip->gpio_dir |= BIT(gpio);
+ iowrite32(chip->gpio_dir, chip->base + CBC_GPIO_DIR);
+
+ if (val)
+ chip->gpio_state |= BIT(gpio);
+ else
+ chip->gpio_state &= ~BIT(gpio);
+ iowrite32(chip->gpio_state, chip->base + CBC_GPIO_OUTPUT);
+
+ spin_unlock_irqrestore(&chip->gpio_lock, flags);
+
+ return 0;
+}
+
+/*
+ * cbc_gpio_setup_one - Setup single bank as gpio_chip
+ */
+static void cbc_gpio_setup_one(struct cbc_gpio_chip *cgc, int ngpio)
+{
+ struct gpio_chip *gpio = &cgc->chip;
+
+ gpio->label = dev_name(cgc->dev);
+ gpio->owner = THIS_MODULE;
+ gpio->direction_input = cbc_gpio_dir_in;
+ gpio->get = cbc_gpio_get;
+ gpio->direction_output = cbc_gpio_dir_out;
+ gpio->set = cbc_gpio_set;
+ gpio->dbg_show = NULL;
+ gpio->base = -1;
+ gpio->ngpio = ngpio;
+ gpio->can_sleep = 0;
+#ifdef CONFIG_OF_GPIO
+ gpio->of_node = cgc->dev->of_node;
+#endif
+}
+
+static int cbc_gpio_probe(struct platform_device *pdev)
+{
+ int i, ret;
+ struct cbc_gpio_chip *chips, *c;
+ struct resource *res;
+ void __iomem *base;
+ struct device *dev = &pdev->dev;
+
+ chips = devm_kzalloc(dev, sizeof(struct cbc_gpio_chip) *
+ ARRAY_SIZE(cbc_gpios), GFP_KERNEL);
+ if (chips == NULL)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!base)
+ return -ENOMEM;
+
+
+ platform_set_drvdata(pdev, chips);
+
+ /* For each GPIO bank, register a GPIO chip. */
+ for (i = 0; i < ARRAY_SIZE(cbc_gpios); i++) {
+ c = &chips[i];
+
+ c->dev = dev;
+ c->base = base;
+ spin_lock_init(&c->gpio_lock);
+ c->offset = cbc_gpios[i].offset;
+ cbc_gpio_setup_one(c, cbc_gpios[i].ngpio);
+
+ ret = gpiochip_add(&c->chip);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to register CBC gpiochip %d: %d\n",
+ i, ret);
+ goto err_gpiochip;
+ }
+ }
+ return 0;
+
+err_gpiochip:
+ for (i = i - 1; i >= 0; i--)
+ gpiochip_remove(&chips[i].chip);
+
+ return ret;
+}
+
+static int cbc_gpio_remove(struct platform_device *pdev)
+{
+ int i;
+ struct cbc_gpio_chip *chips = platform_get_drvdata(pdev);
+
+ for (i = 0; i < ARRAY_SIZE(cbc_gpios); i++)
+ gpiochip_remove(&chips[i].chip);
+
+ return 0;
+}
+
+static const struct of_device_id cbc_gpio_ids[] = {
+ { .compatible = "jnx,gpio-cbc", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, cbc_gpio_ids);
+
+static struct platform_driver cbc_gpio_driver = {
+ .driver = {
+ .name = "gpio-cbc",
+ .owner = THIS_MODULE,
+ .of_match_table = cbc_gpio_ids,
+ },
+ .probe = cbc_gpio_probe,
+ .remove = cbc_gpio_remove,
+};
+module_platform_driver(cbc_gpio_driver);
+
+MODULE_DESCRIPTION("Juniper Networks CBC GPIO Driver");
+MODULE_AUTHOR("Georgi Vlaev <gvlaev@xxxxxxxxxxx>");
+MODULE_LICENSE("GPL");
--
1.9.1