using a wrapper between the transceiver driver and the controller driver to signal the controller driver to turn on/off the controller when VBUS event is detected. based-on: Heikki Krogerus <ext-heikki.krogerus@xxxxxxxxx> Signed-off-by: Arnaud Mandy <ext-arnaud.2.mandy@xxxxxxxxx> --- drivers/usb/musb/musb_core.c | 30 +++++++++++++++++- drivers/usb/musb/musb_core.h | 17 +++++++++- drivers/usb/musb/omap2430.c | 66 ++++++++++++++++++++++++++++++++++++++++- drivers/usb/otg/otg.c | 16 ++++++++++ drivers/usb/otg/twl4030-usb.c | 2 + include/linux/usb/otg.h | 11 +++++++ 6 files changed, 137 insertions(+), 5 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 2f59892..ab97a02 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -965,6 +965,8 @@ void musb_start(struct musb *musb) } musb_platform_enable(musb); musb_writeb(regs, MUSB_DEVCTL, devctl); + + musb_save_context(musb); } @@ -1948,6 +1950,25 @@ static void musb_free(struct musb *musb) #endif } +int musb_power_controller(struct otg_controller *controller, bool vbus) +{ + struct musb *musb = dev_to_musb(controller->dev); + + if (!musb->off_mode_support) + return 0; + + if (vbus) + musb_power_on_controller(musb); + else + musb_power_off_controller(musb); + + return 0; +} + +struct otg_controller musb_controller = { + .power_controller = musb_power_controller, +}; + /* * Perform generic per-controller initialization. * @@ -2044,6 +2065,9 @@ bad_config: goto fail2; } + musb_controller.dev = dev; + musb->xceiv->controller = &musb_controller; + #ifndef CONFIG_MUSB_PIO_ONLY if (use_dma && dev->dma_mask) { struct dma_controller *c; @@ -2313,7 +2337,8 @@ void musb_save_context(struct musb *musb) musb_writeb(musb_base, MUSB_INDEX, musb_context.index); - musb_platform_save_context(musb, &musb_context); + if (!musb->off_mode) + musb_platform_save_context(musb, &musb_context); } void musb_restore_context(struct musb *musb) @@ -2322,7 +2347,8 @@ void musb_restore_context(struct musb *musb) void __iomem *musb_base = musb->mregs; void __iomem *ep_target_regs; - musb_platform_restore_context(musb, &musb_context); + if (!musb->off_mode) + musb_platform_restore_context(musb, &musb_context); if (is_host_enabled(musb)) { musb_writew(musb_base, MUSB_FRAME, musb_context.frame); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 1e3da4e..8105a47 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -448,6 +448,11 @@ struct musb { struct usb_gadget g; /* the gadget */ struct usb_gadget_driver *gadget_driver; /* its driver */ #endif + /* true if off-mode is supported */ + unsigned off_mode_support:1; + + /* true if off-mode is requested */ + unsigned off_mode:1; /* true if we're using dma */ unsigned use_dma:1; @@ -498,8 +503,16 @@ extern void musb_platform_restore_context(struct musb *musb, #define musb_platform_save_context(m, x) do {} while (0) #define musb_platform_restore_context(m, x) do {} while (0) #endif - -#endif +extern void musb_power_on_controller(struct musb *musb); +extern void musb_power_off_controller(struct musb *musb); +void musb_save_context(struct musb *musb); +void musb_restore_context(struct musb *musb); +#else +static inline void musb_power_on_controller(struct musb *musb) {}; +static inline void musb_power_off_controller(struct musb *musb) {}; +static inline void musb_save_context(struct musb *musb) {}; +static inline void musb_restore_context(struct musb *musb) {}; +#endif /* CONFIG_PM */ static inline void musb_set_vbus(struct musb *musb, int is_on) { diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 21cff53..3cc894b 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -220,6 +220,9 @@ int __init musb_platform_init(struct musb *musb) musb_platform_resume(musb); +#ifdef CONFIG_PM + musb->off_mode_support = 1; +#endif l = musb_readl(musb->mregs, OTG_SYSCONFIG); l &= ~ENABLEWAKEUP; /* disable wakeup */ l &= ~NOSTDBY; /* remove possible nostdby */ @@ -271,7 +274,68 @@ 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_power_off_controller(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_FORCESTDBY); + 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_power_off_controller); + +void musb_power_on_controller(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_power_on_controller); +#endif /* CONFIG_PM */ int musb_platform_suspend(struct musb *musb) { diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 0a43a7d..8ab45a7 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -64,3 +64,19 @@ int otg_set_transceiver(struct otg_transceiver *x) return 0; } EXPORT_SYMBOL(otg_set_transceiver); + +/** + * otg_power_controller - control power on/off of tranceiver. + * @x: the USB OTG transceiver to be used; or NULL + * @vbus: VBUS presence. + * This call is to save and power off the usb controller based on + * VBUS level detected by the transceiver. + */ +int otg_power_controller(struct otg_transceiver *x, bool vbus) +{ + if (x->controller && x->controller->power_controller) + x->controller->power_controller(x->controller, vbus); + return 0; +} +EXPORT_SYMBOL(otg_power_controller); + diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 488b0a3..3aa84ea 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -503,6 +503,7 @@ static void twl4030_phy_suspend(struct twl4030_usb *twl, int controller_off) twl4030_phy_power(twl, 0); twl->asleep = 1; + otg_power_controller(&twl->otg, 0); } static void twl4030_phy_resume(struct twl4030_usb *twl) @@ -516,6 +517,7 @@ static void twl4030_phy_resume(struct twl4030_usb *twl) if (twl->usb_mode == T2_USB_MODE_ULPI) twl4030_i2c_access(twl, 0); twl->asleep = 0; + otg_power_controller(&twl->otg, 1); } static int twl4030_usb_ldo_init(struct twl4030_usb *twl) diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index e328a89..77c4bdc 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -60,6 +60,14 @@ struct otg_io_access_ops { int (*write)(struct otg_transceiver *otg, u32 val, u32 reg); }; +/* the otg driver needs to inform the usb controller driver when vbus + * is present or not. + */ +struct otg_controller { + struct device *dev; + int (*power_controller)(struct otg_controller *otg, bool vbus); +}; + /* * the otg driver needs to interact with both device side and host side * usb controllers. it decides which controller is active at a given @@ -93,6 +101,8 @@ struct otg_transceiver { u16 port_status; u16 port_change; + struct otg_controller *controller; + /* initialize/shutdown the OTG controller */ int (*init)(struct otg_transceiver *otg); void (*shutdown)(struct otg_transceiver *otg); @@ -171,6 +181,7 @@ otg_shutdown(struct otg_transceiver *otg) /* for usb host and peripheral controller drivers */ extern struct otg_transceiver *otg_get_transceiver(void); extern void otg_put_transceiver(struct otg_transceiver *); +extern int otg_power_controller(struct otg_transceiver *, bool); /* Context: can sleep */ static inline int -- 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