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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html