USB: musb: support host/gadget role switching on Blackfin parts
Signed-off-by: Cliff Cai <cliff.cai@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c
index eb4392f..8052e76 100644
--- a/drivers/usb/musb/blackfin.c
+++ b/drivers/usb/musb/blackfin.c
@@ -170,6 +170,13 @@
retval = musb_interrupt(musb);
}
+ /* Start sampling ID pin, when plug is removed from MUSB */
+ if (is_otg_enabled(musb) && (musb->xceiv->state == OTG_STATE_B_IDLE
+ || musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) {
+ mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
+ musb->a_wait_bcon = TIMER_DELAY;
+ }
+
spin_unlock_irqrestore(&musb->lock, flags);
return retval;
@@ -180,6 +187,7 @@
struct musb *musb = (void *)_musb;
unsigned long flags;
u16 val;
+ static u8 toggle;
spin_lock_irqsave(&musb->lock, flags);
switch (musb->xceiv->state) {
@@ -187,10 +195,44 @@
case OTG_STATE_A_WAIT_BCON:
/* Start a new session */
val = musb_readw(musb->mregs, MUSB_DEVCTL);
+ val &= ~MUSB_DEVCTL_SESSION;
+ musb_writew(musb->mregs, MUSB_DEVCTL, val);
val |= MUSB_DEVCTL_SESSION;
musb_writew(musb->mregs, MUSB_DEVCTL, val);
-
+ /* Check if musb is host or peripheral. */
val = musb_readw(musb->mregs, MUSB_DEVCTL);
+
+ if (!(val & MUSB_DEVCTL_BDEVICE)) {
+ gpio_set_value(musb->config->gpio_vrsel, 1);
+ musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+ } else {
+ gpio_set_value(musb->config->gpio_vrsel, 0);
+ /* Ignore VBUSERROR and SUSPEND IRQ */
+ val = musb_readb(musb->mregs, MUSB_INTRUSBE);
+ val &= ~MUSB_INTR_VBUSERROR;
+ musb_writeb(musb->mregs, MUSB_INTRUSBE, val);
+
+ val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR;
+ musb_writeb(musb->mregs, MUSB_INTRUSB, val);
+ if (is_otg_enabled(musb))
+ musb->xceiv->state = OTG_STATE_B_IDLE;
+ else
+ musb_writeb(musb->mregs, MUSB_POWER, MUSB_POWER_HSENAB);
+ }
+ mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
+ break;
+ case OTG_STATE_B_IDLE:
+
+ if (!is_peripheral_enabled(musb))
+ break;
+ /* Start a new session. It seems that MUSB needs taking
+ * some time to recognize the type of the plug inserted?
+ */
+ val = musb_readw(musb->mregs, MUSB_DEVCTL);
+ val |= MUSB_DEVCTL_SESSION;
+ musb_writew(musb->mregs, MUSB_DEVCTL, val);
+ val = musb_readw(musb->mregs, MUSB_DEVCTL);
+
if (!(val & MUSB_DEVCTL_BDEVICE)) {
gpio_set_value(musb->config->gpio_vrsel, 1);
musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
@@ -205,12 +247,27 @@
val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR;
musb_writeb(musb->mregs, MUSB_INTRUSB, val);
- val = MUSB_POWER_HSENAB;
- musb_writeb(musb->mregs, MUSB_POWER, val);
+ /* Toggle the Soft Conn bit, so that we can response to
+ * the inserting of either A-plug or B-plug.
+ */
+ if (toggle) {
+ val = musb_readb(musb->mregs, MUSB_POWER);
+ val &= ~MUSB_POWER_SOFTCONN;
+ musb_writeb(musb->mregs, MUSB_POWER, val);
+ toggle = 0;
+ } else {
+ val = musb_readb(musb->mregs, MUSB_POWER);
+ val |= MUSB_POWER_SOFTCONN;
+ musb_writeb(musb->mregs, MUSB_POWER, val);
+ toggle = 1;
+ }
+ /* The delay time is set to 1/4 second by default,
+ * shortening it, if accelerating A-plug detection
+ * is needed in OTG mode.
+ */
+ mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY / 4);
}
- mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
break;
-
default:
DBG(1, "%s state not handled\n", otg_state_string(musb));
break;
@@ -222,7 +279,7 @@
void musb_platform_enable(struct musb *musb)
{
- if (is_host_enabled(musb)) {
+ if (!is_otg_enabled(musb) && is_host_enabled(musb)) {
mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
musb->a_wait_bcon = TIMER_DELAY;
}
@@ -256,7 +313,7 @@
void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
{
- if (is_host_enabled(musb))
+ if (!is_otg_enabled(musb) && is_host_enabled(musb))
mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
}