diff --git a/drivers/usb/crg/crg.c b/drivers/usb/crg/crg.c index d85c92366..8ba7eb3d1 100644 --- a/drivers/usb/crg/crg.c +++ b/drivers/usb/crg/crg.c @@ -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); diff --git a/drivers/usb/crg/crg_drd.c b/drivers/usb/crg/crg_drd.c index e866354e4..3e81a1f84 100644 --- a/drivers/usb/crg/crg_drd.c +++ b/drivers/usb/crg/crg_drd.c @@ -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"); diff --git a/drivers/usb/dwc3_amlogic/Makefile b/drivers/usb/dwc3_amlogic/Makefile index 771c5489a..7dcac82ec 100644 --- a/drivers/usb/dwc3_amlogic/Makefile +++ b/drivers/usb/dwc3_amlogic/Makefile @@ -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 diff --git a/drivers/usb/dwc3_amlogic/core-meson.c b/drivers/usb/dwc3_amlogic/core-meson.c index cdf2e79a8..b5f89c406 100644 --- a/drivers/usb/dwc3_amlogic/core-meson.c +++ b/drivers/usb/dwc3_amlogic/core-meson.c @@ -40,6 +40,10 @@ #include "debug-meson.h" #include "../host/xhci-ext-caps.h" +#if IS_ENABLED(CONFIG_AMLOGIC_COMMON_USB) +#include +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), diff --git a/drivers/usb/dwc3_amlogic/core-meson.h b/drivers/usb/dwc3_amlogic/core-meson.h index f3c68ddfd..76eb28cc7 100644 --- a/drivers/usb/dwc3_amlogic/core-meson.h +++ b/drivers/usb/dwc3_amlogic/core-meson.h @@ -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; diff --git a/drivers/usb/dwc3_amlogic/host-meson.c b/drivers/usb/dwc3_amlogic/host-meson.c index e64979077..0cf158c6e 100644 --- a/drivers/usb/dwc3_amlogic/host-meson.c +++ b/drivers/usb/dwc3_amlogic/host-meson.c @@ -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) { diff --git a/drivers/usb/xhci_amlogic/xhci-hub-meson.c b/drivers/usb/xhci_amlogic/xhci-hub-meson.c index 3d85cd2f3..b3bc75e1d 100644 --- a/drivers/usb/xhci_amlogic/xhci-hub-meson.c +++ b/drivers/usb/xhci_amlogic/xhci-hub-meson.c @@ -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); diff --git a/drivers/usb/xhci_amlogic/xhci-mem-meson.c b/drivers/usb/xhci_amlogic/xhci-mem-meson.c index dfc9edb75..67e6710c6 100644 --- a/drivers/usb/xhci_amlogic/xhci-mem-meson.c +++ b/drivers/usb/xhci_amlogic/xhci-mem-meson.c @@ -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; diff --git a/drivers/usb/xhci_amlogic/xhci-meson.c b/drivers/usb/xhci_amlogic/xhci-meson.c index 3f75a4d92..59a3aa678 100644 --- a/drivers/usb/xhci_amlogic/xhci-meson.c +++ b/drivers/usb/xhci_amlogic/xhci-meson.c @@ -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); diff --git a/drivers/usb/xhci_amlogic/xhci-meson.h b/drivers/usb/xhci_amlogic/xhci-meson.h index e3f854684..dba815ef8 100644 --- a/drivers/usb/xhci_amlogic/xhci-meson.h +++ b/drivers/usb/xhci_amlogic/xhci-meson.h @@ -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 */ diff --git a/drivers/usb/xhci_amlogic/xhci-plat-meson.c b/drivers/usb/xhci_amlogic/xhci-plat-meson.c index eea6da1d6..4f67eae72 100644 --- a/drivers/usb/xhci_amlogic/xhci-plat-meson.c +++ b/drivers/usb/xhci_amlogic/xhci-plat-meson.c @@ -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). diff --git a/drivers/usb/xhci_amlogic/xhci-ring-meson.c b/drivers/usb/xhci_amlogic/xhci-ring-meson.c index 353060d8e..c959bd3cf 100644 --- a/drivers/usb/xhci_amlogic/xhci-ring-meson.c +++ b/drivers/usb/xhci_amlogic/xhci-ring-meson.c @@ -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,