The RK356X DWC3 supports to set the USB 2.0 PHY enter suspend mode if the DWC3 core suspend conditions are valid (as per DWC3 controller databook 6.3.46 GUSB2PHYCFG register bit6). This cause xHC driver failed to send USB resume signal to USB 2.0 device in xhci_bus_resume(). This patch adds a quirk "xhci-u2-broken-suspend" to force the xHC to set the link state to XDEV_RESUME and send USB resume signal to USB 2.0 device. Change-Id: I24c017867f80728890c0562a12e4554625913e67 Signed-off-by: William Wu <william.wu@rock-chips.com>
165 lines
3.8 KiB
C
165 lines
3.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/**
|
|
* host.c - DesignWare USB3 DRD Controller Host Glue
|
|
*
|
|
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com
|
|
*
|
|
* Authors: Felipe Balbi <balbi@ti.com>,
|
|
*/
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include "core.h"
|
|
|
|
static int dwc3_host_get_irq(struct dwc3 *dwc)
|
|
{
|
|
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
|
|
int irq;
|
|
|
|
irq = platform_get_irq_byname(dwc3_pdev, "host");
|
|
if (irq > 0)
|
|
goto out;
|
|
|
|
if (irq == -EPROBE_DEFER)
|
|
goto out;
|
|
|
|
irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
|
|
if (irq > 0)
|
|
goto out;
|
|
|
|
if (irq == -EPROBE_DEFER)
|
|
goto out;
|
|
|
|
irq = platform_get_irq(dwc3_pdev, 0);
|
|
if (irq > 0)
|
|
goto out;
|
|
|
|
if (irq != -EPROBE_DEFER)
|
|
dev_err(dwc->dev, "missing host IRQ\n");
|
|
|
|
if (!irq)
|
|
irq = -EINVAL;
|
|
|
|
out:
|
|
return irq;
|
|
}
|
|
|
|
int dwc3_host_init(struct dwc3 *dwc)
|
|
{
|
|
struct property_entry props[8];
|
|
struct platform_device *xhci;
|
|
int ret, irq;
|
|
struct resource *res;
|
|
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
|
|
int prop_idx = 0;
|
|
|
|
irq = dwc3_host_get_irq(dwc);
|
|
if (irq < 0)
|
|
return irq;
|
|
|
|
res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ, "host");
|
|
if (!res)
|
|
res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ,
|
|
"dwc_usb3");
|
|
if (!res)
|
|
res = platform_get_resource(dwc3_pdev, IORESOURCE_IRQ, 0);
|
|
if (!res)
|
|
return -ENOMEM;
|
|
|
|
dwc->xhci_resources[1].start = irq;
|
|
dwc->xhci_resources[1].end = irq;
|
|
dwc->xhci_resources[1].flags = res->flags;
|
|
dwc->xhci_resources[1].name = res->name;
|
|
|
|
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
|
|
if (!xhci) {
|
|
dev_err(dwc->dev, "couldn't allocate xHCI device\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
xhci->dev.parent = dwc->dev;
|
|
|
|
dwc->xhci = xhci;
|
|
|
|
ret = platform_device_add_resources(xhci, dwc->xhci_resources,
|
|
DWC3_XHCI_RESOURCES_NUM);
|
|
if (ret) {
|
|
dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
|
|
goto err1;
|
|
}
|
|
|
|
memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
|
|
|
|
if (dwc->usb3_lpm_capable)
|
|
props[prop_idx++].name = "usb3-lpm-capable";
|
|
|
|
if (dwc->xhci_slow_suspend_quirk)
|
|
props[prop_idx++].name = "xhci-slow-suspend";
|
|
|
|
if (dwc->xhci_trb_ent_quirk)
|
|
props[prop_idx++].name = "xhci-trb-ent-quirk";
|
|
|
|
if (dwc->usb2_lpm_disable)
|
|
props[prop_idx++].name = "usb2-lpm-disable";
|
|
|
|
if (dwc->dis_u3_autosuspend_quirk)
|
|
props[prop_idx++].name = "usb3-dis-autosuspend";
|
|
/**
|
|
* WORKAROUND: dwc3 revisions <=3.00a have a limitation
|
|
* where Port Disable command doesn't work.
|
|
*
|
|
* The suggested workaround is that we avoid Port Disable
|
|
* completely.
|
|
*
|
|
* This following flag tells XHCI to do just that.
|
|
*/
|
|
if (dwc->revision <= DWC3_REVISION_300A)
|
|
props[prop_idx++].name = "quirk-broken-port-ped";
|
|
|
|
if (dwc->xhci_warm_reset_on_suspend_quirk)
|
|
props[prop_idx++].name = "xhci-warm-reset-on-suspend";
|
|
|
|
if (!dwc->dis_u2_susphy_quirk)
|
|
props[prop_idx++].name = "xhci-u2-broken-suspend";
|
|
|
|
if (prop_idx) {
|
|
ret = platform_device_add_properties(xhci, props);
|
|
if (ret) {
|
|
dev_err(dwc->dev, "failed to add properties to xHCI\n");
|
|
goto err1;
|
|
}
|
|
}
|
|
|
|
phy_create_lookup(dwc->usb2_generic_phy, "usb2-phy",
|
|
dev_name(dwc->dev));
|
|
phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
|
|
dev_name(dwc->dev));
|
|
|
|
ret = platform_device_add(xhci);
|
|
if (ret) {
|
|
dev_err(dwc->dev, "failed to register xHCI device\n");
|
|
goto err2;
|
|
}
|
|
|
|
return 0;
|
|
err2:
|
|
phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
|
|
dev_name(dwc->dev));
|
|
phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
|
|
dev_name(dwc->dev));
|
|
err1:
|
|
platform_device_put(xhci);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(dwc3_host_init);
|
|
|
|
void dwc3_host_exit(struct dwc3 *dwc)
|
|
{
|
|
phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
|
|
dev_name(dwc->dev));
|
|
phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
|
|
dev_name(dwc->dev));
|
|
platform_device_unregister(dwc->xhci);
|
|
}
|
|
EXPORT_SYMBOL_GPL(dwc3_host_exit);
|