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:
he.he
2024-10-18 18:28:07 +08:00
committed by Wanwei Jiang
parent b5af8fbb45
commit 5eb7e67aa3
12 changed files with 1971 additions and 39 deletions
+50
View File
@@ -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
View File
@@ -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");
+2
View File
@@ -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
+137 -2
View File
@@ -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),
+3 -1
View File
@@ -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;
+13 -1
View File
@@ -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) {
+163 -4
View File
@@ -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);
+242
View File
@@ -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;
+538 -5
View File
@@ -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);
+76 -1
View File
@@ -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).
+669 -9
View File
@@ -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,