On Fri, 2010-01-22 at 09:42 +0100, Mandy Arnaud.2 (EXT-Teleca/Helsinki) wrote: Hi, > 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. I think there is one register mismatch, please see below. > 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); Here you read from OTG_FORCESTDBY, tweak bits and write to OTG_SYSCONFIG? I guess the read should be from OTG_SYSCONFIG? > + > + 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 -- Ari -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html