diff options
Diffstat (limited to 'drivers/usb/dwc3/core.c')
-rw-r--r-- | drivers/usb/dwc3/core.c | 358 |
1 files changed, 190 insertions, 168 deletions
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index fea446900cad..369bab16a824 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -169,33 +169,6 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) return -ETIMEDOUT; } -/** - * dwc3_soft_reset - Issue soft reset - * @dwc: Pointer to our controller context structure - */ -static int dwc3_soft_reset(struct dwc3 *dwc) -{ - unsigned long timeout; - u32 reg; - - timeout = jiffies + msecs_to_jiffies(500); - dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); - do { - reg = dwc3_readl(dwc->regs, DWC3_DCTL); - if (!(reg & DWC3_DCTL_CSFTRST)) - break; - - if (time_after(jiffies, timeout)) { - dev_err(dwc->dev, "Reset Timed Out\n"); - return -ETIMEDOUT; - } - - cpu_relax(); - } while (true); - - return 0; -} - /* * dwc3_frame_length_adjustment - Adjusts frame length if required * @dwc3: Pointer to our controller context structure @@ -229,7 +202,7 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc) static void dwc3_free_one_event_buffer(struct dwc3 *dwc, struct dwc3_event_buffer *evt) { - dma_free_coherent(dwc->dev, evt->length, evt->buf, evt->dma); + dma_free_coherent(dwc->sysdev, evt->length, evt->buf, evt->dma); } /** @@ -251,7 +224,11 @@ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc, evt->dwc = dwc; evt->length = length; - evt->buf = dma_alloc_coherent(dwc->dev, length, + evt->cache = devm_kzalloc(dwc->dev, length, GFP_KERNEL); + if (!evt->cache) + return ERR_PTR(-ENOMEM); + + evt->buf = dma_alloc_coherent(dwc->sysdev, length, &evt->dma, GFP_KERNEL); if (!evt->buf) return ERR_PTR(-ENOMEM); @@ -305,13 +282,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc) struct dwc3_event_buffer *evt; evt = dwc->ev_buf; - dwc3_trace(trace_dwc3_core, - "Event buf %p dma %08llx length %d\n", - evt->buf, (unsigned long long) evt->dma, - evt->length); - evt->lpos = 0; - dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), lower_32_bits(evt->dma)); dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), @@ -370,11 +341,11 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc) if (!WARN_ON(dwc->scratchbuf)) return 0; - scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf, + scratch_addr = dma_map_single(dwc->sysdev, dwc->scratchbuf, dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); - if (dma_mapping_error(dwc->dev, scratch_addr)) { - dev_err(dwc->dev, "failed to map scratch buffer\n"); + if (dma_mapping_error(dwc->sysdev, scratch_addr)) { + dev_err(dwc->sysdev, "failed to map scratch buffer\n"); ret = -EFAULT; goto err0; } @@ -398,7 +369,7 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc) return 0; err1: - dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch * + dma_unmap_single(dwc->sysdev, dwc->scratch_addr, dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); err0: @@ -417,7 +388,7 @@ static void dwc3_free_scratch_buffers(struct dwc3 *dwc) if (!WARN_ON(dwc->scratchbuf)) return; - dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch * + dma_unmap_single(dwc->sysdev, dwc->scratch_addr, dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); kfree(dwc->scratchbuf); } @@ -428,9 +399,6 @@ static void dwc3_core_num_eps(struct dwc3 *dwc) dwc->num_in_eps = DWC3_NUM_IN_EPS(parms); dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps; - - dwc3_trace(trace_dwc3_core, "found %d IN and %d OUT endpoints", - dwc->num_in_eps, dwc->num_out_eps); } static void dwc3_cache_hwparams(struct dwc3 *dwc) @@ -524,13 +492,6 @@ static int dwc3_phy_setup(struct dwc3 *dwc) } /* FALLTHROUGH */ case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI: - /* Making sure the interface and PHY are operational */ - ret = dwc3_soft_reset(dwc); - if (ret) - return ret; - - udelay(1); - ret = dwc3_ulpi_init(dwc); if (ret) return ret; @@ -594,19 +555,12 @@ static void dwc3_core_exit(struct dwc3 *dwc) phy_power_off(dwc->usb3_generic_phy); } -/** - * dwc3_core_init - Low-level initialization of DWC3 Core - * @dwc: Pointer to our controller context structure - * - * Returns 0 on success otherwise negative errno. - */ -static int dwc3_core_init(struct dwc3 *dwc) +static bool dwc3_core_is_valid(struct dwc3 *dwc) { - u32 hwparams4 = dwc->hwparams.hwparams4; - u32 reg; - int ret; + u32 reg; reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); + /* This should read as U3 followed by revision number */ if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) { /* Detected DWC_usb3 IP */ @@ -616,36 +570,16 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER); dwc->revision |= DWC3_REVISION_IS_DWC31; } else { - dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); - ret = -ENODEV; - goto err0; + return false; } - /* - * Write Linux Version Code to our GUID register so it's easy to figure - * out which kernel version a bug was found. - */ - dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE); - - /* Handle USB2.0-only core configuration */ - if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == - DWC3_GHWPARAMS3_SSPHY_IFC_DIS) { - if (dwc->maximum_speed == USB_SPEED_SUPER) - dwc->maximum_speed = USB_SPEED_HIGH; - } - - /* issue device SoftReset too */ - ret = dwc3_soft_reset(dwc); - if (ret) - goto err0; - - ret = dwc3_core_soft_reset(dwc); - if (ret) - goto err0; + return true; +} - ret = dwc3_phy_setup(dwc); - if (ret) - goto err0; +static void dwc3_core_setup_global_control(struct dwc3 *dwc) +{ + u32 hwparams4 = dwc->hwparams.hwparams4; + u32 reg; reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~DWC3_GCTL_SCALEDOWN_MASK; @@ -683,13 +617,13 @@ static int dwc3_core_init(struct dwc3 *dwc) reg |= DWC3_GCTL_GBLHIBERNATIONEN; break; default: - dwc3_trace(trace_dwc3_core, "No power optimization available\n"); + /* nothing */ + break; } /* check if current dwc3 is on simulation board */ if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) { - dwc3_trace(trace_dwc3_core, - "running on FPGA platform\n"); + dev_info(dwc->dev, "Running with FPGA optmizations\n"); dwc->is_fpga = true; } @@ -714,7 +648,47 @@ static int dwc3_core_init(struct dwc3 *dwc) reg |= DWC3_GCTL_U2RSTECN; dwc3_writel(dwc->regs, DWC3_GCTL, reg); +} + +/** + * dwc3_core_init - Low-level initialization of DWC3 Core + * @dwc: Pointer to our controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +static int dwc3_core_init(struct dwc3 *dwc) +{ + u32 reg; + int ret; + + if (!dwc3_core_is_valid(dwc)) { + dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); + ret = -ENODEV; + goto err0; + } + + /* + * Write Linux Version Code to our GUID register so it's easy to figure + * out which kernel version a bug was found. + */ + dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE); + + /* Handle USB2.0-only core configuration */ + if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == + DWC3_GHWPARAMS3_SSPHY_IFC_DIS) { + if (dwc->maximum_speed == USB_SPEED_SUPER) + dwc->maximum_speed = USB_SPEED_HIGH; + } + + ret = dwc3_core_soft_reset(dwc); + if (ret) + goto err0; + ret = dwc3_phy_setup(dwc); + if (ret) + goto err0; + + dwc3_core_setup_global_control(dwc); dwc3_core_num_eps(dwc); ret = dwc3_setup_scratch_buffers(dwc); @@ -766,6 +740,16 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GUCTL2, reg); } + /* + * Enable hardware control of sending remote wakeup in HS when + * the device is in the L1 state. + */ + if (dwc->revision >= DWC3_REVISION_290A) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL1); + reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW; + dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); + } + return 0; err4: @@ -919,57 +903,13 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc) } } -#define DWC3_ALIGN_MASK (16 - 1) - -static int dwc3_probe(struct platform_device *pdev) +static void dwc3_get_properties(struct dwc3 *dwc) { - struct device *dev = &pdev->dev; - struct resource *res; - struct dwc3 *dwc; + struct device *dev = dwc->dev; u8 lpm_nyet_threshold; u8 tx_de_emphasis; u8 hird_threshold; - int ret; - - void __iomem *regs; - void *mem; - - mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL); - if (!mem) - return -ENOMEM; - - dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1); - dwc->mem = mem; - dwc->dev = dev; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "missing memory resource\n"); - return -ENODEV; - } - - dwc->xhci_resources[0].start = res->start; - dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + - DWC3_XHCI_REGS_END; - dwc->xhci_resources[0].flags = res->flags; - dwc->xhci_resources[0].name = res->name; - - res->start += DWC3_GLOBALS_REGS_START; - - /* - * Request memory region but exclude xHCI regs, - * since it will be requested by the xhci-plat driver. - */ - regs = devm_ioremap_resource(dev, res); - if (IS_ERR(regs)) { - ret = PTR_ERR(regs); - goto err0; - } - - dwc->regs = regs; - dwc->regs_size = resource_size(res); - /* default to highest possible threshold */ lpm_nyet_threshold = 0xff; @@ -986,6 +926,13 @@ static int dwc3_probe(struct platform_device *pdev) dwc->dr_mode = usb_get_dr_mode(dev); dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node); + dwc->sysdev_is_parent = device_property_read_bool(dev, + "linux,sysdev_is_parent"); + if (dwc->sysdev_is_parent) + dwc->sysdev = dwc->dev->parent; + else + dwc->sysdev = dwc->dev; + dwc->has_lpm_erratum = device_property_read_bool(dev, "snps,has-lpm-erratum"); device_property_read_u8(dev, "snps,lpm-nyet-threshold", @@ -1041,6 +988,112 @@ static int dwc3_probe(struct platform_device *pdev) dwc->hird_threshold = hird_threshold | (dwc->is_utmi_l1_suspend << 4); + dwc->imod_interval = 0; +} + +/* check whether the core supports IMOD */ +bool dwc3_has_imod(struct dwc3 *dwc) +{ + return ((dwc3_is_usb3(dwc) && + dwc->revision >= DWC3_REVISION_300A) || + (dwc3_is_usb31(dwc) && + dwc->revision >= DWC3_USB31_REVISION_120A)); +} + +static void dwc3_check_params(struct dwc3 *dwc) +{ + struct device *dev = dwc->dev; + + /* Check for proper value of imod_interval */ + if (dwc->imod_interval && !dwc3_has_imod(dwc)) { + dev_warn(dwc->dev, "Interrupt moderation not supported\n"); + dwc->imod_interval = 0; + } + + /* + * Workaround for STAR 9000961433 which affects only version + * 3.00a of the DWC_usb3 core. This prevents the controller + * interrupt from being masked while handling events. IMOD + * allows us to work around this issue. Enable it for the + * affected version. + */ + if (!dwc->imod_interval && + (dwc->revision == DWC3_REVISION_300A)) + dwc->imod_interval = 1; + + /* Check the maximum_speed parameter */ + switch (dwc->maximum_speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: + break; + default: + dev_err(dev, "invalid maximum_speed parameter %d\n", + dwc->maximum_speed); + /* fall through */ + case USB_SPEED_UNKNOWN: + /* default to superspeed */ + dwc->maximum_speed = USB_SPEED_SUPER; + + /* + * default to superspeed plus if we are capable. + */ + if (dwc3_is_usb31(dwc) && + (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == + DWC3_GHWPARAMS3_SSPHY_IFC_GEN2)) + dwc->maximum_speed = USB_SPEED_SUPER_PLUS; + + break; + } +} + +static int dwc3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct dwc3 *dwc; + + int ret; + + void __iomem *regs; + + dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL); + if (!dwc) + return -ENOMEM; + + dwc->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "missing memory resource\n"); + return -ENODEV; + } + + dwc->xhci_resources[0].start = res->start; + dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + + DWC3_XHCI_REGS_END; + dwc->xhci_resources[0].flags = res->flags; + dwc->xhci_resources[0].name = res->name; + + res->start += DWC3_GLOBALS_REGS_START; + + /* + * Request memory region but exclude xHCI regs, + * since it will be requested by the xhci-plat driver. + */ + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + goto err0; + } + + dwc->regs = regs; + dwc->regs_size = resource_size(res); + + dwc3_get_properties(dwc); + platform_set_drvdata(pdev, dwc); dwc3_cache_hwparams(dwc); @@ -1050,12 +1103,6 @@ static int dwc3_probe(struct platform_device *pdev) spin_lock_init(&dwc->lock); - if (!dev->dma_mask) { - dev->dma_mask = dev->parent->dma_mask; - dev->dma_parms = dev->parent->dma_parms; - dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask); - } - pm_runtime_set_active(dev); pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY); @@ -1087,32 +1134,7 @@ static int dwc3_probe(struct platform_device *pdev) goto err4; } - /* Check the maximum_speed parameter */ - switch (dwc->maximum_speed) { - case USB_SPEED_LOW: - case USB_SPEED_FULL: - case USB_SPEED_HIGH: - case USB_SPEED_SUPER: - case USB_SPEED_SUPER_PLUS: - break; - default: - dev_err(dev, "invalid maximum_speed parameter %d\n", - dwc->maximum_speed); - /* fall through */ - case USB_SPEED_UNKNOWN: - /* default to superspeed */ - dwc->maximum_speed = USB_SPEED_SUPER; - - /* - * default to superspeed plus if we are capable. - */ - if (dwc3_is_usb31(dwc) && - (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == - DWC3_GHWPARAMS3_SSPHY_IFC_GEN2)) - dwc->maximum_speed = USB_SPEED_SUPER_PLUS; - - break; - } + dwc3_check_params(dwc); ret = dwc3_core_init_mode(dwc); if (ret) |