diff options
Diffstat (limited to 'drivers/reset')
-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 0c32a3d8c9e..a79708cde28 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); |