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@xxxxxx> --- drivers/usb/musb/musb_core.c | 730 +++++++++++++++++++++++-------------------- 1 file changed, 384 insertions(+), 346 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index ce54f48314e1..a3a716197dc1 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -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 -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html