diff options
author | Hans de Goede <hdegoede@redhat.com> | 2015-06-14 11:55:28 +0200 |
---|---|---|
committer | Hans de Goede <hdegoede@redhat.com> | 2015-06-17 15:22:07 +0200 |
commit | e1abfa437a2917834bcb8a9ee20c1e18bfc466f5 (patch) | |
tree | 234d04333006a5212593fdca5c5d14bbbee62043 | |
parent | 0d3f732fd2ba679b4498541f075d1b1bdbea3935 (diff) | |
download | u-boot-imx-e1abfa437a2917834bcb8a9ee20c1e18bfc466f5.zip u-boot-imx-e1abfa437a2917834bcb8a9ee20c1e18bfc466f5.tar.gz u-boot-imx-e1abfa437a2917834bcb8a9ee20c1e18bfc466f5.tar.bz2 |
sunxi: musb: Do not fully reset the controler from sunxi_musb_disable
Fully resetting the controller is a too big hammer, and the musb_core will
then afterwards fail to communicate with any endpoints other then 0 as
too much state was cleared.
Instead report vbus low for 200ms which will effectively end the current
session without needing to do a full reset.
This fixes usb mass-storage devices no longer working after a "usb reset"
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Ian Campbell <ijc@hellion.org.uk>
-rw-r--r-- | drivers/usb/musb-new/sunxi.c | 52 |
1 files changed, 31 insertions, 21 deletions
diff --git a/drivers/usb/musb-new/sunxi.c b/drivers/usb/musb-new/sunxi.c index e8a3a23..42c6725 100644 --- a/drivers/usb/musb-new/sunxi.c +++ b/drivers/usb/musb-new/sunxi.c @@ -157,6 +157,17 @@ static void USBC_ForceIdToHigh(__iomem void *base) musb_writel(base, USBC_REG_o_ISCR, reg_val); } +static void USBC_ForceVbusValidToLow(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); + reg_val |= (0x02 << USBC_BP_ISCR_FORCE_VBUS_VALID); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + static void USBC_ForceVbusValidToHigh(__iomem void *base) { u32 reg_val; @@ -205,42 +216,41 @@ static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci) return retval; } +/* musb_core does not call enable / disable in a balanced manner <sigh> */ +static bool enabled = false; + static void sunxi_musb_enable(struct musb *musb) { pr_debug("%s():\n", __func__); + if (enabled) + return; + /* select PIO mode */ musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0); - if (is_host_enabled(musb)) { - /* port power on */ - sunxi_usb_phy_power_on(0); - } + if (is_host_enabled(musb)) + sunxi_usb_phy_power_on(0); /* port power on */ + + USBC_ForceVbusValidToHigh(musb->mregs); + + enabled = true; } static void sunxi_musb_disable(struct musb *musb) { - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - pr_debug("%s():\n", __func__); - /* Put the controller back in a pristane state for "usb reset" */ - if (musb->is_active) { - sunxi_usb_phy_exit(0); -#ifdef CONFIG_SUNXI_GEN_SUN6I - clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_USB0); -#endif - clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_USB0); + if (!enabled) + return; - mdelay(10); + if (is_host_enabled(musb)) + sunxi_usb_phy_power_off(0); /* port power off */ - setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_USB0); -#ifdef CONFIG_SUNXI_GEN_SUN6I - setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_USB0); -#endif - sunxi_usb_phy_init(0); - musb->is_active = 0; - } + USBC_ForceVbusValidToLow(musb->mregs); + mdelay(200); /* Wait for the current session to timeout */ + + enabled = false; } static int sunxi_musb_init(struct musb *musb) |