usb: musb: break the huge isr musb_stage0_irq() into small functions
musb_stage0_irq() is 400+ lines long. Break its interrupt events handling into each individual functions to make it easy to read. Signed-off-by: Bin Liu <b-liu@ti.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
2bc2e05f92
Коммит
bcb8fd3a2f
|
@ -523,6 +523,383 @@ void musb_hnp_stop(struct musb *musb)
|
|||
|
||||
static void musb_recover_from_babble(struct musb *musb);
|
||||
|
||||
static void musb_handle_intr_resume(struct musb *musb, u8 devctl)
|
||||
{
|
||||
musb_dbg(musb, "RESUME (%s)",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
|
||||
if (devctl & MUSB_DEVCTL_HM) {
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
/* remote wakeup? */
|
||||
musb->port1_status |=
|
||||
(USB_PORT_STAT_C_SUSPEND << 16)
|
||||
| MUSB_PORT_STAT_RESUME;
|
||||
musb->rh_timer = jiffies
|
||||
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
|
||||
musb->xceiv->otg->state = OTG_STATE_A_HOST;
|
||||
musb->is_active = 1;
|
||||
musb_host_resume_root_hub(musb);
|
||||
schedule_delayed_work(&musb->finish_resume_work,
|
||||
msecs_to_jiffies(USB_RESUME_TIMEOUT));
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
|
||||
musb->is_active = 1;
|
||||
MUSB_DEV_MODE(musb);
|
||||
break;
|
||||
default:
|
||||
WARNING("bogus %s RESUME (%s)\n",
|
||||
"host",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
}
|
||||
} else {
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
/* possibly DISCONNECT is upcoming */
|
||||
musb->xceiv->otg->state = OTG_STATE_A_HOST;
|
||||
musb_host_resume_root_hub(musb);
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
/* disconnect while suspended? we may
|
||||
* not get a disconnect irq...
|
||||
*/
|
||||
if ((devctl & MUSB_DEVCTL_VBUS)
|
||||
!= (3 << MUSB_DEVCTL_VBUS_SHIFT)
|
||||
) {
|
||||
musb->int_usb |= MUSB_INTR_DISCONNECT;
|
||||
musb->int_usb &= ~MUSB_INTR_SUSPEND;
|
||||
break;
|
||||
}
|
||||
musb_g_resume(musb);
|
||||
break;
|
||||
case OTG_STATE_B_IDLE:
|
||||
musb->int_usb &= ~MUSB_INTR_SUSPEND;
|
||||
break;
|
||||
default:
|
||||
WARNING("bogus %s RESUME (%s)\n",
|
||||
"peripheral",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return IRQ_HANDLED to tell the caller to return immediately */
|
||||
static irqreturn_t musb_handle_intr_sessreq(struct musb *musb, u8 devctl)
|
||||
{
|
||||
void __iomem *mbase = musb->mregs;
|
||||
|
||||
if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS
|
||||
&& (devctl & MUSB_DEVCTL_BDEVICE)) {
|
||||
musb_dbg(musb, "SessReq while on B state");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
musb_dbg(musb, "SESSION_REQUEST (%s)",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
|
||||
/* IRQ arrives from ID pin sense or (later, if VBUS power
|
||||
* is removed) SRP. responses are time critical:
|
||||
* - turn on VBUS (with silicon-specific mechanism)
|
||||
* - go through A_WAIT_VRISE
|
||||
* - ... to A_WAIT_BCON.
|
||||
* a_wait_vrise_tmout triggers VBUS_ERROR transitions
|
||||
*/
|
||||
musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
|
||||
musb->ep0_stage = MUSB_EP0_START;
|
||||
musb->xceiv->otg->state = OTG_STATE_A_IDLE;
|
||||
MUSB_HST_MODE(musb);
|
||||
musb_platform_set_vbus(musb, 1);
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static void musb_handle_intr_vbuserr(struct musb *musb, u8 devctl)
|
||||
{
|
||||
int ignore = 0;
|
||||
|
||||
/* During connection as an A-Device, we may see a short
|
||||
* current spikes causing voltage drop, because of cable
|
||||
* and peripheral capacitance combined with vbus draw.
|
||||
* (So: less common with truly self-powered devices, where
|
||||
* vbus doesn't act like a power supply.)
|
||||
*
|
||||
* Such spikes are short; usually less than ~500 usec, max
|
||||
* of ~2 msec. That is, they're not sustained overcurrent
|
||||
* errors, though they're reported using VBUSERROR irqs.
|
||||
*
|
||||
* Workarounds: (a) hardware: use self powered devices.
|
||||
* (b) software: ignore non-repeated VBUS errors.
|
||||
*
|
||||
* REVISIT: do delays from lots of DEBUG_KERNEL checks
|
||||
* make trouble here, keeping VBUS < 4.4V ?
|
||||
*/
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_HOST:
|
||||
/* recovery is dicey once we've gotten past the
|
||||
* initial stages of enumeration, but if VBUS
|
||||
* stayed ok at the other end of the link, and
|
||||
* another reset is due (at least for high speed,
|
||||
* to redo the chirp etc), it might work OK...
|
||||
*/
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
case OTG_STATE_A_WAIT_VRISE:
|
||||
if (musb->vbuserr_retry) {
|
||||
void __iomem *mbase = musb->mregs;
|
||||
|
||||
musb->vbuserr_retry--;
|
||||
ignore = 1;
|
||||
devctl |= MUSB_DEVCTL_SESSION;
|
||||
musb_writeb(mbase, MUSB_DEVCTL, devctl);
|
||||
} else {
|
||||
musb->port1_status |=
|
||||
USB_PORT_STAT_OVERCURRENT
|
||||
| (USB_PORT_STAT_C_OVERCURRENT << 16);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
dev_printk(ignore ? KERN_DEBUG : KERN_ERR, musb->controller,
|
||||
"VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n",
|
||||
usb_otg_state_string(musb->xceiv->otg->state),
|
||||
devctl,
|
||||
({ char *s;
|
||||
switch (devctl & MUSB_DEVCTL_VBUS) {
|
||||
case 0 << MUSB_DEVCTL_VBUS_SHIFT:
|
||||
s = "<SessEnd"; break;
|
||||
case 1 << MUSB_DEVCTL_VBUS_SHIFT:
|
||||
s = "<AValid"; break;
|
||||
case 2 << MUSB_DEVCTL_VBUS_SHIFT:
|
||||
s = "<VBusValid"; break;
|
||||
/* case 3 << MUSB_DEVCTL_VBUS_SHIFT: */
|
||||
default:
|
||||
s = "VALID"; break;
|
||||
} s; }),
|
||||
VBUSERR_RETRY_COUNT - musb->vbuserr_retry,
|
||||
musb->port1_status);
|
||||
|
||||
/* go through A_WAIT_VFALL then start a new session */
|
||||
if (!ignore)
|
||||
musb_platform_set_vbus(musb, 0);
|
||||
}
|
||||
|
||||
static void musb_handle_intr_suspend(struct musb *musb, u8 devctl)
|
||||
{
|
||||
musb_dbg(musb, "SUSPEND (%s) devctl %02x",
|
||||
usb_otg_state_string(musb->xceiv->otg->state), devctl);
|
||||
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
/* We also come here if the cable is removed, since
|
||||
* this silicon doesn't report ID-no-longer-grounded.
|
||||
*
|
||||
* We depend on T(a_wait_bcon) to shut us down, and
|
||||
* hope users don't do anything dicey during this
|
||||
* undesired detour through A_WAIT_BCON.
|
||||
*/
|
||||
musb_hnp_stop(musb);
|
||||
musb_host_resume_root_hub(musb);
|
||||
musb_root_disconnect(musb);
|
||||
musb_platform_try_idle(musb, jiffies
|
||||
+ msecs_to_jiffies(musb->a_wait_bcon
|
||||
? : OTG_TIME_A_WAIT_BCON));
|
||||
|
||||
break;
|
||||
case OTG_STATE_B_IDLE:
|
||||
if (!musb->is_active)
|
||||
break;
|
||||
/* fall through */
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
musb_g_suspend(musb);
|
||||
musb->is_active = musb->g.b_hnp_enable;
|
||||
if (musb->is_active) {
|
||||
musb->xceiv->otg->state = OTG_STATE_B_WAIT_ACON;
|
||||
musb_dbg(musb, "HNP: Setting timer for b_ase0_brst");
|
||||
mod_timer(&musb->otg_timer, jiffies
|
||||
+ msecs_to_jiffies(
|
||||
OTG_TIME_B_ASE0_BRST));
|
||||
}
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
if (musb->a_wait_bcon != 0)
|
||||
musb_platform_try_idle(musb, jiffies
|
||||
+ msecs_to_jiffies(musb->a_wait_bcon));
|
||||
break;
|
||||
case OTG_STATE_A_HOST:
|
||||
musb->xceiv->otg->state = OTG_STATE_A_SUSPEND;
|
||||
musb->is_active = musb->hcd->self.b_hnp_enable;
|
||||
break;
|
||||
case OTG_STATE_B_HOST:
|
||||
/* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
|
||||
musb_dbg(musb, "REVISIT: SUSPEND as B_HOST");
|
||||
break;
|
||||
default:
|
||||
/* "should not happen" */
|
||||
musb->is_active = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void musb_handle_intr_connect(struct musb *musb, u8 devctl, u8 int_usb)
|
||||
{
|
||||
struct usb_hcd *hcd = musb->hcd;
|
||||
|
||||
musb->is_active = 1;
|
||||
musb->ep0_stage = MUSB_EP0_START;
|
||||
|
||||
musb->intrtxe = musb->epmask;
|
||||
musb_writew(musb->mregs, MUSB_INTRTXE, musb->intrtxe);
|
||||
musb->intrrxe = musb->epmask & 0xfffe;
|
||||
musb_writew(musb->mregs, MUSB_INTRRXE, musb->intrrxe);
|
||||
musb_writeb(musb->mregs, MUSB_INTRUSBE, 0xf7);
|
||||
musb->port1_status &= ~(USB_PORT_STAT_LOW_SPEED
|
||||
|USB_PORT_STAT_HIGH_SPEED
|
||||
|USB_PORT_STAT_ENABLE
|
||||
);
|
||||
musb->port1_status |= USB_PORT_STAT_CONNECTION
|
||||
|(USB_PORT_STAT_C_CONNECTION << 16);
|
||||
|
||||
/* high vs full speed is just a guess until after reset */
|
||||
if (devctl & MUSB_DEVCTL_LSDEV)
|
||||
musb->port1_status |= USB_PORT_STAT_LOW_SPEED;
|
||||
|
||||
/* indicate new connection to OTG machine */
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
if (int_usb & MUSB_INTR_SUSPEND) {
|
||||
musb_dbg(musb, "HNP: SUSPEND+CONNECT, now b_host");
|
||||
int_usb &= ~MUSB_INTR_SUSPEND;
|
||||
goto b_host;
|
||||
} else
|
||||
musb_dbg(musb, "CONNECT as b_peripheral???");
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
musb_dbg(musb, "HNP: CONNECT, now b_host");
|
||||
b_host:
|
||||
musb->xceiv->otg->state = OTG_STATE_B_HOST;
|
||||
if (musb->hcd)
|
||||
musb->hcd->self.is_b_host = 1;
|
||||
del_timer(&musb->otg_timer);
|
||||
break;
|
||||
default:
|
||||
if ((devctl & MUSB_DEVCTL_VBUS)
|
||||
== (3 << MUSB_DEVCTL_VBUS_SHIFT)) {
|
||||
musb->xceiv->otg->state = OTG_STATE_A_HOST;
|
||||
if (hcd)
|
||||
hcd->self.is_b_host = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
musb_host_poke_root_hub(musb);
|
||||
|
||||
musb_dbg(musb, "CONNECT (%s) devctl %02x",
|
||||
usb_otg_state_string(musb->xceiv->otg->state), devctl);
|
||||
}
|
||||
|
||||
static void musb_handle_intr_disconnect(struct musb *musb, u8 devctl)
|
||||
{
|
||||
musb_dbg(musb, "DISCONNECT (%s) as %s, devctl %02x",
|
||||
usb_otg_state_string(musb->xceiv->otg->state),
|
||||
MUSB_MODE(musb), devctl);
|
||||
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_HOST:
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
musb_host_resume_root_hub(musb);
|
||||
musb_root_disconnect(musb);
|
||||
if (musb->a_wait_bcon != 0)
|
||||
musb_platform_try_idle(musb, jiffies
|
||||
+ msecs_to_jiffies(musb->a_wait_bcon));
|
||||
break;
|
||||
case OTG_STATE_B_HOST:
|
||||
/* REVISIT this behaves for "real disconnect"
|
||||
* cases; make sure the other transitions from
|
||||
* from B_HOST act right too. The B_HOST code
|
||||
* in hnp_stop() is currently not used...
|
||||
*/
|
||||
musb_root_disconnect(musb);
|
||||
if (musb->hcd)
|
||||
musb->hcd->self.is_b_host = 0;
|
||||
musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
|
||||
MUSB_DEV_MODE(musb);
|
||||
musb_g_disconnect(musb);
|
||||
break;
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
musb_hnp_stop(musb);
|
||||
musb_root_disconnect(musb);
|
||||
/* FALLTHROUGH */
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
/* FALLTHROUGH */
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
case OTG_STATE_B_IDLE:
|
||||
musb_g_disconnect(musb);
|
||||
break;
|
||||
default:
|
||||
WARNING("unhandled DISCONNECT transition (%s)\n",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* mentor saves a bit: bus reset and babble share the same irq.
|
||||
* only host sees babble; only peripheral sees bus reset.
|
||||
*/
|
||||
static void musb_handle_intr_reset(struct musb *musb)
|
||||
{
|
||||
if (is_host_active(musb)) {
|
||||
/*
|
||||
* When BABBLE happens what we can depends on which
|
||||
* platform MUSB is running, because some platforms
|
||||
* implemented proprietary means for 'recovering' from
|
||||
* Babble conditions. One such platform is AM335x. In
|
||||
* most cases, however, the only thing we can do is
|
||||
* drop the session.
|
||||
*/
|
||||
dev_err(musb->controller, "Babble\n");
|
||||
musb_recover_from_babble(musb);
|
||||
} else {
|
||||
musb_dbg(musb, "BUS RESET as %s",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
musb_g_reset(musb);
|
||||
/* FALLTHROUGH */
|
||||
case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */
|
||||
/* never use invalid T(a_wait_bcon) */
|
||||
musb_dbg(musb, "HNP: in %s, %d msec timeout",
|
||||
usb_otg_state_string(musb->xceiv->otg->state),
|
||||
TA_WAIT_BCON(musb));
|
||||
mod_timer(&musb->otg_timer, jiffies
|
||||
+ msecs_to_jiffies(TA_WAIT_BCON(musb)));
|
||||
break;
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
del_timer(&musb->otg_timer);
|
||||
musb_g_reset(musb);
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
musb_dbg(musb, "HNP: RESET (%s), to b_peripheral",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
|
||||
musb_g_reset(musb);
|
||||
break;
|
||||
case OTG_STATE_B_IDLE:
|
||||
musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
|
||||
/* FALLTHROUGH */
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
musb_g_reset(musb);
|
||||
break;
|
||||
default:
|
||||
musb_dbg(musb, "Unhandled BUS RESET as %s",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt Service Routine to record USB "global" interrupts.
|
||||
* Since these do not happen often and signify things of
|
||||
|
@ -547,379 +924,40 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
|||
* spurious RESUME irqs happen too, paired with SUSPEND.
|
||||
*/
|
||||
if (int_usb & MUSB_INTR_RESUME) {
|
||||
musb_handle_intr_resume(musb, devctl);
|
||||
handled = IRQ_HANDLED;
|
||||
musb_dbg(musb, "RESUME (%s)",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
|
||||
if (devctl & MUSB_DEVCTL_HM) {
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
/* remote wakeup? */
|
||||
musb->port1_status |=
|
||||
(USB_PORT_STAT_C_SUSPEND << 16)
|
||||
| MUSB_PORT_STAT_RESUME;
|
||||
musb->rh_timer = jiffies
|
||||
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
|
||||
musb->xceiv->otg->state = OTG_STATE_A_HOST;
|
||||
musb->is_active = 1;
|
||||
musb_host_resume_root_hub(musb);
|
||||
schedule_delayed_work(&musb->finish_resume_work,
|
||||
msecs_to_jiffies(USB_RESUME_TIMEOUT));
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
|
||||
musb->is_active = 1;
|
||||
MUSB_DEV_MODE(musb);
|
||||
break;
|
||||
default:
|
||||
WARNING("bogus %s RESUME (%s)\n",
|
||||
"host",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
}
|
||||
} else {
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
/* possibly DISCONNECT is upcoming */
|
||||
musb->xceiv->otg->state = OTG_STATE_A_HOST;
|
||||
musb_host_resume_root_hub(musb);
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
/* disconnect while suspended? we may
|
||||
* not get a disconnect irq...
|
||||
*/
|
||||
if ((devctl & MUSB_DEVCTL_VBUS)
|
||||
!= (3 << MUSB_DEVCTL_VBUS_SHIFT)
|
||||
) {
|
||||
musb->int_usb |= MUSB_INTR_DISCONNECT;
|
||||
musb->int_usb &= ~MUSB_INTR_SUSPEND;
|
||||
break;
|
||||
}
|
||||
musb_g_resume(musb);
|
||||
break;
|
||||
case OTG_STATE_B_IDLE:
|
||||
musb->int_usb &= ~MUSB_INTR_SUSPEND;
|
||||
break;
|
||||
default:
|
||||
WARNING("bogus %s RESUME (%s)\n",
|
||||
"peripheral",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* see manual for the order of the tests */
|
||||
if (int_usb & MUSB_INTR_SESSREQ) {
|
||||
void __iomem *mbase = musb->mregs;
|
||||
|
||||
if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS
|
||||
&& (devctl & MUSB_DEVCTL_BDEVICE)) {
|
||||
musb_dbg(musb, "SessReq while on B state");
|
||||
if (musb_handle_intr_sessreq(musb, devctl))
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
musb_dbg(musb, "SESSION_REQUEST (%s)",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
|
||||
/* IRQ arrives from ID pin sense or (later, if VBUS power
|
||||
* is removed) SRP. responses are time critical:
|
||||
* - turn on VBUS (with silicon-specific mechanism)
|
||||
* - go through A_WAIT_VRISE
|
||||
* - ... to A_WAIT_BCON.
|
||||
* a_wait_vrise_tmout triggers VBUS_ERROR transitions
|
||||
*/
|
||||
musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
|
||||
musb->ep0_stage = MUSB_EP0_START;
|
||||
musb->xceiv->otg->state = OTG_STATE_A_IDLE;
|
||||
MUSB_HST_MODE(musb);
|
||||
musb_platform_set_vbus(musb, 1);
|
||||
|
||||
handled = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (int_usb & MUSB_INTR_VBUSERROR) {
|
||||
int ignore = 0;
|
||||
|
||||
/* During connection as an A-Device, we may see a short
|
||||
* current spikes causing voltage drop, because of cable
|
||||
* and peripheral capacitance combined with vbus draw.
|
||||
* (So: less common with truly self-powered devices, where
|
||||
* vbus doesn't act like a power supply.)
|
||||
*
|
||||
* Such spikes are short; usually less than ~500 usec, max
|
||||
* of ~2 msec. That is, they're not sustained overcurrent
|
||||
* errors, though they're reported using VBUSERROR irqs.
|
||||
*
|
||||
* Workarounds: (a) hardware: use self powered devices.
|
||||
* (b) software: ignore non-repeated VBUS errors.
|
||||
*
|
||||
* REVISIT: do delays from lots of DEBUG_KERNEL checks
|
||||
* make trouble here, keeping VBUS < 4.4V ?
|
||||
*/
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_HOST:
|
||||
/* recovery is dicey once we've gotten past the
|
||||
* initial stages of enumeration, but if VBUS
|
||||
* stayed ok at the other end of the link, and
|
||||
* another reset is due (at least for high speed,
|
||||
* to redo the chirp etc), it might work OK...
|
||||
*/
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
case OTG_STATE_A_WAIT_VRISE:
|
||||
if (musb->vbuserr_retry) {
|
||||
void __iomem *mbase = musb->mregs;
|
||||
|
||||
musb->vbuserr_retry--;
|
||||
ignore = 1;
|
||||
devctl |= MUSB_DEVCTL_SESSION;
|
||||
musb_writeb(mbase, MUSB_DEVCTL, devctl);
|
||||
} else {
|
||||
musb->port1_status |=
|
||||
USB_PORT_STAT_OVERCURRENT
|
||||
| (USB_PORT_STAT_C_OVERCURRENT << 16);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
dev_printk(ignore ? KERN_DEBUG : KERN_ERR, musb->controller,
|
||||
"VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n",
|
||||
usb_otg_state_string(musb->xceiv->otg->state),
|
||||
devctl,
|
||||
({ char *s;
|
||||
switch (devctl & MUSB_DEVCTL_VBUS) {
|
||||
case 0 << MUSB_DEVCTL_VBUS_SHIFT:
|
||||
s = "<SessEnd"; break;
|
||||
case 1 << MUSB_DEVCTL_VBUS_SHIFT:
|
||||
s = "<AValid"; break;
|
||||
case 2 << MUSB_DEVCTL_VBUS_SHIFT:
|
||||
s = "<VBusValid"; break;
|
||||
/* case 3 << MUSB_DEVCTL_VBUS_SHIFT: */
|
||||
default:
|
||||
s = "VALID"; break;
|
||||
} s; }),
|
||||
VBUSERR_RETRY_COUNT - musb->vbuserr_retry,
|
||||
musb->port1_status);
|
||||
|
||||
/* go through A_WAIT_VFALL then start a new session */
|
||||
if (!ignore)
|
||||
musb_platform_set_vbus(musb, 0);
|
||||
musb_handle_intr_vbuserr(musb, devctl);
|
||||
handled = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (int_usb & MUSB_INTR_SUSPEND) {
|
||||
musb_dbg(musb, "SUSPEND (%s) devctl %02x",
|
||||
usb_otg_state_string(musb->xceiv->otg->state), devctl);
|
||||
musb_handle_intr_suspend(musb, devctl);
|
||||
handled = IRQ_HANDLED;
|
||||
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
/* We also come here if the cable is removed, since
|
||||
* this silicon doesn't report ID-no-longer-grounded.
|
||||
*
|
||||
* We depend on T(a_wait_bcon) to shut us down, and
|
||||
* hope users don't do anything dicey during this
|
||||
* undesired detour through A_WAIT_BCON.
|
||||
*/
|
||||
musb_hnp_stop(musb);
|
||||
musb_host_resume_root_hub(musb);
|
||||
musb_root_disconnect(musb);
|
||||
musb_platform_try_idle(musb, jiffies
|
||||
+ msecs_to_jiffies(musb->a_wait_bcon
|
||||
? : OTG_TIME_A_WAIT_BCON));
|
||||
|
||||
break;
|
||||
case OTG_STATE_B_IDLE:
|
||||
if (!musb->is_active)
|
||||
break;
|
||||
/* fall through */
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
musb_g_suspend(musb);
|
||||
musb->is_active = musb->g.b_hnp_enable;
|
||||
if (musb->is_active) {
|
||||
musb->xceiv->otg->state = OTG_STATE_B_WAIT_ACON;
|
||||
musb_dbg(musb, "HNP: Setting timer for b_ase0_brst");
|
||||
mod_timer(&musb->otg_timer, jiffies
|
||||
+ msecs_to_jiffies(
|
||||
OTG_TIME_B_ASE0_BRST));
|
||||
}
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
if (musb->a_wait_bcon != 0)
|
||||
musb_platform_try_idle(musb, jiffies
|
||||
+ msecs_to_jiffies(musb->a_wait_bcon));
|
||||
break;
|
||||
case OTG_STATE_A_HOST:
|
||||
musb->xceiv->otg->state = OTG_STATE_A_SUSPEND;
|
||||
musb->is_active = musb->hcd->self.b_hnp_enable;
|
||||
break;
|
||||
case OTG_STATE_B_HOST:
|
||||
/* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
|
||||
musb_dbg(musb, "REVISIT: SUSPEND as B_HOST");
|
||||
break;
|
||||
default:
|
||||
/* "should not happen" */
|
||||
musb->is_active = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (int_usb & MUSB_INTR_CONNECT) {
|
||||
struct usb_hcd *hcd = musb->hcd;
|
||||
|
||||
musb_handle_intr_connect(musb, devctl, int_usb);
|
||||
handled = IRQ_HANDLED;
|
||||
musb->is_active = 1;
|
||||
|
||||
musb->ep0_stage = MUSB_EP0_START;
|
||||
|
||||
musb->intrtxe = musb->epmask;
|
||||
musb_writew(musb->mregs, MUSB_INTRTXE, musb->intrtxe);
|
||||
musb->intrrxe = musb->epmask & 0xfffe;
|
||||
musb_writew(musb->mregs, MUSB_INTRRXE, musb->intrrxe);
|
||||
musb_writeb(musb->mregs, MUSB_INTRUSBE, 0xf7);
|
||||
musb->port1_status &= ~(USB_PORT_STAT_LOW_SPEED
|
||||
|USB_PORT_STAT_HIGH_SPEED
|
||||
|USB_PORT_STAT_ENABLE
|
||||
);
|
||||
musb->port1_status |= USB_PORT_STAT_CONNECTION
|
||||
|(USB_PORT_STAT_C_CONNECTION << 16);
|
||||
|
||||
/* high vs full speed is just a guess until after reset */
|
||||
if (devctl & MUSB_DEVCTL_LSDEV)
|
||||
musb->port1_status |= USB_PORT_STAT_LOW_SPEED;
|
||||
|
||||
/* indicate new connection to OTG machine */
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
if (int_usb & MUSB_INTR_SUSPEND) {
|
||||
musb_dbg(musb, "HNP: SUSPEND+CONNECT, now b_host");
|
||||
int_usb &= ~MUSB_INTR_SUSPEND;
|
||||
goto b_host;
|
||||
} else
|
||||
musb_dbg(musb, "CONNECT as b_peripheral???");
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
musb_dbg(musb, "HNP: CONNECT, now b_host");
|
||||
b_host:
|
||||
musb->xceiv->otg->state = OTG_STATE_B_HOST;
|
||||
if (musb->hcd)
|
||||
musb->hcd->self.is_b_host = 1;
|
||||
del_timer(&musb->otg_timer);
|
||||
break;
|
||||
default:
|
||||
if ((devctl & MUSB_DEVCTL_VBUS)
|
||||
== (3 << MUSB_DEVCTL_VBUS_SHIFT)) {
|
||||
musb->xceiv->otg->state = OTG_STATE_A_HOST;
|
||||
if (hcd)
|
||||
hcd->self.is_b_host = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
musb_host_poke_root_hub(musb);
|
||||
|
||||
musb_dbg(musb, "CONNECT (%s) devctl %02x",
|
||||
usb_otg_state_string(musb->xceiv->otg->state), devctl);
|
||||
}
|
||||
|
||||
if (int_usb & MUSB_INTR_DISCONNECT) {
|
||||
musb_dbg(musb, "DISCONNECT (%s) as %s, devctl %02x",
|
||||
usb_otg_state_string(musb->xceiv->otg->state),
|
||||
MUSB_MODE(musb), devctl);
|
||||
musb_handle_intr_disconnect(musb, devctl);
|
||||
handled = IRQ_HANDLED;
|
||||
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_HOST:
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
musb_host_resume_root_hub(musb);
|
||||
musb_root_disconnect(musb);
|
||||
if (musb->a_wait_bcon != 0)
|
||||
musb_platform_try_idle(musb, jiffies
|
||||
+ msecs_to_jiffies(musb->a_wait_bcon));
|
||||
break;
|
||||
case OTG_STATE_B_HOST:
|
||||
/* REVISIT this behaves for "real disconnect"
|
||||
* cases; make sure the other transitions from
|
||||
* from B_HOST act right too. The B_HOST code
|
||||
* in hnp_stop() is currently not used...
|
||||
*/
|
||||
musb_root_disconnect(musb);
|
||||
if (musb->hcd)
|
||||
musb->hcd->self.is_b_host = 0;
|
||||
musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
|
||||
MUSB_DEV_MODE(musb);
|
||||
musb_g_disconnect(musb);
|
||||
break;
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
musb_hnp_stop(musb);
|
||||
musb_root_disconnect(musb);
|
||||
/* FALLTHROUGH */
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
/* FALLTHROUGH */
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
case OTG_STATE_B_IDLE:
|
||||
musb_g_disconnect(musb);
|
||||
break;
|
||||
default:
|
||||
WARNING("unhandled DISCONNECT transition (%s)\n",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* mentor saves a bit: bus reset and babble share the same irq.
|
||||
* only host sees babble; only peripheral sees bus reset.
|
||||
*/
|
||||
if (int_usb & MUSB_INTR_RESET) {
|
||||
musb_handle_intr_reset(musb);
|
||||
handled = IRQ_HANDLED;
|
||||
if (is_host_active(musb)) {
|
||||
/*
|
||||
* When BABBLE happens what we can depends on which
|
||||
* platform MUSB is running, because some platforms
|
||||
* implemented proprietary means for 'recovering' from
|
||||
* Babble conditions. One such platform is AM335x. In
|
||||
* most cases, however, the only thing we can do is
|
||||
* drop the session.
|
||||
*/
|
||||
dev_err(musb->controller, "Babble\n");
|
||||
musb_recover_from_babble(musb);
|
||||
} else {
|
||||
musb_dbg(musb, "BUS RESET as %s",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
musb_g_reset(musb);
|
||||
/* FALLTHROUGH */
|
||||
case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */
|
||||
/* never use invalid T(a_wait_bcon) */
|
||||
musb_dbg(musb, "HNP: in %s, %d msec timeout",
|
||||
usb_otg_state_string(musb->xceiv->otg->state),
|
||||
TA_WAIT_BCON(musb));
|
||||
mod_timer(&musb->otg_timer, jiffies
|
||||
+ msecs_to_jiffies(TA_WAIT_BCON(musb)));
|
||||
break;
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
del_timer(&musb->otg_timer);
|
||||
musb_g_reset(musb);
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
musb_dbg(musb, "HNP: RESET (%s), to b_peripheral",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
|
||||
musb_g_reset(musb);
|
||||
break;
|
||||
case OTG_STATE_B_IDLE:
|
||||
musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
|
||||
/* FALLTHROUGH */
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
musb_g_reset(musb);
|
||||
break;
|
||||
default:
|
||||
musb_dbg(musb, "Unhandled BUS RESET as %s",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
|
Загрузка…
Ссылка в новой задаче