Merge tag 'u-boot-atmel-fixes-2021.01-b' of https://gitlab.denx.de/u-boot/custodians...
[platform/kernel/u-boot.git] / drivers / reset / sti-reset.c
index 0c32a3d..2cca67d 100644 (file)
@@ -1,18 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * Copyright (c) 2017
- * Patrice Chotard <patrice.chotard@st.com>
- *
- * SPDX-License-Identifier:    GPL-2.0+
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Patrice Chotard, <patrice.chotard@foss.st.com> for STMicroelectronics.
  */
 
 #include <common.h>
 #include <errno.h>
+#include <log.h>
+#include <malloc.h>
 #include <wait_bit.h>
 #include <dm.h>
 #include <reset-uclass.h>
 #include <regmap.h>
 #include <syscon.h>
 #include <dt-bindings/reset/stih407-resets.h>
+#include <linux/bitops.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -30,6 +32,8 @@ struct sti_reset {
  * @reset_bit: Bit number in reset register.
  * @ack_offset: Ack reset register offset in syscon bank.
  * @ack_bit: Bit number in Ack reset register.
+ * @deassert_cnt: incremented when reset is deasserted, reset can only be
+ *                asserted when equal to 0
  */
 
 struct syscfg_reset_channel_data {
@@ -38,6 +42,7 @@ struct syscfg_reset_channel_data {
        int reset_bit;
        int ack_offset;
        int ack_bit;
+       int deassert_cnt;
 };
 
 /**
@@ -54,7 +59,7 @@ struct syscfg_reset_controller_data {
        bool wait_for_ack;
        bool active_low;
        int nr_channels;
-       const struct syscfg_reset_channel_data *channels;
+       struct syscfg_reset_channel_data *channels;
 };
 
 /* STiH407 Peripheral powerdown definitions. */
@@ -102,7 +107,7 @@ static const char stih407_lpm[] = "st,stih407-lpm-syscfg";
 #define SYSSTAT_4520   0x820
 #define SYSCFG_4002    0x8
 
-static const struct syscfg_reset_channel_data stih407_powerdowns[] = {
+static struct syscfg_reset_channel_data stih407_powerdowns[] = {
        [STIH407_EMISS_POWERDOWN] = STIH407_PDN_0(1),
        [STIH407_NAND_POWERDOWN] = STIH407_PDN_0(0),
        [STIH407_USB3_POWERDOWN] = STIH407_PDN_1(6),
@@ -122,7 +127,7 @@ static const struct syscfg_reset_channel_data stih407_powerdowns[] = {
 
 #define LPM_SYSCFG_1   0x4     /* Softreset IRB & SBC UART */
 
-static const struct syscfg_reset_channel_data stih407_softresets[] = {
+static struct syscfg_reset_channel_data stih407_softresets[] = {
        [STIH407_ETH1_SOFTRESET] = STIH407_SRST_SBC(SYSCFG_4002, 4),
        [STIH407_MMC1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 3),
        [STIH407_USB2_PORT0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 28),
@@ -161,7 +166,7 @@ static const struct syscfg_reset_channel_data stih407_softresets[] = {
 /* PicoPHY reset/control */
 #define SYSCFG_5061    0x0f4
 
-static const struct syscfg_reset_channel_data stih407_picophyresets[] = {
+static struct syscfg_reset_channel_data stih407_picophyresets[] = {
        [STIH407_PICOPHY0_RESET] = STIH407_SRST_CORE(SYSCFG_5061, 5),
        [STIH407_PICOPHY1_RESET] = STIH407_SRST_CORE(SYSCFG_5061, 6),
        [STIH407_PICOPHY2_RESET] = STIH407_SRST_CORE(SYSCFG_5061, 7),
@@ -198,24 +203,24 @@ phys_addr_t sti_reset_get_regmap(const char *compatible)
        node = fdt_node_offset_by_compatible(gd->fdt_blob, -1,
                                             compatible);
        if (node < 0) {
-               error("unable to find %s node\n", compatible);
+               pr_err("unable to find %s node\n", compatible);
                return node;
        }
 
        ret = uclass_get_device_by_of_offset(UCLASS_SYSCON, node, &syscon);
        if (ret) {
-               error("%s: uclass_get_device_by_of_offset failed: %d\n",
+               pr_err("%s: uclass_get_device_by_of_offset failed: %d\n",
                      __func__, ret);
                return ret;
        }
 
        regmap = syscon_get_regmap(syscon);
        if (!regmap) {
-               error("unable to get regmap for %s\n", syscon->name);
+               pr_err("unable to get regmap for %s\n", syscon->name);
                return -ENODEV;
        }
 
-       return regmap->base;
+       return regmap->ranges[0].start;
 }
 
 static int sti_reset_program_hw(struct reset_ctl *reset_ctl, int assert)
@@ -223,7 +228,7 @@ static int sti_reset_program_hw(struct reset_ctl *reset_ctl, int assert)
        struct udevice *dev = reset_ctl->dev;
        struct syscfg_reset_controller_data *reset_desc =
                (struct syscfg_reset_controller_data *)(dev->driver_data);
-       struct syscfg_reset_channel_data ch;
+       struct syscfg_reset_channel_data *ch;
        phys_addr_t base;
        u32 ctrl_val = reset_desc->active_low ? !assert : !!assert;
        void __iomem *reg;
@@ -235,21 +240,37 @@ static int sti_reset_program_hw(struct reset_ctl *reset_ctl, int assert)
        /* get reset sysconf register base address */
        base = sti_reset_get_regmap(reset_desc->channels[reset_ctl->id].compatible);
 
-       ch = reset_desc->channels[reset_ctl->id];
-       reg = (void __iomem *)base + ch.reset_offset;
+       ch = &reset_desc->channels[reset_ctl->id];
+
+       /* check the deassert counter to assert reset when it reaches 0 */
+       if (!assert) {
+               ch->deassert_cnt++;
+               if (ch->deassert_cnt > 1)
+                       return 0;
+       } else {
+               if (ch->deassert_cnt > 0) {
+                       ch->deassert_cnt--;
+                       if (ch->deassert_cnt > 0)
+                               return 0;
+               } else
+                       pr_err("Reset balancing error: reset_ctl=%p dev=%p id=%lu\n",
+                             reset_ctl, reset_ctl->dev, reset_ctl->id);
+       }
+
+       reg = (void __iomem *)base + ch->reset_offset;
 
        if (ctrl_val)
-               generic_set_bit(ch.reset_bit, reg);
+               generic_set_bit(ch->reset_bit, reg);
        else
-               generic_clear_bit(ch.reset_bit, reg);
+               generic_clear_bit(ch->reset_bit, reg);
 
        if (!reset_desc->wait_for_ack)
                return 0;
 
-       reg = (void __iomem *)base + ch.ack_offset;
-       if (wait_for_bit(__func__, reg, BIT(ch.ack_bit), ctrl_val,
-                        1000, false)) {
-               error("Stuck on waiting ack reset_ctl=%p dev=%p id=%lu\n",
+       reg = (void __iomem *)base + ch->ack_offset;
+       if (wait_for_bit_le32(reg, BIT(ch->ack_bit), ctrl_val,
+                             1000, false)) {
+               pr_err("Stuck on waiting ack reset_ctl=%p dev=%p id=%lu\n",
                      reset_ctl, reset_ctl->dev, reset_ctl->id);
 
                return -ETIMEDOUT;
@@ -280,7 +301,7 @@ static int sti_reset_deassert(struct reset_ctl *reset_ctl)
 
 struct reset_ops sti_reset_ops = {
        .request = sti_reset_request,
-       .free = sti_reset_free,
+       .rfree = sti_reset_free,
        .rst_assert = sti_reset_assert,
        .rst_deassert = sti_reset_deassert,
 };