]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
reset: sti: add deassert counter in reset channel descriptor
authorPatrice Chotard <patrice.chotard@st.com>
Thu, 18 May 2017 07:58:00 +0000 (09:58 +0200)
committerTom Rini <trini@konsulko.com>
Mon, 5 Jun 2017 18:13:02 +0000 (14:13 -0400)
This deassert counter allow to manage "shared" reset lines
encountered in some specific case. On STiH410 SoC, DWC3,
EHCI and OHCI are all using a respective PHY, but all of
these PHYs shared a "global" reset.

Currently, during command "usb stop", all host controller are
stopped (XHCI, EHCI and OHCI). XHCI is first shutdowned, which
means that PHY global reset is asserted. Then EHCI is shutdowned,
but its PHY reset has already been asserted which make handshake()
call failed in ehci_shutdown().

This counter allows to really assert a reset lines only when the
"last" user is asserting it.

Signed-off-by: Patrice Chotard <patrice.chotard@st.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
drivers/reset/sti-reset.c

index 0c32a3d8c9eef6effe7972285bc554d416744410..a79708cde2800a7169e905396afb808b15aa6767 100644 (file)
@@ -30,6 +30,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 +40,7 @@ struct syscfg_reset_channel_data {
        int reset_bit;
        int ack_offset;
        int ack_bit;
+       int deassert_cnt;
 };
 
 /**
@@ -54,7 +57,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 +105,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 +125,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 +164,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),
@@ -223,7 +226,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,19 +238,35 @@ 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
+                       error("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,
+       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",
                      reset_ctl, reset_ctl->dev, reset_ctl->id);