mirror of https://github.com/hak5/openwrt.git
156 lines
4.7 KiB
Diff
156 lines
4.7 KiB
Diff
|
From 37df205cdb69d17d5e815385fc1af3c97d1fe20b Mon Sep 17 00:00:00 2001
|
||
|
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
|
||
|
Date: Sat, 1 Oct 2016 22:54:48 +0200
|
||
|
Subject: [PATCH] usb: xhci: add support for performing fake doorbell
|
||
|
MIME-Version: 1.0
|
||
|
Content-Type: text/plain; charset=UTF-8
|
||
|
Content-Transfer-Encoding: 8bit
|
||
|
|
||
|
Broadcom's Northstar XHCI controllers seem to need a special start
|
||
|
procedure to work correctly. There isn't any official documentation on
|
||
|
this, the problem is that controller doesn't detect any connected
|
||
|
devices with default setup. Moreover connecting USB device to controller
|
||
|
that doesn't run properly can cause SoC's watchdog issues.
|
||
|
|
||
|
A workaround that was successfully tested on multiple devices is to
|
||
|
perform a fake doorbell. This patch adds code for doing that and a DT
|
||
|
binding enabling it.
|
||
|
|
||
|
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
|
||
|
---
|
||
|
Documentation/devicetree/bindings/usb/usb-xhci.txt | 2 +
|
||
|
drivers/usb/host/xhci-plat.c | 6 +++
|
||
|
drivers/usb/host/xhci.c | 63 ++++++++++++++++++++--
|
||
|
drivers/usb/host/xhci.h | 1 +
|
||
|
4 files changed, 69 insertions(+), 3 deletions(-)
|
||
|
|
||
|
--- 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 if controller requires a fake doorbell when
|
||
|
+ starting it
|
||
|
|
||
|
Example:
|
||
|
usb@f0931000 {
|
||
|
--- a/drivers/usb/host/xhci-plat.c
|
||
|
+++ b/drivers/usb/host/xhci-plat.c
|
||
|
@@ -38,12 +38,18 @@ static const struct xhci_driver_override
|
||
|
|
||
|
static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||
|
{
|
||
|
+ struct platform_device *pdev = to_platform_device(dev);
|
||
|
+ struct device_node *node = pdev->dev.of_node;
|
||
|
+
|
||
|
/*
|
||
|
* As of now platform drivers don't provide MSI support so we ensure
|
||
|
* here that the generic code does not try to make a pci_dev from our
|
||
|
* dev struct in order to setup MSI
|
||
|
*/
|
||
|
xhci->quirks |= XHCI_PLAT;
|
||
|
+
|
||
|
+ if (node && of_property_read_bool(node, "usb3-fake-doorbell"))
|
||
|
+ xhci->quirks |= XHCI_FAKE_DOORBELL;
|
||
|
}
|
||
|
|
||
|
/* called during probe() after chip reset completes */
|
||
|
--- a/drivers/usb/host/xhci.c
|
||
|
+++ b/drivers/usb/host/xhci.c
|
||
|
@@ -152,6 +152,49 @@ static int xhci_start(struct xhci_hcd *x
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
+/**
|
||
|
+ * xhci_fake_doorbell - Perform a fake doorbell on a specified slot
|
||
|
+ *
|
||
|
+ * Some controllers require a fake doorbell to start correctly. Without that
|
||
|
+ * they simply don't detect any devices.
|
||
|
+ */
|
||
|
+static int xhci_fake_doorbell(struct xhci_hcd *xhci, int slot_id)
|
||
|
+{
|
||
|
+ u32 temp;
|
||
|
+
|
||
|
+ /* Alloc a virt device for that 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 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%08x\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);
|
||
|
+
|
||
|
+ /* We're done if controller is already running */
|
||
|
+ if (readl(&xhci->op_regs->command) & CMD_RUN)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ return xhci_start(xhci);
|
||
|
+}
|
||
|
+
|
||
|
/*
|
||
|
* Reset a halted HC.
|
||
|
*
|
||
|
@@ -568,10 +611,20 @@ 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 err_halt;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (xhci->quirks & XHCI_FAKE_DOORBELL) {
|
||
|
+ err = xhci_fake_doorbell(xhci, 1);
|
||
|
+ if (err)
|
||
|
+ goto err_halt;
|
||
|
}
|
||
|
+
|
||
|
xhci->shared_hcd->state = HC_STATE_RUNNING;
|
||
|
xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
|
||
|
|
||
|
@@ -581,6 +634,10 @@ static int xhci_run_finished(struct xhci
|
||
|
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||
|
"Finished xhci_run for USB3 roothub");
|
||
|
return 0;
|
||
|
+
|
||
|
+err_halt:
|
||
|
+ xhci_halt(xhci);
|
||
|
+ return err;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
--- a/drivers/usb/host/xhci.h
|
||
|
+++ b/drivers/usb/host/xhci.h
|
||
|
@@ -1631,6 +1631,7 @@ struct xhci_hcd {
|
||
|
/* For controllers with a broken beyond repair streams implementation */
|
||
|
#define XHCI_BROKEN_STREAMS (1 << 19)
|
||
|
#define XHCI_PME_STUCK_QUIRK (1 << 20)
|
||
|
+#define XHCI_FAKE_DOORBELL (1 << 24)
|
||
|
unsigned int num_active_eps;
|
||
|
unsigned int limit_active_eps;
|
||
|
/* There are two roothubs to keep track of bus suspend info for */
|