123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- 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
- Broadcom's Northstar XHCI controllers seem to need a special start
- procedure to work correctly. There isn't any official documentation of
- 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 this and enables
- it on BCM4708 family.
- ---
- drivers/usb/host/xhci-plat.c | 6 +++++
- drivers/usb/host/xhci.c | 63 +++++++++++++++++++++++++++++++++++++++++---
- drivers/usb/host/xhci.h | 1 +
- 3 files changed, 67 insertions(+), 3 deletions(-)
- --- 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_machine_is_compatible("brcm,bcm4708"))
- + xhci->quirks |= XHCI_FAKE_DOORBELL;
- }
-
- /* called during probe() after chip reset completes */
- --- a/drivers/usb/host/xhci.c
- +++ b/drivers/usb/host/xhci.c
- @@ -150,6 +150,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.
- *
- @@ -566,10 +609,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;
-
- @@ -579,6 +632,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
- @@ -1640,6 +1640,7 @@ struct xhci_hcd {
- #define XHCI_BROKEN_STREAMS (1 << 19)
- #define XHCI_PME_STUCK_QUIRK (1 << 20)
- #define XHCI_MISSING_CAS (1 << 24)
- +#define XHCI_FAKE_DOORBELL (1 << 25)
- unsigned int num_active_eps;
- unsigned int limit_active_eps;
- /* There are two roothubs to keep track of bus suspend info for */
|