diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 9542d20b2785..92cf68f8f2c1 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -118,6 +118,7 @@ struct dsps_glue { struct device *dev; struct platform_device *musb; /* child musb pdev */ const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */ + int vbus_irq; /* optional vbus irq */ struct timer_list timer; /* otg_workaround timer */ unsigned long last_timer; /* last timer data for each instance */ bool sw_babble_enabled; @@ -145,6 +146,29 @@ static const struct debugfs_reg32 dsps_musb_regs[] = { { "mode", 0xe8 }, }; +static void dsps_mod_timer(struct dsps_glue *glue, int wait_ms) +{ + int wait; + + if (wait_ms < 0) + wait = msecs_to_jiffies(glue->wrp->poll_timeout); + else + wait = msecs_to_jiffies(wait_ms); + + mod_timer(&glue->timer, jiffies + wait); +} + +/* + * If no vbus irq from the PMIC is configured, we need to poll VBUS status. + */ +static void dsps_mod_timer_optional(struct dsps_glue *glue) +{ + if (glue->vbus_irq) + return; + + dsps_mod_timer(glue, -1); +} + /** * dsps_musb_enable - enable interrupts */ @@ -167,8 +191,7 @@ static void dsps_musb_enable(struct musb *musb) /* start polling for ID change in dual-role idle mode */ if (musb->xceiv->otg->state == OTG_STATE_B_IDLE && musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(wrp->poll_timeout)); + dsps_mod_timer(glue, -1); } /** @@ -198,6 +221,9 @@ static int dsps_check_status(struct musb *musb, void *unused) 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. @@ -208,8 +234,7 @@ static int dsps_check_status(struct musb *musb, void *unused) switch (musb->xceiv->otg->state) { case OTG_STATE_A_WAIT_VRISE: - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(wrp->poll_timeout)); + dsps_mod_timer_optional(glue); break; case OTG_STATE_A_WAIT_BCON: musb_writeb(musb->mregs, MUSB_DEVCTL, 0); @@ -218,17 +243,19 @@ static int dsps_check_status(struct musb *musb, void *unused) case OTG_STATE_A_IDLE: case OTG_STATE_B_IDLE: - 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 (!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); } - if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session) - musb_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(wrp->poll_timeout)); + dsps_mod_timer_optional(glue); break; case OTG_STATE_A_WAIT_VFALL: musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; @@ -331,15 +358,13 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) */ musb->int_usb &= ~MUSB_INTR_VBUSERROR; musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL; - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(wrp->poll_timeout)); + 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; - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(wrp->poll_timeout)); + dsps_mod_timer_optional(glue); } else { musb->is_active = 0; MUSB_DEV_MODE(musb); @@ -363,8 +388,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) switch (musb->xceiv->otg->state) { case OTG_STATE_B_IDLE: case OTG_STATE_A_WAIT_BCON: - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(wrp->poll_timeout)); + dsps_mod_timer_optional(glue); break; default: break; @@ -468,8 +492,7 @@ static int dsps_musb_init(struct musb *musb) musb_writeb(musb->mregs, MUSB_BABBLE_CTL, val); } - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(glue->wrp->poll_timeout)); + dsps_mod_timer(glue, -1); return dsps_musb_dbg_init(musb, glue); } @@ -765,6 +788,47 @@ err: return ret; } +static irqreturn_t dsps_vbus_threaded_irq(int irq, void *priv) +{ + struct dsps_glue *glue = priv; + struct musb *musb = platform_get_drvdata(glue->musb); + + if (!musb) + return IRQ_NONE; + + dev_dbg(glue->dev, "VBUS interrupt\n"); + dsps_mod_timer(glue, 0); + + return IRQ_HANDLED; +} + +static int dsps_setup_optional_vbus_irq(struct platform_device *pdev, + struct dsps_glue *glue) +{ + int error; + + glue->vbus_irq = platform_get_irq_byname(pdev, "vbus"); + if (glue->vbus_irq == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (glue->vbus_irq <= 0) { + glue->vbus_irq = 0; + return 0; + } + + error = devm_request_threaded_irq(glue->dev, glue->vbus_irq, + NULL, dsps_vbus_threaded_irq, + IRQF_ONESHOT, + "vbus", glue); + if (error) { + glue->vbus_irq = 0; + return error; + } + dev_dbg(glue->dev, "VBUS irq %i configured\n", glue->vbus_irq); + + return 0; +} + static int dsps_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -793,6 +857,12 @@ static int dsps_probe(struct platform_device *pdev) glue->dev = &pdev->dev; glue->wrp = wrp; + if (usb_get_dr_mode(&pdev->dev) == USB_DR_MODE_PERIPHERAL) { + ret = dsps_setup_optional_vbus_irq(pdev, glue); + if (ret) + return ret; + } + platform_set_drvdata(pdev, glue); pm_runtime_enable(&pdev->dev); ret = dsps_create_musb_pdev(glue, pdev); @@ -903,8 +973,7 @@ static int dsps_resume(struct device *dev) musb_writel(mbase, wrp->rx_mode, glue->context.rx_mode); if (musb->xceiv->otg->state == OTG_STATE_B_IDLE && musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) - mod_timer(&glue->timer, jiffies + - msecs_to_jiffies(wrp->poll_timeout)); + dsps_mod_timer(glue, -1); return 0; }