diff options
author | Patrice Chotard <patrice.chotard@st.com> | 2017-05-18 07:58:00 (GMT) |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2017-06-05 18:13:02 (GMT) |
commit | aef5b738c97995fca9f39855ce0668cc28b71757 (patch) | |
tree | d0320e8d8639d0ee35a5a82660cc5c2be46eb8ad | |
parent | 5bdb31706509c0dbf44228fc69538e90cc7d510e (diff) | |
download | u-boot-aef5b738c97995fca9f39855ce0668cc28b71757.tar.xz |
reset: sti: add deassert counter in reset channel descriptor
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>
-rw-r--r-- | drivers/reset/sti-reset.c | 41 |
1 files changed, 30 insertions, 11 deletions
diff --git a/drivers/reset/sti-reset.c b/drivers/reset/sti-reset.c index 0c32a3d..a79708c 100644 --- a/drivers/reset/sti-reset.c +++ b/drivers/reset/sti-reset.c @@ -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); |