phy: Fix possible NULL pointer deference
[oweals/u-boot.git] / drivers / gpio / dwapb_gpio.c
index 471e18aeaa0b3c54091a100214dab6ab735ff69c..e5e35181940ce2a41ad6458dea74c7b668e26503 100644 (file)
@@ -1,26 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * (C) Copyright 2015 Marek Vasut <marex@denx.de>
  *
  * DesignWare APB GPIO driver
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
+#include <log.h>
 #include <malloc.h>
 #include <asm/arch/gpio.h>
 #include <asm/gpio.h>
 #include <asm/io.h>
 #include <dm.h>
 #include <dm/device-internal.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
 #include <dm/lists.h>
 #include <dm/root.h>
 #include <errno.h>
+#include <reset.h>
+#include <linux/bitops.h>
 
-DECLARE_GLOBAL_DATA_PTR;
-
-#define GPIO_SWPORTA_DR                0x00
-#define GPIO_SWPORTA_DDR       0x04
+#define GPIO_SWPORT_DR(p)      (0x00 + (p) * 0xc)
+#define GPIO_SWPORT_DDR(p)     (0x04 + (p) * 0xc)
 #define GPIO_INTEN             0x30
 #define GPIO_INTMASK           0x34
 #define GPIO_INTTYPE_LEVEL     0x38
@@ -28,7 +30,11 @@ DECLARE_GLOBAL_DATA_PTR;
 #define GPIO_INTSTATUS         0x40
 #define GPIO_PORTA_DEBOUNCE    0x48
 #define GPIO_PORTA_EOI         0x4c
-#define GPIO_EXT_PORTA         0x50
+#define GPIO_EXT_PORT(p)       (0x50 + (p) * 4)
+
+struct gpio_dwapb_priv {
+       struct reset_ctl_bulk   resets;
+};
 
 struct gpio_dwapb_platdata {
        const char      *name;
@@ -41,7 +47,7 @@ static int dwapb_gpio_direction_input(struct udevice *dev, unsigned pin)
 {
        struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
 
-       clrbits_le32(plat->base + GPIO_SWPORTA_DDR, 1 << pin);
+       clrbits_le32(plat->base + GPIO_SWPORT_DDR(plat->bank), 1 << pin);
        return 0;
 }
 
@@ -50,12 +56,12 @@ static int dwapb_gpio_direction_output(struct udevice *dev, unsigned pin,
 {
        struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
 
-       setbits_le32(plat->base + GPIO_SWPORTA_DDR, 1 << pin);
+       setbits_le32(plat->base + GPIO_SWPORT_DDR(plat->bank), 1 << pin);
 
        if (val)
-               setbits_le32(plat->base + GPIO_SWPORTA_DR, 1 << pin);
+               setbits_le32(plat->base + GPIO_SWPORT_DR(plat->bank), 1 << pin);
        else
-               clrbits_le32(plat->base + GPIO_SWPORTA_DR, 1 << pin);
+               clrbits_le32(plat->base + GPIO_SWPORT_DR(plat->bank), 1 << pin);
 
        return 0;
 }
@@ -63,7 +69,7 @@ static int dwapb_gpio_direction_output(struct udevice *dev, unsigned pin,
 static int dwapb_gpio_get_value(struct udevice *dev, unsigned pin)
 {
        struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
-       return !!(readl(plat->base + GPIO_EXT_PORTA) & (1 << pin));
+       return !!(readl(plat->base + GPIO_EXT_PORT(plat->bank)) & (1 << pin));
 }
 
 
@@ -72,27 +78,70 @@ static int dwapb_gpio_set_value(struct udevice *dev, unsigned pin, int val)
        struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
 
        if (val)
-               setbits_le32(plat->base + GPIO_SWPORTA_DR, 1 << pin);
+               setbits_le32(plat->base + GPIO_SWPORT_DR(plat->bank), 1 << pin);
        else
-               clrbits_le32(plat->base + GPIO_SWPORTA_DR, 1 << pin);
+               clrbits_le32(plat->base + GPIO_SWPORT_DR(plat->bank), 1 << pin);
 
        return 0;
 }
 
+static int dwapb_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+       struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
+       u32 gpio;
+
+       gpio = readl(plat->base + GPIO_SWPORT_DDR(plat->bank));
+
+       if (gpio & BIT(offset))
+               return GPIOF_OUTPUT;
+       else
+               return GPIOF_INPUT;
+}
+
 static const struct dm_gpio_ops gpio_dwapb_ops = {
        .direction_input        = dwapb_gpio_direction_input,
        .direction_output       = dwapb_gpio_direction_output,
        .get_value              = dwapb_gpio_get_value,
        .set_value              = dwapb_gpio_set_value,
+       .get_function           = dwapb_gpio_get_function,
 };
 
+static int gpio_dwapb_reset(struct udevice *dev)
+{
+       int ret;
+       struct gpio_dwapb_priv *priv = dev_get_priv(dev);
+
+       ret = reset_get_bulk(dev, &priv->resets);
+       if (ret) {
+               /* Return 0 if error due to !CONFIG_DM_RESET and reset
+                * DT property is not present.
+                */
+               if (ret == -ENOENT || ret == -ENOTSUPP)
+                       return 0;
+
+               dev_warn(dev, "Can't get reset: %d\n", ret);
+               return ret;
+       }
+
+       ret = reset_deassert_bulk(&priv->resets);
+       if (ret) {
+               reset_release_bulk(&priv->resets);
+               dev_err(dev, "Failed to reset: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 static int gpio_dwapb_probe(struct udevice *dev)
 {
        struct gpio_dev_priv *priv = dev_get_uclass_priv(dev);
        struct gpio_dwapb_platdata *plat = dev->platdata;
 
-       if (!plat)
-               return 0;
+       if (!plat) {
+               /* Reset on parent device only */
+               return gpio_dwapb_reset(dev);
+       }
 
        priv->gpio_count = plat->pins;
        priv->bank_name = plat->name;
@@ -103,54 +152,63 @@ static int gpio_dwapb_probe(struct udevice *dev)
 static int gpio_dwapb_bind(struct udevice *dev)
 {
        struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
-       const void *blob = gd->fdt_blob;
        struct udevice *subdev;
        fdt_addr_t base;
-       int ret, node, bank = 0;
+       int ret, bank = 0;
+       ofnode node;
 
        /* If this is a child device, there is nothing to do here */
        if (plat)
                return 0;
 
-       base = fdtdec_get_addr(blob, dev->of_offset, "reg");
+       base = dev_read_addr(dev);
        if (base == FDT_ADDR_T_NONE) {
                debug("Can't get the GPIO register base address\n");
                return -ENXIO;
        }
 
-       for (node = fdt_first_subnode(blob, dev->of_offset);
-            node > 0;
-            node = fdt_next_subnode(blob, node)) {
-               if (!fdtdec_get_bool(blob, node, "gpio-controller"))
+       for (node = dev_read_first_subnode(dev); ofnode_valid(node);
+            node = dev_read_next_subnode(node)) {
+               if (!ofnode_read_bool(node, "gpio-controller"))
                        continue;
 
-               plat = NULL;
-               plat = calloc(1, sizeof(*plat));
+               plat = devm_kcalloc(dev, 1, sizeof(*plat), GFP_KERNEL);
                if (!plat)
                        return -ENOMEM;
 
                plat->base = base;
                plat->bank = bank;
-               plat->pins = fdtdec_get_int(blob, node, "snps,nr-gpios", 0);
-               plat->name = fdt_stringlist_get(blob, node, "bank-name", 0,
-                                               NULL);
-               if (ret)
-                       goto err;
-
-               ret = device_bind(dev, dev->driver, plat->name,
-                                 plat, -1, &subdev);
+               plat->pins = ofnode_read_u32_default(node, "snps,nr-gpios", 0);
+
+               if (ofnode_read_string_index(node, "bank-name", 0,
+                                            &plat->name)) {
+                       /*
+                        * Fall back to node name. This means accessing pins
+                        * via bank name won't work.
+                        */
+                       plat->name = ofnode_get_name(node);
+               }
+
+               ret = device_bind_ofnode(dev, dev->driver, plat->name,
+                                        plat, node, &subdev);
                if (ret)
-                       goto err;
+                       return ret;
 
-               subdev->of_offset = node;
                bank++;
        }
 
        return 0;
+}
 
-err:
-       free(plat);
-       return ret;
+static int gpio_dwapb_remove(struct udevice *dev)
+{
+       struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
+       struct gpio_dwapb_priv *priv = dev_get_priv(dev);
+
+       if (!plat && priv)
+               return reset_release_bulk(&priv->resets);
+
+       return 0;
 }
 
 static const struct udevice_id gpio_dwapb_ids[] = {
@@ -165,4 +223,6 @@ U_BOOT_DRIVER(gpio_dwapb) = {
        .ops            = &gpio_dwapb_ops,
        .bind           = gpio_dwapb_bind,
        .probe          = gpio_dwapb_probe,
+       .remove         = gpio_dwapb_remove,
+       .priv_auto_alloc_size   = sizeof(struct gpio_dwapb_priv),
 };