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>
master
Patrice Chotard 8 years ago committed by Tom Rini
parent 5bdb317065
commit aef5b738c9
  1. 41
      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);

Loading…
Cancel
Save