usb: hub: Power-cycle on root-hub ports
authorVivek Gautam <gautam.vivek@samsung.com>
Fri, 12 Apr 2013 11:04:35 +0000 (16:34 +0530)
committerMarek Vasut <marex@denx.de>
Sun, 5 May 2013 21:54:22 +0000 (23:54 +0200)
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

index f2a02854faa13fb4d727be5dd2092d5e07bad05f..e4f4e3cd32e0a84bb25c915ecfe62e9fc44ac323 100644 (file)
@@ -100,11 +100,45 @@ 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 */
        debug("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);
+               debug("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) {
+                       debug("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)) {
+                       debug("port %d: Port power change failed\n", i + 1);
+                       return;
+               }
+
                usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
                debug("port %d returns %lX\n", i + 1, dev->status);
        }