mirror of
https://github.com/hardkernel/kernel_common_drivers.git
synced 2026-06-25 12:03:48 +09:00
usb: add some patches [1/1]
PD#SWPL-188390 Problem: add some patches Solution: add some patches Verify: all Change-Id: Iea01df348e0363d77c5673395d5f4b0689adbe03 Signed-off-by: he.he <he.he@amlogic.com>
This commit is contained in:
@@ -220,6 +220,7 @@ int crg_host_init(struct crg *crg)
|
||||
//struct resource *res;
|
||||
//struct platform_device *crg_pdev = to_platform_device(crg->dev);
|
||||
int prop_idx = 0;
|
||||
int cpu_type;
|
||||
|
||||
irq = crg_host_get_irq(crg);
|
||||
if (irq < 0)
|
||||
@@ -253,6 +254,55 @@ int crg_host_init(struct crg *crg)
|
||||
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb2-lpm-disable");
|
||||
cpu_type = get_cpu_type();
|
||||
switch (cpu_type) {
|
||||
case MESON_CPU_MAJOR_ID_T5:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-003");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-008");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-011");
|
||||
break;
|
||||
case MESON_CPU_MAJOR_ID_T5D:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-003");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-008");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-011");
|
||||
break;
|
||||
case MESON_CPU_MAJOR_ID_T7:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-007");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-010");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-014");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-008");
|
||||
break;
|
||||
case MESON_CPU_MAJOR_ID_S4:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-008");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-009");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-011");
|
||||
break;
|
||||
case MESON_CPU_MAJOR_ID_T3:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-007");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-010");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-014");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-008");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-011");
|
||||
break;
|
||||
case MESON_CPU_MAJOR_ID_S4D:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-008");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-009");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-011");
|
||||
break;
|
||||
case MESON_CPU_MAJOR_ID_T5W:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-007");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-011");
|
||||
break;
|
||||
case MESON_CPU_MAJOR_ID_S6:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-008");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-plug-died");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-eproto");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-016");
|
||||
|
||||
if (prop_idx) {
|
||||
ret = device_create_managed_software_node(&xhci->dev, props, NULL);
|
||||
|
||||
+40
-16
@@ -263,6 +263,7 @@ static int crg_host_init(struct crg_drd *crg)
|
||||
//struct resource *res;
|
||||
//struct platform_device *crg_pdev = to_platform_device(crg->dev);
|
||||
int prop_idx = 0;
|
||||
int cpu_type;
|
||||
|
||||
irq = crg_host_get_irq(crg);
|
||||
if (irq < 0)
|
||||
@@ -297,30 +298,53 @@ static int crg_host_init(struct crg_drd *crg)
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb2-lpm-disable");
|
||||
|
||||
if (is_meson_t5_cpu() || is_meson_t5d_cpu())
|
||||
cpu_type = get_cpu_type();
|
||||
switch (cpu_type) {
|
||||
case MESON_CPU_MAJOR_ID_T5:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-003");
|
||||
|
||||
if (is_meson_t7_cpu() || is_meson_t3_cpu()) {
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-008");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-011");
|
||||
break;
|
||||
case MESON_CPU_MAJOR_ID_T5D:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-003");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-008");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-011");
|
||||
break;
|
||||
case MESON_CPU_MAJOR_ID_T7:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-007");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-010");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-014");
|
||||
}
|
||||
if (is_meson_t7_cpu() || is_meson_t3_cpu() ||
|
||||
is_meson_t5_cpu() || is_meson_t5d_cpu() ||
|
||||
is_meson_s4_cpu() || is_meson_s4d_cpu() ||
|
||||
is_meson_s6_cpu())
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-008");
|
||||
|
||||
if (is_meson_s4_cpu() || is_meson_s4d_cpu())
|
||||
break;
|
||||
case MESON_CPU_MAJOR_ID_S4:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-008");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-009");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-011");
|
||||
break;
|
||||
case MESON_CPU_MAJOR_ID_T3:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-007");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-010");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-014");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-008");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-011");
|
||||
break;
|
||||
case MESON_CPU_MAJOR_ID_S4D:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-008");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-009");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-011");
|
||||
break;
|
||||
case MESON_CPU_MAJOR_ID_T5W:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-007");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-011");
|
||||
break;
|
||||
case MESON_CPU_MAJOR_ID_S6:
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-008");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-plug-died");
|
||||
|
||||
if (is_meson_t5w_cpu() || is_meson_t3_cpu() ||
|
||||
is_meson_t5_cpu() || is_meson_t5d_cpu() ||
|
||||
is_meson_s4_cpu() || is_meson_s4d_cpu())
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-011");
|
||||
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-eproto");
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-crg-host-016");
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ CFLAGS_trace.o := -I$(src)
|
||||
|
||||
$(MODULE_NAME)-$(CONFIG_USB_DWC3) += dwc3_amlogic/core-meson.o
|
||||
|
||||
ifneq ($(CONFIG_USB_DWC3),)
|
||||
ifneq ($(CONFIG_TRACING),)
|
||||
$(MODULE_NAME)-y += dwc3_amlogic/trace-meson.o
|
||||
endif
|
||||
@@ -29,6 +30,7 @@ endif
|
||||
ifneq ($(CONFIG_DEBUG_FS),)
|
||||
$(MODULE_NAME)-y += dwc3_amlogic/debugfs-meson.o
|
||||
endif
|
||||
endif
|
||||
|
||||
##
|
||||
# Platform-specific glue layers go here
|
||||
|
||||
@@ -40,6 +40,10 @@
|
||||
|
||||
#include "debug-meson.h"
|
||||
#include "../host/xhci-ext-caps.h"
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
#include <linux/amlogic/usbtype.h>
|
||||
static int dwc3_phy_init(struct aml_dwc3 *dwc);
|
||||
#endif
|
||||
|
||||
#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
|
||||
|
||||
@@ -301,6 +305,7 @@ u32 aml_dwc3_core_fifo_space(struct aml_dwc3_ep *dep, u8 type)
|
||||
* aml_dwc3_core_soft_reset - Issues core soft reset and PHY reset
|
||||
* @dwc: pointer to our context structure
|
||||
*/
|
||||
#if !IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
int aml_dwc3_core_soft_reset(struct aml_dwc3 *dwc)
|
||||
{
|
||||
u32 reg;
|
||||
@@ -353,6 +358,93 @@ done:
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int aml_dwc3_core_soft_reset(struct aml_dwc3 *dwc)
|
||||
{
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
/* Before Resetting PHY, put Core in Reset */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg |= DWC3_GCTL_CORESOFTRESET;
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
|
||||
/* Assert USB3 PHY reset */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
|
||||
reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
|
||||
|
||||
/* Assert USB3 PHY reset */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(1));
|
||||
reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(1), reg);
|
||||
|
||||
/* Assert USB2 PHY reset */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(1));
|
||||
reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(1), reg);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(2));
|
||||
reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(2), reg);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(3));
|
||||
reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(3), reg);
|
||||
|
||||
ret = dwc3_phy_init(dwc);
|
||||
if (ret)
|
||||
goto err_exit_ulpi;
|
||||
|
||||
usleep_range(1000, 1100);
|
||||
|
||||
/* Clear USB3 PHY reset */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
|
||||
reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
|
||||
|
||||
/* Clear USB3 PHY reset */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(1));
|
||||
reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(1), reg);
|
||||
|
||||
/* Clear USB2 PHY reset */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
|
||||
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
|
||||
/* Clear USB2 PHY reset */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(1));
|
||||
reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(1), reg);
|
||||
|
||||
/* Clear USB2 PHY reset */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(2));
|
||||
reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(2), reg);
|
||||
|
||||
/* Clear USB2 PHY reset */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(3));
|
||||
reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(3), reg);
|
||||
|
||||
usleep_range(1000, 1100);
|
||||
|
||||
/* After PHYs are stable we can take Core out of reset state */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg &= ~DWC3_GCTL_CORESOFTRESET;
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
|
||||
return 0;
|
||||
err_exit_ulpi:
|
||||
aml_dwc3_ulpi_exit(dwc);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* dwc3_frame_length_adjustment - Adjusts frame length if required
|
||||
@@ -1340,10 +1432,11 @@ static int dwc3_core_init(struct aml_dwc3 *dwc)
|
||||
goto err_exit_ulpi;
|
||||
dwc->phys_ready = true;
|
||||
}
|
||||
|
||||
#if !IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
ret = dwc3_phy_init(dwc);
|
||||
if (ret)
|
||||
goto err_exit_ulpi;
|
||||
#endif
|
||||
|
||||
ret = aml_dwc3_core_soft_reset(dwc);
|
||||
if (ret)
|
||||
@@ -1505,6 +1598,13 @@ static int dwc3_core_get_phy(struct aml_dwc3 *dwc)
|
||||
return dev_err_probe(dev, ret, "no usb2 phy configured\n");
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
dwc->super_speed_support = 0;
|
||||
if (dwc->usb3_phy)
|
||||
if (dwc->usb3_phy->flags == AML_USB3_PHY_ENABLE)
|
||||
dwc->super_speed_support = 1;
|
||||
#endif
|
||||
|
||||
if (IS_ERR(dwc->usb3_phy)) {
|
||||
ret = PTR_ERR(dwc->usb3_phy);
|
||||
if (ret == -ENXIO || ret == -ENODEV)
|
||||
@@ -2275,6 +2375,38 @@ err_put_psy:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
static void dwc3_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct aml_dwc3 *dwc = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
dwc3_core_exit_mode(dwc);
|
||||
aml_dwc3_debugfs_exit(dwc);
|
||||
|
||||
dwc3_core_exit(dwc);
|
||||
aml_dwc3_ulpi_exit(dwc);
|
||||
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
/*
|
||||
* HACK: Clear the driver data, which is currently accessed by parent
|
||||
* glue drivers, before allowing the parent to suspend.
|
||||
*/
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
|
||||
dwc3_free_event_buffers(dwc);
|
||||
|
||||
if (dwc->usb_psy)
|
||||
power_supply_put(dwc->usb_psy);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void dwc3_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct aml_dwc3 *dwc = platform_get_drvdata(pdev);
|
||||
@@ -2619,7 +2751,10 @@ MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match);
|
||||
|
||||
static struct platform_driver dwc3_driver = {
|
||||
.probe = dwc3_probe,
|
||||
.remove_new = dwc3_remove,
|
||||
.remove = dwc3_remove,
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
.shutdown = dwc3_shutdown,
|
||||
#endif
|
||||
.driver = {
|
||||
.name = "dwc3-meson",
|
||||
.of_match_table = of_match_ptr(of_dwc3_match),
|
||||
|
||||
@@ -1382,7 +1382,9 @@ struct aml_dwc3 {
|
||||
unsigned sys_wakeup:1;
|
||||
unsigned wakeup_configured:1;
|
||||
unsigned suspended:1;
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
unsigned super_speed_support:1;
|
||||
#endif
|
||||
u16 imod_interval;
|
||||
|
||||
int max_cfg_eps;
|
||||
|
||||
@@ -126,7 +126,11 @@ out:
|
||||
|
||||
int aml_dwc3_host_init(struct aml_dwc3 *dwc)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
struct property_entry props[8];
|
||||
#else
|
||||
struct property_entry props[6];
|
||||
#endif
|
||||
struct platform_device *xhci;
|
||||
int ret, irq;
|
||||
int prop_idx = 0;
|
||||
@@ -140,8 +144,11 @@ int aml_dwc3_host_init(struct aml_dwc3 *dwc)
|
||||
irq = dwc3_host_get_irq(dwc);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
xhci = platform_device_alloc("xhci-hcd-meson", PLATFORM_DEVID_AUTO);
|
||||
#else
|
||||
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
|
||||
#endif
|
||||
if (!xhci) {
|
||||
dev_err(dwc->dev, "couldn't allocate xHCI device\n");
|
||||
return -ENOMEM;
|
||||
@@ -182,6 +189,11 @@ int aml_dwc3_host_init(struct aml_dwc3 *dwc)
|
||||
if (DWC3_VER_IS_WITHIN(DWC3, ANY, 300A))
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped");
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (dwc->super_speed_support)
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("super_speed_support");
|
||||
#endif
|
||||
|
||||
if (prop_idx) {
|
||||
ret = device_create_managed_software_node(&xhci->dev, props, NULL);
|
||||
if (ret) {
|
||||
|
||||
@@ -454,7 +454,11 @@ EXPORT_SYMBOL_GPL(aml_xhci_port_state_to_neutral);
|
||||
* to complete.
|
||||
* suspend will set to 1, if suspend bit need to set in command.
|
||||
*/
|
||||
static int xhci_stop_device(struct aml_xhci_hcd *xhci, int slot_id, int suspend)
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
int aml_xhci_stop_device(struct aml_xhci_hcd *xhci, int slot_id, int suspend)
|
||||
#else
|
||||
static int aml_xhci_stop_device(struct aml_xhci_hcd *xhci, int slot_id, int suspend)
|
||||
#endif
|
||||
{
|
||||
struct aml_xhci_virt_device *virt_dev;
|
||||
struct aml_xhci_command *cmd;
|
||||
@@ -686,8 +690,14 @@ static void xhci_port_set_test_mode(struct aml_xhci_hcd *xhci,
|
||||
temp |= test_mode << PORT_TEST_MODE_SHIFT;
|
||||
writel(temp, port->addr + PORTPMSC);
|
||||
xhci->test_mode = test_mode;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (test_mode == USB_TEST_FORCE_ENABLE &&
|
||||
(xhci->meson_quirks & XHCI_CRG_HOST))
|
||||
aml_xhci_start(xhci);
|
||||
#else
|
||||
if (test_mode == USB_TEST_FORCE_ENABLE)
|
||||
aml_xhci_start(xhci);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int xhci_enter_test_mode(struct aml_xhci_hcd *xhci,
|
||||
@@ -1195,6 +1205,98 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
|
||||
return status;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
static int xhci_test_suspend_resume(struct usb_hcd *hcd,
|
||||
u16 wIndex)
|
||||
{
|
||||
struct aml_xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
unsigned long flags = 0;
|
||||
u32 temp;
|
||||
int slot_id;
|
||||
//__le32 __iomem **port_array = xhci->usb2_ports;
|
||||
struct aml_xhci_port *port;
|
||||
|
||||
/* xhci only supports test mode for usb2 ports */
|
||||
port = xhci->usb2_rhub.ports[wIndex];
|
||||
//temp = readl(port->addr + PORTPMSC);
|
||||
|
||||
/* 15 second delay per the test spec */
|
||||
aml_xhci_err(xhci, "into suspend\n");
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
/*suspend*/
|
||||
temp = readl(port->addr);
|
||||
if ((temp & PORT_PLS_MASK) != XDEV_U0) {
|
||||
/* Resume the port to U0 first */
|
||||
aml_xhci_set_link_state(xhci, port, XDEV_U0);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
usleep_range(10000 - 1, 10000);
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
}
|
||||
/* In spec software should not attempt to suspend
|
||||
* a port unless the port reports that it is in the
|
||||
* enabled (PED = ‘1’,PLS < ‘3’) state.
|
||||
*/
|
||||
temp = readl(port->addr);
|
||||
if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) ||
|
||||
(temp & PORT_PLS_MASK) >= XDEV_U3) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
aml_xhci_warn(xhci, "USB core suspending device not in U0/U1/U2.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//slot_id = aml_xhci_find_slot_id_by_port(hcd, xhci,
|
||||
// wIndex + 1);
|
||||
slot_id = port->slot_id;
|
||||
if (!slot_id) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
aml_xhci_warn(xhci, "slot_id is zero\n");
|
||||
return -1;
|
||||
}
|
||||
/* unlock to execute stop endpoint commands */
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
aml_xhci_stop_device(xhci, slot_id, 1);
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
aml_xhci_set_link_state(xhci, port, XDEV_U3);
|
||||
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
usleep_range(10000 - 1, 10000); /* wait device to enter */
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
/* 15 second delay per the test spec */
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
aml_xhci_err(xhci, "wait 15s\n");
|
||||
msleep(15000);
|
||||
aml_xhci_err(xhci, "into resume\n");
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
temp = readl(port->addr);
|
||||
aml_xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n");
|
||||
aml_xhci_dbg(xhci, "PORTSC %04x\n", temp);
|
||||
if (temp & PORT_RESET) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
return -1;
|
||||
}
|
||||
if ((temp & PORT_PLS_MASK) == XDEV_U3) {
|
||||
if ((temp & PORT_PE) == 0) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
return -1;
|
||||
}
|
||||
|
||||
aml_xhci_set_link_state(xhci, port, XDEV_RESUME);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
msleep(20);
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
aml_xhci_set_link_state(xhci, port, XDEV_U0);
|
||||
}
|
||||
|
||||
aml_xhci_ring_device(xhci, slot_id);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int aml_xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
u16 wIndex, char *buf, u16 wLength)
|
||||
{
|
||||
@@ -1309,6 +1411,14 @@ int aml_xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
break;
|
||||
}
|
||||
temp = aml_xhci_port_state_to_neutral(temp);
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_014 &&
|
||||
hcd->self.root_hub->descriptor.bDeviceProtocol >= USB_HUB_PR_SS &&
|
||||
wValue == USB_PORT_FEAT_RESET) {
|
||||
wValue = USB_PORT_FEAT_BH_PORT_RESET;
|
||||
aml_xhci_info(xhci, "change hot reset to warm reset\n");
|
||||
}
|
||||
#endif
|
||||
/* FIXME: What new port features do we need to support? */
|
||||
switch (wValue) {
|
||||
case USB_PORT_FEAT_SUSPEND:
|
||||
@@ -1338,7 +1448,7 @@ int aml_xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
}
|
||||
/* unlock to execute stop endpoint commands */
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
xhci_stop_device(xhci, port->slot_id, 1);
|
||||
aml_xhci_stop_device(xhci, port->slot_id, 1);
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
aml_xhci_set_link_state(xhci, port, XDEV_U3);
|
||||
@@ -1466,7 +1576,7 @@ int aml_xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
* commands */
|
||||
spin_unlock_irqrestore(&xhci->lock,
|
||||
flags);
|
||||
xhci_stop_device(xhci, port->slot_id, 1);
|
||||
aml_xhci_stop_device(xhci, port->slot_id, 1);
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
}
|
||||
aml_xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U3);
|
||||
@@ -1526,6 +1636,32 @@ int aml_xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
temp |= PORT_U2_TIMEOUT(timeout);
|
||||
writel(temp, port->addr + PORTPMSC);
|
||||
break;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
case USB_PORT_FEAT_TEST:
|
||||
/* 4.19.6 Port Test Modes (USB2 Test Mode) */
|
||||
if (hcd->speed != HCD_USB2)
|
||||
goto error;
|
||||
if (test_mode > 6 || test_mode < 1)
|
||||
goto error;
|
||||
|
||||
if (test_mode >= 1 && test_mode <= 4) {
|
||||
retval = xhci_enter_test_mode(xhci,
|
||||
test_mode,
|
||||
wIndex, &flags);
|
||||
} else if (test_mode == 5) {
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST)
|
||||
retval = xhci_enter_test_mode(xhci,
|
||||
test_mode,
|
||||
wIndex, &flags);
|
||||
xhci_port_set_test_mode(xhci,
|
||||
test_mode, wIndex);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
retval = xhci_test_suspend_resume(hcd, wIndex);
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
}
|
||||
break;
|
||||
#else
|
||||
case USB_PORT_FEAT_TEST:
|
||||
/* 4.19.6 Port Test Modes (USB2 Test Mode) */
|
||||
if (hcd->speed != HCD_USB2)
|
||||
@@ -1536,6 +1672,7 @@ int aml_xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
retval = xhci_enter_test_mode(xhci, test_mode, wIndex,
|
||||
&flags);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
@@ -1818,7 +1955,7 @@ retry:
|
||||
int slot_id = ports[port_index]->slot_id;
|
||||
if (slot_id) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
xhci_stop_device(xhci, slot_id, 1);
|
||||
aml_xhci_stop_device(xhci, slot_id, 1);
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
}
|
||||
}
|
||||
@@ -1873,6 +2010,9 @@ int aml_xhci_bus_resume(struct usb_hcd *hcd)
|
||||
u32 temp, portsc;
|
||||
struct aml_xhci_hub *rhub;
|
||||
struct aml_xhci_port **ports;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
int val;
|
||||
#endif
|
||||
|
||||
rhub = aml_xhci_get_rhub(hcd);
|
||||
ports = rhub->ports;
|
||||
@@ -1954,11 +2094,30 @@ int aml_xhci_bus_resume(struct usb_hcd *hcd)
|
||||
for_each_set_bit(port_index, &bus_state->bus_suspended, BITS_PER_LONG) {
|
||||
sret = aml_xhci_handshake(ports[port_index]->addr, PORT_PLC,
|
||||
PORT_PLC, 10 * 1000);
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (sret) {
|
||||
aml_xhci_warn(xhci, "port %d-%d resume PLC timeout\n",
|
||||
hcd->self.busnum, port_index + 1);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
val = aml_xhci_resume(xhci, PMSG_RESTORE);
|
||||
if (val) {
|
||||
aml_xhci_warn(xhci, "### failed val is %d\n", val);
|
||||
return 0;
|
||||
}
|
||||
rhub = aml_xhci_get_rhub(hcd);
|
||||
ports = rhub->ports;
|
||||
max_ports = rhub->num_ports;
|
||||
bus_state = &rhub->bus_state;
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
if (sret) {
|
||||
aml_xhci_warn(xhci, "port %d-%d resume PLC timeout\n",
|
||||
hcd->self.busnum, port_index + 1);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
aml_xhci_test_and_clear_bit(xhci, ports[port_index], PORT_PLC);
|
||||
if (ports[port_index]->slot_id)
|
||||
aml_xhci_ring_device(xhci, ports[port_index]->slot_id);
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
#include "xhci-trace-meson.h"
|
||||
#include "xhci-debugfs-meson.h"
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
static DEFINE_SPINLOCK(crg_urb_lock);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Allocates a generic ring segment from the ring pool, sets the dma address,
|
||||
* initializes the segment to zero, and sets the private next pointer to NULL.
|
||||
@@ -767,6 +771,15 @@ void aml_xhci_free_stream_info(struct aml_xhci_hcd *xhci,
|
||||
|
||||
|
||||
/***************** Device context manipulation *************************/
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
static void xhci_init_endpoint_timer(struct aml_xhci_hcd *xhci,
|
||||
struct aml_xhci_virt_ep *ep)
|
||||
{
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_DELAY)
|
||||
timer_setup(&ep->stop_cmd_queue_timer, xhci_stop_endpoint_command_timer,
|
||||
0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void xhci_free_tt_info(struct aml_xhci_hcd *xhci,
|
||||
struct aml_xhci_virt_device *virt_dev,
|
||||
@@ -836,6 +849,60 @@ free_tts:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
void xhci_free_stop_ep_timer(struct aml_xhci_hcd *xhci, int slot_id)
|
||||
{
|
||||
struct aml_xhci_virt_device *virt_dev;
|
||||
int i, ep_index = -1;
|
||||
|
||||
/* Slot ID 0 is reserved */
|
||||
if (slot_id == 0 || !xhci->devs[slot_id])
|
||||
return;
|
||||
|
||||
virt_dev = xhci->devs[slot_id];
|
||||
if (!virt_dev)
|
||||
return;
|
||||
|
||||
/* Stop any wayward timer functions (which may grab the lock) */
|
||||
//aml_xhci_err(xhci, "---free timer start-%s----\n", __func__);
|
||||
for (i = 0; i < 31; i++) {
|
||||
virt_dev->eps[i].ep_state &= ~EP_STOP_CMD_PENDING;
|
||||
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_DELAY) {
|
||||
while (timer_pending(&virt_dev->eps[i].stop_cmd_queue_timer)) {
|
||||
ep_index = i;
|
||||
msleep(20);
|
||||
}
|
||||
del_timer_sync(&virt_dev->eps[i].stop_cmd_queue_timer);
|
||||
}
|
||||
}
|
||||
if (ep_index != -1)
|
||||
aml_xhci_info(xhci, "when xhci suspend, waiting stop ep=%d, slot_id=%d\n",
|
||||
ep_index, slot_id);
|
||||
}
|
||||
|
||||
void xhci_del_stop_ep_timer(struct aml_xhci_hcd *xhci, int slot_id)
|
||||
{
|
||||
struct aml_xhci_virt_device *virt_dev;
|
||||
int i;
|
||||
|
||||
/* Slot ID 0 is reserved */
|
||||
if (slot_id == 0 || !xhci->devs[slot_id])
|
||||
return;
|
||||
|
||||
virt_dev = xhci->devs[slot_id];
|
||||
if (!virt_dev)
|
||||
return;
|
||||
|
||||
/* Stop any wayward timer functions (which may grab the lock) */
|
||||
for (i = 0; i < 31; i++) {
|
||||
virt_dev->eps[i].ep_state &= ~EP_STOP_CMD_PENDING;
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_DELAY)
|
||||
del_timer(&virt_dev->eps[i].stop_cmd_queue_timer);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* All the xhci_tds in the ring's TD list should be freed at this point.
|
||||
* Should be called with xhci->lock held if there is any chance the TT lists
|
||||
@@ -942,6 +1009,100 @@ out:
|
||||
aml_xhci_free_virt_device(xhci, slot_id);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
int aml_xhci_usb_get_status(struct usb_device *dev, int recip, int type, int target,
|
||||
void *data)
|
||||
{
|
||||
int ret;
|
||||
void *status;
|
||||
int length;
|
||||
|
||||
switch (type) {
|
||||
case USB_STATUS_TYPE_STANDARD:
|
||||
length = 2;
|
||||
break;
|
||||
case USB_STATUS_TYPE_PTM:
|
||||
if (recip != USB_RECIP_DEVICE)
|
||||
return -EINVAL;
|
||||
|
||||
length = 4;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
status = kmalloc(length, GFP_KERNEL);
|
||||
if (!status)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
|
||||
USB_REQ_GET_STATUS, USB_DIR_IN | recip, USB_STATUS_TYPE_STANDARD,
|
||||
target, status, length, USB_CTRL_GET_TIMEOUT);
|
||||
|
||||
switch (ret) {
|
||||
case 4:
|
||||
if (type != USB_STATUS_TYPE_PTM) {
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
*(u32 *)data = le32_to_cpu(*(__le32 *)status);
|
||||
ret = 0;
|
||||
break;
|
||||
case 2:
|
||||
if (type != USB_STATUS_TYPE_STANDARD) {
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
*(u16 *)data = le16_to_cpu(*(__le16 *)status);
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
kfree(status);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(aml_xhci_usb_get_status);
|
||||
|
||||
void stop_ep_cmd_work(struct work_struct *work)
|
||||
{
|
||||
struct aml_xhci_hcd *xhci;
|
||||
struct usb_device *udev;
|
||||
u32 i;
|
||||
struct aml_xhci_virt_ep *eps =
|
||||
container_of(work, struct aml_xhci_virt_ep, stop_work);
|
||||
|
||||
if (!eps)
|
||||
return;
|
||||
xhci = eps->xhci;
|
||||
if (eps->q_status_count == 0)
|
||||
eps->q_status_count = 12;
|
||||
while (1) {
|
||||
if (eps->xhci && eps->udev) {
|
||||
u16 stat;
|
||||
|
||||
udev = eps->udev;
|
||||
if (!udev)
|
||||
return;
|
||||
if (udev->state == USB_STATE_NOTATTACHED)
|
||||
return;
|
||||
for (i = 0; i < eps->q_status_count; i++) {
|
||||
if (!udev)
|
||||
return;
|
||||
if (udev->state == USB_STATE_NOTATTACHED)
|
||||
return;
|
||||
aml_xhci_usb_get_status(udev, USB_RECIP_ENDPOINT,
|
||||
USB_STATUS_TYPE_STANDARD, eps->bendpointaddress, &stat);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int aml_xhci_alloc_virt_device(struct aml_xhci_hcd *xhci, int slot_id,
|
||||
struct usb_device *udev, gfp_t flags)
|
||||
{
|
||||
@@ -979,6 +1140,11 @@ int aml_xhci_alloc_virt_device(struct aml_xhci_hcd *xhci, int slot_id,
|
||||
dev->eps[i].ep_index = i;
|
||||
dev->eps[i].vdev = dev;
|
||||
dev->eps[i].xhci = xhci;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
xhci_init_endpoint_timer(xhci, &dev->eps[i]);
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_011)
|
||||
INIT_WORK(&dev->eps[i].stop_work, stop_ep_cmd_work);
|
||||
#endif
|
||||
INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list);
|
||||
INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list);
|
||||
}
|
||||
@@ -1404,6 +1570,9 @@ int aml_xhci_endpoint_init(struct aml_xhci_hcd *xhci,
|
||||
unsigned int mult;
|
||||
unsigned int avg_trb_len;
|
||||
unsigned int err_count = 0;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
int i = 0, j = 0;
|
||||
#endif
|
||||
|
||||
ep_index = aml_xhci_get_endpoint_index(&ep->desc);
|
||||
ep_ctx = aml_xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
|
||||
@@ -1460,13 +1629,46 @@ int aml_xhci_endpoint_init(struct aml_xhci_hcd *xhci,
|
||||
mult = 0;
|
||||
|
||||
/* Set up the endpoint ring */
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_008)
|
||||
virt_dev->eps[ep_index].new_ring =
|
||||
aml_xhci_ring_alloc(xhci, 18, 1, ring_type, max_packet, mem_flags);
|
||||
else
|
||||
virt_dev->eps[ep_index].new_ring =
|
||||
aml_xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags);
|
||||
#else
|
||||
virt_dev->eps[ep_index].new_ring =
|
||||
aml_xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags);
|
||||
#endif
|
||||
if (!virt_dev->eps[ep_index].new_ring)
|
||||
return -ENOMEM;
|
||||
|
||||
virt_dev->eps[ep_index].skip = false;
|
||||
ep_ring = virt_dev->eps[ep_index].new_ring;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if ((xhci->meson_quirks & XHCI_CRG_HOST_010) &&
|
||||
udev->speed == USB_SPEED_SUPER) {
|
||||
if (endpoint_type == BULK_IN_EP) {
|
||||
max_burst = 0;
|
||||
aml_xhci_warn(xhci, "##### crg set max_burst 0\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (udev->speed == USB_SPEED_SUPER && endpoint_type == BULK_IN_EP) {
|
||||
for (i = 0; i < udev->descriptor.bNumConfigurations; i++) {
|
||||
for (j = 0; j < udev->config[i].desc.bNumInterfaces; j++) {
|
||||
if (udev->config[i].intf_cache[j]->altsetting->desc.bInterfaceClass
|
||||
== USB_CLASS_VIDEO) {
|
||||
max_burst = 0;
|
||||
aml_xhci_warn(xhci, "##### crg set max_burst 0\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Fill the endpoint context */
|
||||
ep_ctx->ep_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) |
|
||||
@@ -1749,10 +1951,37 @@ struct aml_xhci_command *aml_xhci_alloc_command_with_ctx(struct aml_xhci_hcd *xh
|
||||
return command;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
void aml_xhci_urb_free_priv(struct aml_urb_priv *urb_priv)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct usb_hcd *hcd;
|
||||
struct aml_xhci_hcd *xhci;
|
||||
|
||||
if (urb_priv && urb_priv->urb && urb_priv->need_div) {
|
||||
hcd = bus_to_hcd(urb_priv->urb->dev->bus);
|
||||
xhci = hcd_to_xhci(hcd);
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_010) {
|
||||
spin_lock_irqsave(&crg_urb_lock, flags);
|
||||
memset(urb_priv->dst_buf, 0, sizeof(urb_priv->dst_buf));
|
||||
if (urb_priv->tmp_dma)
|
||||
dma_unmap_single(urb_priv->urb->dev->bus->controller,
|
||||
urb_priv->tmp_dma, 4096, DMA_FROM_DEVICE);
|
||||
kfree(urb_priv->tmp_buf);
|
||||
|
||||
urb_priv->tmp_dma = 0;
|
||||
urb_priv->tmp_buf = NULL;
|
||||
spin_unlock_irqrestore(&crg_urb_lock, flags);
|
||||
}
|
||||
}
|
||||
kfree(urb_priv);
|
||||
}
|
||||
#else
|
||||
void aml_xhci_urb_free_priv(struct aml_urb_priv *urb_priv)
|
||||
{
|
||||
kfree(urb_priv);
|
||||
}
|
||||
#endif
|
||||
|
||||
void aml_xhci_free_command(struct aml_xhci_hcd *xhci,
|
||||
struct aml_xhci_command *command)
|
||||
@@ -2225,6 +2454,10 @@ static int xhci_setup_port_arrays(struct aml_xhci_hcd *xhci, gfp_t flags)
|
||||
"Found %u USB 2.0 ports and %u USB 3.0 ports.",
|
||||
xhci->usb2_rhub.num_ports, xhci->usb3_rhub.num_ports);
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if ((xhci->quirks & XHCI_AML_SUPER_SPEED_SUPPORT) == 0)
|
||||
xhci->usb3_rhub.num_ports = 0;
|
||||
#endif
|
||||
/* Place limits on the number of roothub ports so that the hub
|
||||
* descriptors aren't longer than the USB core will allocate.
|
||||
*/
|
||||
@@ -2509,7 +2742,16 @@ int aml_xhci_mem_init(struct aml_xhci_hcd *xhci, gfp_t flags)
|
||||
xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters),
|
||||
flags, dev_to_node(dev));
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_011)
|
||||
ir = xhci_alloc_interrupter(xhci, AML_HOST11_ERST_NUM_SEGS, flags);
|
||||
else if (xhci->meson_quirks & XHCI_CRG_HOST_008)
|
||||
ir = xhci_alloc_interrupter(xhci, AML_ERST_NUM_SEGS, flags);
|
||||
else
|
||||
ir = xhci_alloc_interrupter(xhci, 0, flags);
|
||||
#else
|
||||
ir = xhci_alloc_interrupter(xhci, 0, flags);
|
||||
#endif
|
||||
if (!ir)
|
||||
goto fail;
|
||||
|
||||
|
||||
@@ -38,6 +38,19 @@ static unsigned long long quirks;
|
||||
module_param(quirks, ullong, S_IRUGO);
|
||||
MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default");
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
unsigned int db_wait;
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
struct usb_hub *aml_xhci_usb_hub_to_struct_hub(struct usb_device *hdev)
|
||||
{
|
||||
if (!hdev || !hdev->actconfig || !hdev->maxchild)
|
||||
return NULL;
|
||||
return usb_get_intfdata(hdev->actconfig->interface[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool td_on_ring(struct aml_xhci_td *td, struct aml_xhci_ring *ring)
|
||||
{
|
||||
struct aml_xhci_segment *seg = ring->first_seg;
|
||||
@@ -158,6 +171,18 @@ int aml_xhci_start(struct aml_xhci_hcd *xhci)
|
||||
{
|
||||
u32 temp;
|
||||
int ret;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST) {
|
||||
hcd = xhci_to_hcd(xhci);
|
||||
temp = readl(hcd->regs + 0x20b0);
|
||||
temp |= 1 << 26;
|
||||
writel(temp, hcd->regs + 0x20b0);
|
||||
temp = readl(hcd->regs + 0x20b0);
|
||||
temp = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
temp = readl(&xhci->op_regs->command);
|
||||
temp |= (CMD_RUN);
|
||||
@@ -894,13 +919,28 @@ int aml_xhci_suspend(struct aml_xhci_hcd *xhci, bool do_wakeup)
|
||||
struct usb_hcd *hcd = xhci_to_hcd(xhci);
|
||||
u32 command;
|
||||
u32 res;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
int i;
|
||||
#endif
|
||||
|
||||
if (!hcd->state)
|
||||
return 0;
|
||||
|
||||
if (hcd->state != HC_STATE_SUSPENDED ||
|
||||
(xhci->shared_hcd && xhci->shared_hcd->state != HC_STATE_SUSPENDED))
|
||||
xhci->shared_hcd->state != HC_STATE_SUSPENDED) {
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->xhc_state & XHCI_STATE_DYING) {
|
||||
aml_xhci_info(xhci, "-------xhci has been died-----\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_DELAY)
|
||||
for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--)
|
||||
xhci_free_stop_ep_timer(xhci, i);
|
||||
#endif
|
||||
|
||||
/* Clear root port wake on bits if wakeup not allowed. */
|
||||
xhci_disable_hub_port_wake(xhci, &xhci->usb3_rhub, do_wakeup);
|
||||
@@ -1011,6 +1051,9 @@ int aml_xhci_resume(struct aml_xhci_hcd *xhci, pm_message_t msg)
|
||||
bool pending_portevent = false;
|
||||
bool suspended_usb3_devs = false;
|
||||
bool reinit_xhc = false;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
int i;
|
||||
#endif
|
||||
|
||||
if (!hcd->state)
|
||||
return 0;
|
||||
@@ -1018,6 +1061,11 @@ int aml_xhci_resume(struct aml_xhci_hcd *xhci, pm_message_t msg)
|
||||
/* Wait a bit if either of the roothubs need to settle from the
|
||||
* transition into bus suspend.
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_DELAY)
|
||||
for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--)
|
||||
xhci_del_stop_ep_timer(xhci, i);
|
||||
#endif
|
||||
|
||||
if (time_before(jiffies, xhci->usb2_rhub.bus_state.next_statechange) ||
|
||||
time_before(jiffies, xhci->usb3_rhub.bus_state.next_statechange))
|
||||
@@ -1581,6 +1629,25 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
|
||||
goto free_priv;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_010) {
|
||||
if (urb->dev->level != 1 && urb->dev->speed == USB_SPEED_SUPER &&
|
||||
usb_urb_dir_in(urb) && urb->dev->bus->sg_tablesize > 0) {
|
||||
urb_priv->need_div = 1;
|
||||
}
|
||||
urb_priv->urb = urb;
|
||||
}
|
||||
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_007) {
|
||||
if (urb->dev->level >= 2 &&
|
||||
urb->dev->speed == USB_SPEED_LOW &&
|
||||
(usb_endpoint_xfer_int(&urb->ep->desc))) {
|
||||
urb_priv->need_event_data = 1;
|
||||
//aml_xhci_info(xhci, "007-########%s\n", __func__);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
slot_id = urb->dev->slot_id;
|
||||
|
||||
if (!HCD_HW_ACCESSIBLE(hcd)) {
|
||||
@@ -1687,6 +1754,10 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
struct aml_xhci_virt_ep *ep;
|
||||
struct aml_xhci_command *command;
|
||||
struct aml_xhci_virt_device *vdev;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
int delay_stop;
|
||||
#endif
|
||||
|
||||
|
||||
xhci = hcd_to_xhci(hcd);
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
@@ -1772,15 +1843,65 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
* the first cancellation to be handled.
|
||||
*/
|
||||
if (!(ep->ep_state & EP_STOP_CMD_PENDING)) {
|
||||
ep->ep_state |= EP_STOP_CMD_PENDING;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (urb->dev->level == 1 &&
|
||||
(xhci->meson_quirks & XHCI_CRG_HOST_011) &&
|
||||
urb->ep && usb_endpoint_xfer_isoc(&urb->ep->desc) &&
|
||||
urb->dev->descriptor.bDeviceClass == 0xef &&
|
||||
urb->dev->speed == USB_SPEED_HIGH) {
|
||||
delay_stop = 5;
|
||||
|
||||
ep->stop_cmd_queue_timer.expires = jiffies +
|
||||
1 * delay_stop;
|
||||
ep->slot_id = urb->dev->slot_id;
|
||||
ep->ep_index = ep_index;
|
||||
if (!timer_pending(&ep->stop_cmd_queue_timer))
|
||||
add_timer(&ep->stop_cmd_queue_timer);
|
||||
//aml_xhci_info(xhci, "host10 delay 5--ep=%d, slot=%d\n",
|
||||
//ep_index, ep->slot_id);
|
||||
} else if ((xhci->meson_quirks & XHCI_CRG_HOST_DELAY) &&
|
||||
urb->ep && !usb_endpoint_xfer_control(&urb->ep->desc)) {
|
||||
if (urb->dev->level == 1 &&
|
||||
(xhci->meson_quirks & XHCI_CRG_HOST_011) &&
|
||||
urb->ep && usb_endpoint_xfer_int(&urb->ep->desc) &&
|
||||
urb->dev->descriptor.bDeviceClass == 0xef &&
|
||||
urb->dev->speed == USB_SPEED_HIGH)
|
||||
delay_stop = 30;
|
||||
else
|
||||
delay_stop = 20;
|
||||
|
||||
ep->stop_cmd_queue_timer.expires = jiffies +
|
||||
1 * delay_stop;
|
||||
ep->slot_id = urb->dev->slot_id;
|
||||
ep->ep_index = ep_index;
|
||||
if (!timer_pending(&ep->stop_cmd_queue_timer))
|
||||
add_timer(&ep->stop_cmd_queue_timer);
|
||||
//aml_xhci_info(xhci, "host001, delay=%d, ep=%d, slot=%d\n",
|
||||
//delay_stop, ep_index, ep->slot_id);
|
||||
} else {
|
||||
command = aml_xhci_alloc_command(xhci, false, GFP_ATOMIC);
|
||||
if (!command) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
aml_xhci_queue_stop_endpoint(xhci, command, urb->dev->slot_id,
|
||||
ep_index, 0);
|
||||
aml_xhci_ring_cmd_db(xhci);
|
||||
//aml_xhci_info(xhci, "no delay---ep=%d, slot=%d\n",
|
||||
//ep_index, urb->dev->slot_id);
|
||||
}
|
||||
#else
|
||||
command = aml_xhci_alloc_command(xhci, false, GFP_ATOMIC);
|
||||
if (!command) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
ep->ep_state |= EP_STOP_CMD_PENDING;
|
||||
//ep->ep_state |= EP_STOP_CMD_PENDING;
|
||||
aml_xhci_queue_stop_endpoint(xhci, command, urb->dev->slot_id,
|
||||
ep_index, 0);
|
||||
aml_xhci_ring_cmd_db(xhci);
|
||||
#endif
|
||||
}
|
||||
done:
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
@@ -2808,14 +2929,42 @@ static int xhci_configure_endpoint(struct aml_xhci_hcd *xhci,
|
||||
struct aml_xhci_input_control_ctx *ctrl_ctx;
|
||||
struct aml_xhci_virt_device *virt_dev;
|
||||
struct aml_xhci_slot_ctx *slot_ctx;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
int i;
|
||||
struct aml_xhci_ring *ring;
|
||||
struct usb_device *tmp_udev;
|
||||
#endif
|
||||
|
||||
if (!command)
|
||||
return -EINVAL;
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_003) {
|
||||
if (udev->speed == USB_SPEED_FULL &&
|
||||
udev->level == 1 && udev->state == USB_STATE_NOTATTACHED) {
|
||||
db_wait = 1;
|
||||
for (i = 0; i < MAX_HC_SLOTS; i++) {
|
||||
if (xhci->devs[i] && xhci->devs[i]->udev) {
|
||||
tmp_udev = xhci->devs[i]->udev;
|
||||
if (tmp_udev != udev) {
|
||||
aml_xhci_info(xhci, "########aml_xhci_stop_device slot id = %d\n",
|
||||
tmp_udev->slot_id);
|
||||
aml_xhci_stop_device(xhci, tmp_udev->slot_id, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
msleep(100);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
if (xhci->xhc_state & XHCI_STATE_DYING) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
db_wait = 0;
|
||||
#endif
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
@@ -2826,6 +2975,9 @@ static int xhci_configure_endpoint(struct aml_xhci_hcd *xhci,
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
aml_xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
|
||||
__func__);
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
db_wait = 0;
|
||||
#endif
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@@ -2835,6 +2987,9 @@ static int xhci_configure_endpoint(struct aml_xhci_hcd *xhci,
|
||||
aml_xhci_warn(xhci, "Not enough host resources, "
|
||||
"active endpoint contexts = %u\n",
|
||||
xhci->num_active_eps);
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
db_wait = 0;
|
||||
#endif
|
||||
return -ENOMEM;
|
||||
}
|
||||
if ((xhci->quirks & XHCI_SW_BW_CHECKING) && !ctx_change &&
|
||||
@@ -2843,6 +2998,9 @@ static int xhci_configure_endpoint(struct aml_xhci_hcd *xhci,
|
||||
xhci_free_host_resources(xhci, ctrl_ctx);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
aml_xhci_warn(xhci, "Not enough bandwidth\n");
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
db_wait = 0;
|
||||
#endif
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@@ -2865,14 +3023,107 @@ static int xhci_configure_endpoint(struct aml_xhci_hcd *xhci,
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
aml_xhci_dbg_trace(xhci, trace_aml_xhci_dbg_context_change,
|
||||
"FIXME allocate a new ring segment");
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
db_wait = 0;
|
||||
#endif
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
/*Full speed device disconnect*/
|
||||
if ((xhci->meson_quirks & XHCI_CRG_HOST_003) &&
|
||||
(udev) && udev->speed == USB_SPEED_FULL && udev->level == 1 &&
|
||||
udev->state == USB_STATE_NOTATTACHED) {
|
||||
for (i = 0; i < 31; i++) {
|
||||
ring = xhci->devs[udev->slot_id]->eps[i].ring;
|
||||
if (ring) {
|
||||
aml_xhci_info(xhci, "#######aml_queue_trb i = %d####\n", i);
|
||||
aml_queue_trb(xhci, ring, 0,
|
||||
ring->first_seg->trbs->generic.field[0],
|
||||
ring->first_seg->trbs->generic.field[1],
|
||||
ring->first_seg->trbs->generic.field[2],
|
||||
(ring->first_seg->trbs->generic.field[3] & ~0x1) |
|
||||
ring->cycle_state | TRB_IOC);
|
||||
|
||||
writel(DB_VALUE(i, 0), &xhci->dba->doorbell[udev->slot_id]);
|
||||
mdelay(5);
|
||||
|
||||
aml_queue_trb(xhci, ring, 0,
|
||||
ring->first_seg->trbs->generic.field[0],
|
||||
ring->first_seg->trbs->generic.field[1],
|
||||
ring->first_seg->trbs->generic.field[2],
|
||||
(ring->first_seg->trbs->generic.field[3] & ~0x1) |
|
||||
ring->cycle_state | TRB_IOC);
|
||||
|
||||
writel(DB_VALUE(i, 0), &xhci->dba->doorbell[udev->slot_id]);
|
||||
mdelay(5);
|
||||
|
||||
aml_queue_trb(xhci, ring, 0,
|
||||
ring->first_seg->trbs->generic.field[0],
|
||||
ring->first_seg->trbs->generic.field[1],
|
||||
ring->first_seg->trbs->generic.field[2],
|
||||
(ring->first_seg->trbs->generic.field[3] & ~0x1) |
|
||||
ring->cycle_state | TRB_IOC);
|
||||
|
||||
writel(DB_VALUE(i, 0), &xhci->dba->doorbell[udev->slot_id]);
|
||||
mdelay(5);
|
||||
|
||||
aml_queue_trb(xhci, ring, 0,
|
||||
ring->first_seg->trbs->generic.field[0],
|
||||
ring->first_seg->trbs->generic.field[1],
|
||||
ring->first_seg->trbs->generic.field[2],
|
||||
(ring->first_seg->trbs->generic.field[3] & ~0x1) |
|
||||
ring->cycle_state | TRB_IOC);
|
||||
|
||||
writel(DB_VALUE(i, 0), &xhci->dba->doorbell[udev->slot_id]);
|
||||
mdelay(5);
|
||||
|
||||
aml_queue_trb(xhci, ring, 0,
|
||||
ring->first_seg->trbs->generic.field[0],
|
||||
ring->first_seg->trbs->generic.field[1],
|
||||
ring->first_seg->trbs->generic.field[2],
|
||||
(ring->first_seg->trbs->generic.field[3] & ~0x1) |
|
||||
ring->cycle_state | TRB_IOC);
|
||||
|
||||
writel(DB_VALUE(i, 0), &xhci->dba->doorbell[udev->slot_id]);
|
||||
mdelay(5);
|
||||
|
||||
aml_queue_trb(xhci, ring, 0,
|
||||
ring->first_seg->trbs->generic.field[0],
|
||||
ring->first_seg->trbs->generic.field[1],
|
||||
ring->first_seg->trbs->generic.field[2],
|
||||
(ring->first_seg->trbs->generic.field[3] & ~0x1) |
|
||||
ring->cycle_state | TRB_IOC);
|
||||
writel(DB_VALUE(i, 0), &xhci->dba->doorbell[udev->slot_id]);
|
||||
mdelay(5);
|
||||
//break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
aml_xhci_ring_cmd_db(xhci);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
|
||||
/* Wait for the configure endpoint command to complete */
|
||||
wait_for_completion(command->completion);
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if ((xhci->meson_quirks & XHCI_CRG_HOST_003) &&
|
||||
(udev) && udev->speed == USB_SPEED_FULL && udev->level == 1 &&
|
||||
udev->state == USB_STATE_NOTATTACHED) {
|
||||
db_wait = 0;
|
||||
for (i = 0; i < MAX_HC_SLOTS; i++) {
|
||||
if (xhci->devs[i] && xhci->devs[i]->udev) {
|
||||
if (xhci->devs[i]->udev != udev) {
|
||||
aml_xhci_info(xhci, "########aml_xhci_ring_device slot id = %d\n",
|
||||
xhci->devs[i]->udev->slot_id);
|
||||
aml_xhci_ring_device(xhci, xhci->devs[i]->udev->slot_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ctx_change)
|
||||
ret = xhci_configure_endpoint_result(xhci, udev,
|
||||
&command->status);
|
||||
@@ -3710,6 +3961,252 @@ void aml_xhci_free_device_endpoint_resources(struct aml_xhci_hcd *xhci,
|
||||
* re-initialization during S3/S4. In this case, call aml_xhci_alloc_dev() to
|
||||
* re-allocate the device.
|
||||
*/
|
||||
/* used when updating an endpoint's URB list */
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
static DEFINE_SPINLOCK(xhci_hcd_urb_list_lock);
|
||||
|
||||
static struct urb *xhci_usb_get_urb(struct urb *urb)
|
||||
{
|
||||
if (urb)
|
||||
kref_get(&urb->kref);
|
||||
return urb;
|
||||
}
|
||||
|
||||
static void xhci_usb_hcd_disable_endpoint(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
might_sleep();
|
||||
hcd = bus_to_hcd(udev->bus);
|
||||
if (hcd->driver->endpoint_disable)
|
||||
hcd->driver->endpoint_disable(hcd, ep);
|
||||
}
|
||||
|
||||
static void xhci_usb_hcd_flush_endpoint(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct urb *urb;
|
||||
|
||||
if (!ep)
|
||||
return;
|
||||
might_sleep();
|
||||
hcd = bus_to_hcd(udev->bus);
|
||||
|
||||
/* No more submits can occur */
|
||||
spin_lock_irq(&xhci_hcd_urb_list_lock);
|
||||
rescan:
|
||||
list_for_each_entry_reverse(urb, &ep->urb_list, urb_list) {
|
||||
int is_in;
|
||||
|
||||
if (urb->unlinked)
|
||||
continue;
|
||||
xhci_usb_get_urb(urb);
|
||||
is_in = usb_urb_dir_in(urb);
|
||||
spin_unlock(&xhci_hcd_urb_list_lock);
|
||||
|
||||
/* kick hcd */
|
||||
//unlink1(hcd, urb, -ESHUTDOWN);
|
||||
hcd->driver->urb_dequeue(hcd, urb, -ESHUTDOWN);
|
||||
dev_dbg(hcd->self.controller,
|
||||
"shutdown urb %pK ep%d%s-%s\n",
|
||||
urb, usb_endpoint_num(&ep->desc),
|
||||
is_in ? "in" : "out",
|
||||
usb_ep_type_string(usb_endpoint_type(&ep->desc)));
|
||||
usb_put_urb(urb);
|
||||
|
||||
/* list contents may have changed */
|
||||
spin_lock(&xhci_hcd_urb_list_lock);
|
||||
goto rescan;
|
||||
}
|
||||
spin_unlock_irq(&xhci_hcd_urb_list_lock);
|
||||
|
||||
/* Wait until the endpoint queue is completely empty */
|
||||
while (!list_empty(&ep->urb_list)) {
|
||||
spin_lock_irq(&xhci_hcd_urb_list_lock);
|
||||
|
||||
/* The list may have changed while we acquired the spinlock */
|
||||
urb = NULL;
|
||||
if (!list_empty(&ep->urb_list)) {
|
||||
urb = list_entry(ep->urb_list.prev, struct urb,
|
||||
urb_list);
|
||||
xhci_usb_get_urb(urb);
|
||||
}
|
||||
spin_unlock_irq(&xhci_hcd_urb_list_lock);
|
||||
|
||||
if (urb) {
|
||||
usb_kill_urb(urb);
|
||||
usb_put_urb(urb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void xhci_usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr,
|
||||
bool reset_hardware)
|
||||
{
|
||||
unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
|
||||
struct usb_host_endpoint *ep;
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
if (usb_endpoint_out(epaddr)) {
|
||||
ep = dev->ep_out[epnum];
|
||||
if (reset_hardware && epnum != 0)
|
||||
dev->ep_out[epnum] = NULL;
|
||||
} else {
|
||||
ep = dev->ep_in[epnum];
|
||||
if (reset_hardware && epnum != 0)
|
||||
dev->ep_in[epnum] = NULL;
|
||||
}
|
||||
if (ep) {
|
||||
ep->enabled = 0;
|
||||
xhci_usb_hcd_flush_endpoint(dev, ep);
|
||||
if (reset_hardware)
|
||||
xhci_usb_hcd_disable_endpoint(dev, ep);
|
||||
}
|
||||
}
|
||||
|
||||
static int xhci_hcd_alloc_bandwidth(struct usb_device *udev,
|
||||
struct usb_host_config *new_config,
|
||||
struct usb_host_interface *cur_alt,
|
||||
struct usb_host_interface *new_alt)
|
||||
{
|
||||
int num_intfs, i, j;
|
||||
struct usb_host_interface *alt = NULL;
|
||||
int ret = 0;
|
||||
struct usb_hcd *hcd;
|
||||
struct usb_host_endpoint *ep;
|
||||
|
||||
hcd = bus_to_hcd(udev->bus);
|
||||
if (!hcd->driver->check_bandwidth)
|
||||
return 0;
|
||||
|
||||
/* Configuration is being removed - set configuration 0 */
|
||||
if (!new_config && !cur_alt) {
|
||||
for (i = 1; i < 16; ++i) {
|
||||
ep = udev->ep_out[i];
|
||||
if (ep)
|
||||
hcd->driver->drop_endpoint(hcd, udev, ep);
|
||||
ep = udev->ep_in[i];
|
||||
if (ep)
|
||||
hcd->driver->drop_endpoint(hcd, udev, ep);
|
||||
}
|
||||
hcd->driver->check_bandwidth(hcd, udev);
|
||||
return 0;
|
||||
}
|
||||
/* Check if the HCD says there's enough bandwidth. Enable all endpoints
|
||||
* each interface's alt setting 0 and ask the HCD to check the bandwidth
|
||||
* of the bus. There will always be bandwidth for endpoint 0, so it's
|
||||
* ok to exclude it.
|
||||
*/
|
||||
if (new_config) {
|
||||
num_intfs = new_config->desc.bNumInterfaces;
|
||||
/* Remove endpoints (except endpoint 0, which is always on the
|
||||
* schedule) from the old config from the schedule
|
||||
*/
|
||||
for (i = 1; i < 16; ++i) {
|
||||
ep = udev->ep_out[i];
|
||||
if (ep) {
|
||||
ret = hcd->driver->drop_endpoint(hcd, udev, ep);
|
||||
if (ret < 0)
|
||||
goto reset;
|
||||
}
|
||||
ep = udev->ep_in[i];
|
||||
if (ep) {
|
||||
ret = hcd->driver->drop_endpoint(hcd, udev, ep);
|
||||
if (ret < 0)
|
||||
goto reset;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < num_intfs; ++i) {
|
||||
struct usb_host_interface *first_alt;
|
||||
int iface_num;
|
||||
|
||||
first_alt = &new_config->intf_cache[i]->altsetting[0];
|
||||
iface_num = first_alt->desc.bInterfaceNumber;
|
||||
/* Set up endpoints for alternate interface setting 0 */
|
||||
|
||||
alt = usb_find_alt_setting(new_config, iface_num, 0);
|
||||
if (!alt)
|
||||
/* No alt setting 0? Pick the first setting. */
|
||||
alt = first_alt;
|
||||
|
||||
for (j = 0; j < alt->desc.bNumEndpoints; j++) {
|
||||
ret = hcd->driver->add_endpoint(hcd, udev, &alt->endpoint[j]);
|
||||
if (ret < 0)
|
||||
goto reset;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cur_alt && new_alt) {
|
||||
struct usb_interface *iface = usb_ifnum_to_if(udev,
|
||||
cur_alt->desc.bInterfaceNumber);
|
||||
|
||||
if (!iface)
|
||||
return -EINVAL;
|
||||
if (iface->resetting_device) {
|
||||
/*
|
||||
* The USB core just reset the device, so the xHCI host
|
||||
* and the device will think alt setting 0 is installed.
|
||||
* However, the USB core will pass in the alternate
|
||||
* setting installed before the reset as cur_alt. Dig
|
||||
* out the alternate setting 0 structure, or the first
|
||||
* alternate setting if a broken device doesn't have alt
|
||||
* setting 0.
|
||||
*/
|
||||
cur_alt = usb_altnum_to_altsetting(iface, 0);
|
||||
if (!cur_alt)
|
||||
cur_alt = &iface->altsetting[0];
|
||||
}
|
||||
|
||||
/* Drop all the endpoints in the current alt setting */
|
||||
for (i = 0; i < cur_alt->desc.bNumEndpoints; i++) {
|
||||
ret = hcd->driver->drop_endpoint(hcd, udev,
|
||||
&cur_alt->endpoint[i]);
|
||||
if (ret < 0)
|
||||
goto reset;
|
||||
}
|
||||
/* Add all the endpoints in the new alt setting */
|
||||
for (i = 0; i < new_alt->desc.bNumEndpoints; i++) {
|
||||
ret = hcd->driver->add_endpoint(hcd, udev,
|
||||
&new_alt->endpoint[i]);
|
||||
if (ret < 0)
|
||||
goto reset;
|
||||
}
|
||||
}
|
||||
ret = hcd->driver->check_bandwidth(hcd, udev);
|
||||
reset:
|
||||
if (ret < 0)
|
||||
hcd->driver->reset_bandwidth(hcd, udev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void xhci_usb_disable_device_endpoints(struct usb_device *dev, int skip_ep0)
|
||||
{
|
||||
struct usb_hcd *hcd = bus_to_hcd(dev->bus);
|
||||
int i;
|
||||
|
||||
if (hcd->driver->check_bandwidth) {
|
||||
/* First pass: Cancel URBs, leave endpoint pointers intact. */
|
||||
for (i = skip_ep0; i < 16; ++i) {
|
||||
xhci_usb_disable_endpoint(dev, i, false);
|
||||
xhci_usb_disable_endpoint(dev, i + USB_DIR_IN, false);
|
||||
}
|
||||
/* Remove endpoints from the host controller internal state */
|
||||
mutex_lock(hcd->bandwidth_mutex);
|
||||
xhci_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
}
|
||||
/* Second pass: remove endpoint pointers */
|
||||
for (i = skip_ep0; i < 16; ++i) {
|
||||
xhci_usb_disable_endpoint(dev, i, true);
|
||||
xhci_usb_disable_endpoint(dev, i + USB_DIR_IN, true);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
|
||||
struct usb_device *udev)
|
||||
{
|
||||
@@ -3738,6 +4235,16 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_016) {
|
||||
if (udev->state == USB_STATE_CONFIGURED &&
|
||||
udev->level > 1 &&
|
||||
udev->speed == USB_SPEED_FULL) {
|
||||
xhci_usb_disable_device_endpoints(udev, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (virt_dev->tt_info)
|
||||
old_active_eps = virt_dev->tt_info->active_eps;
|
||||
|
||||
@@ -3900,8 +4407,19 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
|
||||
trace_aml_xhci_free_dev(slot_ctx);
|
||||
|
||||
/* Stop any wayward timer functions (which may grab the lock) */
|
||||
for (i = 0; i < 31; i++)
|
||||
for (i = 0; i < 31; i++) {
|
||||
virt_dev->eps[i].ep_state &= ~EP_STOP_CMD_PENDING;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_DELAY) {
|
||||
while (timer_pending(&virt_dev->eps[i].stop_cmd_queue_timer)) {
|
||||
aml_xhci_info(xhci, "waiting stop ep=%d, slot_id=%d\n",
|
||||
i, udev->slot_id);
|
||||
usleep_range(500, 1000);
|
||||
}
|
||||
del_timer_sync(&virt_dev->eps[i].stop_cmd_queue_timer);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
virt_dev->udev = NULL;
|
||||
aml_xhci_disable_slot(xhci, udev->slot_id);
|
||||
|
||||
@@ -4038,6 +4556,10 @@ int aml_xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
|
||||
* mass storage driver error handling.
|
||||
*/
|
||||
if (!aml_xhci_alloc_virt_device(xhci, slot_id, udev, GFP_NOIO)) {
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (slot_id != 0)
|
||||
udev->slot_id = slot_id;
|
||||
#endif
|
||||
aml_xhci_warn(xhci, "Could not allocate xHCI USB device data structures\n");
|
||||
goto disable_slot;
|
||||
}
|
||||
@@ -5213,6 +5735,11 @@ int aml_xhci_gen_setup(struct usb_hcd *hcd, aml_xhci_get_quirks_t get_quirks)
|
||||
|
||||
xhci = hcd_to_xhci(hcd);
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
/* CRG_SG_NUM */
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_010)
|
||||
hcd->self.sg_tablesize = 4;
|
||||
#endif
|
||||
if (!usb_hcd_is_primary_hcd(hcd)) {
|
||||
xhci_hcd_init_usb3_data(xhci, hcd);
|
||||
return 0;
|
||||
@@ -5300,10 +5827,16 @@ int aml_xhci_gen_setup(struct usb_hcd *hcd, aml_xhci_get_quirks_t get_quirks)
|
||||
return retval;
|
||||
aml_xhci_dbg(xhci, "Called HCD init\n");
|
||||
|
||||
if (xhci_hcd_is_usb3(hcd))
|
||||
if (xhci_hcd_is_usb3(hcd)) {
|
||||
xhci_hcd_init_usb3_data(xhci, hcd);
|
||||
else
|
||||
} else {
|
||||
xhci_hcd_init_usb2_data(xhci, hcd);
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
/* CRG_SG_NUM */
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_010)
|
||||
hcd->self.sg_tablesize = ~0;
|
||||
#endif
|
||||
}
|
||||
|
||||
aml_xhci_info(xhci, "hcc params 0x%08x hci version 0x%x quirks 0x%016llx\n",
|
||||
xhci->hcc_params, xhci->hci_version, xhci->quirks);
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
/* Section 5.3.3 - MaxPorts */
|
||||
#define MAX_HC_PORTS 127
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
#define XHCI_TRANS_ERRS_CNT 300
|
||||
#endif
|
||||
|
||||
/*
|
||||
* xHCI register interface.
|
||||
* This corresponds to the eXtensible Host Controller Interface (xHCI)
|
||||
@@ -694,6 +698,15 @@ struct aml_xhci_virt_ep {
|
||||
int next_frame_id;
|
||||
/* Use new Isoch TRB layout needed for extended TBC support */
|
||||
bool use_extended_tbc;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
__u8 bendpointaddress;
|
||||
struct work_struct stop_work;
|
||||
int q_status_count;
|
||||
struct usb_device *udev;
|
||||
/*for xhci-crg-host-plug-died*/
|
||||
struct timer_list stop_cmd_queue_timer;
|
||||
int slot_id;
|
||||
#endif
|
||||
};
|
||||
|
||||
enum aml_xhci_overhead_type {
|
||||
@@ -1387,10 +1400,25 @@ struct aml_xhci_scratchpad {
|
||||
struct aml_urb_priv {
|
||||
int num_tds;
|
||||
int num_tds_done;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
int need_event_data;
|
||||
u8 need_div;
|
||||
//unsigned char tmp_buf2[1024*8];
|
||||
void *tmp_buf;
|
||||
dma_addr_t tmp_dma;
|
||||
u64 dst_dma[6];
|
||||
u64 dst_buf[6];
|
||||
struct urb *urb;
|
||||
int need_event_data_flag;
|
||||
#endif
|
||||
struct aml_xhci_td td[] __counted_by(num_tds);
|
||||
};
|
||||
|
||||
/* Number of Event Ring segments to allocate, when amount is not specified. (spec allows 32k) */
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
#define AML_ERST_NUM_SEGS 10
|
||||
#define AML_HOST11_ERST_NUM_SEGS 32
|
||||
#endif
|
||||
#define ERST_DEFAULT_SEGS 2
|
||||
/* Poll every 60 seconds */
|
||||
#define POLL_TIMEOUT 60
|
||||
@@ -1624,6 +1652,24 @@ struct aml_xhci_hcd {
|
||||
#define XHCI_ZHAOXIN_HOST BIT_ULL(46)
|
||||
#define XHCI_WRITE_64_HI_LO BIT_ULL(47)
|
||||
#define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48)
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
#define XHCI_DISABLE_IDT BIT_ULL(61)
|
||||
#define XHCI_AML_SUPER_SPEED_SUPPORT BIT_ULL(62)
|
||||
unsigned long long meson_quirks;
|
||||
#define XHCI_CRG_HOST_DELAY BIT_ULL(1)
|
||||
#define XHCI_CRG_HOST_EPROTO BIT_ULL(2)
|
||||
#define XHCI_CRG_HOST_003 BIT_ULL(3)
|
||||
#define XHCI_CRG_HOST_007 BIT_ULL(7)
|
||||
#define XHCI_CRG_HOST_008 BIT_ULL(8)
|
||||
#define XHCI_CRG_HOST_009 BIT_ULL(9)
|
||||
#define XHCI_CRG_HOST_010 BIT_ULL(10)
|
||||
#define XHCI_CRG_HOST_011 BIT_ULL(11)
|
||||
#define XHCI_CRG_HOST_014 BIT_ULL(14)
|
||||
#define XHCI_CRG_HOST_016 BIT_ULL(16)
|
||||
#define XHCI_CRG_HOST BIT_ULL(19)
|
||||
|
||||
int xhci_trans_err_cnt;
|
||||
#endif
|
||||
|
||||
unsigned int num_active_eps;
|
||||
unsigned int limit_active_eps;
|
||||
@@ -1762,6 +1808,10 @@ void aml_xhci_dbg_trace(struct aml_xhci_hcd *xhci, void (*trace)(struct va_forma
|
||||
/* xHCI memory management */
|
||||
void aml_xhci_mem_cleanup(struct aml_xhci_hcd *xhci);
|
||||
int aml_xhci_mem_init(struct aml_xhci_hcd *xhci, gfp_t flags);
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
void xhci_free_stop_ep_timer(struct aml_xhci_hcd *xhci, int slot_id);
|
||||
void xhci_del_stop_ep_timer(struct aml_xhci_hcd *xhci, int slot_id);
|
||||
#endif
|
||||
void aml_xhci_free_virt_device(struct aml_xhci_hcd *xhci, int slot_id);
|
||||
int aml_xhci_alloc_virt_device(struct aml_xhci_hcd *xhci, int slot_id, struct usb_device *udev, gfp_t flags);
|
||||
int aml_xhci_setup_addressable_virt_dev(struct aml_xhci_hcd *xhci, struct usb_device *udev);
|
||||
@@ -1965,12 +2015,23 @@ static inline struct aml_xhci_ring *xhci_urb_to_transfer_ring(struct aml_xhci_hc
|
||||
*/
|
||||
static inline bool xhci_urb_suitable_for_idt(struct urb *urb)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
|
||||
struct aml_xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
#endif
|
||||
|
||||
if (!usb_endpoint_xfer_isoc(&urb->ep->desc) && usb_urb_dir_out(urb) &&
|
||||
usb_endpoint_maxp(&urb->ep->desc) >= TRB_IDT_MAX_SIZE &&
|
||||
urb->transfer_buffer_length <= TRB_IDT_MAX_SIZE &&
|
||||
!(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) &&
|
||||
!urb->num_sgs)
|
||||
!urb->num_sgs) {
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if ((xhci->quirks & XHCI_DISABLE_IDT) &&
|
||||
urb->transfer_buffer_length == 0)
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -2527,4 +2588,18 @@ static inline const char *xhci_decode_ep_context(char *str, u32 info,
|
||||
return str;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
extern unsigned int db_wait;
|
||||
void xhci_start_stop_endpoint_work(struct aml_xhci_hcd *xhci,
|
||||
int slot_id, unsigned int ep_index);
|
||||
int aml_xhci_stop_device(struct aml_xhci_hcd *xhci, int slot_id, int suspend);
|
||||
void aml_xhci_ring_device(struct aml_xhci_hcd *xhci, int slot_id);
|
||||
void aml_queue_trb(struct aml_xhci_hcd *xhci, struct aml_xhci_ring *ring,
|
||||
bool more_trbs_coming,
|
||||
u32 field1, u32 field2, u32 field3, u32 field4);
|
||||
int xhci_find_low_speed_dev(struct aml_xhci_hcd *xhci);
|
||||
void xhci_stop_endpoint_command_timer(struct timer_list *t);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __LINUX_XHCI_HCD_H */
|
||||
|
||||
@@ -240,6 +240,9 @@ int aml_xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, con
|
||||
|
||||
/* imod_interval is the interrupt moderation value in nanoseconds. */
|
||||
xhci->imod_interval = 40000;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
xhci->meson_quirks = 0;
|
||||
#endif
|
||||
|
||||
/* Iterate over all parent nodes for finding quirks */
|
||||
for (tmpdev = &pdev->dev; tmpdev; tmpdev = tmpdev->parent) {
|
||||
@@ -265,10 +268,45 @@ int aml_xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, con
|
||||
if (device_property_read_bool(tmpdev, "xhci-skip-phy-init-quirk"))
|
||||
xhci->quirks |= XHCI_SKIP_PHY_INIT;
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (device_property_read_bool(tmpdev, "resume_stuck_warm_reset"))
|
||||
xhci->quirks |= XHCI_MISSING_CAS;
|
||||
if (device_property_read_bool(tmpdev, "xhci-crg-host")) {
|
||||
xhci->quirks |= XHCI_DISABLE_IDT;
|
||||
xhci->meson_quirks |= XHCI_CRG_HOST;
|
||||
}
|
||||
if (device_property_read_bool(tmpdev, "super_speed_support"))
|
||||
xhci->quirks |= XHCI_AML_SUPER_SPEED_SUPPORT;
|
||||
if (device_property_read_bool(tmpdev, "xhci-crg-host-003"))
|
||||
xhci->meson_quirks |= XHCI_CRG_HOST_003;
|
||||
if (device_property_read_bool(tmpdev, "xhci-crg-host-007"))
|
||||
xhci->meson_quirks |= XHCI_CRG_HOST_007;
|
||||
if (device_property_read_bool(tmpdev, "xhci-crg-host-008"))
|
||||
xhci->meson_quirks |= XHCI_CRG_HOST_008;
|
||||
if (device_property_read_bool(tmpdev, "xhci-crg-host-009"))
|
||||
xhci->meson_quirks |= XHCI_CRG_HOST_009;
|
||||
if (device_property_read_bool(tmpdev, "xhci-crg-host-010"))
|
||||
xhci->meson_quirks |= XHCI_CRG_HOST_010;
|
||||
if (device_property_read_bool(tmpdev, "xhci-crg-host-011"))
|
||||
xhci->meson_quirks |= XHCI_CRG_HOST_011;
|
||||
if (device_property_read_bool(tmpdev, "xhci-crg-host-plug-died"))
|
||||
xhci->meson_quirks |= XHCI_CRG_HOST_DELAY;
|
||||
if (device_property_read_bool(tmpdev, "xhci-crg-host-eproto"))
|
||||
xhci->meson_quirks |= XHCI_CRG_HOST_EPROTO;
|
||||
if (device_property_read_bool(tmpdev, "xhci-crg-host-014"))
|
||||
xhci->meson_quirks |= XHCI_CRG_HOST_014;
|
||||
if (device_property_read_bool(tmpdev, "xhci-crg-host-016"))
|
||||
xhci->meson_quirks |= XHCI_CRG_HOST_016;
|
||||
#endif
|
||||
|
||||
device_property_read_u32(tmpdev, "imod-interval-ns",
|
||||
&xhci->imod_interval);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
aml_xhci_info(xhci, "meson-quirks=0x%llx---\n", xhci->meson_quirks);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Drivers such as dwc3 manages PHYs themself (and rely on driver name
|
||||
* matching for the xhci platform device).
|
||||
|
||||
@@ -505,6 +505,11 @@ void aml_xhci_ring_ep_doorbell(struct aml_xhci_hcd *xhci,
|
||||
if ((ep_state & EP_STOP_CMD_PENDING) || (ep_state & SET_DEQ_PENDING) ||
|
||||
(ep_state & EP_HALTED) || (ep_state & EP_CLEARING_TT))
|
||||
return;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_003)
|
||||
if (db_wait == 1)
|
||||
return;
|
||||
#endif
|
||||
|
||||
trace_aml_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id));
|
||||
|
||||
@@ -1599,7 +1604,12 @@ static void xhci_handle_cmd_nec_get_fw(struct aml_xhci_hcd *xhci,
|
||||
NEC_FW_MINOR(le32_to_cpu(event->status)));
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
static void xhci_complete_del_and_free_cmd(struct aml_xhci_hcd *xhci,
|
||||
struct aml_xhci_command *cmd, u32 status)
|
||||
#else
|
||||
static void xhci_complete_del_and_free_cmd(struct aml_xhci_command *cmd, u32 status)
|
||||
#endif
|
||||
{
|
||||
list_del(&cmd->cmd_list);
|
||||
|
||||
@@ -1609,14 +1619,23 @@ static void xhci_complete_del_and_free_cmd(struct aml_xhci_command *cmd, u32 sta
|
||||
} else {
|
||||
kfree(cmd);
|
||||
}
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->cmd_ring)
|
||||
aml_inc_deq(xhci, xhci->cmd_ring);
|
||||
#endif
|
||||
}
|
||||
|
||||
void aml_xhci_cleanup_command_queue(struct aml_xhci_hcd *xhci)
|
||||
{
|
||||
struct aml_xhci_command *cur_cmd, *tmp_cmd;
|
||||
xhci->current_cmd = NULL;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
list_for_each_entry_safe(cur_cmd, tmp_cmd, &xhci->cmd_list, cmd_list)
|
||||
xhci_complete_del_and_free_cmd(xhci, cur_cmd, COMP_COMMAND_ABORTED);
|
||||
#else
|
||||
list_for_each_entry_safe(cur_cmd, tmp_cmd, &xhci->cmd_list, cmd_list)
|
||||
xhci_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED);
|
||||
#endif
|
||||
}
|
||||
|
||||
void aml_xhci_handle_command_timeout(struct work_struct *work)
|
||||
@@ -1829,9 +1848,14 @@ static void handle_cmd_completion(struct aml_xhci_hcd *xhci,
|
||||
}
|
||||
|
||||
event_handled:
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
xhci_complete_del_and_free_cmd(xhci, cmd, cmd_comp_code);
|
||||
#else
|
||||
xhci_complete_del_and_free_cmd(cmd, cmd_comp_code);
|
||||
|
||||
aml_inc_deq(xhci, xhci->cmd_ring);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
static void handle_vendor_event(struct aml_xhci_hcd *xhci,
|
||||
@@ -2086,6 +2110,7 @@ struct aml_xhci_segment *aml_trb_in_td(struct aml_xhci_hcd *xhci, struct aml_xhc
|
||||
/* If the end TRB isn't in this segment, this is set to 0 */
|
||||
end_trb_dma = aml_xhci_trb_virt_to_dma(cur_seg, td->last_trb);
|
||||
|
||||
#if !IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (debug)
|
||||
aml_xhci_warn(xhci,
|
||||
"Looking for event-dma %016llx trb-start %016llx trb-end %016llx seg-start %016llx seg-end %016llx\n",
|
||||
@@ -2094,6 +2119,7 @@ struct aml_xhci_segment *aml_trb_in_td(struct aml_xhci_hcd *xhci, struct aml_xhc
|
||||
(unsigned long long)end_trb_dma,
|
||||
(unsigned long long)cur_seg->dma,
|
||||
(unsigned long long)end_seg_dma);
|
||||
#endif
|
||||
|
||||
if (end_trb_dma > 0) {
|
||||
/* The end TRB is in this segment, so suspect should be here */
|
||||
@@ -2189,8 +2215,45 @@ static int finish_td(struct aml_xhci_hcd *xhci, struct aml_xhci_virt_ep *ep,
|
||||
u32 trb_comp_code)
|
||||
{
|
||||
struct aml_xhci_ep_ctx *ep_ctx;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
int i = 0;
|
||||
int offset;
|
||||
dma_addr_t tmp_dma_align;
|
||||
struct aml_urb_priv *urb_priv;
|
||||
struct urb *urb = td->urb;
|
||||
#endif
|
||||
|
||||
ep_ctx = aml_xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep->ep_index);
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_010)
|
||||
urb_priv = urb->hcpriv;
|
||||
if ((xhci->meson_quirks & XHCI_CRG_HOST_010) &&
|
||||
urb_priv->need_div == 1) {
|
||||
tmp_dma_align = (urb_priv->tmp_dma + 1023) / 1024 * 1024;
|
||||
offset = (u64)tmp_dma_align - (u64)urb_priv->tmp_dma;
|
||||
if (urb_priv->tmp_buf) {
|
||||
while (urb_priv->dst_buf[i] != 0) {
|
||||
dma_sync_single_for_cpu(urb->dev->bus->controller,
|
||||
(unsigned long)phys_to_virt(tmp_dma_align + 512 * i),
|
||||
512, DMA_BIDIRECTIONAL);
|
||||
memcpy(phys_to_virt(urb_priv->dst_dma[i]),
|
||||
phys_to_virt(tmp_dma_align + 512 * i), 512);
|
||||
dma_sync_single_for_device(urb->dev->bus->controller,
|
||||
(unsigned long)phys_to_virt(urb_priv->dst_dma[i]),
|
||||
512, DMA_BIDIRECTIONAL);
|
||||
i++;
|
||||
}
|
||||
urb_priv->need_div = 0;
|
||||
/* amlogic add */
|
||||
wmb();
|
||||
dma_unmap_single(urb->dev->bus->controller,
|
||||
urb_priv->tmp_dma, 4096, DMA_FROM_DEVICE);
|
||||
kfree(urb_priv->tmp_buf);
|
||||
urb_priv->tmp_buf = NULL;
|
||||
urb_priv->tmp_dma = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (trb_comp_code) {
|
||||
case COMP_STOPPED_LENGTH_INVALID:
|
||||
@@ -2515,6 +2578,9 @@ static int process_bulk_intr_td(struct aml_xhci_hcd *xhci, struct aml_xhci_virt_
|
||||
struct aml_xhci_slot_ctx *slot_ctx;
|
||||
u32 trb_comp_code;
|
||||
u32 remaining, requested, ep_trb_len;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
struct aml_urb_priv *urb_priv;
|
||||
#endif
|
||||
|
||||
slot_ctx = aml_xhci_get_slot_ctx(xhci, ep->vdev->out_ctx);
|
||||
trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
|
||||
@@ -2525,9 +2591,23 @@ static int process_bulk_intr_td(struct aml_xhci_hcd *xhci, struct aml_xhci_virt_
|
||||
switch (trb_comp_code) {
|
||||
case COMP_SUCCESS:
|
||||
ep->err_count = 0;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
urb_priv = td->urb->hcpriv;
|
||||
if ((xhci->meson_quirks & XHCI_CRG_HOST_007) &&
|
||||
urb_priv && urb_priv->need_event_data) {
|
||||
td->urb->actual_length = requested;
|
||||
remaining = 0;
|
||||
}
|
||||
#endif
|
||||
/* handle success with untransferred data as short packet */
|
||||
if (ep_trb != td->last_trb || remaining) {
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if ((xhci->meson_quirks & XHCI_CRG_HOST_007) &&
|
||||
urb_priv && !urb_priv->need_event_data)
|
||||
aml_xhci_warn(xhci, "WARN Successful completion on short TX\n");
|
||||
#else
|
||||
aml_xhci_warn(xhci, "WARN Successful completion on short TX\n");
|
||||
#endif
|
||||
aml_xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n",
|
||||
td->urb->ep->desc.bEndpointAddress,
|
||||
requested, remaining);
|
||||
@@ -2774,6 +2854,21 @@ static int handle_tx_event(struct aml_xhci_hcd *xhci,
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_EPROTO) {
|
||||
if (status == -EPROTO) {
|
||||
xhci->xhci_trans_err_cnt++;
|
||||
if (xhci->xhci_trans_err_cnt == XHCI_TRANS_ERRS_CNT) {
|
||||
status = -ENOENT;
|
||||
aml_xhci_info(xhci, "-71 errors have been %d\n",
|
||||
xhci->xhci_trans_err_cnt);
|
||||
xhci->xhci_trans_err_cnt = 0;
|
||||
}
|
||||
} else {
|
||||
xhci->xhci_trans_err_cnt = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (list_empty(&ep_ring->td_list)) {
|
||||
/*
|
||||
@@ -2865,11 +2960,20 @@ static int handle_tx_event(struct aml_xhci_hcd *xhci,
|
||||
|
||||
if (!ep_seg) {
|
||||
/* HC is busted, give up! */
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (!(xhci->meson_quirks & XHCI_CRG_HOST_008))
|
||||
aml_xhci_err(xhci,
|
||||
"ERROR Transfer event TRB DMA ptr not "
|
||||
"part of current TD ep_index %d "
|
||||
"comp_code %u\n", ep_index,
|
||||
trb_comp_code);
|
||||
#else
|
||||
aml_xhci_err(xhci,
|
||||
"ERROR Transfer event TRB DMA ptr not "
|
||||
"part of current TD ep_index %d "
|
||||
"comp_code %u\n", ep_index,
|
||||
trb_comp_code);
|
||||
#endif
|
||||
aml_trb_in_td(xhci, td, ep_trb_dma, true);
|
||||
|
||||
return -ESHUTDOWN;
|
||||
@@ -3110,8 +3214,19 @@ irqreturn_t aml_xhci_irq(struct usb_hcd *hcd)
|
||||
}
|
||||
|
||||
if (!(status & STS_EINT)) {
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST) {
|
||||
ret = IRQ_HANDLED;
|
||||
aml_xhci_info(xhci, "irq is set, but the status register=0x%08x\n",
|
||||
status);
|
||||
} else {
|
||||
ret = IRQ_NONE;
|
||||
goto out;
|
||||
}
|
||||
#else
|
||||
ret = IRQ_NONE;
|
||||
goto out;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (status & STS_HCE) {
|
||||
@@ -3156,9 +3271,15 @@ EXPORT_SYMBOL_GPL(aml_xhci_msi_irq);
|
||||
* @more_trbs_coming: Will you enqueue more TRBs before calling
|
||||
* prepare_transfer()?
|
||||
*/
|
||||
static void queue_trb(struct aml_xhci_hcd *xhci, struct aml_xhci_ring *ring,
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
void aml_queue_trb(struct aml_xhci_hcd *xhci, struct aml_xhci_ring *ring,
|
||||
bool more_trbs_coming,
|
||||
u32 field1, u32 field2, u32 field3, u32 field4)
|
||||
#else
|
||||
static void aml_queue_trb(struct aml_xhci_hcd *xhci, struct aml_xhci_ring *ring,
|
||||
bool more_trbs_coming,
|
||||
u32 field1, u32 field2, u32 field3, u32 field4)
|
||||
#endif
|
||||
{
|
||||
struct aml_xhci_generic_trb *trb;
|
||||
|
||||
@@ -3569,6 +3690,26 @@ int aml_xhci_queue_bulk_tx(struct aml_xhci_hcd *xhci, gfp_t mem_flags,
|
||||
int sent_len, ret;
|
||||
u32 field, length_field, remainder;
|
||||
u64 addr, send_addr;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
u32 loop_cnt = 0;
|
||||
u32 len_array[32] = {0};
|
||||
u64 addr_array[32] = {0};
|
||||
//u64 event_data_ptr;
|
||||
u32 tmp_num_sgs;
|
||||
u32 tmp_block_len;
|
||||
struct scatterlist *tmp_sg;
|
||||
u64 tmp_addr;
|
||||
dma_addr_t tmp_dma_align;
|
||||
int i, k, len1, len2;
|
||||
u64 j, event_data_ptr = 0;
|
||||
|
||||
urb_priv = urb->hcpriv;
|
||||
if ((xhci->meson_quirks & XHCI_CRG_HOST_007) &&
|
||||
urb->dev->level >= 2 && urb_priv &&
|
||||
urb->dev->speed == USB_SPEED_LOW &&
|
||||
(usb_endpoint_xfer_int(&urb->ep->desc)))
|
||||
urb_priv->need_event_data = 1;
|
||||
#endif
|
||||
|
||||
ring = xhci_urb_to_transfer_ring(xhci, urb);
|
||||
if (!ring)
|
||||
@@ -3587,9 +3728,137 @@ int aml_xhci_queue_bulk_tx(struct aml_xhci_hcd *xhci, gfp_t mem_flags,
|
||||
addr = (u64) urb->transfer_dma;
|
||||
block_len = full_len;
|
||||
}
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if ((xhci->meson_quirks & XHCI_CRG_HOST_010) && urb_priv) {
|
||||
if (urb_priv->need_div == 1) {
|
||||
urb_priv->need_div = 0;
|
||||
if (urb->num_sgs > 1) {
|
||||
tmp_num_sgs = num_sgs;
|
||||
tmp_block_len = block_len;
|
||||
tmp_sg = sg;
|
||||
tmp_addr = addr;
|
||||
while (tmp_sg) {
|
||||
if (tmp_block_len % 1024 && tmp_num_sgs != 1 &&
|
||||
sg_next(tmp_sg))
|
||||
urb_priv->need_div = 1;
|
||||
--tmp_num_sgs;
|
||||
tmp_sg = sg_next(tmp_sg);
|
||||
if (tmp_num_sgs != 0 && tmp_sg) {
|
||||
tmp_block_len = sg_dma_len(tmp_sg);
|
||||
tmp_addr = (u64)sg_dma_address(tmp_sg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (urb_priv->need_div) {
|
||||
i = 0;
|
||||
j = 0;
|
||||
k = 0;
|
||||
len1 = 0;
|
||||
len2 = 0;
|
||||
tmp_num_sgs = num_sgs;
|
||||
tmp_block_len = block_len;
|
||||
tmp_sg = sg;
|
||||
tmp_addr = addr;
|
||||
|
||||
urb_priv->tmp_buf = kmalloc(4096, GFP_ATOMIC);
|
||||
if (!urb_priv->tmp_buf) {
|
||||
urb_priv->need_div = 0;
|
||||
goto out_host010;
|
||||
}
|
||||
memset(urb_priv->tmp_buf, 0, 4096);
|
||||
|
||||
memset(urb_priv->dst_dma, 0, sizeof(urb_priv->dst_dma));
|
||||
memset(urb_priv->dst_buf, 0, sizeof(urb_priv->dst_buf));
|
||||
|
||||
urb_priv->tmp_dma = dma_map_single(urb->dev->bus->controller,
|
||||
urb_priv->tmp_buf, 4096, DMA_FROM_DEVICE);
|
||||
tmp_dma_align = (urb_priv->tmp_dma + 1023) / 1024 * 1024;
|
||||
|
||||
while (tmp_sg) {
|
||||
if (len1) {
|
||||
len_array[i] = 1024;
|
||||
addr_array[i] = tmp_dma_align + 1024 * j++;
|
||||
urb_priv->dst_dma[k] = tmp_addr;
|
||||
urb_priv->dst_buf[k++] = (u64)(uintptr_t)sg_virt(tmp_sg) +
|
||||
(u64)sg_dma_len(tmp_sg) - (u64)len1;
|
||||
len1 = 0;
|
||||
tmp_block_len = 0;
|
||||
i++;
|
||||
goto NEXT_SG;
|
||||
}
|
||||
|
||||
if (len2) {
|
||||
urb_priv->dst_dma[k] = tmp_addr;
|
||||
urb_priv->dst_buf[k++] = (u64)(uintptr_t)sg_virt(tmp_sg);
|
||||
tmp_block_len -= len2;
|
||||
tmp_addr += len2;
|
||||
len2 = 0;
|
||||
}
|
||||
|
||||
if (tmp_block_len >= TRB_MAX_BUFF_SIZE) {
|
||||
len_array[i] = TRB_MAX_BUFF_SIZE;
|
||||
addr_array[i] = tmp_addr;
|
||||
tmp_block_len -= TRB_MAX_BUFF_SIZE;
|
||||
tmp_addr += TRB_MAX_BUFF_SIZE;
|
||||
i++;
|
||||
continue;
|
||||
} else if (tmp_block_len > 0) {
|
||||
if ((tmp_block_len % 1024) && (sg_next(tmp_sg))) {
|
||||
len1 = tmp_block_len % 1024;
|
||||
len2 = 1024 - len1;
|
||||
if (tmp_block_len > len1) {
|
||||
len_array[i] = tmp_block_len - len1;
|
||||
addr_array[i] = tmp_addr;
|
||||
tmp_addr = addr_array[i] + len_array[i];
|
||||
tmp_block_len = len1;
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
len_array[i] = tmp_block_len;
|
||||
addr_array[i] = tmp_addr;
|
||||
tmp_block_len = 0;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
NEXT_SG:
|
||||
--tmp_num_sgs;
|
||||
tmp_sg = sg_next(tmp_sg);
|
||||
if (tmp_num_sgs != 0 && tmp_sg) {
|
||||
tmp_block_len = sg_dma_len(tmp_sg);
|
||||
tmp_addr = (u64)sg_dma_address(tmp_sg);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out_host010:
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_007) {
|
||||
if (urb_priv->need_event_data) {
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id],
|
||||
ep_index, urb->stream_id,
|
||||
num_trbs + 1, urb, 0, mem_flags);
|
||||
} else {
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id],
|
||||
ep_index, urb->stream_id,
|
||||
num_trbs, urb, 0, mem_flags);
|
||||
}
|
||||
} else {
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id],
|
||||
ep_index, urb->stream_id,
|
||||
num_trbs, urb, 0, mem_flags);
|
||||
}
|
||||
|
||||
#else
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id],
|
||||
ep_index, urb->stream_id,
|
||||
num_trbs, urb, 0, mem_flags);
|
||||
#endif
|
||||
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
|
||||
@@ -3617,10 +3886,27 @@ int aml_xhci_queue_bulk_tx(struct aml_xhci_hcd *xhci, gfp_t mem_flags,
|
||||
|
||||
/* TRB buffer should not cross 64KB boundaries */
|
||||
trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(addr);
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if ((xhci->meson_quirks & XHCI_CRG_HOST_010) &&
|
||||
urb->dev->speed >= USB_SPEED_SUPER)
|
||||
trb_buff_len = TRB_MAX_BUFF_SIZE;
|
||||
#endif
|
||||
trb_buff_len = min_t(unsigned int, trb_buff_len, block_len);
|
||||
|
||||
if (enqd_len + trb_buff_len > full_len)
|
||||
trb_buff_len = full_len - enqd_len;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_010) {
|
||||
if (urb_priv->need_div) {
|
||||
trb_buff_len = len_array[loop_cnt];
|
||||
send_addr = addr_array[loop_cnt];
|
||||
|
||||
if (!send_addr || !trb_buff_len)
|
||||
WARN_ON(1);
|
||||
loop_cnt++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Don't change the cycle bit of the first TRB until later */
|
||||
if (first_trb) {
|
||||
@@ -3635,6 +3921,21 @@ int aml_xhci_queue_bulk_tx(struct aml_xhci_hcd *xhci, gfp_t mem_flags,
|
||||
*/
|
||||
if (enqd_len + trb_buff_len < full_len) {
|
||||
field |= TRB_CHAIN;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_010) {
|
||||
if (!(usb_urb_dir_in(urb) && ring->type == TYPE_BULK &&
|
||||
urb->dev->speed >= USB_SPEED_SUPER &&
|
||||
urb_priv->need_div == 1) &&
|
||||
trb_is_link(ring->enqueue + 1)) {
|
||||
if (xhci_align_td(xhci, urb,
|
||||
enqd_len, &trb_buff_len,
|
||||
ring->enq_seg)) {
|
||||
send_addr = ring->enq_seg->bounce_dma;
|
||||
/* assuming TD won't span 2 segs */
|
||||
td->bounce_seg = ring->enq_seg;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (trb_is_link(ring->enqueue + 1)) {
|
||||
if (xhci_align_td(xhci, urb, enqd_len,
|
||||
&trb_buff_len,
|
||||
@@ -3645,10 +3946,39 @@ int aml_xhci_queue_bulk_tx(struct aml_xhci_hcd *xhci, gfp_t mem_flags,
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (trb_is_link(ring->enqueue + 1)) {
|
||||
if (xhci_align_td(xhci, urb, enqd_len,
|
||||
&trb_buff_len,
|
||||
ring->enq_seg)) {
|
||||
send_addr = ring->enq_seg->bounce_dma;
|
||||
/* assuming TD won't span 2 segs */
|
||||
td->bounce_seg = ring->enq_seg;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (enqd_len + trb_buff_len >= full_len) {
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if ((xhci->meson_quirks & XHCI_CRG_HOST_007) &&
|
||||
urb_priv->need_event_data) {
|
||||
field |= TRB_CHAIN;
|
||||
field &= ~TRB_IOC;
|
||||
more_trbs_coming = true;
|
||||
event_data_ptr = ring->enq_seg->dma +
|
||||
(le64_to_cpu((long)ring->enqueue) -
|
||||
le64_to_cpu((long)ring->enq_seg->trbs));
|
||||
|
||||
} else {
|
||||
field &= ~TRB_CHAIN;
|
||||
field |= TRB_IOC;
|
||||
more_trbs_coming = false;
|
||||
}
|
||||
#else
|
||||
field &= ~TRB_CHAIN;
|
||||
field |= TRB_IOC;
|
||||
more_trbs_coming = false;
|
||||
#endif
|
||||
td->last_trb = ring->enqueue;
|
||||
td->last_trb_seg = ring->enq_seg;
|
||||
if (xhci_urb_suitable_for_idt(urb)) {
|
||||
@@ -3671,7 +4001,7 @@ int aml_xhci_queue_bulk_tx(struct aml_xhci_hcd *xhci, gfp_t mem_flags,
|
||||
TRB_TD_SIZE(remainder) |
|
||||
TRB_INTR_TARGET(0);
|
||||
|
||||
queue_trb(xhci, ring, more_trbs_coming | need_zero_pkt,
|
||||
aml_queue_trb(xhci, ring, more_trbs_coming | need_zero_pkt,
|
||||
lower_32_bits(send_addr),
|
||||
upper_32_bits(send_addr),
|
||||
length_field,
|
||||
@@ -3694,6 +4024,19 @@ int aml_xhci_queue_bulk_tx(struct aml_xhci_hcd *xhci, gfp_t mem_flags,
|
||||
send_addr = addr;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if ((xhci->meson_quirks & XHCI_CRG_HOST_007) &&
|
||||
urb_priv->need_event_data && event_data_ptr) {
|
||||
field = TRB_TYPE(TRB_EVENT_DATA) | ring->cycle_state | TRB_IOC;
|
||||
urb_priv->td[0].last_trb = ring->enqueue;
|
||||
urb_priv->td[0].last_trb_seg = ring->enq_seg;
|
||||
aml_queue_trb(xhci, ring, false,
|
||||
lower_32_bits(event_data_ptr),
|
||||
upper_32_bits(event_data_ptr),
|
||||
TRB_INTR_TARGET(0), field);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (need_zero_pkt) {
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id],
|
||||
ep_index, urb->stream_id,
|
||||
@@ -3701,7 +4044,7 @@ int aml_xhci_queue_bulk_tx(struct aml_xhci_hcd *xhci, gfp_t mem_flags,
|
||||
urb_priv->td[1].last_trb = ring->enqueue;
|
||||
urb_priv->td[1].last_trb_seg = ring->enq_seg;
|
||||
field = TRB_TYPE(TRB_NORMAL) | ring->cycle_state | TRB_IOC;
|
||||
queue_trb(xhci, ring, 0, 0, 0, TRB_INTR_TARGET(0), field);
|
||||
aml_queue_trb(xhci, ring, 0, 0, 0, TRB_INTR_TARGET(0), field);
|
||||
}
|
||||
|
||||
check_trb_math(urb, enqd_len);
|
||||
@@ -3779,7 +4122,7 @@ int aml_xhci_queue_ctrl_tx(struct aml_xhci_hcd *xhci, gfp_t mem_flags,
|
||||
}
|
||||
}
|
||||
|
||||
queue_trb(xhci, ep_ring, true,
|
||||
aml_queue_trb(xhci, ep_ring, true,
|
||||
setup->bRequestType | setup->bRequest << 8 | le16_to_cpu(setup->wValue) << 16,
|
||||
le16_to_cpu(setup->wIndex) | le16_to_cpu(setup->wLength) << 16,
|
||||
TRB_LEN(8) | TRB_INTR_TARGET(0),
|
||||
@@ -3815,7 +4158,7 @@ int aml_xhci_queue_ctrl_tx(struct aml_xhci_hcd *xhci, gfp_t mem_flags,
|
||||
TRB_INTR_TARGET(0);
|
||||
if (setup->bRequestType & USB_DIR_IN)
|
||||
field |= TRB_DIR_IN;
|
||||
queue_trb(xhci, ep_ring, true,
|
||||
aml_queue_trb(xhci, ep_ring, true,
|
||||
lower_32_bits(addr),
|
||||
upper_32_bits(addr),
|
||||
length_field,
|
||||
@@ -3832,7 +4175,7 @@ int aml_xhci_queue_ctrl_tx(struct aml_xhci_hcd *xhci, gfp_t mem_flags,
|
||||
field = 0;
|
||||
else
|
||||
field = TRB_DIR_IN;
|
||||
queue_trb(xhci, ep_ring, false,
|
||||
aml_queue_trb(xhci, ep_ring, false,
|
||||
0,
|
||||
0,
|
||||
TRB_INTR_TARGET(0),
|
||||
@@ -4135,7 +4478,7 @@ static int xhci_queue_isoc_tx(struct aml_xhci_hcd *xhci, gfp_t mem_flags,
|
||||
length_field |= TRB_TD_SIZE(remainder);
|
||||
first_trb = false;
|
||||
|
||||
queue_trb(xhci, ep_ring, more_trbs_coming,
|
||||
aml_queue_trb(xhci, ep_ring, more_trbs_coming,
|
||||
lower_32_bits(addr),
|
||||
upper_32_bits(addr),
|
||||
length_field,
|
||||
@@ -4190,6 +4533,248 @@ cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
static int m_xhci_queue_isoc_tx(struct aml_xhci_hcd *xhci, gfp_t mem_flags,
|
||||
struct urb *urb, int slot_id, unsigned int ep_index)
|
||||
{
|
||||
struct aml_xhci_interrupter *ir;
|
||||
struct aml_xhci_ring *ep_ring;
|
||||
struct aml_urb_priv *urb_priv;
|
||||
struct aml_xhci_td *td;
|
||||
int num_tds, trbs_per_td;
|
||||
struct aml_xhci_generic_trb *start_trb;
|
||||
bool first_trb;
|
||||
int start_cycle;
|
||||
u32 field, length_field;
|
||||
int running_total, trb_buff_len, td_len, td_remain_len, ret;
|
||||
u64 start_addr, addr;
|
||||
int i, j;
|
||||
bool more_trbs_coming;
|
||||
struct aml_xhci_virt_ep *xep;
|
||||
int frame_id;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
u64 event_data_ptr;
|
||||
|
||||
if ((xhci->meson_quirks & XHCI_CRG_HOST_008) &&
|
||||
(le16_to_cpu(urb->dev->descriptor.bDeviceClass) == 0xef) &&
|
||||
urb->dev->speed >= USB_SPEED_SUPER) {
|
||||
urb_priv = urb->hcpriv;
|
||||
urb_priv->need_event_data_flag = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
xep = &xhci->devs[slot_id]->eps[ep_index];
|
||||
ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
|
||||
ir = xhci->interrupters[0];
|
||||
|
||||
num_tds = urb->number_of_packets;
|
||||
if (num_tds < 1) {
|
||||
aml_xhci_dbg(xhci, "Isoc URB with zero packets?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
start_addr = (u64)urb->transfer_dma;
|
||||
start_trb = &ep_ring->enqueue->generic;
|
||||
start_cycle = ep_ring->cycle_state;
|
||||
|
||||
urb_priv = urb->hcpriv;
|
||||
/* Queue the TRBs for each TD, even if they are zero-length */
|
||||
for (i = 0; i < num_tds; i++) {
|
||||
unsigned int total_pkt_count, max_pkt;
|
||||
unsigned int burst_count, last_burst_pkt_count;
|
||||
u32 sia_frame_id;
|
||||
|
||||
first_trb = true;
|
||||
running_total = 0;
|
||||
addr = start_addr + urb->iso_frame_desc[i].offset;
|
||||
td_len = urb->iso_frame_desc[i].length;
|
||||
td_remain_len = td_len;
|
||||
max_pkt = usb_endpoint_maxp(&urb->ep->desc);
|
||||
total_pkt_count = DIV_ROUND_UP(td_len, max_pkt);
|
||||
|
||||
/* A zero-length transfer still involves at least one packet. */
|
||||
if (total_pkt_count == 0)
|
||||
total_pkt_count++;
|
||||
burst_count = xhci_get_burst_count(xhci, urb, total_pkt_count);
|
||||
last_burst_pkt_count = xhci_get_last_burst_packet_count(xhci,
|
||||
urb, total_pkt_count);
|
||||
|
||||
trbs_per_td = count_isoc_trbs_needed(urb, i);
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (urb_priv->need_event_data_flag)
|
||||
trbs_per_td++;
|
||||
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_010)
|
||||
if (urb->dev->speed == USB_SPEED_HIGH)
|
||||
trbs_per_td = 1;
|
||||
#endif
|
||||
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index,
|
||||
urb->stream_id, trbs_per_td, urb, i, mem_flags);
|
||||
if (ret < 0) {
|
||||
if (i == 0)
|
||||
return ret;
|
||||
goto cleanup;
|
||||
}
|
||||
td = &urb_priv->td[i];
|
||||
/* use SIA as default, if frame id is used overwrite it */
|
||||
sia_frame_id = TRB_SIA;
|
||||
if (!(urb->transfer_flags & URB_ISO_ASAP) &&
|
||||
HCC_CFC(xhci->hcc_params)) {
|
||||
frame_id = xhci_get_isoc_frame_id(xhci, urb, i);
|
||||
if (frame_id >= 0)
|
||||
sia_frame_id = TRB_FRAME_ID(frame_id);
|
||||
}
|
||||
/*
|
||||
* Set isoc specific data for the first TRB in a TD.
|
||||
* Prevent HW from getting the TRBs by keeping the cycle state
|
||||
* inverted in the first TDs isoc TRB.
|
||||
*/
|
||||
field = TRB_TYPE(TRB_ISOC) |
|
||||
TRB_TLBPC(last_burst_pkt_count) |
|
||||
sia_frame_id |
|
||||
(i ? ep_ring->cycle_state : !start_cycle);
|
||||
|
||||
/* xhci 1.1 with ETE uses TD_Size field for TBC, old is Rsvdz */
|
||||
if (!xep->use_extended_tbc)
|
||||
field |= TRB_TBC(burst_count);
|
||||
|
||||
/* fill the rest of the TRB fields, and remaining normal TRBs */
|
||||
for (j = 0; j < trbs_per_td; j++) {
|
||||
u32 remainder = 0;
|
||||
|
||||
/* only first TRB is isoc, overwrite otherwise */
|
||||
if (!first_trb)
|
||||
field = TRB_TYPE(TRB_NORMAL) |
|
||||
ep_ring->cycle_state;
|
||||
|
||||
/* Only set interrupt on short packet for IN EPs */
|
||||
if (usb_urb_dir_in(urb))
|
||||
field |= TRB_ISP;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if ((j == (trbs_per_td - 2)) &&
|
||||
urb_priv->need_event_data_flag) {
|
||||
event_data_ptr = ep_ring->enq_seg->dma +
|
||||
(le64_to_cpu((long)ep_ring->enqueue) -
|
||||
le64_to_cpu((long)ep_ring->enq_seg->trbs));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set the chain bit for all except the last TRB */
|
||||
if (j < trbs_per_td - 1) {
|
||||
more_trbs_coming = true;
|
||||
field |= TRB_CHAIN;
|
||||
} else {
|
||||
more_trbs_coming = false;
|
||||
td->last_trb = ep_ring->enqueue;
|
||||
td->last_trb_seg = ep_ring->enq_seg;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (urb_priv->need_event_data_flag) {
|
||||
field = TRB_TYPE(TRB_EVENT_DATA) |
|
||||
ep_ring->cycle_state | TRB_IOC;
|
||||
} else {
|
||||
field |= TRB_IOC;
|
||||
}
|
||||
#else
|
||||
field |= TRB_IOC;
|
||||
#endif
|
||||
if (trb_block_event_intr(xhci, num_tds, i, ir))
|
||||
field |= TRB_BEI;
|
||||
}
|
||||
/* Calculate TRB length */
|
||||
trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(addr);
|
||||
if (trb_buff_len > td_remain_len)
|
||||
trb_buff_len = td_remain_len;
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
else if ((xhci->meson_quirks & XHCI_CRG_HOST_010) &&
|
||||
urb->dev->speed == USB_SPEED_HIGH)
|
||||
trb_buff_len = td_remain_len;
|
||||
#endif
|
||||
|
||||
/* Set the TRB length, TD size, & interrupter fields. */
|
||||
remainder = xhci_td_remainder(xhci, running_total,
|
||||
trb_buff_len, td_len,
|
||||
urb, more_trbs_coming);
|
||||
|
||||
length_field = TRB_LEN(trb_buff_len) |
|
||||
TRB_INTR_TARGET(0);
|
||||
|
||||
/* xhci 1.1 with ETE uses TD Size field for TBC */
|
||||
if (first_trb && xep->use_extended_tbc)
|
||||
length_field |= TRB_TD_SIZE_TBC(burst_count);
|
||||
else
|
||||
length_field |= TRB_TD_SIZE(remainder);
|
||||
first_trb = false;
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (urb_priv->need_event_data_flag &&
|
||||
(j == (trbs_per_td - 1))) {
|
||||
addr = event_data_ptr;
|
||||
/* amlogic fix */
|
||||
wmb();
|
||||
/* amlogic fix */
|
||||
wmb();
|
||||
/* amlogic fix */
|
||||
wmb();
|
||||
}
|
||||
#endif
|
||||
|
||||
aml_queue_trb(xhci, ep_ring, more_trbs_coming,
|
||||
lower_32_bits(addr),
|
||||
upper_32_bits(addr),
|
||||
length_field,
|
||||
field);
|
||||
running_total += trb_buff_len;
|
||||
|
||||
addr += trb_buff_len;
|
||||
td_remain_len -= trb_buff_len;
|
||||
}
|
||||
|
||||
/* Check TD length */
|
||||
if (running_total != td_len) {
|
||||
aml_xhci_err(xhci, "ISOC TD length unmatch\n");
|
||||
ret = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* store the next frame id */
|
||||
if (HCC_CFC(xhci->hcc_params))
|
||||
xep->next_frame_id = urb->start_frame + num_tds * urb->interval;
|
||||
|
||||
if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
|
||||
if (xhci->quirks & XHCI_AMD_PLL_FIX)
|
||||
aml_usb_amd_quirk_pll_disable();
|
||||
}
|
||||
xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs++;
|
||||
|
||||
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
|
||||
start_cycle, start_trb);
|
||||
return 0;
|
||||
cleanup:
|
||||
/* Clean up a partially enqueued isoc transfer. */
|
||||
|
||||
for (i--; i >= 0; i--)
|
||||
list_del_init(&urb_priv->td[i].td_list);
|
||||
|
||||
/* Use the first TD as a temporary variable to turn the TDs we've queued
|
||||
* into No-ops with a software-owned cycle bit. That way the hardware
|
||||
* won't accidentally start executing bogus TDs when we partially
|
||||
* overwrite them. td->first_trb and td->start_seg are already set.
|
||||
*/
|
||||
urb_priv->td[0].last_trb = ep_ring->enqueue;
|
||||
/* Every TRB except the first & last will have its cycle bit flipped. */
|
||||
td_to_noop(xhci, ep_ring, &urb_priv->td[0], true);
|
||||
|
||||
/* Reset the ring enqueue back to the first TRB and its cycle bit. */
|
||||
ep_ring->enqueue = urb_priv->td[0].first_trb;
|
||||
ep_ring->enq_seg = urb_priv->td[0].start_seg;
|
||||
ep_ring->cycle_state = start_cycle;
|
||||
usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check transfer ring to guarantee there is enough room for the urb.
|
||||
* Update ISO URB start_frame and interval.
|
||||
@@ -4267,8 +4852,15 @@ int aml_xhci_queue_isoc_tx_prepare(struct aml_xhci_hcd *xhci, gfp_t mem_flags,
|
||||
}
|
||||
|
||||
skip_start_over:
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if ((xhci->meson_quirks & XHCI_CRG_HOST_008) ||
|
||||
(xhci->meson_quirks & XHCI_CRG_HOST_010))
|
||||
return m_xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index);
|
||||
else
|
||||
return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index);
|
||||
#else
|
||||
return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**** Command Ring Operations ****/
|
||||
@@ -4317,7 +4909,7 @@ static int queue_command(struct aml_xhci_hcd *xhci, struct aml_xhci_command *cmd
|
||||
|
||||
list_add_tail(&cmd->cmd_list, &xhci->cmd_list);
|
||||
|
||||
queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3,
|
||||
aml_queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3,
|
||||
field4 | xhci->cmd_ring->cycle_state);
|
||||
return 0;
|
||||
}
|
||||
@@ -4376,6 +4968,69 @@ int aml_xhci_queue_evaluate_context(struct aml_xhci_hcd *xhci, struct aml_xhci_c
|
||||
command_must_succeed);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
void xhci_start_stop_endpoint_work(struct aml_xhci_hcd *xhci, int slot_id, unsigned int ep_index)
|
||||
{
|
||||
if (xhci && (xhci->meson_quirks & XHCI_CRG_HOST_011)) {
|
||||
struct usb_device *udev;
|
||||
struct aml_xhci_virt_ep *eps;
|
||||
struct usb_host_endpoint *endpoint;
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
if (xhci->devs[slot_id] && xhci->devs[slot_id]->udev)
|
||||
udev = xhci->devs[slot_id]->udev;
|
||||
else
|
||||
return;
|
||||
|
||||
if (udev->state == USB_STATE_NOTATTACHED)
|
||||
return;
|
||||
|
||||
eps = &xhci->devs[slot_id]->eps[ep_index];
|
||||
|
||||
if (ep_index % 2)
|
||||
endpoint = udev->ep_out[(ep_index + 1) / 2];
|
||||
else
|
||||
endpoint = udev->ep_in[(ep_index + 1) / 2];
|
||||
|
||||
hcd = bus_to_hcd(udev->bus);
|
||||
if (endpoint && (usb_endpoint_xfer_isoc(&endpoint->desc) ||
|
||||
usb_endpoint_xfer_int(&endpoint->desc)) &&
|
||||
udev->descriptor.bDeviceClass == 0xef &&
|
||||
udev->speed == USB_SPEED_HIGH) {
|
||||
eps->udev = udev;
|
||||
eps->bendpointaddress = endpoint->desc.bEndpointAddress;
|
||||
schedule_work(&eps->stop_work);
|
||||
//usleep_range(200, 250);
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_start_stop_endpoint_work);
|
||||
|
||||
void xhci_stop_endpoint_command_timer(struct timer_list *t)
|
||||
{
|
||||
struct aml_xhci_virt_ep *ep = from_timer(ep, t, stop_cmd_queue_timer);
|
||||
struct aml_xhci_hcd *xhci = ep->xhci;
|
||||
struct aml_xhci_command *command;
|
||||
unsigned long flags;
|
||||
|
||||
aml_xhci_info(xhci, "start stop ep=%d, slot=%d\n", ep->ep_index, ep->slot_id);
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
command = aml_xhci_alloc_command(xhci, false, GFP_ATOMIC);
|
||||
if (!command) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
aml_xhci_info(xhci, "not stop ep=%d, slot=%d\n", ep->ep_index, ep->slot_id);
|
||||
return;
|
||||
}
|
||||
ep->ep_state |= EP_STOP_CMD_PENDING;
|
||||
aml_xhci_queue_stop_endpoint(xhci, command, ep->slot_id,
|
||||
ep->ep_index, 0);
|
||||
aml_xhci_ring_cmd_db(xhci);
|
||||
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Suspend is set to indicate "Stop Endpoint Command" is being issued to stop
|
||||
* activity on an endpoint that is about to be suspended.
|
||||
@@ -4388,9 +5043,14 @@ int aml_xhci_queue_stop_endpoint(struct aml_xhci_hcd *xhci, struct aml_xhci_comm
|
||||
u32 type = TRB_TYPE(TRB_STOP_RING);
|
||||
u32 trb_suspend = SUSPEND_PORT_FOR_TRB(suspend);
|
||||
|
||||
#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB)
|
||||
if (xhci->meson_quirks & XHCI_CRG_HOST_011)
|
||||
xhci_start_stop_endpoint_work(xhci, slot_id, ep_index);
|
||||
#endif
|
||||
return queue_command(xhci, cmd, 0, 0, 0,
|
||||
trb_slot_id | trb_ep_index | type | trb_suspend, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(aml_xhci_queue_stop_endpoint);
|
||||
|
||||
int aml_xhci_queue_reset_ep(struct aml_xhci_hcd *xhci, struct aml_xhci_command *cmd,
|
||||
int slot_id, unsigned int ep_index,
|
||||
|
||||
Reference in New Issue
Block a user