diff mbox

[v2,2/5] usb: hub: Power-cycle on root-hub ports

Message ID 1365417358-31921-3-git-send-email-gautam.vivek@samsung.com
State New
Headers show

Commit Message

Vivek Gautam April 8, 2013, 10:35 a.m. UTC
XHCI ports are powered on after a H/W reset, however
EHCI ports are not. So disabling and re-enabling power
on all ports invariably.

Signed-off-by: Amar <amarendra.xt@samsung.com>
Signed-off-by: Vivek Gautam <gautam.vivek@samsung.com>
---
 common/usb_hub.c |   36 ++++++++++++++++++++++++++++++++++++
 1 files changed, 36 insertions(+), 0 deletions(-)
diff mbox

Patch

diff --git a/common/usb_hub.c b/common/usb_hub.c
index b5eeb62..4bfed09 100644
--- a/common/usb_hub.c
+++ b/common/usb_hub.c
@@ -111,11 +111,47 @@  static void usb_hub_power_on(struct usb_hub_device *hub)
 	int i;
 	struct usb_device *dev;
 	unsigned pgood_delay = hub->desc.bPwrOn2PwrGood * 2;
+	ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1);
+	unsigned short portstatus;
+	int ret;
 
 	dev = hub->pusb_dev;
 	/* Enable power to the ports */
 	USB_HUB_PRINTF("enabling power on all ports\n");
 	for (i = 0; i < dev->maxchild; i++) {
+		/*
+		 * Power-cycle the ports here: aka,
+		 * turning them off and turning on again.
+		 */
+		usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
+		USB_HUB_PRINTF("port %d returns %lX\n", i + 1, dev->status);
+
+		/* Wait at least 2*bPwrOn2PwrGood for PP to change */
+		mdelay(pgood_delay);
+
+		ret = usb_get_port_status(dev, i + 1, portsts);
+		if (ret < 0) {
+			USB_HUB_PRINTF("port %d: get_port_status failed\n",
+					i + 1);
+			return;
+		}
+
+		/*
+		 * Check to confirm the state of Port Power:
+		 * xHCI says "After modifying PP, s/w shall read
+		 * PP and confirm that it has reached the desired state
+		 * before modifying it again, undefined behavior may occur
+		 * if this procedure is not followed".
+		 * EHCI doesn't say anything like this, but no harm in keeping
+		 * this.
+		 */
+		portstatus = le16_to_cpu(portsts->wPortStatus);
+		if (portstatus & (USB_PORT_STAT_POWER << 1)) {
+			USB_HUB_PRINTF("port %d: Port power change failed\n",
+					i + 1);
+			return;
+		}
+
 		usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
 		USB_HUB_PRINTF("port %d returns %lX\n", i + 1, dev->status);
 	}