RE: Reg: USB: ehci-omap: Suspend the controller during idle.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Dear All,

With continuation to below mail, our client requirement is pushing us to implement the EHCI runtime suspend/resume.

I have implemented EHCI runtime suspend/resume using timer concept,Please find the below design and attached patch for the same.

Design Approach:
     - A timer of 10sec is scheduled.
     - During EHCI transaction (tx/rx), this timer is reset.
       If the EHCI clocks are switched off, it is switched on before any actual
       transaction.
     - If there are no transaction on EHCI for >10secs, the timer callback is invoked.
     - EHCI clocks are disabled in the timer expire function.

NOTE : Runtime remote wake up feature need to be implement.

With the above approach the ECHI is suspended,if there are no transaction for > 10secs,during next transaction
i am enabling the clocks and resuming the EHCI controller, but we are getting below errors and its not working properly

[   66.986175] catalina_wwan_ctrl_attr_wwan_on_show: wwan_on status = 1
[   66.993743] ehci-omap ehci-omap.0: ehci_omap_bus_resume
[   66.999542] ehci-omap ehci-omap.0: resume root hub
[   67.024475] ehci_bus_resume: port_status[2]=0x501000, PORT_SUSPEND=0
[   67.031311] ehci_bus_resume: port_status[1]=0x501000, PORT_SUSPEND=0
[   67.038238] ehci_bus_resume: port_status[0]=0x601885, PORT_SUSPEND=128
[   67.079223] ehci-omap ehci-omap.0: detected XactErr len 0/10 retry 1
[   67.086090] ehci-omap ehci-omap.0: detected XactErr len 0/512 retry 1
[   67.093139] ehci-omap ehci-omap.0: detected XactErr len 0/10 retry 2
[   67.099975] ehci-omap ehci-omap.0: detected XactErr len 0/512 retry 2
[   67.106994] ehci-omap ehci-omap.0: detected XactErr len 0/10 retry 3
[   67.113830] ehci-omap ehci-omap.0: detected XactErr len 0/512 retry 3
[   67.120819] ehci-omap ehci-omap.0: detected XactErr len 0/10 retry 4

Can you please guide me why we are getting above errors.

Regards
Bharath

________________________________________
From: Roger Quadros [rogerq@xxxxxx]
Sent: Tuesday, August 06, 2013 7:57 PM
To: Bharathraj Nagaraju
Cc: linux-omap@xxxxxxxxxxxxxxx; linux-usb@xxxxxxxxxxxxxxx; Kevin Hilman; Alan Stern
Subject: Re: Reg: USB: ehci-omap: Suspend the controller during idle.

Hi,

On 08/06/2013 03:55 PM, Bharathraj Nagaraju wrote:
>   Dear All,
>
>   We are working on omap4470 based device,kernel-3.0.31 is running on this.
>   In our device modem is connected to OMAP4470 using USB EHCI.
>
>   In our current kernel code, clocks are enabled when ports get connected
>   and disabled during disconnect.Due to this current consumption is more in idle use case.
>
>   I have tried to put the host in smart idle/standby mode,due to erratas around Host doesn't allow us
>   use smart idle/standby capabilities.
>
>   1) i571: USB host EHCI may stall when entering smart-standby mode
>   2) i660: USBHOST Configured In Smart-Idle Can Lead To a Deadlock
>
>   we are thinking to implement USB EHCI runtime suspend/resume feature.
>
>   In kernel-3.0.31 architecture the usbcore driver is parent of ehci and ohci drivers.
>   The ehci and ohci drivers call the pm_runtime_get_sync and pm_runtime_put_sync of parent device usbhs core.
>
>   From the ehci-omap driver what i can see only partial implementation of runtime PM.
>   what i mean is that runtime PM methods are used only during the normal suspend path
>   and when nothing is connected to ports(modem disconnected in our case).
>
>   I am thinking to change the ehci driver code, could claver enough to use runtime PM
>   to disable the hardware when modem is connected and the system is in idle state.
>
>   Kindly guide me how can i go about this.
>
>   Let me know if you need further clarification from me on this.
>
>   Thanks for spending your valuable time .......:)


The problem with OMAP EHCI controller is that it needs to rely on IO Daisy chain mechanism
to wake up when it is suspended. Without that, we can't really put the controller in to runtime
suspend, else we will not be able to detect a new device connect.

IO daisy chaining is still not supported in mainline. One approach had been posted [1] and it works
but it is still in transition.

I've made some patches [2] which implement USB runtime suspend with remote wakeup working
for OMAP3 beagle-xm board. This depends on [1]. It shouldn't be hard to get it working on OMAP4.

[1] http://article.gmane.org/gmane.linux.ports.arm.omap/101105
[2] http://article.gmane.org/gmane.linux.ports.arm.kernel/251250

cheers,
-roger


________________________________

SASKEN BUSINESS DISCLAIMER: This message may contain confidential, proprietary or legally privileged information. In case you are not the original intended Recipient of the message, you must not, directly or indirectly, use, disclose, distribute, print, or copy any part of this message and you are requested to delete it and inform the sender. Any views expressed in this message are those of the individual sender unless otherwise stated. Nothing contained in this message shall be construed as an offer or acceptance of any offer by Sasken Communication Technologies Limited ("Sasken") unless sent with that express intent and with due authority of Sasken. Sasken has taken enough precautions to prevent the spread of viruses. However the company accepts no liability for any damage caused by any virus transmitted by this email.
Read Disclaimer at http://www.sasken.com/extras/mail_disclaimer.html
From 5fbe17aa3724be957e90ac570a1085c61df31f94 Mon Sep 17 00:00:00 2001
From: bharath <bharathraj.nagaraju@xxxxxxxxxx>
Date: Mon, 26 Aug 2013 16:18:18 +0530
Subject: [PATCH] [EHCI] Selective suspend/resume implementation - Draft
 Revision 0.1

Issue:
 In system Idle case, the EHCI related clocks are kept ON.
 This consumes current in that condition.

 Dyanmic means of suspending the EHCI clocks should be done
 in system idle condition to save more power.

 Note:
   In system suspend path, the EHCI clocks are disabled and enabled during resume.
   This ensure the current consumption is NOT affected in the suspend use case.

Design Approach:
 - A timer of 10sec is scheduled.
 - During EHCI transcation (tx/rx), this timer is reset.
    If the EHCI clocks are swtiched off, it is switched on before any actual
    transaction.
 - If there are no transaction on EHCI for >10secs, the timer callback is invoked.
 - EHCI clocks are disabled in the timer expire function.

Signed-off-by: bharath <bharathraj.nagaraju@xxxxxxxxxx>
---
 drivers/usb/host/ehci-hcd.c  |   23 +++++++++++
 drivers/usb/host/ehci-omap.c |   90 ++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/ehci.h      |    6 +++
 3 files changed, 119 insertions(+)
 mode change 100644 => 100755 drivers/usb/host/ehci-hcd.c
 mode change 100644 => 100755 drivers/usb/host/ehci.h

diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
old mode 100644
new mode 100755
index be0fad2..a980b5d
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -413,6 +413,15 @@ static void ehci_watchdog(unsigned long param)
 	spin_unlock_irqrestore (&ehci->lock, flags);
 }
 
+
+static void ehci_timer_func (unsigned long _ehci)
+{
+        struct ehci_hcd          *ehci = (struct ehci_hcd *)_ehci;
+        struct usb_hcd *hcd =ehci_to_hcd(ehci);
+
+	    ehci_omap_bus_clk_disable(hcd);
+}
+
 /* On some systems, leaving remote wakeup enabled prevents system shutdown.
  * The firmware seems to think that powering off is a wakeup event!
  * This routine turns off remote wakeup and everything else, on all ports.
@@ -580,6 +589,12 @@ static int ehci_init(struct usb_hcd *hcd)
 	ehci->iaa_watchdog.function = ehci_iaa_watchdog;
 	ehci->iaa_watchdog.data = (unsigned long) ehci;
 
+	init_timer(&ehci->ehci_timer);
+    ehci->ehci_timer.function = ehci_timer_func;
+    ehci->ehci_timer.data = (unsigned long) ehci;
+
+    mod_timer(&ehci->ehci_timer, jiffies + msecs_to_jiffies(300000));
+
 	hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
 
 	/*
@@ -974,6 +989,14 @@ static int ehci_urb_enqueue (
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
 	struct list_head	qtd_list;
 
+	if (ehci->ehci_state) {
+		ehci->ehci_state=0;
+		ehci_omap_bus_clk_enable(hcd);
+	} else {
+		del_timer (&ehci->ehci_timer);
+		mod_timer(&ehci->ehci_timer, jiffies + msecs_to_jiffies(10000));	
+	}
+
 	INIT_LIST_HEAD (&qtd_list);
 
 	switch (usb_pipetype (urb->pipe)) {
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index f2fcec5..bfa285d 100755
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -794,6 +794,14 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
 		}
 	}
 
+//	pm_runtime_enable(dev->parent);
+	pm_runtime_irq_safe(dev->parent);
+
+#if 0
+	spin_lock_irq(&dev->parent->power.lock);
+        dev->parent->power.irq_safe = 1;
+        spin_unlock_irq(&dev->parent->power.lock);
+#endif
 	pm_runtime_get_sync(dev->parent);
 	*pdata->usbhs_update_sar = 1;
 
@@ -817,6 +825,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
 
 	omap_ehci = hcd_to_ehci(hcd);
 	omap_ehci->sbrn = 0x20;
+	omap_ehci->ehci_state =0;
 
 	wake_lock_init(&(omap_ehci->wakelock), WAKE_LOCK_SUSPEND,
 		"ehci_hcd-omap");
@@ -944,10 +953,85 @@ static void ehci_hcd_omap_shutdown(struct platform_device *pdev)
 	}
 }
 
+int ehci_omap_bus_clk_enable(struct usb_hcd *hcd)
+{
+        struct device *dev = hcd->self.controller;
+		struct ehci_hcd_omap_platform_data      *pdata = dev->platform_data;
+	    struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	    struct omap_hwmod       *oh;
+        struct clk *clk;
+        int i;
+
+        int ret;
+
+        dev_dbg(dev, "ehci_omap_bus_resume\n");
+	
+        oh = omap_hwmod_lookup(USBHS_EHCI_HWMODNAME);
+
+        ret = omap_hwmod_disable_ioring_wakeup(oh);
+
+        /* Re-enable any external transceiver clocks first */
+        pdata = dev->platform_data;
+        for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) {
+                clk = pdata->transceiver_clk[i];
+                if (clk)
+                        clk_enable(clk);
+        }
+
+        omap_pm_set_min_bus_tput(dev,
+                        OCP_INITIATOR_AGENT,
+                        (200*1000*4));
+
+        if (dev->parent)
+                pm_runtime_get_sync(dev->parent);
+
+       *pdata->usbhs_update_sar = 1;
+       return ehci_bus_resume(hcd);
+}
+EXPORT_SYMBOL(ehci_omap_bus_clk_enable);
+
+int ehci_omap_bus_clk_disable(struct usb_hcd *hcd)
+{
+       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+       struct device *dev = hcd->self.controller;
+       struct ehci_hcd_omap_platform_data  *pdata;
+       struct omap_hwmod       *oh;
+       struct clk *clk;
+       int ret = 0;
+       int i;
+
+	ehci->ehci_state =1;
+
+	ret = ehci_bus_suspend(hcd);
+
+	if (ret != 0) {
+               dev_dbg(dev, "ehci_omap_bus_suspend failed %d\n", ret);
+               return ret;
+       }
+       oh = omap_hwmod_lookup(USBHS_EHCI_HWMODNAME);
+
+       omap_hwmod_enable_ioring_wakeup(oh);
+
+       if (dev->parent)
+               pm_runtime_put_sync_suspend(dev->parent);
+
+        pdata = dev->platform_data;
+        for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) {
+		clk = pdata->transceiver_clk[i];
+		if (clk){
+			clk_disable(clk);
+               }
+        }
+
+        return ret;
+}
+EXPORT_SYMBOL(ehci_omap_bus_clk_disable);
+
 static int ehci_omap_bus_suspend(struct usb_hcd *hcd)
 {
 	struct device *dev = hcd->self.controller;
 	struct ehci_hcd_omap_platform_data  *pdata;
+        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
 	struct omap_hwmod	*oh;
 	struct clk *clk;
 	int ret = 0;
@@ -955,6 +1039,12 @@ static int ehci_omap_bus_suspend(struct usb_hcd *hcd)
 
 	dev_dbg(dev, "ehci_omap_bus_suspend\n");
 
+
+       if (pm_runtime_suspended(dev->parent))
+               return 0;
+
+	del_timer (&ehci->ehci_timer);
+
 	ret = ehci_bus_suspend(hcd);
 
 	if (ret != 0) {
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
old mode 100644
new mode 100755
index 1c818ed..145fe93
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -79,6 +79,7 @@ struct ehci_hcd {			/* one per controller */
 	struct ehci_qh		*reclaim;
 	struct ehci_qh		*qh_scan_next;
 	unsigned		scanning : 1;
+	unsigned                ehci_state;
 
 	/* periodic schedule support */
 #define	DEFAULT_I_TDPS		1024		/* some HCs can do less */
@@ -121,6 +122,8 @@ struct ehci_hcd {			/* one per controller */
 
 	struct timer_list	iaa_watchdog;
 	struct timer_list	watchdog;
+	struct timer_list       ehci_timer;     /* drives ehci-interuupt routine polling */
+	//struct delayed_work      runtime_ehci;    /* for runtime suspen n resume ehci */
 	unsigned long		actions;
 	unsigned		periodic_stamp;
 	unsigned		random_frame;
@@ -182,6 +185,9 @@ struct ehci_hcd {			/* one per controller */
 	struct otg_transceiver	*transceiver;
 };
 
+extern int ehci_omap_bus_clk_enable(struct usb_hcd *);
+extern int ehci_omap_bus_clk_disable(struct usb_hcd *);
+
 /* convert between an HCD pointer and the corresponding EHCI_HCD */
 static inline struct ehci_hcd *hcd_to_ehci (struct usb_hcd *hcd)
 {
-- 
1.7.9.5


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux