summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorJun Li <r65092@freescale.com>2009-12-01 21:13:48 +0800
committerAlejandro Gonzalez <alex.gonzalez@digi.com>2010-02-12 17:19:42 +0100
commit537d1eef7468cbd391c40822277f53d819261ca1 (patch)
tree413a79ae0dd5e3609c3515ef339d050b46e854c8 /drivers/usb
parent812878d9c1822a18a9383234f5f2f4fba3375676 (diff)
ENGR00117147-1 USB clock gating and PHY low power mode.
If there is no usb devices connectted or all connectted usb devices are in suspend state, usb host can suspend its whole bus, then put the PHY into low power mode and close all usb clocks. (The patch is splitted 2 patches, this is common code part.) Signed-off-by: Li Jun <r65092@freescale.com>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/core/generic.c38
-rw-r--r--drivers/usb/core/hcd.c10
-rw-r--r--drivers/usb/core/hub.c7
-rw-r--r--drivers/usb/host/ehci-hub.c9
4 files changed, 50 insertions, 14 deletions
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 30ecac3af15a..66e8a424c9f4 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -20,6 +20,8 @@
#include <linux/usb.h>
#include "usb.h"
#include "hcd.h"
+#include <linux/io.h>
+#include <linux/fsl_devices.h>
static inline const char *plural(int n)
{
@@ -190,20 +192,40 @@ static void generic_disconnect(struct usb_device *udev)
#ifdef CONFIG_PM
+extern void usb_host_set_wakeup(struct device *wkup_dev, bool para);
static int generic_suspend(struct usb_device *udev, pm_message_t msg)
{
int rc;
+ u32 temp;
/* Normal USB devices suspend through their upstream port.
* Root hubs don't have upstream ports to suspend,
* so we have to shut down their downstream HC-to-USB
* interfaces manually by doing a bus (or "global") suspend.
*/
- if (!udev->parent)
+ if (!udev->parent) {
+ struct usb_hcd *hcd =
+ container_of(udev->bus, struct usb_hcd, self);
+ struct fsl_usb2_platform_data *pdata;
+ pdata = hcd->self.controller->platform_data;
+
rc = hcd_bus_suspend(udev, msg);
+ if (device_may_wakeup(hcd->self.controller)) {
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ /* enable remote wake up irq */
+ usb_host_set_wakeup(hcd->self.controller, true);
+
+ /* Put PHY into low power mode */
+ temp = readl(hcd->regs + 0x184);
+ writel(temp | (1 << 23), (hcd->regs + 0x184));
+
+ if (pdata->usb_clock_for_pm)
+ pdata->usb_clock_for_pm(false);
+ }
/* Non-root devices don't need to do anything for FREEZE or PRETHAW */
- else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
+ } else if (msg.event == PM_EVENT_FREEZE ||
+ msg.event == PM_EVENT_PRETHAW)
rc = 0;
else
rc = usb_port_suspend(udev, msg);
@@ -214,15 +236,23 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg)
static int generic_resume(struct usb_device *udev, pm_message_t msg)
{
int rc;
+ u32 temp;
/* Normal USB devices resume/reset through their upstream port.
* Root hubs don't have upstream ports to resume or reset,
* so we have to start up their downstream HC-to-USB
* interfaces manually by doing a bus (or "global") resume.
*/
- if (!udev->parent)
+ if (!udev->parent) {
+ struct usb_hcd *hcd =
+ container_of(udev->bus, struct usb_hcd, self);
+
+ if (device_may_wakeup(hcd->self.controller)) {
+ temp = readl(hcd->regs + 0x184);
+ writel(temp & (~(1 << 23)), (hcd->regs + 0x184));
+ }
rc = hcd_bus_resume(udev, msg);
- else
+ } else
rc = usb_port_resume(udev, msg);
return rc;
}
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index d622f7099a7d..d27ad104731c 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -40,6 +40,7 @@
#include <linux/workqueue.h>
#include <linux/usb.h>
+#include <linux/fsl_devices.h>
#include "usb.h"
#include "hcd.h"
@@ -117,10 +118,8 @@ static inline int is_root_hub(struct usb_device *udev)
return (udev->parent == NULL);
}
-#if CONFIG_PM
extern int usb_host_wakeup_irq(struct device *wkup_dev);
extern void usb_host_set_wakeup(struct device *wkup_dev, bool para);
-#endif
/*-------------------------------------------------------------------------*/
/*
@@ -1877,6 +1876,7 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum);
irqreturn_t usb_hcd_irq (int irq, void *__hcd)
{
struct usb_hcd *hcd = __hcd;
+ struct fsl_usb2_platform_data *pdata;
unsigned long flags;
irqreturn_t rc;
@@ -1887,10 +1887,16 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
local_irq_save(flags);
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ /* Need open clock for register access */
+ pdata = hcd->self.controller->platform_data;
+ if (pdata->usb_clock_for_pm)
+ pdata->usb_clock_for_pm(true);
+
/* if receive a remote wakeup interrrupt after suspend */
if (usb_host_wakeup_irq(hcd->self.controller)) {
/* disable remote wake up irq */
usb_host_set_wakeup(hcd->self.controller, false);
+
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
hcd->driver->irq(hcd);
rc = IRQ_HANDLED;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index b3f934af7f22..64ad4fe3f1f0 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1170,13 +1170,10 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
return -E2BIG;
}
- /* With OTG enabled, suspending root hub results in gadget not
- * working because gadget uses the same root hub. We disable
- * this feature when OTG is selected.
+ /* Defaultly disable autosuspend for hub and reley on sys
+ * to enable it.
*/
-#if defined(CONFIG_PM) && defined(CONFIG_USB_EHCI_ARC_OTG)
hdev->autosuspend_disabled = 1;
-#endif
#ifdef CONFIG_USB_OTG_BLACKLIST_HUB
if (hdev->parent) {
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 615d263c57c1..de459bbd1eb1 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -151,9 +151,12 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
}
/* enable remote wakeup on all ports */
- if (hcd->self.root_hub->do_remote_wakeup)
- t2 |= PORT_WAKE_BITS;
- else
+ if (hcd->self.root_hub->do_remote_wakeup) {
+ if (t1 & PORT_CONNECT)
+ t2 |= PORT_WKOC_E|PORT_WKDISC_E;
+ else
+ t2 |= PORT_WKOC_E|PORT_WKCONN_E;
+ } else
t2 &= ~PORT_WAKE_BITS;
if (t1 != t2) {