On 11/25/2013 08:48 PM, Felipe Balbi wrote: > On Mon, Nov 25, 2013 at 08:39:52PM +0100, Daniel Mack wrote: >> The dsps platform needs to save save some registers at suspend time and >> restore them after resume. This patch adds a struct for these registers, >> and also lets the musb core know that the core registers need to be >> saved as well. >> >> We also have to call musb_port_reset() for this platform upon resume, so >> this function has to be made non-static. >> >> Signed-off-by: Daniel Mack <zonque@xxxxxxxxx> >> --- >> drivers/usb/musb/musb_core.h | 1 + >> drivers/usb/musb/musb_dsps.c | 59 +++++++++++++++++++++++++++++++++++++++++ >> drivers/usb/musb/musb_host.h | 2 ++ >> drivers/usb/musb/musb_virthub.c | 2 +- >> 4 files changed, 63 insertions(+), 1 deletion(-) >> >> diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h >> index 29f7cd7..a423037 100644 >> --- a/drivers/usb/musb/musb_core.h >> +++ b/drivers/usb/musb/musb_core.h >> @@ -295,6 +295,7 @@ struct musb { >> >> irqreturn_t (*isr)(int, void *); >> struct work_struct irq_work; >> + >> u16 hwvers; >> >> u16 intrrxe; >> diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c >> index e57d712..361ddf8 100644 >> --- a/drivers/usb/musb/musb_dsps.c >> +++ b/drivers/usb/musb/musb_dsps.c >> @@ -112,6 +112,19 @@ struct dsps_musb_wrapper { >> u8 poll_seconds; >> }; >> >> +/* >> + * register shadow for suspend >> + */ >> +struct dsps_context { >> + u32 control; >> + u32 epintr; >> + u32 coreintr; >> + u32 phy_utmi; >> + u32 mode; >> + u32 tx_mode; >> + u32 rx_mode; >> +}; >> + >> /** >> * DSPS glue structure. >> */ >> @@ -121,6 +134,8 @@ struct dsps_glue { >> const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */ >> struct timer_list timer; /* otg_workaround timer */ >> unsigned long last_timer; /* last timer data for each instance */ >> + >> + struct dsps_context context; >> }; >> >> static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) >> @@ -506,6 +521,7 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, >> } >> pdata.config = config; >> pdata.platform_ops = &dsps_ops; >> + pdata.restore_after_suspend = 1; >> >> config->num_eps = get_int_prop(dn, "mentor,num-eps"); >> config->ram_bits = get_int_prop(dn, "mentor,ram-bits"); >> @@ -632,11 +648,54 @@ static const struct of_device_id musb_dsps_of_match[] = { >> }; >> MODULE_DEVICE_TABLE(of, musb_dsps_of_match); >> >> +#ifdef CONFIG_PM >> +static int dsps_suspend(struct device *dev) >> +{ >> + struct dsps_glue *glue = dev_get_drvdata(dev); >> + const struct dsps_musb_wrapper *wrp = glue->wrp; >> + struct musb *musb = platform_get_drvdata(glue->musb); >> + void __iomem *mbase = musb->ctrl_base; >> + >> + glue->context.control = dsps_readl(mbase, wrp->control); >> + glue->context.epintr = dsps_readl(mbase, wrp->epintr_set); >> + glue->context.coreintr = dsps_readl(mbase, wrp->coreintr_set); >> + glue->context.phy_utmi = dsps_readl(mbase, wrp->phy_utmi); >> + glue->context.mode = dsps_readl(mbase, wrp->mode); >> + glue->context.tx_mode = dsps_readl(mbase, wrp->tx_mode); >> + glue->context.rx_mode = dsps_readl(mbase, wrp->rx_mode); >> + >> + return 0; >> +} >> + >> +static int dsps_resume(struct device *dev) >> +{ >> + struct dsps_glue *glue = dev_get_drvdata(dev); >> + const struct dsps_musb_wrapper *wrp = glue->wrp; >> + struct musb *musb = platform_get_drvdata(glue->musb); >> + void __iomem *mbase = musb->ctrl_base; >> + >> + dsps_writel(mbase, wrp->control, glue->context.control); >> + dsps_writel(mbase, wrp->epintr_set, glue->context.epintr); >> + dsps_writel(mbase, wrp->coreintr_set, glue->context.coreintr); >> + dsps_writel(mbase, wrp->phy_utmi, glue->context.phy_utmi); >> + dsps_writel(mbase, wrp->mode, glue->context.mode); >> + dsps_writel(mbase, wrp->tx_mode, glue->context.tx_mode); >> + dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode); >> + >> + musb_port_reset(musb, false); >> + >> + return 0; >> +} >> +#endif >> + >> +static SIMPLE_DEV_PM_OPS(dsps_pm_ops, dsps_suspend, dsps_resume); >> + >> static struct platform_driver dsps_usbss_driver = { >> .probe = dsps_probe, >> .remove = dsps_remove, >> .driver = { >> .name = "musb-dsps", >> + .pm = &dsps_pm_ops, >> .of_match_table = musb_dsps_of_match, >> }, >> }; >> diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h >> index e660af9..7436c24 100644 >> --- a/drivers/usb/musb/musb_host.h >> +++ b/drivers/usb/musb/musb_host.h >> @@ -93,6 +93,7 @@ extern void musb_root_disconnect(struct musb *musb); >> extern void musb_host_resume_root_hub(struct musb *musb); >> extern void musb_host_poke_root_hub(struct musb *musb); >> extern void musb_port_suspend(struct musb *musb, bool do_suspend); >> +extern void musb_port_reset(struct musb *musb, bool do_reset); >> #else >> static inline struct musb *hcd_to_musb(struct usb_hcd *hcd) >> { >> @@ -123,6 +124,7 @@ static inline void musb_host_resume_root_hub(struct musb *musb) {} >> static inline void musb_host_poll_rh_status(struct musb *musb) {} >> static inline void musb_host_poke_root_hub(struct musb *musb) {} >> static inline void musb_port_suspend(struct musb *musb, bool do_suspend) {} >> +static inline void musb_port_reset(struct musb *musb) {} >> #endif >> >> struct usb_hcd; >> diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c >> index e977441..24e46c0 100644 >> --- a/drivers/usb/musb/musb_virthub.c >> +++ b/drivers/usb/musb/musb_virthub.c >> @@ -109,7 +109,7 @@ void musb_port_suspend(struct musb *musb, bool do_suspend) >> } >> } >> >> -static void musb_port_reset(struct musb *musb, bool do_reset) >> +void musb_port_reset(struct musb *musb, bool do_reset) > > NAK, this should not be called from the glue layers at all. If anything > call from musb_core's resume callback. That will only be called after > parent's resume has been called anyway. Given that this driver is successfully used with suspend/resume on other platforms without that reset, but it's clearly needed for dsps, I'm fairly sure this should be a glue-layer specific thing. Hence the change. I think it'll break things on other platforms if we unconditionally reset the port after resume, but I can't test it. Anyone? Thanks, Daniel -- 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