Commit ff927add authored by Cliff Cai's avatar Cliff Cai Committed by Greg Kroah-Hartman

USB: musb: support host/gadget role switching on Blackfin parts

Signed-off-by: default avatarCliff Cai <cliff.cai@analog.com>
Signed-off-by: default avatarMike Frysinger <vapier@gentoo.org>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@nokia.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent b0f9da7e
...@@ -170,6 +170,13 @@ static irqreturn_t blackfin_interrupt(int irq, void *__hci) ...@@ -170,6 +170,13 @@ static irqreturn_t blackfin_interrupt(int irq, void *__hci)
retval = musb_interrupt(musb); 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); spin_unlock_irqrestore(&musb->lock, flags);
return retval; return retval;
...@@ -180,6 +187,7 @@ static void musb_conn_timer_handler(unsigned long _musb) ...@@ -180,6 +187,7 @@ static void musb_conn_timer_handler(unsigned long _musb)
struct musb *musb = (void *)_musb; struct musb *musb = (void *)_musb;
unsigned long flags; unsigned long flags;
u16 val; u16 val;
static u8 toggle;
spin_lock_irqsave(&musb->lock, flags); spin_lock_irqsave(&musb->lock, flags);
switch (musb->xceiv->state) { switch (musb->xceiv->state) {
...@@ -187,10 +195,44 @@ static void musb_conn_timer_handler(unsigned long _musb) ...@@ -187,10 +195,44 @@ static void musb_conn_timer_handler(unsigned long _musb)
case OTG_STATE_A_WAIT_BCON: case OTG_STATE_A_WAIT_BCON:
/* Start a new session */ /* Start a new session */
val = musb_readw(musb->mregs, MUSB_DEVCTL); val = musb_readw(musb->mregs, MUSB_DEVCTL);
val &= ~MUSB_DEVCTL_SESSION;
musb_writew(musb->mregs, MUSB_DEVCTL, val);
val |= MUSB_DEVCTL_SESSION; val |= MUSB_DEVCTL_SESSION;
musb_writew(musb->mregs, MUSB_DEVCTL, val); 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); val = musb_readw(musb->mregs, MUSB_DEVCTL);
if (!(val & MUSB_DEVCTL_BDEVICE)) { if (!(val & MUSB_DEVCTL_BDEVICE)) {
gpio_set_value(musb->config->gpio_vrsel, 1); gpio_set_value(musb->config->gpio_vrsel, 1);
musb->xceiv->state = OTG_STATE_A_WAIT_BCON; musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
...@@ -205,12 +247,27 @@ static void musb_conn_timer_handler(unsigned long _musb) ...@@ -205,12 +247,27 @@ static void musb_conn_timer_handler(unsigned long _musb)
val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR; val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR;
musb_writeb(musb->mregs, MUSB_INTRUSB, val); musb_writeb(musb->mregs, MUSB_INTRUSB, val);
val = MUSB_POWER_HSENAB; /* 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); 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; break;
default: default:
DBG(1, "%s state not handled\n", otg_state_string(musb)); DBG(1, "%s state not handled\n", otg_state_string(musb));
break; break;
...@@ -222,7 +279,7 @@ static void musb_conn_timer_handler(unsigned long _musb) ...@@ -222,7 +279,7 @@ static void musb_conn_timer_handler(unsigned long _musb)
void musb_platform_enable(struct musb *musb) 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); mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
musb->a_wait_bcon = TIMER_DELAY; musb->a_wait_bcon = TIMER_DELAY;
} }
...@@ -256,7 +313,7 @@ static int bfin_set_power(struct otg_transceiver *x, unsigned mA) ...@@ -256,7 +313,7 @@ static int bfin_set_power(struct otg_transceiver *x, unsigned mA)
void musb_platform_try_idle(struct musb *musb, unsigned long timeout) 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); mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment