summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/musb/musb_dsps.c117
1 files changed, 93 insertions, 24 deletions
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;
}