mmc: fsl_esdhc: workaround for hardware 3.3v IO reliability issue
[oweals/u-boot.git] / drivers / reset / reset-socfpga.c
index 466455d073991db6ec950550ca12c24551ca2b9c..830eda9430ecf8718c1cfee768e44a8f32638a24 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Socfpga Reset Controller Driver
  *
  * Copyright 2013 Maxime Ripard
  *
  * Maxime Ripard <maxime.ripard@free-electrons.com>
- *
- * SPDX-License-Identifier:     GPL-2.0+
  */
 
 #include <common.h>
 #include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <dm/lists.h>
 #include <dm/of_access.h>
+#include <env.h>
 #include <reset-uclass.h>
+#include <wait_bit.h>
 #include <linux/bitops.h>
 #include <linux/io.h>
 #include <linux/sizes.h>
 #define NR_BANKS               8
 
 struct socfpga_reset_data {
-       void __iomem *membase;
+       void __iomem *modrst_base;
 };
 
+/*
+ * For compatibility with Kernels that don't support peripheral reset, this
+ * driver can keep the old behaviour of not asserting peripheral reset before
+ * starting the OS and deasserting all peripheral resets (enabling all
+ * peripherals).
+ *
+ * For that, the reset driver checks the environment variable
+ * "socfpga_legacy_reset_compat". If this variable is '1', perihperals are not
+ * reset again once taken out of reset and all peripherals in 'permodrst' are
+ * taken out of reset before booting into the OS.
+ * Note that this should be required for gen5 systems only that are running
+ * Linux kernels without proper peripheral reset support for all drivers used.
+ */
+static bool socfpga_reset_keep_enabled(void)
+{
+#if !defined(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(ENV_SUPPORT)
+       const char *env_str;
+       long val;
+
+       env_str = env_get("socfpga_legacy_reset_compat");
+       if (env_str) {
+               val = simple_strtol(env_str, NULL, 0);
+               if (val == 1)
+                       return true;
+       }
+#endif
+
+       return false;
+}
+
 static int socfpga_reset_assert(struct reset_ctl *reset_ctl)
 {
        struct socfpga_reset_data *data = dev_get_priv(reset_ctl->dev);
@@ -36,7 +70,7 @@ static int socfpga_reset_assert(struct reset_ctl *reset_ctl)
        int bank = id / (reg_width * BITS_PER_BYTE);
        int offset = id % (reg_width * BITS_PER_BYTE);
 
-       setbits_le32(data->membase + (bank * BANK_INCREMENT), BIT(offset));
+       setbits_le32(data->modrst_base + (bank * BANK_INCREMENT), BIT(offset));
        return 0;
 }
 
@@ -48,8 +82,11 @@ static int socfpga_reset_deassert(struct reset_ctl *reset_ctl)
        int bank = id / (reg_width * BITS_PER_BYTE);
        int offset = id % (reg_width * BITS_PER_BYTE);
 
-       clrbits_le32(data->membase + (bank * BANK_INCREMENT), BIT(offset));
-       return 0;
+       clrbits_le32(data->modrst_base + (bank * BANK_INCREMENT), BIT(offset));
+
+       return wait_for_bit_le32(data->modrst_base + (bank * BANK_INCREMENT),
+                                BIT(offset),
+                                false, 500, false);
 }
 
 static int socfpga_reset_request(struct reset_ctl *reset_ctl)
@@ -70,7 +107,7 @@ static int socfpga_reset_free(struct reset_ctl *reset_ctl)
 
 static const struct reset_ops socfpga_reset_ops = {
        .request = socfpga_reset_request,
-       .free = socfpga_reset_free,
+       .rfree = socfpga_reset_free,
        .rst_assert = socfpga_reset_assert,
        .rst_deassert = socfpga_reset_deassert,
 };
@@ -78,14 +115,42 @@ static const struct reset_ops socfpga_reset_ops = {
 static int socfpga_reset_probe(struct udevice *dev)
 {
        struct socfpga_reset_data *data = dev_get_priv(dev);
-       const void *blob = gd->fdt_blob;
-       int node = dev_of_offset(dev);
        u32 modrst_offset;
+       void __iomem *membase;
+
+       membase = devfdt_get_addr_ptr(dev);
 
-       data->membase = devfdt_get_addr_ptr(dev);
+       modrst_offset = dev_read_u32_default(dev, "altr,modrst-offset", 0x10);
+       data->modrst_base = membase + modrst_offset;
 
-       modrst_offset = fdtdec_get_int(blob, node, "altr,modrst-offset", 0x10);
-       data->membase += modrst_offset;
+       return 0;
+}
+
+static int socfpga_reset_remove(struct udevice *dev)
+{
+       struct socfpga_reset_data *data = dev_get_priv(dev);
+
+       if (socfpga_reset_keep_enabled()) {
+               puts("Deasserting all peripheral resets\n");
+               writel(0, data->modrst_base + 4);
+       }
+
+       return 0;
+}
+
+static int socfpga_reset_bind(struct udevice *dev)
+{
+       int ret;
+       struct udevice *sys_child;
+
+       /*
+        * The sysreset driver does not have a device node, so bind it here.
+        * Bind it to the node, too, so that it can get its base address.
+        */
+       ret = device_bind_driver_to_node(dev, "socfpga_sysreset", "sysreset",
+                                        dev->node, &sys_child);
+       if (ret)
+               debug("Warning: No sysreset driver: ret=%d\n", ret);
 
        return 0;
 }
@@ -99,7 +164,10 @@ U_BOOT_DRIVER(socfpga_reset) = {
        .name = "socfpga-reset",
        .id = UCLASS_RESET,
        .of_match = socfpga_reset_match,
+       .bind = socfpga_reset_bind,
        .probe = socfpga_reset_probe,
        .priv_auto_alloc_size = sizeof(struct socfpga_reset_data),
        .ops = &socfpga_reset_ops,
+       .remove = socfpga_reset_remove,
+       .flags  = DM_FLAG_OS_PREPARE,
 };