From: Rafał Miłecki <zaj...@gmail.com>

This fixes a problem with the XHCI controller found in the Broadcom
Northstar SoCs. These controllers see the devices connected via USB
only in a small percentage of cold boots.
This quirk is also added to the platform data so we can activate it
when we register our platform driver.

Currently it is unknown to me what exactly is wrong in the XHCI controller.

Signed-off-by: Rafał Miłecki <zaj...@gmail.com>
Signed-off-by: Hauke Mehrtens <ha...@hauke-m.de>
---
 Documentation/devicetree/bindings/usb/usb-xhci.txt |  2 +
 drivers/usb/host/xhci-plat.c                       |  4 ++
 drivers/usb/host/xhci.c                            | 59 ++++++++++++++++++++--
 drivers/usb/host/xhci.h                            |  1 +
 include/linux/usb/xhci_pdriver.h                   |  3 ++
 5 files changed, 66 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt 
b/Documentation/devicetree/bindings/usb/usb-xhci.txt
index 0825732..c014eca 100644
--- a/Documentation/devicetree/bindings/usb/usb-xhci.txt
+++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt
@@ -12,6 +12,8 @@ Required properties:
 Optional properties:
   - clocks: reference to a clock
   - usb3-lpm-capable: determines if platform is USB3 LPM capable
+  - usb3-fake-doorbell: determines is the controller needs a fake doorbell
+    to detect the device
 
 Example:
        usb@f0931000 {
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 4fb295b..119ae3e 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -53,6 +53,10 @@ static void xhci_plat_quirks(struct device *dev, struct 
xhci_hcd *xhci)
        if ((node && of_property_read_bool(node, "usb3-lpm-capable")) ||
                        (pdata && pdata->usb3_lpm_capable))
                xhci->quirks |= XHCI_LPM_SUPPORT;
+
+       if ((node && of_property_read_bool(node, "usb3-fake-doorbell")) ||
+                       (pdata && pdata->usb3_fake_doorbell))
+               xhci->quirks |= XHCI_FAKE_DOORBELL;
 }
 
 /* called during probe() after chip reset completes */
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 643d312..17166aa 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -122,6 +122,39 @@ int xhci_halt(struct xhci_hcd *xhci)
        return ret;
 }
 
+static int xhci_fake_doorbell(struct xhci_hcd *xhci, int slot_id)
+{
+       u32 temp;
+
+       /* alloc a virt device for slot */
+       if (!xhci_alloc_virt_device(xhci, slot_id, NULL, GFP_NOIO)) {
+               xhci_warn(xhci, "Could not allocate xHCI USB device data 
structures\n");
+               return -ENOMEM;
+       }
+
+       /* ring fake doorbell for slot_id ep 0 */
+       xhci_ring_ep_doorbell(xhci, slot_id, 0, 0);
+       usleep_range(1000, 1500);
+
+       /* read the status register to check if HSE is set or not? */
+       temp = readl(&xhci->op_regs->status);
+
+       /* clear HSE if set */
+       if (temp & STS_FATAL) {
+               xhci_dbg(xhci, "HSE problem detected, status: 0x%x\n", temp);
+               temp &= ~0x1fff;
+               temp |= STS_FATAL;
+               writel(temp, &xhci->op_regs->status);
+               usleep_range(1000, 1500);
+               readl(&xhci->op_regs->status);
+       }
+
+       /* Free virt device */
+       xhci_free_virt_device(xhci, slot_id);
+
+       return 0;
+}
+
 /*
  * Set the run bit and wait for the host to be running.
  */
@@ -568,10 +601,26 @@ int xhci_init(struct usb_hcd *hcd)
 
 static int xhci_run_finished(struct xhci_hcd *xhci)
 {
-       if (xhci_start(xhci)) {
-               xhci_halt(xhci);
-               return -ENODEV;
+       int err;
+
+       err = xhci_start(xhci);
+       if (err) {
+               err = -ENODEV;
+               goto out_err;
        }
+
+       if (xhci->quirks & XHCI_FAKE_DOORBELL) {
+               err = xhci_fake_doorbell(xhci, 1);
+               if (err)
+                       goto out_err;
+
+               err = xhci_start(xhci);
+               if (err) {
+                       err = -ENODEV;
+                       goto out_err;
+               }
+       }
+
        xhci->shared_hcd->state = HC_STATE_RUNNING;
        xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
 
@@ -581,6 +630,10 @@ static int xhci_run_finished(struct xhci_hcd *xhci)
        xhci_dbg_trace(xhci, trace_xhci_dbg_init,
                        "Finished xhci_run for USB3 roothub");
        return 0;
+
+out_err:
+       xhci_halt(xhci);
+       return err;
 }
 
 /*
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 9be7348..8c1faf4 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1631,6 +1631,7 @@ struct xhci_hcd {
 #define XHCI_BROKEN_STREAMS    (1 << 19)
 #define XHCI_PME_STUCK_QUIRK   (1 << 20)
 #define XHCI_MTK_HOST          (1 << 21)
+#define XHCI_FAKE_DOORBELL     (1 << 22)
        unsigned int            num_active_eps;
        unsigned int            limit_active_eps;
        /* There are two roothubs to keep track of bus suspend info for */
diff --git a/include/linux/usb/xhci_pdriver.h b/include/linux/usb/xhci_pdriver.h
index 376654b..3b5c5e5 100644
--- a/include/linux/usb/xhci_pdriver.h
+++ b/include/linux/usb/xhci_pdriver.h
@@ -18,10 +18,13 @@
  *
  * @usb3_lpm_capable:  determines if this xhci platform supports USB3
  *                     LPM capability
+ * @usb3_fake_doorbell: determines is the controller needs a fake doorbell
+ *                     to detect the device
  *
  */
 struct usb_xhci_pdata {
        unsigned        usb3_lpm_capable:1;
+       unsigned        usb3_fake_doorbell:1;
 };
 
 #endif /* __USB_CORE_XHCI_PDRIVER_H */
-- 
2.6.2

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to