xhci: tegra: fix for usb2 utmi pad power saving
TW Chiu [Mon, 2 Nov 2015 08:11:04 +0000 (16:11 +0800)]
USB2 pad PD bits can be set to save power when device is disconnected
or suspended. When device is connected or in resume state, we need to
clear these PD bits.

During boot, there is a case that CSC bit is cleared by hub init
function. This causes PD bits set and failures in port reset and set
address.

Bug 200146188

Change-Id: Id12deb97689ef08b3e9236124c2fc4775b039c3c
Signed-off-by: TW Chiu <twchiu@nvidia.com>
Reviewed-on: http://git-master/r/826136
(cherry picked from commit b4d127dc90fd4aaf89921ece952a4251bdee9076)
Reviewed-on: http://git-master/r/829411
GVS: Gerrit_Virtual_Submit
Reviewed-by: Ashutosh Jha <ajha@nvidia.com>
Reviewed-by: Ajay Gupta <ajayg@nvidia.com>

drivers/usb/host/xhci-tegra.c

index 7ca548a..8415b71 100644 (file)
@@ -4134,8 +4134,8 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req,
 
        ret = xhci_hub_control(hcd, type_req, value, index, buf, length);
 
-       /* power off after port suspend */
        if ((hcd->speed == HCD_USB2) && (ret == 0)) {
+               /* power off after port suspend */
                if ((type_req == SetPortFeature) &&
                        (value == USB_PORT_FEAT_SUSPEND))
                        /* We dont suspend the PAD while HNP role swap happens
@@ -4146,6 +4146,26 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req,
                            hcd->self.otg_quick_hnp))) {
                                xusb_utmi_pad_driver_power(port, false);
                        }
+
+               /* power on/off after CSC clear for connect/disconnect event */
+               if ((type_req == ClearPortFeature) &&
+                       (value == USB_PORT_FEAT_C_CONNECTION)) {
+                       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+                       u32 portsc = xhci_readl(xhci, xhci->usb2_ports[port]);
+
+                       if (portsc & PORT_CONNECT)
+                               xusb_utmi_pad_driver_power(port, true);
+                       else {
+                               /* We dont suspend the PAD while HNP
+                                * role swap happens on the OTG port
+                                */
+                               if (!((hcd->self.otg_port == (port + 1))
+                                       && (hcd->self.b_hnp_enable ||
+                                       hcd->self.otg_quick_hnp)))
+                                       xusb_utmi_pad_driver_power(
+                                                       port, false);
+                       }
+               }
        }
 
        return ret;
@@ -4166,22 +4186,6 @@ static int tegra_xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
                        /* power on for remote wakeup event */
                        if ((portsc & PORT_PLS_MASK) == XDEV_RESUME)
                                xusb_utmi_pad_driver_power(port, true);
-
-                       /* power on/off for connect/disconnect event */
-                       if (portsc & PORT_CSC) {
-                               if (portsc & PORT_CONNECT)
-                                       xusb_utmi_pad_driver_power(port, true);
-                               else {
-                                       /* We dont suspend the PAD while HNP
-                                        * role swap happens on the OTG port
-                                        */
-                                       if (!((hcd->self.otg_port == (port + 1))
-                                               && (hcd->self.b_hnp_enable ||
-                                               hcd->self.otg_quick_hnp)))
-                                               xusb_utmi_pad_driver_power(
-                                                               port, false);
-                               }
-                       }
                }
        }