diff options
Diffstat (limited to 'drivers/usb/musb/omap2430.c')
-rw-r--r-- | drivers/usb/musb/omap2430.c | 257 |
1 files changed, 89 insertions, 168 deletions
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index c84e0322c..0b4cec940 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -49,97 +49,14 @@ struct omap2430_glue { enum musb_vbus_id_status status; struct work_struct omap_musb_mailbox_work; struct device *control_otghs; + bool cable_connected; + bool enabled; + bool powered; }; #define glue_to_musb(g) platform_get_drvdata(g->musb) static struct omap2430_glue *_glue; -static struct timer_list musb_idle_timer; - -static void musb_do_idle(unsigned long _musb) -{ - struct musb *musb = (void *)_musb; - unsigned long flags; - u8 power; - u8 devctl; - - spin_lock_irqsave(&musb->lock, flags); - - switch (musb->xceiv->otg->state) { - case OTG_STATE_A_WAIT_BCON: - - devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - 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); - } - break; - case OTG_STATE_A_SUSPEND: - /* finish RESUME signaling? */ - if (musb->port1_status & MUSB_PORT_STAT_RESUME) { - power = musb_readb(musb->mregs, MUSB_POWER); - power &= ~MUSB_POWER_RESUME; - dev_dbg(musb->controller, "root port resume stopped, power %02x\n", power); - musb_writeb(musb->mregs, MUSB_POWER, power); - musb->is_active = 1; - musb->port1_status &= ~(USB_PORT_STAT_SUSPEND - | MUSB_PORT_STAT_RESUME); - musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; - usb_hcd_poll_rh_status(musb->hcd); - /* NOTE: it might really be A_WAIT_BCON ... */ - musb->xceiv->otg->state = OTG_STATE_A_HOST; - } - break; - case OTG_STATE_A_HOST: - devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - if (devctl & MUSB_DEVCTL_BDEVICE) - musb->xceiv->otg->state = OTG_STATE_B_IDLE; - else - musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON; - default: - break; - } - spin_unlock_irqrestore(&musb->lock, flags); -} - - -static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout) -{ - unsigned long default_timeout = jiffies + msecs_to_jiffies(3); - static unsigned long last_timer; - - if (timeout == 0) - timeout = default_timeout; - - /* Never idle if active, or when VBUS timeout is not set as host */ - if (musb->is_active || ((musb->a_wait_bcon == 0) - && (musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON))) { - dev_dbg(musb->controller, "%s active, deleting timer\n", - usb_otg_state_string(musb->xceiv->otg->state)); - del_timer(&musb_idle_timer); - last_timer = jiffies; - return; - } - - if (time_after(last_timer, timeout)) { - if (!timer_pending(&musb_idle_timer)) - last_timer = timeout; - else { - dev_dbg(musb->controller, "Longer idle timer already pending, ignoring\n"); - return; - } - } - last_timer = timeout; - - dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n", - usb_otg_state_string(musb->xceiv->otg->state), - (unsigned long)jiffies_to_msecs(timeout - jiffies)); - mod_timer(&musb_idle_timer, timeout); -} - static void omap2430_musb_set_vbus(struct musb *musb, int is_on) { struct usb_otg *otg = musb->xceiv->otg; @@ -205,16 +122,6 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on) musb_readb(musb->mregs, MUSB_DEVCTL)); } -static int omap2430_musb_set_mode(struct musb *musb, u8 musb_mode) -{ - u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - - devctl |= MUSB_DEVCTL_SESSION; - musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); - - return 0; -} - static inline void omap2430_low_level_exit(struct musb *musb) { u32 l; @@ -234,22 +141,63 @@ static inline void omap2430_low_level_init(struct musb *musb) musb_writel(musb->mregs, OTG_FORCESTDBY, l); } -static void omap2430_musb_mailbox(enum musb_vbus_id_status status) +/* + * We can get multiple cable events so we need to keep track + * of the power state. Only keep power enabled if USB cable is + * connected and a gadget is started. + */ +static void omap2430_set_power(struct musb *musb, bool enabled, bool cable) +{ + struct device *dev = musb->controller; + struct omap2430_glue *glue = dev_get_drvdata(dev->parent); + bool power_up; + int res; + + if (glue->enabled != enabled) + glue->enabled = enabled; + + if (glue->cable_connected != cable) + glue->cable_connected = cable; + + power_up = glue->enabled && glue->cable_connected; + if (power_up == glue->powered) { + dev_warn(musb->controller, "power state already %i\n", + power_up); + return; + } + + glue->powered = power_up; + + if (power_up) { + res = pm_runtime_get_sync(musb->controller); + if (res < 0) { + dev_err(musb->controller, "could not enable: %i", res); + glue->powered = false; + } + } else { + pm_runtime_mark_last_busy(musb->controller); + pm_runtime_put_autosuspend(musb->controller); + } +} + +static int omap2430_musb_mailbox(enum musb_vbus_id_status status) { struct omap2430_glue *glue = _glue; if (!glue) { pr_err("%s: musb core is not yet initialized\n", __func__); - return; + return -EPROBE_DEFER; } glue->status = status; if (!glue_to_musb(glue)) { pr_err("%s: musb core is not yet ready\n", __func__); - return; + return -EPROBE_DEFER; } schedule_work(&glue->omap_musb_mailbox_work); + + return 0; } static void omap_musb_set_mailbox(struct omap2430_glue *glue) @@ -259,6 +207,13 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) struct musb_hdrc_platform_data *pdata = dev_get_platdata(dev); struct omap_musb_board_data *data = pdata->board_data; struct usb_otg *otg = musb->xceiv->otg; + bool cable_connected; + + cable_connected = ((glue->status == MUSB_ID_GROUND) || + (glue->status == MUSB_VBUS_VALID)); + + if (cable_connected) + omap2430_set_power(musb, glue->enabled, cable_connected); switch (glue->status) { case MUSB_ID_GROUND: @@ -268,7 +223,6 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) musb->xceiv->otg->state = OTG_STATE_A_IDLE; musb->xceiv->last_event = USB_EVENT_ID; if (musb->gadget_driver) { - pm_runtime_get_sync(dev); omap_control_usb_set_mode(glue->control_otghs, USB_MODE_HOST); omap2430_musb_set_vbus(musb, 1); @@ -281,8 +235,6 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) otg->default_a = false; musb->xceiv->otg->state = OTG_STATE_B_IDLE; musb->xceiv->last_event = USB_EVENT_VBUS; - if (musb->gadget_driver) - pm_runtime_get_sync(dev); omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DEVICE); break; @@ -291,11 +243,8 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) dev_dbg(dev, "VBUS Disconnect\n"); musb->xceiv->last_event = USB_EVENT_NONE; - if (musb->gadget_driver) { + if (musb->gadget_driver) omap2430_musb_set_vbus(musb, 0); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); - } if (data->interface_type == MUSB_INTERFACE_UTMI) otg_set_vbus(musb->xceiv->otg, 0); @@ -307,6 +256,9 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) dev_dbg(dev, "ID float\n"); } + if (!cable_connected) + omap2430_set_power(musb, glue->enabled, cable_connected); + atomic_notifier_call_chain(&musb->xceiv->notifier, musb->xceiv->last_event, NULL); } @@ -316,13 +268,8 @@ static void omap_musb_mailbox_work(struct work_struct *mailbox_work) { struct omap2430_glue *glue = container_of(mailbox_work, struct omap2430_glue, omap_musb_mailbox_work); - struct musb *musb = glue_to_musb(glue); - struct device *dev = musb->controller; - pm_runtime_get_sync(dev); omap_musb_set_mailbox(glue); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); } static irqreturn_t omap2430_musb_interrupt(int irq, void *__hci) @@ -389,23 +336,7 @@ static int omap2430_musb_init(struct musb *musb) return PTR_ERR(musb->phy); } musb->isr = omap2430_musb_interrupt; - - /* - * Enable runtime PM for musb parent (this driver). We can't - * do it earlier as struct musb is not yet allocated and we - * need to touch the musb registers for runtime PM. - */ - pm_runtime_enable(glue->dev); - status = pm_runtime_get_sync(glue->dev); - if (status < 0) - goto err1; - - status = pm_runtime_get_sync(dev); - if (status < 0) { - dev_err(dev, "pm_runtime_get_sync FAILED %d\n", status); - pm_runtime_put_sync(glue->dev); - goto err1; - } + phy_init(musb->phy); l = musb_readl(musb->mregs, OTG_INTERFSEL); @@ -427,20 +358,10 @@ static int omap2430_musb_init(struct musb *musb) musb_readl(musb->mregs, OTG_INTERFSEL), musb_readl(musb->mregs, OTG_SIMENABLE)); - setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); - if (glue->status != MUSB_UNKNOWN) omap_musb_set_mailbox(glue); - phy_init(musb->phy); - phy_power_on(musb->phy); - - pm_runtime_put_noidle(musb->controller); - pm_runtime_put_noidle(glue->dev); return 0; - -err1: - return status; } static void omap2430_musb_enable(struct musb *musb) @@ -452,6 +373,11 @@ static void omap2430_musb_enable(struct musb *musb) struct musb_hdrc_platform_data *pdata = dev_get_platdata(dev); struct omap_musb_board_data *data = pdata->board_data; + if (!WARN_ON(!musb->phy)) + phy_power_on(musb->phy); + + omap2430_set_power(musb, true, glue->cable_connected); + switch (glue->status) { case MUSB_ID_GROUND: @@ -487,18 +413,25 @@ static void omap2430_musb_disable(struct musb *musb) struct device *dev = musb->controller; struct omap2430_glue *glue = dev_get_drvdata(dev->parent); + if (!WARN_ON(!musb->phy)) + phy_power_off(musb->phy); + if (glue->status != MUSB_UNKNOWN) omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DISCONNECT); + + omap2430_set_power(musb, false, glue->cable_connected); } static int omap2430_musb_exit(struct musb *musb) { - del_timer_sync(&musb_idle_timer); + struct device *dev = musb->controller; + struct omap2430_glue *glue = dev_get_drvdata(dev->parent); omap2430_low_level_exit(musb); - phy_power_off(musb->phy); phy_exit(musb->phy); + musb->phy = NULL; + cancel_work_sync(&glue->omap_musb_mailbox_work); return 0; } @@ -512,9 +445,6 @@ static const struct musb_platform_ops omap2430_ops = { .init = omap2430_musb_init, .exit = omap2430_musb_exit, - .set_mode = omap2430_musb_set_mode, - .try_idle = omap2430_musb_try_idle, - .set_vbus = omap2430_musb_set_vbus, .enable = omap2430_musb_enable, @@ -639,11 +569,9 @@ static int omap2430_probe(struct platform_device *pdev) goto err2; } - /* - * Note that we cannot enable PM runtime yet for this - * driver as we need struct musb initialized first. - * See omap2430_musb_init above. - */ + pm_runtime_enable(glue->dev); + pm_runtime_use_autosuspend(glue->dev); + pm_runtime_set_autosuspend_delay(glue->dev, 500); ret = platform_device_add(musb); if (ret) { @@ -662,12 +590,14 @@ err0: static int omap2430_remove(struct platform_device *pdev) { - struct omap2430_glue *glue = platform_get_drvdata(pdev); + struct omap2430_glue *glue = platform_get_drvdata(pdev); + struct musb *musb = glue_to_musb(glue); pm_runtime_get_sync(glue->dev); - cancel_work_sync(&glue->omap_musb_mailbox_work); platform_device_unregister(glue->musb); + omap2430_set_power(musb, false, false); pm_runtime_put_sync(glue->dev); + pm_runtime_dont_use_autosuspend(glue->dev); pm_runtime_disable(glue->dev); return 0; @@ -680,12 +610,13 @@ static int omap2430_runtime_suspend(struct device *dev) struct omap2430_glue *glue = dev_get_drvdata(dev); struct musb *musb = glue_to_musb(glue); - if (musb) { - musb->context.otg_interfsel = musb_readl(musb->mregs, - OTG_INTERFSEL); + if (!musb) + return 0; - omap2430_low_level_exit(musb); - } + musb->context.otg_interfsel = musb_readl(musb->mregs, + OTG_INTERFSEL); + + omap2430_low_level_exit(musb); return 0; } @@ -696,7 +627,7 @@ static int omap2430_runtime_resume(struct device *dev) struct musb *musb = glue_to_musb(glue); if (!musb) - return -EPROBE_DEFER; + return 0; omap2430_low_level_init(musb); musb_writel(musb->mregs, OTG_INTERFSEL, @@ -738,18 +669,8 @@ static struct platform_driver omap2430_driver = { }, }; +module_platform_driver(omap2430_driver); + MODULE_DESCRIPTION("OMAP2PLUS MUSB Glue Layer"); MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); MODULE_LICENSE("GPL v2"); - -static int __init omap2430_init(void) -{ - return platform_driver_register(&omap2430_driver); -} -subsys_initcall(omap2430_init); - -static void __exit omap2430_exit(void) -{ - platform_driver_unregister(&omap2430_driver); -} -module_exit(omap2430_exit); |