Merge branch 'master' of git://git.denx.de/u-boot-spi
[platform/kernel/u-boot.git] / drivers / gpio / dwapb_gpio.c
index a118f58..e55fb4a 100644 (file)
@@ -15,6 +15,7 @@
 #include <dm/lists.h>
 #include <dm/root.h>
 #include <errno.h>
+#include <reset.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -29,6 +30,10 @@ DECLARE_GLOBAL_DATA_PTR;
 #define GPIO_PORTA_EOI         0x4c
 #define GPIO_EXT_PORT(p)       (0x50 + (p) * 4)
 
+struct gpio_dwapb_priv {
+       struct reset_ctl_bulk   resets;
+};
+
 struct gpio_dwapb_platdata {
        const char      *name;
        int             bank;
@@ -78,20 +83,63 @@ static int dwapb_gpio_set_value(struct udevice *dev, unsigned pin, int val)
        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;
@@ -111,7 +159,7 @@ static int gpio_dwapb_bind(struct udevice *dev)
        if (plat)
                return 0;
 
-       base = fdtdec_get_addr(blob, dev_of_offset(dev), "reg");
+       base = dev_read_addr(dev);
        if (base == FDT_ADDR_T_NONE) {
                debug("Can't get the GPIO register base address\n");
                return -ENXIO;
@@ -123,8 +171,7 @@ static int gpio_dwapb_bind(struct udevice *dev)
                if (!fdtdec_get_bool(blob, node, "gpio-controller"))
                        continue;
 
-               plat = NULL;
-               plat = calloc(1, sizeof(*plat));
+               plat = devm_kcalloc(dev, 1, sizeof(*plat), GFP_KERNEL);
                if (!plat)
                        return -ENOMEM;
 
@@ -133,23 +180,35 @@ static int gpio_dwapb_bind(struct udevice *dev)
                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;
+               if (!plat->name) {
+                       /*
+                        * Fall back to node name. This means accessing pins
+                        * via bank name won't work.
+                        */
+                       plat->name = fdt_get_name(blob, node, NULL);
+               }
 
                ret = device_bind(dev, dev->driver, plat->name,
                                  plat, -1, &subdev);
                if (ret)
-                       goto err;
+                       return ret;
 
                dev_set_of_offset(subdev, node);
                bank++;
        }
 
        return 0;
+}
+
+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);
 
-err:
-       free(plat);
-       return ret;
+       return 0;
 }
 
 static const struct udevice_id gpio_dwapb_ids[] = {
@@ -164,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),
 };