/* Caller must take musb->lock */ static int dsps_check_status(struct musb *musb, void *unused) { void __iomem *mregs = musb->mregs; struct device *dev = musb->controller; struct dsps_glue *glue = dev_get_drvdata(dev->parent); const struct dsps_musb_wrapper *wrp = glue->wrp; u8 devctl; int skip_session = 0; if (glue->vbus_irq) del_timer(&glue->timer); /* * We poll because DSPS IP's won't expose several OTG-critical * status change events (from the transceiver) otherwise. */ devctl = musb_readb(mregs, MUSB_DEVCTL); dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, usb_otg_state_string(musb->xceiv->otg->state)); switch (musb->xceiv->otg->state) { case OTG_STATE_A_WAIT_VRISE: dsps_mod_timer_optional(glue); break; case OTG_STATE_A_WAIT_BCON: musb_writeb(musb->mregs, MUSB_DEVCTL, 0); skip_session = 1; /* fall */ case OTG_STATE_A_IDLE: case OTG_STATE_B_IDLE: if (!glue->vbus_irq) { if (devctl & MUSB_DEVCTL_BDEVICE) { musb->xceiv->otg->state = OTG_STATE_B_IDLE; MUSB_DEV_MODE(musb); } else { musb->xceiv->otg->state = OTG_STATE_A_IDLE; MUSB_HST_MODE(musb); } if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session) musb_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); } dsps_mod_timer_optional(glue); break; case OTG_STATE_A_WAIT_VFALL: musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; musb_writel(musb->ctrl_base, wrp->coreintr_set, MUSB_INTR_VBUSERROR << wrp->usb_shift); break; default: break; } return 0; }
static irqreturn_t dsps_interrupt(int irq, void *hci) { struct musb *musb = hci; void __iomem *reg_base = musb->ctrl_base; struct device *dev = musb->controller; struct dsps_glue *glue = dev_get_drvdata(dev->parent); const struct dsps_musb_wrapper *wrp = glue->wrp; unsigned long flags; irqreturn_t ret = IRQ_NONE; u32 epintr, usbintr; spin_lock_irqsave(&musb->lock, flags); /* Get endpoint interrupts */ epintr = musb_readl(reg_base, wrp->epintr_status); musb->int_rx = (epintr & wrp->rxep_bitmap) >> wrp->rxep_shift; musb->int_tx = (epintr & wrp->txep_bitmap) >> wrp->txep_shift; if (epintr) musb_writel(reg_base, wrp->epintr_status, epintr); /* Get usb core interrupts */ usbintr = musb_readl(reg_base, wrp->coreintr_status); if (!usbintr && !epintr) goto out; musb->int_usb = (usbintr & wrp->usb_bitmap) >> wrp->usb_shift; if (usbintr) musb_writel(reg_base, wrp->coreintr_status, usbintr); dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n", usbintr, epintr); if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) { int drvvbus = musb_readl(reg_base, wrp->status); void __iomem *mregs = musb->mregs; u8 devctl = musb_readb(mregs, MUSB_DEVCTL); int err; err = musb->int_usb & MUSB_INTR_VBUSERROR; if (err) { /* * The Mentor core doesn't debounce VBUS as needed * to cope with device connect current spikes. This * means it's not uncommon for bus-powered devices * to get VBUS errors during enumeration. * * This is a workaround, but newer RTL from Mentor * seems to allow a better one: "re"-starting sessions * without waiting for VBUS to stop registering in * devctl. */ musb->int_usb &= ~MUSB_INTR_VBUSERROR; musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL; dsps_mod_timer_optional(glue); WARNING("VBUS error workaround (delay coming)\n"); } else if (drvvbus) { MUSB_HST_MODE(musb); musb->xceiv->otg->default_a = 1; musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; dsps_mod_timer_optional(glue); } else { musb->is_active = 0; MUSB_DEV_MODE(musb); musb->xceiv->otg->default_a = 0; musb->xceiv->otg->state = OTG_STATE_B_IDLE; } /* NOTE: this must complete power-on within 100 ms. */ dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", usb_otg_state_string(musb->xceiv->otg->state), err ? " ERROR" : "", devctl); ret = IRQ_HANDLED; } if (musb->int_tx || musb->int_rx || musb->int_usb) ret |= musb_interrupt(musb); /* Poll for ID change and connect */ switch (musb->xceiv->otg->state) { case OTG_STATE_B_IDLE: case OTG_STATE_A_WAIT_BCON: dsps_mod_timer_optional(glue); break; default: break; } out: spin_unlock_irqrestore(&musb->lock, flags); return ret; }