Merge tag 'wberr-v4.14-1' of git://git.kernel.org/pub/scm/linux/kernel/git/jlayton...
[sfrench/cifs-2.6.git] / drivers / gpio / gpio-vf610.c
index 521fbe338589d03ea9cb1777a97a99c1261fc0f6..cbe9e06861de0d490f592107de6704a651e2d644 100644 (file)
 
 #define VF610_GPIO_PER_PORT            32
 
+struct fsl_gpio_soc_data {
+       /* SoCs has a Port Data Direction Register (PDDR) */
+       bool have_paddr;
+};
+
 struct vf610_gpio_port {
        struct gpio_chip gc;
        void __iomem *base;
        void __iomem *gpio_base;
+       const struct fsl_gpio_soc_data *sdata;
        u8 irqc[VF610_GPIO_PER_PORT];
        int irq;
 };
@@ -43,6 +49,7 @@ struct vf610_gpio_port {
 #define GPIO_PCOR              0x08
 #define GPIO_PTOR              0x0c
 #define GPIO_PDIR              0x10
+#define GPIO_PDDR              0x14
 
 #define PORT_PCR(n)            ((n) * 0x4)
 #define PORT_PCR_IRQC_OFFSET   16
@@ -61,8 +68,13 @@ struct vf610_gpio_port {
 
 static struct irq_chip vf610_gpio_irq_chip;
 
+static const struct fsl_gpio_soc_data imx_data = {
+       .have_paddr = true,
+};
+
 static const struct of_device_id vf610_gpio_dt_ids[] = {
-       { .compatible = "fsl,vf610-gpio" },
+       { .compatible = "fsl,vf610-gpio",       .data = NULL, },
+       { .compatible = "fsl,imx7ulp-gpio",     .data = &imx_data, },
        { /* sentinel */ }
 };
 
@@ -79,8 +91,18 @@ static inline u32 vf610_gpio_readl(void __iomem *reg)
 static int vf610_gpio_get(struct gpio_chip *gc, unsigned int gpio)
 {
        struct vf610_gpio_port *port = gpiochip_get_data(gc);
-
-       return !!(vf610_gpio_readl(port->gpio_base + GPIO_PDIR) & BIT(gpio));
+       unsigned long mask = BIT(gpio);
+       void __iomem *addr;
+
+       if (port->sdata && port->sdata->have_paddr) {
+               mask &= vf610_gpio_readl(port->gpio_base + GPIO_PDDR);
+               addr = mask ? port->gpio_base + GPIO_PDOR :
+                             port->gpio_base + GPIO_PDIR;
+               return !!(vf610_gpio_readl(addr) & BIT(gpio));
+       } else {
+               return !!(vf610_gpio_readl(port->gpio_base + GPIO_PDIR)
+                                          & BIT(gpio));
+       }
 }
 
 static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
@@ -96,12 +118,28 @@ static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
 
 static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
 {
+       struct vf610_gpio_port *port = gpiochip_get_data(chip);
+       unsigned long mask = BIT(gpio);
+       u32 val;
+
+       if (port->sdata && port->sdata->have_paddr) {
+               val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR);
+               val &= ~mask;
+               vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR);
+       }
+
        return pinctrl_gpio_direction_input(chip->base + gpio);
 }
 
 static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
                                       int value)
 {
+       struct vf610_gpio_port *port = gpiochip_get_data(chip);
+       unsigned long mask = BIT(gpio);
+
+       if (port->sdata && port->sdata->have_paddr)
+               vf610_gpio_writel(mask, port->gpio_base + GPIO_PDDR);
+
        vf610_gpio_set(chip, gpio, value);
 
        return pinctrl_gpio_direction_output(chip->base + gpio);
@@ -216,6 +254,8 @@ static struct irq_chip vf610_gpio_irq_chip = {
 
 static int vf610_gpio_probe(struct platform_device *pdev)
 {
+       const struct of_device_id *of_id = of_match_device(vf610_gpio_dt_ids,
+                                                          &pdev->dev);
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
        struct vf610_gpio_port *port;
@@ -227,6 +267,7 @@ static int vf610_gpio_probe(struct platform_device *pdev)
        if (!port)
                return -ENOMEM;
 
+       port->sdata = of_id->data;
        iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        port->base = devm_ioremap_resource(dev, iores);
        if (IS_ERR(port->base))