Hi Mathias,
In our SOC, xHCI controller has three ports. In that one is SS and two
HS ports, all ports are connected with separate phy controller.
We have a bug in HS phy controllers.
Because of this bug, HS phy controller needs to reset after
disconnected the device in the corresponding HS port.
I could not find any available mechanism or quirks in kernel to handle
these cases.
So, I did modifications in xhci-plat.c, xhci.c and xhci-hub.c files to
fix this issue.
But the modifications are not cleaner to upstream.
Please help me in the implementation of proper fix.
Below are the code changes.
---
drivers/usb/host/xhci-hub.c | 9 +++++++++
drivers/usb/host/xhci-plat.c | 11 +++++++++++
drivers/usb/host/xhci.c | 2 ++
drivers/usb/host/xhci.h | 1 +
4 files changed, 23 insertions(+)
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 0ef1690..3fdb07d 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -1193,6 +1193,15 @@ int xhci_hub_control(struct usb_hcd *hcd, u16
typeReq, u16 wValue,
case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
xhci_clear_port_change_bit(xhci, wValue, wIndex,
port_array[wIndex], temp);
+ if ((wValue == USB_PORT_FEAT_C_CONNECTION) &&
+ !(temp & PORT_CONNECT) &&
+ (hcd->speed < HCD_USB3)) {
+
+ hcd->driver->port_power(hcd,
+ wIndex +
+ xhci->num_usb3_ports,
+ false);
+ }
break;
case USB_PORT_FEAT_ENABLE:
xhci_disable_port(hcd, xhci, wIndex,
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 1a1d6b8..2e1facd 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -30,11 +30,13 @@ static struct hc_driver __read_mostly xhci_plat_hc_driver;
static int xhci_plat_setup(struct usb_hcd *hcd);
static int xhci_plat_start(struct usb_hcd *hcd);
+static int xhci_plat_portpower(struct usb_hcd *hcd, int portnum, bool enable);
static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
.extra_priv_size = sizeof(struct xhci_plat_priv),
.reset = xhci_plat_setup,
.start = xhci_plat_start,
+ .port_power = xhci_plat_portpower,
};
static void xhci_priv_plat_start(struct usb_hcd *hcd)
@@ -64,6 +66,15 @@ static void xhci_plat_quirks(struct device *dev,
struct xhci_hcd *xhci)
*/
xhci->quirks |= XHCI_PLAT;
}
+static int xhci_plat_portpower(struct usb_hcd *hcd, int portnum, bool enable)
+{
+ struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
+
+ if (enable == false)
+ phy_reset(priv->phys[portnum]);
+
+ return 0;
+}
/* called during probe() after chip reset completes */
static int xhci_plat_setup(struct usb_hcd *hcd)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 1a4ca02..a41c009 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -5028,6 +5028,8 @@ void xhci_init_driver(struct hc_driver *drv,
drv->reset = over->reset;
if (over->start)
drv->start = over->start;
+ if (over->port_power)
+ drv->port_power = over->port_power;
}
}
EXPORT_SYMBOL_GPL(xhci_init_driver);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index f945380..08c573c 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1692,6 +1692,7 @@ struct xhci_driver_overrides {
size_t extra_priv_size;
int (*reset)(struct usb_hcd *hcd);
int (*start)(struct usb_hcd *hcd);
+ int (*port_power)(struct usb_hcd *hcd, int portnum, bool enable);
};
#define XHCI_CFC_DELAY 10
--
2.7.4
Regards,
Srinath.
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html