===================================================================
@@ -418,7 +418,9 @@
omap3_core_save_context();
omap3_prcm_save_context();
/* Save MUSB context */
- musb_context_save_restore(1);
+ musb_context_save_restore(save_context);
+ } else {
+ musb_context_save_restore(disable_clk);
}
}
@@ -462,8 +464,10 @@
omap3_sram_restore_context();
omap2_sms_restore_context();
/* restore MUSB context */
- musb_context_save_restore(0);
- }
+ musb_context_save_restore(restore_context);
+ } else {
+ musb_context_save_restore(enable_clk);
+ }
omap_uart_resume_idle(0);
omap_uart_resume_idle(1);
if (core_next_state == PWRDM_POWER_OFF)
===================================================================
@@ -25,11 +25,13 @@
#include <linux/io.h>
#include <linux/usb/musb.h>
+#include <linux/pm_runtime.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <plat/usb.h>
#include <plat/omap_device.h>
+#include <plat/omap_hwmod.h>
#ifdef CONFIG_USB_MUSB_SOC
@@ -99,6 +101,18 @@
musb_plat.board_data = board_data;
musb_plat.power = board_data->power >> 1;
musb_plat.mode = board_data->mode;
+ musb_plat.device_enable = omap_device_enable;
+ musb_plat.device_idle = omap_device_idle;
+ musb_plat.enable_wakeup = omap_device_enable_wakeup;
+ musb_plat.disable_wakeup = omap_device_disable_wakeup;
+ /*
+ * Errata 1.166 idle_req/ack is broken in omap3430
+ * workaround is to disable the autodile bit for omap3430.
+ */
+ if (cpu_is_omap3430())
+ oh->flags |= HWMOD_NO_OCP_AUTOIDLE;
+
+ musb_plat.oh = oh;
pdata = &musb_plat;
od = omap_device_build(name, bus_id, oh, pdata,
@@ -120,7 +134,7 @@
}
}
-void musb_context_save_restore(int save)
+void musb_context_save_restore(enum musb_state state)
{
struct omap_hwmod *oh = omap_hwmod_lookup("usb_otg_hs");
struct omap_device *od = oh->od;
@@ -129,14 +143,64 @@
struct device_driver *drv = dev->driver;
if (drv) {
+#ifdef CONFIG_PM_RUNTIME
struct musb_hdrc_platform_data *pdata = dev->platform_data;
const struct dev_pm_ops *pm = drv->pm;
- if (!pdata->is_usb_active(dev)) {
- if (save)
+ if (!pdata->is_usb_active(dev)) {
+ switch (state) {
+ case save_context:
+ /* If the usb device not active then Save
+ * the context,set the sysconfig setting to
+ * force standby force idle during idle and
+ * disable the clock.
+ */
+ oh->flags |= HWMOD_SWSUP_SIDLE
+ | HWMOD_SWSUP_MSTANDBY;
pm->suspend(dev);
- else
+ pdata->device_idle(pdev);
+
+ break;
+ case disable_clk:
+ /* If the usb device not active then
+ * Save the context, set the sysconfig setting
+ * to force standby force idle during idle and
+ * disable the clock.
+ */
+
+ oh->flags |= HWMOD_SWSUP_SIDLE
+ | HWMOD_SWSUP_MSTANDBY;
+ pdata->device_idle(pdev);
+
+ break;
+
+ case restore_context:
+ /* Enable the clock, set the sysconfig
+ * back to smart idle and smart stndby after
+ * wakeup. restore the context.
+ */
+ oh->flags &= ~(HWMOD_SWSUP_SIDLE
+ | HWMOD_SWSUP_MSTANDBY);
+ pdata->device_enable(pdev);
pm->resume_noirq(dev);
+
+ break;
+
+ case enable_clk:
+ /* Set the sysconfig back to smart
+ * idle and smart stndby after wakeup and
+ * enable the clock
+ */
+ oh->flags &= ~(HWMOD_SWSUP_SIDLE
+ | HWMOD_SWSUP_MSTANDBY);
+ pdata->device_enable(pdev);
+
+ break;
+
+ default:
+ break;
+ }
+#endif
}
}
}
===================================================================
@@ -71,6 +71,13 @@
unsigned extvbus:1;
};
+enum musb_state {
+ save_context = 1,
+ disable_clk,
+ restore_context,
+ enable_clk,
+ };
+
enum musb_interface {MUSB_INTERFACE_ULPI, MUSB_INTERFACE_UTMI};
extern void usb_musb_init(struct omap_musb_board_data *board_data);
@@ -80,7 +87,7 @@
extern void usb_ohci_init(const struct ohci_hcd_omap_platform_data *pdata);
/* For saving and restoring the musb context during off/wakeup*/
-extern void musb_context_save_restore(int save);
+extern void musb_context_save_restore(enum musb_state state);
#endif
===================================================================
@@ -2447,9 +2447,21 @@
return 0;
}
+static int musb_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int musb_runtime_resume(struct device *dev)
+{
+ return 0;
+}
+
static const struct dev_pm_ops musb_dev_pm_ops = {
.suspend = musb_suspend,
.resume_noirq = musb_resume_noirq,
+ .runtime_suspend = musb_runtime_suspend,
+ .runtime_resume = musb_runtime_resume,
};
#define MUSB_DEV_PM_OPS (&musb_dev_pm_ops)
===================================================================
@@ -31,6 +31,7 @@
#include <linux/list.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/pm_runtime.h>
#include <plat/mux.h>
@@ -209,10 +210,6 @@
struct musb_hdrc_platform_data *plat = dev->platform_data;
struct omap_musb_board_data *data = plat->board_data;
-#if defined(CONFIG_ARCH_OMAP2430)
- omap_cfg_reg(AE5_2430_USB0HS_STP);
-#endif
-
/* We require some kind of external transceiver, hooked
* up through ULPI. TWL4030-family PMICs include one,
* which needs a driver, drivers aren't always needed.
@@ -224,22 +221,6 @@
}
musb_platform_resume(musb);
-
- l = musb_readl(musb->mregs, OTG_SYSCONFIG);
- l &= ~ENABLEWAKEUP; /* disable wakeup */
- l &= ~NOSTDBY; /* remove possible nostdby */
- l |= SMARTSTDBY; /* enable smart standby */
- l &= ~AUTOIDLE; /* disable auto idle */
- l &= ~NOIDLE; /* remove possible noidle */
- l |= SMARTIDLE; /* enable smart idle */
- /*
- * MUSB AUTOIDLE don't work in 3430.
- * Workaround by Richard Woodruff/TI
- */
- if (!cpu_is_omap3430())
- l |= AUTOIDLE; /* enable auto idle */
- musb_writel(musb->mregs, OTG_SYSCONFIG, l);
-
l = musb_readl(musb->mregs, OTG_INTERFSEL);
if (data->interface_type == MUSB_INTERFACE_UTMI) {
@@ -273,48 +254,26 @@
void musb_platform_save_context(struct musb *musb,
struct musb_context_registers *musb_context)
{
- /*
- * As per the omap-usbotg specification, configure it to forced standby
- * and force idle mode when no activity on usb.
- */
void __iomem *musb_base = musb->mregs;
- musb_writel(musb_base, OTG_FORCESTDBY, 0);
-
- musb_writel(musb_base, OTG_SYSCONFIG, musb_readl(musb_base,
- OTG_SYSCONFIG) & ~(NOSTDBY | SMARTSTDBY));
-
- musb_writel(musb_base, OTG_SYSCONFIG, musb_readl(musb_base,
- OTG_SYSCONFIG) & ~(AUTOIDLE));
-
- musb_writel(musb_base, OTG_SYSCONFIG, musb_readl(musb_base,
- OTG_SYSCONFIG) & ~(NOIDLE | SMARTIDLE));
-
musb_writel(musb_base, OTG_FORCESTDBY, 1);
}
void musb_platform_restore_context(struct musb *musb,
struct musb_context_registers *musb_context)
{
- /*
- * As per the omap-usbotg specification, configure it smart standby
- * and smart idle during operation.
- */
void __iomem *musb_base = musb->mregs;
musb_writel(musb_base, OTG_FORCESTDBY, 0);
-
- musb_writel(musb_base, OTG_SYSCONFIG, musb_readl(musb_base,
- OTG_SYSCONFIG) | (SMARTSTDBY));
-
- musb_writel(musb_base, OTG_SYSCONFIG, musb_readl(musb_base,
- OTG_SYSCONFIG) | (SMARTIDLE));
}
#endif
static int musb_platform_suspend(struct musb *musb)
{
u32 l;
+ struct device *dev = musb->controller;
+ struct musb_hdrc_platform_data *pdata = dev->platform_data;
+ struct omap_hwmod *oh = pdata->oh;
if (!musb->clock)
return 0;
@@ -324,16 +283,9 @@
l |= ENABLEFORCE; /* enable MSTANDBY */
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
- l = musb_readl(musb->mregs, OTG_SYSCONFIG);
- l |= ENABLEWAKEUP; /* enable wakeup */
- musb_writel(musb->mregs, OTG_SYSCONFIG, l);
-
+ pdata->enable_wakeup(oh->od);
otg_set_suspend(musb->xceiv, 1);
-
- if (musb->set_clock)
- musb->set_clock(musb->clock, 0);
- else
- clk_disable(musb->clock);
+ pm_runtime_put_sync(dev);
return 0;
}
@@ -341,21 +293,18 @@
static int musb_platform_resume(struct musb *musb)
{
u32 l;
+ struct device *dev = musb->controller;
+ struct musb_hdrc_platform_data *pdata = dev->platform_data;
+ struct omap_hwmod *oh = pdata->oh;
if (!musb->clock)
return 0;
otg_set_suspend(musb->xceiv, 0);
-
- if (musb->set_clock)
- musb->set_clock(musb->clock, 1);
- else
- clk_enable(musb->clock);
-
- l = musb_readl(musb->mregs, OTG_SYSCONFIG);
- l &= ~ENABLEWAKEUP; /* disable wakeup */
- musb_writel(musb->mregs, OTG_SYSCONFIG, l);
-
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+ pdata->disable_wakeup(oh->od);
+ pdata->enable_wakeup(oh->od);
l = musb_readl(musb->mregs, OTG_FORCESTDBY);
l &= ~ENABLEFORCE; /* disable MSTANDBY */
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
===================================================================
@@ -10,6 +10,9 @@
#ifndef __LINUX_USB_MUSB_H
#define __LINUX_USB_MUSB_H
+#include <linux/platform_device.h>
+#include <plat/omap_hwmod.h>
+
/* The USB role is defined by the connector used on the board, so long as
* standards are being followed. (Developer boards sometimes won't.)
*/
@@ -129,6 +132,21 @@
/* check usb device active state*/
int (*is_usb_active)(struct device *dev);
+
+ /* omap hwmod data structure */
+ struct omap_hwmod *oh;
+
+ /* enable clocks and set sysconfig register*/
+ int (*device_enable)(struct platform_device *pdev);
+
+ /* Disable clock and reset the sysconfig register settings*/
+ int (*device_idle)(struct platform_device *pdev);
+
+ /* set the enable wakeup bit of sysconfig register */
+ int (*enable_wakeup)(struct omap_device *od);
+
+ /* Clear the enable wakeup bit of sysconfig register */
+ int (*disable_wakeup)(struct omap_device *od);
};