Skip to content

Commit

Permalink
usb: musb: handle irqs in the order dictated by programming guide
Browse files Browse the repository at this point in the history
MUSB's programming guide dictates how we should handle its
irqs and in which order. Follow that.

Signed-off-by: Arnaud Mandy <ext-arnaud.2.mandy@nokia.com>
Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Arnaud Mandy authored and Greg Kroah-Hartman committed Mar 2, 2010
1 parent 1ca9e9c commit 1c25fda
Showing 1 changed file with 116 additions and 139 deletions.
255 changes: 116 additions & 139 deletions drivers/usb/musb/musb_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,69 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
handled = IRQ_HANDLED;
}


if (int_usb & MUSB_INTR_SUSPEND) {
DBG(1, "SUSPEND (%s) devctl %02x power %02x\n",
otg_state_string(musb), devctl, power);
handled = IRQ_HANDLED;

switch (musb->xceiv->state) {
#ifdef CONFIG_USB_MUSB_OTG
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);
usb_hcd_resume_root_hub(musb_to_hcd(musb));
musb_root_disconnect(musb);
musb_platform_try_idle(musb, jiffies
+ msecs_to_jiffies(musb->a_wait_bcon
? : OTG_TIME_A_WAIT_BCON));

break;
#endif
case OTG_STATE_B_IDLE:
if (!musb->is_active)
break;
case OTG_STATE_B_PERIPHERAL:
musb_g_suspend(musb);
musb->is_active = is_otg_enabled(musb)
&& musb->xceiv->gadget->b_hnp_enable;
if (musb->is_active) {
#ifdef CONFIG_USB_MUSB_OTG
musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
DBG(1, "HNP: Setting timer for b_ase0_brst\n");
mod_timer(&musb->otg_timer, jiffies
+ msecs_to_jiffies(
OTG_TIME_B_ASE0_BRST));
#endif
}
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->state = OTG_STATE_A_SUSPEND;
musb->is_active = is_otg_enabled(musb)
&& musb->xceiv->host->b_hnp_enable;
break;
case OTG_STATE_B_HOST:
/* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
DBG(1, "REVISIT: SUSPEND as B_HOST\n");
break;
default:
/* "should not happen" */
musb->is_active = 0;
break;
}
}

if (int_usb & MUSB_INTR_CONNECT) {
struct usb_hcd *hcd = musb_to_hcd(musb);

Expand Down Expand Up @@ -625,10 +688,61 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
}
#endif /* CONFIG_USB_MUSB_HDRC_HCD */

if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) {
DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n",
otg_state_string(musb),
MUSB_MODE(musb), devctl);
handled = IRQ_HANDLED;

switch (musb->xceiv->state) {
#ifdef CONFIG_USB_MUSB_HDRC_HCD
case OTG_STATE_A_HOST:
case OTG_STATE_A_SUSPEND:
usb_hcd_resume_root_hub(musb_to_hcd(musb));
musb_root_disconnect(musb);
if (musb->a_wait_bcon != 0 && is_otg_enabled(musb))
musb_platform_try_idle(musb, jiffies
+ msecs_to_jiffies(musb->a_wait_bcon));
break;
#endif /* HOST */
#ifdef CONFIG_USB_MUSB_OTG
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);
musb_to_hcd(musb)->self.is_b_host = 0;
musb->xceiv->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 */
#endif /* OTG */
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
case OTG_STATE_B_PERIPHERAL:
case OTG_STATE_B_IDLE:
musb_g_disconnect(musb);
break;
#endif /* GADGET */
default:
WARNING("unhandled DISCONNECT transition (%s)\n",
otg_state_string(musb));
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) {
handled = IRQ_HANDLED;
if (is_host_capable() && (devctl & MUSB_DEVCTL_HM) != 0) {
/*
* Looks like non-HS BABBLE can be ignored, but
Expand All @@ -641,7 +755,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
DBG(1, "BABBLE devctl: %02x\n", devctl);
else {
ERR("Stopping host session -- babble\n");
musb_writeb(mbase, MUSB_DEVCTL, 0);
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
}
} else if (is_peripheral_capable()) {
DBG(1, "BUS RESET as %s\n", otg_state_string(musb));
Expand Down Expand Up @@ -686,29 +800,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
otg_state_string(musb));
}
}

handled = IRQ_HANDLED;
}
schedule_work(&musb->irq_work);

return handled;
}

/*
* Interrupt Service Routine to record USB "global" interrupts.
* Since these do not happen often and signify things of
* paramount importance, it seems OK to check them individually;
* the order of the tests is specified in the manual
*
* @param musb instance pointer
* @param int_usb register contents
* @param devctl
* @param power
*/
static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
u8 devctl, u8 power)
{
irqreturn_t handled = IRQ_NONE;

#if 0
/* REVISIT ... this would be for multiplexing periodic endpoints, or
Expand Down Expand Up @@ -755,117 +847,7 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
}
#endif

if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) {
DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n",
otg_state_string(musb),
MUSB_MODE(musb), devctl);
handled = IRQ_HANDLED;

switch (musb->xceiv->state) {
#ifdef CONFIG_USB_MUSB_HDRC_HCD
case OTG_STATE_A_HOST:
case OTG_STATE_A_SUSPEND:
usb_hcd_resume_root_hub(musb_to_hcd(musb));
musb_root_disconnect(musb);
if (musb->a_wait_bcon != 0 && is_otg_enabled(musb))
musb_platform_try_idle(musb, jiffies
+ msecs_to_jiffies(musb->a_wait_bcon));
break;
#endif /* HOST */
#ifdef CONFIG_USB_MUSB_OTG
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);
musb_to_hcd(musb)->self.is_b_host = 0;
musb->xceiv->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 */
#endif /* OTG */
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
case OTG_STATE_B_PERIPHERAL:
case OTG_STATE_B_IDLE:
musb_g_disconnect(musb);
break;
#endif /* GADGET */
default:
WARNING("unhandled DISCONNECT transition (%s)\n",
otg_state_string(musb));
break;
}

schedule_work(&musb->irq_work);
}

if (int_usb & MUSB_INTR_SUSPEND) {
DBG(1, "SUSPEND (%s) devctl %02x power %02x\n",
otg_state_string(musb), devctl, power);
handled = IRQ_HANDLED;

switch (musb->xceiv->state) {
#ifdef CONFIG_USB_MUSB_OTG
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);
usb_hcd_resume_root_hub(musb_to_hcd(musb));
musb_root_disconnect(musb);
musb_platform_try_idle(musb, jiffies
+ msecs_to_jiffies(musb->a_wait_bcon
? : OTG_TIME_A_WAIT_BCON));
break;
#endif
case OTG_STATE_B_PERIPHERAL:
musb_g_suspend(musb);
musb->is_active = is_otg_enabled(musb)
&& musb->xceiv->gadget->b_hnp_enable;
if (musb->is_active) {
#ifdef CONFIG_USB_MUSB_OTG
musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
DBG(1, "HNP: Setting timer for b_ase0_brst\n");
mod_timer(&musb->otg_timer, jiffies
+ msecs_to_jiffies(
OTG_TIME_B_ASE0_BRST));
#endif
}
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->state = OTG_STATE_A_SUSPEND;
musb->is_active = is_otg_enabled(musb)
&& musb->xceiv->host->b_hnp_enable;
break;
case OTG_STATE_B_HOST:
/* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
DBG(1, "REVISIT: SUSPEND as B_HOST\n");
break;
default:
/* "should not happen" */
musb->is_active = 0;
break;
}
schedule_work(&musb->irq_work);
}

schedule_work(&musb->irq_work);

return handled;
}
Expand Down Expand Up @@ -1597,11 +1579,6 @@ irqreturn_t musb_interrupt(struct musb *musb)
ep_num++;
}

/* finish handling "global" interrupts after handling fifos */
if (musb->int_usb)
retval |= musb_stage2_irq(musb,
musb->int_usb, devctl, power);

return retval;
}

Expand Down

0 comments on commit 1c25fda

Please sign in to comment.