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>
This commit is contained in:
Hans de Goede 2015-06-14 11:55:28 +02:00
parent 0d3f732fd2
commit e1abfa437a
1 changed files with 31 additions and 21 deletions

View File

@ -157,6 +157,17 @@ static void USBC_ForceIdToHigh(__iomem void *base)
musb_writel(base, USBC_REG_o_ISCR, reg_val); 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) static void USBC_ForceVbusValidToHigh(__iomem void *base)
{ {
u32 reg_val; u32 reg_val;
@ -205,42 +216,41 @@ static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci)
return retval; 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) static void sunxi_musb_enable(struct musb *musb)
{ {
pr_debug("%s():\n", __func__); pr_debug("%s():\n", __func__);
if (enabled)
return;
/* select PIO mode */ /* select PIO mode */
musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0); musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0);
if (is_host_enabled(musb)) { if (is_host_enabled(musb))
/* port power on */ sunxi_usb_phy_power_on(0); /* port power on */
sunxi_usb_phy_power_on(0);
} USBC_ForceVbusValidToHigh(musb->mregs);
enabled = true;
} }
static void sunxi_musb_disable(struct musb *musb) 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__); pr_debug("%s():\n", __func__);
/* Put the controller back in a pristane state for "usb reset" */ if (!enabled)
if (musb->is_active) { return;
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);
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); USBC_ForceVbusValidToLow(musb->mregs);
#ifdef CONFIG_SUNXI_GEN_SUN6I mdelay(200); /* Wait for the current session to timeout */
setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_USB0);
#endif enabled = false;
sunxi_usb_phy_init(0);
musb->is_active = 0;
}
} }
static int sunxi_musb_init(struct musb *musb) static int sunxi_musb_init(struct musb *musb)