This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

USB EHCI Power management

Other Parts Discussed in Thread: SYSCONFIG

1) I use linux 2.6.29. I have EHCI and OTG port.
   2) I am testing Power Management.

   When i put the board to suspend doing

   echo mem > /sys/power/state

   1) When i enabled EHCI Only - I find that sometimes linux refuses to go to suspend. (It wakes up immediately - neon, mpu, core, usb are active
   2) Sometimes - I find that it goes into suspend - But usb pwr domain and core power domain are active.
   3) If i enable OTG Only and android gadget - I find that when i connect the board to my PC using USB cable - If i put linux to suspend.
       or during suspend If i connect the cable to my PC.

   Are there any "work around" patches/any patches which will fix PM issues on both EHCI and OTG

  •  

    EHCI PM is not supported so far and so it wouldn't allow system to enter into suspend state. I have a patch (copied below) which will be submitted to linux-omap list

    OTG PM is implemented but context save/restore not yet in available. You Can use below patch for musb context save/restore.

     

    http://marc.info/?l=linux-omap&m=125197870230036&q=raw

     Regards,

    Ajay

    ------------------------

    From fa9a777b0cb7d08d49d9ccf7a8a5e2f7f0b6ec4f Mon Sep 17 00:00:00 2001

    From: Ajay Kumar Gupta <ajay.gupta@ti.com>

    Date: Tue, 3 Nov 2009 17:38:33 +0530

    Subject: [PATCH] ehci: pm: Add power management support

    Tested on OMAP3EVM (with SMSC3320 PHY) and observations are,

    - system suspend and resume works fine using

    $ echo mem > /sys/power/state

    - EHCI works fine after resume even when the USB devices

    were connected before suspend.

    - if CONFIG_USB_SUSPEND is enabled then EHCI doesn't work

    after resume if the devices were connected before suspend.

    we get below error messages in this case,

    > Hub 1-0:1.0: unable to enumerate USB device on port 2.

    > Hub 1-0:1.0: Cannot enable port 2, Maybe the USB cable is bad?

    Debugging: core-off in idle cycle

    - EHCI port doesn't work if enable_off_mode/sleep_while_idle

    is enabled.

    - We added a change in arach/arm/mach-omap2/powerdomains34xx.h

    - /*.flags = PWRDM_HAS_HDWR_SAR,*/ /* for USBHOST ctrlr only */

    + .flags = PWRDM_HAS_HDWR_SAR, /* for USBHOST ctrlr only */

    With this change, we observed,

    1. selective-suspend=OFF, enable_off_mode/sleep_while_idle =ON

    system hangs and it shows crash when used no_console_suspend.

    cm: Module associated with clock usbhost_48m_fck didn't enable in 100000 tries

    Unhandled fault: external abort on non-linefetch (0x1028) at 0xd8064010

    [<c0244344>] (omap_ehci_bus_resume+0x0/0x5d4) from [<c0232b4c>] (hcd_bus_resume+0x8c/0x114)

    [<c0232ac0>] (hcd_bus_resume+0x0/0x114) from [<c023e53c>] (generic_resume+0x1c/0x28)

    [<c023e520>] (generic_resume+0x0/0x28) from [<c02375e8>] (usb_resume_device+0x50/0x68)

    [<c0237598>] (usb_resume_device+0x0/0x68) from [<c0238250>] (usb_external_resume_device+0xec/0x180)

    r5:00000000 r4:cf9f4c00

    [<c0238164>] (usb_external_resume_device+0x0/0x180) from [<c0238304>] (usb_resume+0x20/0x30)

    2. selective-suspend=OFF, enable_off_mode/sleep_while_idle =OFF

    everything works fine.

    3. selective-suspend=ON, enable_off_mode/sleep_while_idle =ON

    susprisingly everything works fine.

    4. selective-suspend=ON, enable_off_mode/sleep_while_idle=OFF

    susprisingly EHCI port doesn't work, and we get below message,

    > Hub 1-0:1.0: unable to enumerate USB device on port 2.

    > Hub 1-0:1.0: Cannot enable port 2, Maybe the USB cable is bad?

    Signed-off-by: Ajay Kumar Gupta <ajay.gupta@ti.com>

    ---

    drivers/usb/host/ehci-omap.c | 167 ++++++++++++++++++++++++++++++++++++++----

    1 files changed, 152 insertions(+), 15 deletions(-)

    diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c

    index 8a0bc07..97ce7d5 100644

    --- a/drivers/usb/host/ehci-omap.c

    +++ b/drivers/usb/host/ehci-omap.c

    @@ -41,6 +41,7 @@

     

    struct usb_hcd *ghcd;

     

    +struct ehci_hcd_omap *gb_omap;

    /*

    * OMAP USBHOST Register addresses: VIRTUAL ADDRESSES

    * Use ehci_omap_readl()/ehci_omap_writel() functions

    @@ -50,7 +51,10 @@ struct usb_hcd *ghcd;

    #define OMAP_USBTLL_REVISION (0x00)

    #define OMAP_USBTLL_SYSCONFIG (0x10)

    #define OMAP_USBTLL_SYSCONFIG_CACTIVITY (1 << 8)

    -#define OMAP_USBTLL_SYSCONFIG_SIDLEMODE (1 << 3)

    +#define OMAP_USBTLL_SYSCONFIG_S_SMART_IDLE (2 << 3)

    +#define OMAP_USBTLL_SYSCONFIG_S_NO_IDLE (1 << 3)

    +#define OMAP_USBTLL_SYSCONFIG_S_FORCED_IDLE (0 << 3)

    +#define OMAP_USBTLL_SYSCONFIG_SIDLEMASK (3 << 3)

    #define OMAP_USBTLL_SYSCONFIG_ENAWAKEUP (1 << 2)

    #define OMAP_USBTLL_SYSCONFIG_SOFTRESET (1 << 1)

    #define OMAP_USBTLL_SYSCONFIG_AUTOIDLE (1 << 0)

    @@ -93,9 +97,15 @@ struct usb_hcd *ghcd;

    /* UHH Register Set */

    #define OMAP_UHH_REVISION (0x00)

    #define OMAP_UHH_SYSCONFIG (0x10)

    -#define OMAP_UHH_SYSCONFIG_MIDLEMODE (1 << 12)

    +#define OMAP_UHH_SYSCONFIG_M_SMART_STDBY (2 << 12)

    +#define OMAP_UHH_SYSCONFIG_M_NO_STDBY (1 << 12)

    +#define OMAP_UHH_SYSCONFIG_M_FORCED_STDBY (0 << 12)

    +#define OMAP_UHH_SYSCONFIG_MIDLEMASK (3 << 12)

    #define OMAP_UHH_SYSCONFIG_CACTIVITY (1 << 8)

    -#define OMAP_UHH_SYSCONFIG_SIDLEMODE (1 << 3)

    +#define OMAP_UHH_SYSCONFIG_S_SMART_IDLE (2 << 3)

    +#define OMAP_UHH_SYSCONFIG_S_NO_IDLE (1 << 3)

    +#define OMAP_UHH_SYSCONFIG_S_FORCED_IDLE (0 << 3)

    +#define OMAP_UHH_SYSCONFIG_SIDLEMASK (3 << 3)

    #define OMAP_UHH_SYSCONFIG_ENAWAKEUP (1 << 2)

    #define OMAP_UHH_SYSCONFIG_SOFTRESET (1 << 1)

    #define OMAP_UHH_SYSCONFIG_AUTOIDLE (1 << 0)

    @@ -158,6 +168,7 @@ struct ehci_hcd_omap {

    struct clk *usbhost1_48m_fck;

    struct clk *usbtll_fck;

    struct clk *usbtll_ick;

    + unsigned suspended:1;

     

    /* FIXME the following two workarounds are

    * board specific not silicon-specific so these

    @@ -476,20 +487,21 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)

     

    dev_dbg(omap->dev, "TLL RESET DONE\n");

     

    - /* (1<<3) = no idle mode only for initial debugging */

    - ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG,

    - OMAP_USBTLL_SYSCONFIG_ENAWAKEUP |

    - OMAP_USBTLL_SYSCONFIG_SIDLEMODE |

    - OMAP_USBTLL_SYSCONFIG_CACTIVITY);

    -

    + /* Enable smart-idle, wakeup */

    + reg = OMAP_USBTLL_SYSCONFIG_CACTIVITY

    + | OMAP_USBTLL_SYSCONFIG_AUTOIDLE

    + | OMAP_USBTLL_SYSCONFIG_ENAWAKEUP

    + | OMAP_USBTLL_SYSCONFIG_S_SMART_IDLE;

    + ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, reg);

     

    - /* Put UHH in NoIdle/NoStandby mode */

    + /* Put UHH in smart Idle/Standby mode */

    reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSCONFIG);

    - reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP

    - | OMAP_UHH_SYSCONFIG_SIDLEMODE

    - | OMAP_UHH_SYSCONFIG_CACTIVITY

    - | OMAP_UHH_SYSCONFIG_MIDLEMODE);

    - reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE;

    + reg |= OMAP_UHH_SYSCONFIG_CACTIVITY

    + | OMAP_UHH_SYSCONFIG_AUTOIDLE

    + | OMAP_UHH_SYSCONFIG_ENAWAKEUP;

    + reg &= ~(OMAP_UHH_SYSCONFIG_SIDLEMASK | OMAP_UHH_SYSCONFIG_MIDLEMASK);

    + reg |= OMAP_UHH_SYSCONFIG_S_SMART_IDLE

    + | OMAP_UHH_SYSCONFIG_M_SMART_STDBY;

     

    ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg);

     

    @@ -686,7 +698,125 @@ static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)

    dev_dbg(omap->dev, "Clock to USB host has been disabled\n");

    }

     

    +#ifdef CONFIG_PM

    /*-------------------------------------------------------------------------*/

    +static int omap_ehci_bus_suspend(struct usb_hcd *hcd)

    +{

    + struct ehci_hcd_omap *omap = gb_omap;

    + int ret = 0;

    + unsigned reg = 0;

    +

    + ret = ehci_bus_suspend(hcd);

    +

    + if (!omap->suspended) {

    + /* Enable forced idle mode */

    + reg = ehci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSCONFIG);

    + reg &= ~OMAP_USBTLL_SYSCONFIG_SIDLEMASK;

    + reg |= OMAP_USBTLL_SYSCONFIG_S_FORCED_IDLE;

    + ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, reg);

    +

    + /* Enable forced Idle/Standby mode */

    + reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSCONFIG);

    + reg &= ~(OMAP_UHH_SYSCONFIG_SIDLEMASK

    + | OMAP_UHH_SYSCONFIG_MIDLEMASK);

    + reg |= OMAP_UHH_SYSCONFIG_S_FORCED_IDLE

    + | OMAP_UHH_SYSCONFIG_M_FORCED_STDBY;

    + ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg);

    +

    + clk_disable(omap->usbhost1_48m_fck);

    + clk_put(omap->usbhost1_48m_fck);

    + omap->usbhost1_48m_fck = NULL;

    +

    + clk_disable(omap->usbhost2_120m_fck);

    + clk_put(omap->usbhost2_120m_fck);

    + omap->usbhost2_120m_fck = NULL;

    +

    + clk_disable(omap->usbtll_fck);

    + clk_put(omap->usbtll_fck);

    + omap->usbtll_fck = NULL;

    +

    + omap->suspended = 1;

    + }

    +

    + return ret;

    +}

    +

    +static int omap_ehci_bus_resume(struct usb_hcd *hcd)

    +{

    + struct ehci_hcd_omap *omap = gb_omap;

    + int ret = 0;

    + unsigned reg = 0;

    +

    + if (omap->suspended) {

    + omap->usbtll_fck = clk_get(omap->dev, "usbtll_fck");

    + if (IS_ERR(omap->usbtll_fck)) {

    + ret = PTR_ERR(omap->usbtll_fck);

    + goto clk_error;

    + }

    + clk_enable(omap->usbtll_fck);

    +

    + omap->usbhost2_120m_fck =

    + clk_get(omap->dev, "usbhost_120m_fck");

    + if (IS_ERR(omap->usbhost2_120m_fck)) {

    + ret = PTR_ERR(omap->usbhost2_120m_fck);

    + goto clk_error_120m_fck;

    + }

    + clk_enable(omap->usbhost2_120m_fck);

    +

    + omap->usbhost1_48m_fck = clk_get(omap->dev, "usbhost_48m_fck");

    + if (IS_ERR(omap->usbhost1_48m_fck)) {

    + ret = PTR_ERR(omap->usbhost1_48m_fck);

    + goto clk_error_48m_fck;

    + }

    + clk_enable(omap->usbhost1_48m_fck);

    +

    + /* Enable smart idle mode */

    + reg = ehci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSCONFIG);

    + reg &= ~OMAP_USBTLL_SYSCONFIG_SIDLEMASK;

    + reg |= OMAP_USBTLL_SYSCONFIG_S_SMART_IDLE;

    + ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, reg);

    +

    + /* Enable smart Idle/Standby mode */

    + reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSCONFIG);

    + reg &= ~(OMAP_UHH_SYSCONFIG_SIDLEMASK

    + | OMAP_UHH_SYSCONFIG_MIDLEMASK);

    + reg |= OMAP_UHH_SYSCONFIG_S_SMART_IDLE

    + | OMAP_UHH_SYSCONFIG_M_SMART_STDBY;

    + ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg);

    +

    + omap->suspended = 0;

    + }

    + ret = ehci_bus_resume(hcd);

    +

    + /* reset the EHCI PHY */

    + if (omap->phy_reset) {

    + if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY &&

    + gpio_is_valid(omap->reset_gpio_port[0])) {

    + gpio_set_value(omap->reset_gpio_port[1], 0);

    + udelay(30);

    + gpio_set_value(omap->reset_gpio_port[1], 1);

    + }

    + if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY &&

    + gpio_is_valid(omap->reset_gpio_port[1])) {

    + gpio_set_value(omap->reset_gpio_port[1], 0);

    + udelay(30);

    + gpio_set_value(omap->reset_gpio_port[1], 1);

    + }

    + }

    + return ret;

    +

    +clk_error_48m_fck:

    + clk_disable(omap->usbhost2_120m_fck);

    + clk_put(omap->usbhost2_120m_fck);

    + omap->usbhost2_120m_fck = NULL;

    +clk_error_120m_fck:

    + clk_disable(omap->usbtll_fck);

    + clk_put(omap->usbtll_fck);

    + omap->usbtll_fck = NULL;

    +clk_error:

    + return ret;

    +}

    +#endif

     

    static const struct hc_driver ehci_omap_hc_driver;

     

    @@ -753,6 +883,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)

    omap->port_mode[2] = pdata->port_mode[2];

    omap->ehci = hcd_to_ehci(hcd);

    omap->ehci->sbrn = 0x20;

    + gb_omap = omap;

    + omap->suspended = 0;

     

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

     

    @@ -926,8 +1058,13 @@ static const struct hc_driver ehci_omap_hc_driver = {

    */

    .hub_status_data = ehci_hub_status_data,

    .hub_control = ehci_hub_control,

    +#ifdef CONFIG_PM

    + .bus_suspend = omap_ehci_bus_suspend,

    + .bus_resume = omap_ehci_bus_resume,

    +#else

    .bus_suspend = ehci_bus_suspend,

    .bus_resume = ehci_bus_resume,

    +#endif

    .relinquish_port = ehci_relinquish_port,

    .port_handed_over = ehci_port_handed_over,

     

    --

    1.6.2.4

     

    -----------------------