New support for OFF-mode on OMAP using notifier communicating usb events In the present case VBUS events are checked. Signed-off-by: Arnaud Mandy <ext-arnaud.2.mandy@xxxxxxxxx> --- drivers/usb/musb/musb_core.c | 34 ++++++++++++++++++++++- drivers/usb/musb/musb_core.h | 13 ++++++++- drivers/usb/musb/omap2430.c | 62 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 106 insertions(+), 3 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index a3da667..3245f1e 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -137,6 +137,28 @@ MODULE_ALIAS("platform:" MUSB_DRIVER_NAME); /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_PM +/* blocking notifier support */ +static int musb_notifier_call(struct notifier_block *nb, + unsigned long event, void *unused) +{ + struct musb *musb = container_of(nb, struct musb, nb); + + switch (event) { + case USB_EVENT_VBUS: + musb_platform_power_on(musb); + break; + case USB_EVENT_NONE: + musb_platform_power_off(musb); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} +#endif + static inline struct musb *dev_to_musb(struct device *dev) { #ifdef CONFIG_USB_MUSB_HDRC_HCD @@ -956,6 +978,9 @@ static void musb_shutdown(struct platform_device *pdev) struct musb *musb = dev_to_musb(&pdev->dev); unsigned long flags; +#ifdef CONFIG_PM + otg_unregister_notifier(musb->xceiv, &musb->nb); +#endif spin_lock_irqsave(&musb->lock, flags); musb_platform_disable(musb); musb_generic_disable(musb); @@ -1824,6 +1849,9 @@ allocate_instance(struct device *dev, } musb->controller = dev; +#ifdef CONFIG_PM + musb->nb.notifier_call = musb_notifier_call; +#endif return musb; } @@ -2089,7 +2117,11 @@ bad_config: #endif if (status) goto fail2; - +#ifdef CONFIG_PM + status = otg_register_notifier(musb->xceiv, &musb->nb); + if (status) + goto fail2; +#endif return 0; fail2: diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index e8e7d1c..60556b2 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -330,7 +330,9 @@ struct musb { #define MUSB_HWVERS_1800 0x720 #define MUSB_HWVERS_2000 0x800 u16 hwvers; - +#ifdef CONFIG_PM + struct notifier_block nb; +#endif /* this hub status bit is reserved by USB 2.0 and not seen by usbcore */ #define MUSB_PORT_STAT_RESUME (1 << 31) @@ -406,6 +408,9 @@ struct musb { /* active means connected and not suspended */ unsigned is_active:1; + /* off mode is supported */ + unsigned off_mode:1; + unsigned is_multipoint:1; unsigned ignore_disconnect:1; /* during bus resets */ @@ -486,9 +491,15 @@ extern void musb_platform_save_context(struct musb *musb, struct musb_context_registers *musb_context); extern void musb_platform_restore_context(struct musb *musb, struct musb_context_registers *musb_context); +extern void musb_save_context(struct musb *musb); +extern void musb_restore_context(struct musb *musb); +extern void musb_platform_power_on(struct musb *musb); +extern void musb_platform_power_off(struct musb *musb); #else #define musb_platform_save_context(m, x) do {} while (0) #define musb_platform_restore_context(m, x) do {} while (0) +#define musb_platform_power_on(x) do {} while (0) +#define musb_platform_power_off(x) do {} while (0) #endif #endif diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index c4cba52..01b7041 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -271,7 +271,67 @@ void musb_platform_restore_context(struct musb *musb, musb_writel(musb->mregs, OTG_SYSCONFIG, musb_context->otg_sysconfig); musb_writel(musb->mregs, OTG_FORCESTDBY, musb_context->otg_forcestandby); } -#endif + +void musb_platform_power_off(struct musb *musb) +{ + u32 l; + + DBG(3, "allow OFF-mode\n"); + + l = musb_readl(musb->mregs, OTG_FORCESTDBY); + l |= ENABLEFORCE; /* enable MSTANDBY */ + musb_writel(musb->mregs, OTG_FORCESTDBY, l); + + l = musb_readl(musb->mregs, OTG_SYSCONFIG); + l |= ENABLEWAKEUP; /* enable wakeup */ + l &= ~NOSTDBY; /* disable nostdby */ + l |= SMARTSTDBY; /* enable smart standby */ + + l |= AUTOIDLE; /* enable auto idle */ + l &= ~NOIDLE; /* disable noidle */ + l |= SMARTIDLE; /* enable smart idle */ + musb_writel(musb->mregs, OTG_SYSCONFIG, l); + + if (!cpu_is_omap3430()) + l |= AUTOIDLE; /* enable auto idle */ + musb_writel(musb->mregs, OTG_SYSCONFIG, l); +} +EXPORT_SYMBOL_GPL(musb_platform_power_off); + +void musb_platform_power_on(struct musb *musb) +{ + u32 l; + + DBG(3, "wake-up from OFF-mode\n"); + + l = musb_readl(musb->mregs, OTG_SYSCONFIG); + l &= ~ENABLEWAKEUP; /* disable wakeup */ + musb_writel(musb->mregs, OTG_SYSCONFIG, l); + + l = musb_readl(musb->mregs, OTG_FORCESTDBY); + l &= ~ENABLEFORCE; /* disable MSTANDBY */ + musb_writel(musb->mregs, OTG_FORCESTDBY, l); + + l = musb_readl(musb->mregs, OTG_SYSCONFIG); + l |= NOSTDBY; /* enable nostdby */ + l &= ~SMARTSTDBY; /* disable smart standby */ + + l &= ~AUTOIDLE; /* disable auto idle */ + l |= NOIDLE; /* enable noidle */ + l &= ~SMARTIDLE; /* disable smart idle */ + musb_writel(musb->mregs, OTG_SYSCONFIG, l); + + l = musb_readl(musb->mregs, OTG_INTERFSEL); + l |= ULPI_12PIN; + musb_writel(musb->mregs, OTG_INTERFSEL, l); + + /* Restore register context */ + musb->off_mode = 1; + musb_restore_context(musb); + musb->off_mode = 0; +} +EXPORT_SYMBOL_GPL(musb_platform_power_on); +#endif /* CONFIG_PM */ int musb_platform_suspend(struct musb *musb) { -- 1.6.0.4 -- 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