===================================================================
@@ -31,6 +31,7 @@
#include <plat/clockdomain.h>
#include <plat/control.h>
#include <plat/serial.h>
+#include <plat/usb.h>
#include "pm.h"
===================================================================
@@ -38,6 +38,7 @@
#include <plat/prcm.h>
#include <plat/gpmc.h>
#include <plat/dma.h>
+#include <plat/usb.h>
#include <asm/tlbflush.h>
@@ -324,11 +325,13 @@ static void restore_table_entry(void)
void omap3_device_idle(void)
{
omap2_gpio_prepare_for_idle();
+ musb_prepare_for_idle();
}
void omap3_device_resume(void)
{
omap2_gpio_resume_after_idle();
+ musb_wakeup_from_idle();
}
void omap_sram_idle(void)
===================================================================
@@ -25,16 +25,21 @@
#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/powerdomain.h>
#ifdef CONFIG_USB_MUSB_SOC
static const char name[] = "musb_hdrc";
#define MAX_OMAP_MUSB_HWMOD_NAME_LEN 16
+struct omap_hwmod *oh_p;
+static struct powerdomain *core_pwrdm;
+
static struct musb_hdrc_config musb_config = {
.multipoint = 1,
.dyn_fifo = 1,
@@ -58,6 +63,10 @@ static struct musb_hdrc_platform_data mu
* "mode", and should be passed to usb_musb_init().
*/
.power = 50, /* up to 100 mA */
+
+ /* OMAP supports offmode */
+ .save_context = 1,
+ .restore_context = 1,
};
static u64 musb_dmamask = DMA_BIT_MASK(32);
@@ -80,6 +89,7 @@ void __init usb_musb_init(struct omap_mu
const char *oh_name = "usb_otg_hs";
struct musb_hdrc_platform_data *pdata;
+ core_pwrdm = pwrdm_lookup("per_pwrdm");
oh = omap_hwmod_lookup(oh_name);
if (!oh) {
@@ -97,6 +107,7 @@ void __init usb_musb_init(struct omap_mu
musb_plat.extvbus = board_data->extvbus;
pdata = &musb_plat;
+ oh_p = oh;
od = omap_device_build(name, bus_id, oh, pdata,
sizeof(struct musb_hdrc_platform_data),
@@ -115,8 +126,101 @@ void __init usb_musb_init(struct omap_mu
put_device(dev);
}
+void musb_prepare_for_idle()
+{
+ int core_next_state;
+ struct omap_hwmod *oh = oh_p;
+ struct omap_device *od;
+ struct platform_device *pdev;
+ struct musb_hdrc_platform_data *pdata;
+ struct device *dev;
+
+ if (!core_pwrdm)
+ return;
+
+ core_next_state = pwrdm_read_next_pwrst(core_pwrdm);
+ if (core_next_state >= PWRDM_POWER_INACTIVE)
+ return;
+ if (!oh)
+ return;
+
+ od = oh->od;
+ pdev = &od->pdev;
+
+ if (!pdev)
+ return;
+ dev = &pdev->dev;
+
+ if (dev->driver) {
+ pdata = dev->platform_data;
+
+ if (pdata->is_usb_active)
+ if (!pdata->is_usb_active(dev)) {
+ if (core_next_state == PWRDM_POWER_OFF) {
+ pdata->save_context = 1;
+ pm_runtime_put_sync(dev);
+ } else if (core_next_state == PWRDM_POWER_RET) {
+ pdata->save_context = 0;
+ pm_runtime_put_sync(dev);
+ }
+ }
+ }
+}
+
+void musb_wakeup_from_idle()
+{
+ int core_next_state;
+ int core_prev_state;
+ struct omap_hwmod *oh = oh_p;
+ struct omap_device *od;
+ struct platform_device *pdev;
+ struct device *dev;
+ struct musb_hdrc_platform_data *pdata;
+
+ if (!core_pwrdm)
+ return;
+
+ core_next_state = pwrdm_read_next_pwrst(core_pwrdm);
+
+ if (core_next_state >= PWRDM_POWER_INACTIVE)
+ return;
+ core_prev_state = pwrdm_read_prev_pwrst(core_pwrdm);
+
+ if (!oh)
+ return;
+ od = oh->od;
+ pdev = &od->pdev;
+
+ if (!pdev)
+ return;
+
+ dev = &pdev->dev;
+
+ if (dev->driver) {
+ pdata = dev->platform_data;
+
+ if (pdata->is_usb_active)
+ if (!pdata->is_usb_active(dev)) {
+ if (core_prev_state == PWRDM_POWER_OFF) {
+ pdata->restore_context = 1;
+ pm_runtime_get_sync(dev);
+ } else {
+ pdata->restore_context = 0;
+ pm_runtime_get_sync(dev);
+ }
+ }
+ }
+}
#else
void __init usb_musb_init(struct omap_musb_board_data *board_data)
{
}
+
+void musb_prepare_for_idle()
+{
+}
+
+void musb_wakeup_from_idle()
+{
+}
#endif /* CONFIG_USB_MUSB_SOC */
===================================================================
@@ -79,6 +79,8 @@ extern void usb_ehci_init(const struct e
extern void usb_ohci_init(const struct ohci_hcd_omap_platform_data *pdata);
+extern void musb_prepare_for_idle(void);
+extern void musb_wakeup_from_idle(void);
#endif
===================================================================
@@ -2410,6 +2410,7 @@ static int musb_suspend(struct device *d
struct platform_device *pdev = to_platform_device(dev);
unsigned long flags;
struct musb *musb = dev_to_musb(&pdev->dev);
+ struct musb_hdrc_platform_data *plat = dev->platform_data;
if (!musb->clock)
return 0;
@@ -2425,6 +2426,9 @@ static int musb_suspend(struct device *d
* they will even be wakeup-enabled.
*/
}
+
+ if (plat->save_context)
+ plat->save_context = 1;
pm_runtime_put_sync(dev);
#ifndef CONFIG_PM_RUNTIME
@@ -2443,10 +2447,13 @@ static int musb_resume_noirq(struct devi
{
struct platform_device *pdev = to_platform_device(dev);
struct musb *musb = dev_to_musb(&pdev->dev);
+ struct musb_hdrc_platform_data *plat = dev->platform_data;
if (!musb->clock)
return 0;
+ if (plat->restore_context)
+ plat->restore_context = 1;
pm_runtime_get_sync(dev);
#ifndef CONFIG_PM_RUNTIME
@@ -2468,16 +2475,20 @@ static int musb_resume_noirq(struct devi
static int musb_runtime_suspend(struct device *dev)
{
struct musb *musb = dev_to_musb(dev);
+ struct musb_hdrc_platform_data *plat = dev->platform_data;
- musb_save_context(musb);
+ if (plat->save_context)
+ musb_save_context(musb);
return 0;
}
static int musb_runtime_resume(struct device *dev)
{
struct musb *musb = dev_to_musb(dev);
+ struct musb_hdrc_platform_data *plat = dev->platform_data;
- musb_restore_context(musb);
+ if (plat->restore_context)
+ musb_restore_context(musb);
return 0;
}
static const struct dev_pm_ops musb_dev_pm_ops = {
===================================================================
@@ -189,6 +189,19 @@ int musb_platform_set_mode(struct musb *
return 0;
}
+int is_musb_active(struct device *dev)
+{
+ struct musb *musb;
+
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ /* usbcore insists dev->driver_data is a "struct hcd *" */
+ musb = hcd_to_musb(dev_get_drvdata(dev));
+#else
+ musb = dev_get_drvdata(dev);
+#endif
+ return musb->is_active;
+}
+
int __init musb_platform_init(struct musb *musb)
{
u32 l;
@@ -232,6 +245,7 @@ int __init musb_platform_init(struct mus
musb->board_set_vbus = omap_set_vbus;
setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
+ plat->is_usb_active = is_musb_active;
return 0;
}
===================================================================
@@ -93,6 +93,8 @@ struct musb_hdrc_config {
};
+struct device;
+
struct musb_hdrc_platform_data {
/* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */
u8 mode;
@@ -126,6 +128,17 @@ struct musb_hdrc_platform_data {
/* Architecture specific board data */
void *board_data;
+
+ /* check usb device active state*/
+ int (*is_usb_active)(struct device *dev);
+
+ /*
+ * Used for saving and restoring the registers only when core
+ * next state is off and previous state was off.
+ * Otherwise avoid save restore.
+ */
+ int save_context;
+ int restore_context;
};