OSDN Git Service

gpio: visconti: Add Toshiba Visconti GPIO support
authorNobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
Thu, 17 Dec 2020 14:43:36 +0000 (23:43 +0900)
committerBartosz Golaszewski <bgolaszewski@baylibre.com>
Mon, 15 Feb 2021 10:43:26 +0000 (11:43 +0100)
Add the GPIO driver for Toshiba Visconti ARM SoCs.

Signed-off-by: Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
Reviewed-by: Punit Agrawal <punit1.agrawal@toshiba.co.jp>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-visconti.c [new file with mode: 0644]
drivers/pinctrl/visconti/pinctrl-common.c

index 6addc92..520fdf2 100644 (file)
@@ -648,6 +648,16 @@ config GPIO_VF610
        help
          Say yes here to support Vybrid vf610 GPIOs.
 
+config GPIO_VISCONTI
+       tristate "Toshiba Visconti GPIO support"
+       depends on ARCH_VISCONTI || COMPILE_TEST
+       depends on OF_GPIO
+       select GPIOLIB_IRQCHIP
+       select GPIO_GENERIC
+       select IRQ_DOMAIN_HIERARCHY
+       help
+         Say yes here to support GPIO on Tohisba Visconti.
+
 config GPIO_VR41XX
        tristate "NEC VR4100 series General-purpose I/O Uint support"
        depends on CPU_VR41XX
index 35e3b60..2c2a705 100644 (file)
@@ -164,6 +164,7 @@ obj-$(CONFIG_GPIO_UCB1400)          += gpio-ucb1400.o
 obj-$(CONFIG_GPIO_UNIPHIER)            += gpio-uniphier.o
 obj-$(CONFIG_GPIO_VF610)               += gpio-vf610.o
 obj-$(CONFIG_GPIO_VIPERBOARD)          += gpio-viperboard.o
+obj-$(CONFIG_GPIO_VISCONTI)            += gpio-visconti.o
 obj-$(CONFIG_GPIO_VR41XX)              += gpio-vr41xx.o
 obj-$(CONFIG_GPIO_VX855)               += gpio-vx855.o
 obj-$(CONFIG_GPIO_WCD934X)             += gpio-wcd934x.o
diff --git a/drivers/gpio/gpio-visconti.c b/drivers/gpio/gpio-visconti.c
new file mode 100644 (file)
index 0000000..0e3d198
--- /dev/null
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Toshiba Visconti GPIO Support
+ *
+ * (C) Copyright 2020 Toshiba Electronic Devices & Storage Corporation
+ * (C) Copyright 2020 TOSHIBA CORPORATION
+ *
+ * Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+
+/* register offset */
+#define GPIO_DIR       0x00
+#define GPIO_IDATA     0x08
+#define GPIO_ODATA     0x10
+#define GPIO_OSET      0x18
+#define GPIO_OCLR      0x20
+#define GPIO_INTMODE   0x30
+
+#define BASE_HW_IRQ 24
+
+struct visconti_gpio {
+       void __iomem *base;
+       spinlock_t lock; /* protect gpio register */
+       struct gpio_chip gpio_chip;
+       struct irq_chip irq_chip;
+};
+
+static int visconti_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct visconti_gpio *priv = gpiochip_get_data(gc);
+       u32 offset = irqd_to_hwirq(d);
+       u32 bit = BIT(offset);
+       u32 intc_type = IRQ_TYPE_EDGE_RISING;
+       u32 intmode, odata;
+       int ret = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       odata = readl(priv->base + GPIO_ODATA);
+       intmode = readl(priv->base + GPIO_INTMODE);
+
+       switch (type) {
+       case IRQ_TYPE_EDGE_RISING:
+               odata &= ~bit;
+               intmode &= ~bit;
+               break;
+       case IRQ_TYPE_EDGE_FALLING:
+               odata |= bit;
+               intmode &= ~bit;
+               break;
+       case IRQ_TYPE_EDGE_BOTH:
+               intmode |= bit;
+               break;
+       case IRQ_TYPE_LEVEL_HIGH:
+               intc_type = IRQ_TYPE_LEVEL_HIGH;
+               odata &= ~bit;
+               intmode &= ~bit;
+               break;
+       case IRQ_TYPE_LEVEL_LOW:
+               intc_type = IRQ_TYPE_LEVEL_HIGH;
+               odata |= bit;
+               intmode &= ~bit;
+               break;
+       default:
+               ret = -EINVAL;
+               goto err;
+       }
+
+       writel(odata, priv->base + GPIO_ODATA);
+       writel(intmode, priv->base + GPIO_INTMODE);
+       irq_set_irq_type(offset, intc_type);
+
+       ret = irq_chip_set_type_parent(d, type);
+err:
+       spin_unlock_irqrestore(&priv->lock, flags);
+       return ret;
+}
+
+static int visconti_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
+                                              unsigned int child,
+                                              unsigned int child_type,
+                                              unsigned int *parent,
+                                              unsigned int *parent_type)
+{
+       /* Interrupts 0..15 mapped to interrupts 24..39 on the GIC */
+       if (child < 16) {
+               /* All these interrupts are level high in the CPU */
+               *parent_type = IRQ_TYPE_LEVEL_HIGH;
+               *parent = child + BASE_HW_IRQ;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static void *visconti_gpio_populate_parent_fwspec(struct gpio_chip *chip,
+                                                 unsigned int parent_hwirq,
+                                                 unsigned int parent_type)
+{
+       struct irq_fwspec *fwspec;
+
+       fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
+       if (!fwspec)
+               return NULL;
+
+       fwspec->fwnode = chip->irq.parent_domain->fwnode;
+       fwspec->param_count = 3;
+       fwspec->param[0] = 0;
+       fwspec->param[1] = parent_hwirq;
+       fwspec->param[2] = parent_type;
+
+       return fwspec;
+}
+
+static int visconti_gpio_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct visconti_gpio *priv;
+       struct irq_chip *irq_chip;
+       struct gpio_irq_chip *girq;
+       struct irq_domain *parent;
+       struct device_node *irq_parent;
+       struct fwnode_handle *fwnode;
+       int ret;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       spin_lock_init(&priv->lock);
+
+       priv->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(priv->base))
+               return PTR_ERR(priv->base);
+
+       irq_parent = of_irq_find_parent(dev->of_node);
+       if (!irq_parent) {
+               dev_err(dev, "No IRQ parent node\n");
+               return -ENODEV;
+       }
+
+       parent = irq_find_host(irq_parent);
+       if (!parent) {
+               dev_err(dev, "No IRQ parent domain\n");
+               return -ENODEV;
+       }
+
+       fwnode = of_node_to_fwnode(irq_parent);
+       of_node_put(irq_parent);
+
+       ret = bgpio_init(&priv->gpio_chip, dev, 4,
+                        priv->base + GPIO_IDATA,
+                        priv->base + GPIO_OSET,
+                        priv->base + GPIO_OCLR,
+                        priv->base + GPIO_DIR,
+                        NULL,
+                        0);
+       if (ret) {
+               dev_err(dev, "unable to init generic GPIO\n");
+               return ret;
+       }
+
+       irq_chip = &priv->irq_chip;
+       irq_chip->name = dev_name(dev);
+       irq_chip->irq_mask = irq_chip_mask_parent;
+       irq_chip->irq_unmask = irq_chip_unmask_parent;
+       irq_chip->irq_eoi = irq_chip_eoi_parent;
+       irq_chip->irq_set_type = visconti_gpio_irq_set_type;
+       irq_chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND;
+
+       girq = &priv->gpio_chip.irq;
+       girq->chip = irq_chip;
+       girq->fwnode = fwnode;
+       girq->parent_domain = parent;
+       girq->child_to_parent_hwirq = visconti_gpio_child_to_parent_hwirq;
+       girq->populate_parent_alloc_arg = visconti_gpio_populate_parent_fwspec;
+       girq->default_type = IRQ_TYPE_NONE;
+       girq->handler = handle_level_irq;
+
+       ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv);
+       if (ret) {
+               dev_err(dev, "failed to add GPIO chip\n");
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, priv);
+
+       return ret;
+}
+
+static const struct of_device_id visconti_gpio_of_match[] = {
+       { .compatible = "toshiba,gpio-tmpv7708", },
+       { /* end of table */ }
+};
+MODULE_DEVICE_TABLE(of, visconti_gpio_of_match);
+
+static struct platform_driver visconti_gpio_driver = {
+       .probe          = visconti_gpio_probe,
+       .driver         = {
+               .name   = "visconti_gpio",
+               .of_match_table = of_match_ptr(visconti_gpio_of_match),
+       }
+};
+module_platform_driver(visconti_gpio_driver);
+
+MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>");
+MODULE_DESCRIPTION("Toshiba Visconti GPIO Driver");
+MODULE_LICENSE("GPL v2");
index 0cb10b7..21c7e0d 100644 (file)
@@ -245,11 +245,34 @@ static int visconti_set_mux(struct pinctrl_dev *pctldev,
        return 0;
 }
 
+static int visconti_gpio_request_enable(struct pinctrl_dev *pctldev,
+                                     struct pinctrl_gpio_range *range,
+                                     unsigned int pin)
+{
+       struct visconti_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev);
+       const struct visconti_mux *gpio_mux = &priv->devdata->gpio_mux[pin];
+       unsigned long flags;
+       unsigned int val;
+
+       dev_dbg(priv->dev, "%s: pin = %d\n", __func__, pin);
+
+       /* update mux */
+       spin_lock_irqsave(&priv->lock, flags);
+       val = readl(priv->base + gpio_mux->offset);
+       val &= ~gpio_mux->mask;
+       val |= gpio_mux->val;
+       writel(val, priv->base + gpio_mux->offset);
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return 0;
+}
+
 static const struct pinmux_ops visconti_pinmux_ops = {
        .get_functions_count    = visconti_get_functions_count,
        .get_function_name      = visconti_get_function_name,
        .get_function_groups    = visconti_get_function_groups,
        .set_mux                = visconti_set_mux,
+       .gpio_request_enable    = visconti_gpio_request_enable,
        .strict                 = true,
 };