summaryrefslogtreecommitdiff
path: root/drivers/usb/core/hub.c
diff options
context:
space:
mode:
authorAndiry Xu <andiry.xu@amd.com>2011-04-27 18:07:43 +0800
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2011-05-02 16:42:53 -0700
commit0ed9a57e052a3d20df052a2ff12a3b42380867aa (patch)
tree3e9f2abb102045cbd162a7ebe8563d4e9ee47148 /drivers/usb/core/hub.c
parent2c44178032b046c4113c40d0d459a0d36e39b920 (diff)
xHCI: report USB3.0 portstatus comply with USB3.0 specification
USB3.0 specification has different wPortStatus and wPortChange definitions from USB2.0 specification. Since USB3 root hub and USB2 root hub are split now and USB3 hub only has USB3 protocol ports, we should modify the portstatus and portchange report of USB3 ports to comply with USB3.0 specification. Signed-off-by: Andiry Xu <andiry.xu@amd.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r--drivers/usb/core/hub.c52
1 files changed, 37 insertions, 15 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 93720bdc9efd..dcd78c15f8cd 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -379,15 +379,6 @@ static int hub_port_status(struct usb_hub *hub, int port1,
*status = le16_to_cpu(hub->status->port.wPortStatus);
*change = le16_to_cpu(hub->status->port.wPortChange);
- if ((hub->hdev->parent != NULL) &&
- hub_is_superspeed(hub->hdev)) {
- /* Translate the USB 3 port status */
- u16 tmp = *status & USB_SS_PORT_STAT_MASK;
- if (*status & USB_SS_PORT_STAT_POWER)
- tmp |= USB_PORT_STAT_POWER;
- *status = tmp;
- }
-
ret = 0;
}
mutex_unlock(&hub->status_mutex);
@@ -2160,11 +2151,40 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
return status;
}
+/* Check if a port is power on */
+static int port_is_power_on(struct usb_hub *hub, unsigned portstatus)
+{
+ int ret = 0;
+
+ if (hub_is_superspeed(hub->hdev)) {
+ if (portstatus & USB_SS_PORT_STAT_POWER)
+ ret = 1;
+ } else {
+ if (portstatus & USB_PORT_STAT_POWER)
+ ret = 1;
+ }
+
+ return ret;
+}
+
#ifdef CONFIG_PM
-#define MASK_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION | \
- USB_PORT_STAT_SUSPEND)
-#define WANT_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION)
+/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */
+static int port_is_suspended(struct usb_hub *hub, unsigned portstatus)
+{
+ int ret = 0;
+
+ if (hub_is_superspeed(hub->hdev)) {
+ if ((portstatus & USB_PORT_STAT_LINK_STATE)
+ == USB_SS_PORT_LS_U3)
+ ret = 1;
+ } else {
+ if (portstatus & USB_PORT_STAT_SUSPEND)
+ ret = 1;
+ }
+
+ return ret;
+}
/* Determine whether the device on a port is ready for a normal resume,
* is ready for a reset-resume, or should be disconnected.
@@ -2174,7 +2194,9 @@ static int check_port_resume_type(struct usb_device *udev,
int status, unsigned portchange, unsigned portstatus)
{
/* Is the device still present? */
- if (status || (portstatus & MASK_BITS) != WANT_BITS) {
+ if (status || port_is_suspended(hub, portstatus) ||
+ !port_is_power_on(hub, portstatus) ||
+ !(portstatus & USB_PORT_STAT_CONNECTION)) {
if (status >= 0)
status = -ENODEV;
}
@@ -2439,7 +2461,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
/* Skip the initial Clear-Suspend step for a remote wakeup */
status = hub_port_status(hub, port1, &portstatus, &portchange);
- if (status == 0 && !(portstatus & USB_PORT_STAT_SUSPEND))
+ if (status == 0 && !port_is_suspended(hub, portstatus))
goto SuspendCleared;
// dev_dbg(hub->intfdev, "resume port %d\n", port1);
@@ -3147,7 +3169,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
/* maybe switch power back on (e.g. root hub was reset) */
if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
- && !(portstatus & USB_PORT_STAT_POWER))
+ && !port_is_power_on(hub, portstatus))
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
if (portstatus & USB_PORT_STAT_ENABLE)