From 32a4a135847b1e600c64756b7c7c7a91eb2f0aa9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 25 Feb 2014 14:00:13 -0600 Subject: usb: dwc3: workaround: clock gating issues Revisions between 2.10a and 2.50a (included) have a known issue which may cause xHCI compliance tests to fail and/or quality issues with Isochronous transactions. Note that this issue only impacts certain configurations of those revisions, namely the ones which have clock gating enabled. The suggested workaround is to disable clock gating in known broken revisions, make sure HW LPM is disabled and set GCTL.SOFITPSYNC to 1. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) (limited to 'drivers/usb/dwc3/core.c') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index a49217ae3533..785feeeac3c1 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -314,7 +314,25 @@ static int dwc3_core_init(struct dwc3 *dwc) switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) { case DWC3_GHWPARAMS1_EN_PWROPT_CLK: - reg &= ~DWC3_GCTL_DSBLCLKGTNG; + /** + * WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an + * issue which would cause xHCI compliance tests to fail. + * + * Because of that we cannot enable clock gating on such + * configurations. + * + * Refers to: + * + * STAR#9000588375: Clock Gating, SOF Issues when ref_clk-Based + * SOF/ITP Mode Used + */ + if ((dwc->dr_mode == USB_DR_MODE_HOST || + dwc->dr_mode == USB_DR_MODE_OTG) && + (dwc->revision >= DWC3_REVISION_210A && + dwc->revision <= DWC3_REVISION_250A)) + reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC; + else + reg &= ~DWC3_GCTL_DSBLCLKGTNG; break; default: dev_dbg(dwc->dev, "No power optimization available\n"); @@ -479,6 +497,14 @@ static int dwc3_probe(struct platform_device *pdev) goto err0; } + if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) + dwc->dr_mode = USB_DR_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) + dwc->dr_mode = USB_DR_MODE_PERIPHERAL; + + if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) + dwc->dr_mode = USB_DR_MODE_OTG; + ret = dwc3_core_init(dwc); if (ret) { dev_err(dev, "failed to initialize core\n"); @@ -494,14 +520,6 @@ static int dwc3_probe(struct platform_device *pdev) goto err1; } - if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) - dwc->dr_mode = USB_DR_MODE_HOST; - else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) - dwc->dr_mode = USB_DR_MODE_PERIPHERAL; - - if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) - dwc->dr_mode = USB_DR_MODE_OTG; - switch (dwc->dr_mode) { case USB_DR_MODE_PERIPHERAL: dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); -- cgit From 0ffcaf3798bfd83aed0a650cddb9d1ee825ff2bb Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 19 Dec 2013 13:04:28 -0600 Subject: usb: dwc3: core: allocate scratch buffers We must read HWPARAMS4 register to figure out how many scratch buffers we should allocate. Later patch will use "Set Scratchpad Buffer Array" command to pass the pointer to the IP so it can be used during hibernation. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) (limited to 'drivers/usb/dwc3/core.c') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 785feeeac3c1..8c627c9a5130 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -242,6 +242,90 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) } } +static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc) +{ + if (!dwc->has_hibernation) + return 0; + + if (!dwc->nr_scratch) + return 0; + + dwc->scratchbuf = kmalloc_array(dwc->nr_scratch, + DWC3_SCRATCHBUF_SIZE, GFP_KERNEL); + if (!dwc->scratchbuf) + return -ENOMEM; + + return 0; +} + +static int dwc3_setup_scratch_buffers(struct dwc3 *dwc) +{ + dma_addr_t scratch_addr; + u32 param; + int ret; + + if (!dwc->has_hibernation) + return 0; + + if (!dwc->nr_scratch) + return 0; + + /* should never fall here */ + if (!WARN_ON(dwc->scratchbuf)) + return 0; + + scratch_addr = dma_map_single(dwc->dev, 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"); + ret = -EFAULT; + goto err0; + } + + dwc->scratch_addr = scratch_addr; + + param = lower_32_bits(scratch_addr); + + ret = dwc3_send_gadget_generic_command(dwc, + DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, param); + if (ret < 0) + goto err1; + + param = upper_32_bits(scratch_addr); + + ret = dwc3_send_gadget_generic_command(dwc, + DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, param); + if (ret < 0) + goto err1; + + return 0; + +err1: + dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch * + DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); + +err0: + return ret; +} + +static void dwc3_free_scratch_buffers(struct dwc3 *dwc) +{ + if (!dwc->has_hibernation) + return; + + if (!dwc->nr_scratch) + return; + + /* should never fall here */ + if (!WARN_ON(dwc->scratchbuf)) + return; + + dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch * + DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); + kfree(dwc->scratchbuf); +} + static void dwc3_core_num_eps(struct dwc3 *dwc) { struct dwc3_hwparams *parms = &dwc->hwparams; @@ -277,6 +361,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) static int dwc3_core_init(struct dwc3 *dwc) { unsigned long timeout; + u32 hwparams4 = dwc->hwparams.hwparams4; u32 reg; int ret; @@ -334,6 +419,10 @@ static int dwc3_core_init(struct dwc3 *dwc) else reg &= ~DWC3_GCTL_DSBLCLKGTNG; break; + case DWC3_GHWPARAMS1_EN_PWROPT_HIB: + /* enable hibernation here */ + dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4); + break; default: dev_dbg(dwc->dev, "No power optimization available\n"); } @@ -351,14 +440,30 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GCTL, reg); + ret = dwc3_alloc_scratch_buffers(dwc); + if (ret) + goto err1; + + ret = dwc3_setup_scratch_buffers(dwc); + if (ret) + goto err2; + return 0; +err2: + dwc3_free_scratch_buffers(dwc); + +err1: + usb_phy_shutdown(dwc->usb2_phy); + usb_phy_shutdown(dwc->usb3_phy); + err0: return ret; } static void dwc3_core_exit(struct dwc3 *dwc) { + dwc3_free_scratch_buffers(dwc); usb_phy_shutdown(dwc->usb2_phy); usb_phy_shutdown(dwc->usb3_phy); } -- cgit From 122f06e60f90a43d9b2fb30662af688dfb759379 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 3 Mar 2014 17:08:10 +0530 Subject: usb: dwc3: core: support optional PHYs Since PHYs for dwc3 is optional (not all SoCs having PHYs for DWC3 should be programmed), do not return from probe if the USB PHY library returns -ENODEV as that indicates the platform does not have a programmable PHY. While this can be considered as a temporary fix, a long term solution would be to add 'nop' PHY for platforms that does not have programmable PHY. Signed-off-by: Kishon Vijay Abraham I Reviewed-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) (limited to 'drivers/usb/dwc3/core.c') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 8c627c9a5130..c89570cc0689 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -534,32 +534,26 @@ static int dwc3_probe(struct platform_device *pdev) if (IS_ERR(dwc->usb2_phy)) { ret = PTR_ERR(dwc->usb2_phy); - - /* - * if -ENXIO is returned, it means PHY layer wasn't - * enabled, so it makes no sense to return -EPROBE_DEFER - * in that case, since no PHY driver will ever probe. - */ - if (ret == -ENXIO) + if (ret == -ENXIO || ret == -ENODEV) { + dwc->usb2_phy = NULL; + } else if (ret == -EPROBE_DEFER) { return ret; - - dev_err(dev, "no usb2 phy configured\n"); - return -EPROBE_DEFER; + } else { + dev_err(dev, "no usb2 phy configured\n"); + return ret; + } } if (IS_ERR(dwc->usb3_phy)) { ret = PTR_ERR(dwc->usb3_phy); - - /* - * if -ENXIO is returned, it means PHY layer wasn't - * enabled, so it makes no sense to return -EPROBE_DEFER - * in that case, since no PHY driver will ever probe. - */ - if (ret == -ENXIO) + if (ret == -ENXIO || ret == -ENODEV) { + dwc->usb3_phy = NULL; + } else if (ret == -EPROBE_DEFER) { return ret; - - dev_err(dev, "no usb3 phy configured\n"); - return -EPROBE_DEFER; + } else { + dev_err(dev, "no usb3 phy configured\n"); + return ret; + } } dwc->xhci_resources[0].start = res->start; -- cgit From 57303488cd37da58263e842de134dc65f7c626d5 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 3 Mar 2014 17:08:11 +0530 Subject: usb: dwc3: adapt dwc3 core to use Generic PHY Framework Adapted dwc3 core to use the Generic PHY Framework. So for init, exit, power_on and power_off the following APIs are used phy_init(), phy_exit(), phy_power_on() and phy_power_off(). However using the old USB phy library wont be removed till the PHYs of all other SoC's using dwc3 core is adapted to the Generic PHY Framework. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 3 deletions(-) (limited to 'drivers/usb/dwc3/core.c') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index c89570cc0689..d001417e8e37 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -61,9 +61,10 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode) * dwc3_core_soft_reset - Issues core soft reset and PHY reset * @dwc: pointer to our context structure */ -static void dwc3_core_soft_reset(struct dwc3 *dwc) +static int dwc3_core_soft_reset(struct dwc3 *dwc) { u32 reg; + int ret; /* Before Resetting PHY, put Core in Reset */ reg = dwc3_readl(dwc->regs, DWC3_GCTL); @@ -82,6 +83,15 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc) usb_phy_init(dwc->usb2_phy); usb_phy_init(dwc->usb3_phy); + ret = phy_init(dwc->usb2_generic_phy); + if (ret < 0) + return ret; + + ret = phy_init(dwc->usb3_generic_phy); + if (ret < 0) { + phy_exit(dwc->usb2_generic_phy); + return ret; + } mdelay(100); /* Clear USB3 PHY reset */ @@ -100,6 +110,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~DWC3_GCTL_CORESOFTRESET; dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + return 0; } /** @@ -391,7 +403,9 @@ static int dwc3_core_init(struct dwc3 *dwc) cpu_relax(); } while (true); - dwc3_core_soft_reset(dwc); + ret = dwc3_core_soft_reset(dwc); + if (ret) + goto err0; reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~DWC3_GCTL_SCALEDOWN_MASK; @@ -456,6 +470,8 @@ err2: err1: usb_phy_shutdown(dwc->usb2_phy); usb_phy_shutdown(dwc->usb3_phy); + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); err0: return ret; @@ -466,6 +482,8 @@ static void dwc3_core_exit(struct dwc3 *dwc) dwc3_free_scratch_buffers(dwc); usb_phy_shutdown(dwc->usb2_phy); usb_phy_shutdown(dwc->usb3_phy); + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); } #define DWC3_ALIGN_MASK (16 - 1) @@ -556,6 +574,32 @@ static int dwc3_probe(struct platform_device *pdev) } } + dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy"); + if (IS_ERR(dwc->usb2_generic_phy)) { + ret = PTR_ERR(dwc->usb2_generic_phy); + if (ret == -ENOSYS || ret == -ENODEV) { + dwc->usb2_generic_phy = NULL; + } else if (ret == -EPROBE_DEFER) { + return ret; + } else { + dev_err(dev, "no usb2 phy configured\n"); + return ret; + } + } + + dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy"); + if (IS_ERR(dwc->usb3_generic_phy)) { + ret = PTR_ERR(dwc->usb3_generic_phy); + if (ret == -ENOSYS || ret == -ENODEV) { + dwc->usb3_generic_phy = NULL; + } else if (ret == -EPROBE_DEFER) { + return ret; + } else { + dev_err(dev, "no usb3 phy configured\n"); + return ret; + } + } + dwc->xhci_resources[0].start = res->start; dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + DWC3_XHCI_REGS_END; @@ -612,11 +656,18 @@ static int dwc3_probe(struct platform_device *pdev) usb_phy_set_suspend(dwc->usb2_phy, 0); usb_phy_set_suspend(dwc->usb3_phy, 0); + ret = phy_power_on(dwc->usb2_generic_phy); + if (ret < 0) + goto err1; + + ret = phy_power_on(dwc->usb3_generic_phy); + if (ret < 0) + goto err_usb2phy_power; ret = dwc3_event_buffers_setup(dwc); if (ret) { dev_err(dwc->dev, "failed to setup event buffers\n"); - goto err1; + goto err_usb3phy_power; } switch (dwc->dr_mode) { @@ -685,6 +736,12 @@ err3: err2: dwc3_event_buffers_cleanup(dwc); +err_usb3phy_power: + phy_power_off(dwc->usb3_generic_phy); + +err_usb2phy_power: + phy_power_off(dwc->usb2_generic_phy); + err1: usb_phy_set_suspend(dwc->usb2_phy, 1); usb_phy_set_suspend(dwc->usb3_phy, 1); @@ -702,6 +759,8 @@ static int dwc3_remove(struct platform_device *pdev) usb_phy_set_suspend(dwc->usb2_phy, 1); usb_phy_set_suspend(dwc->usb3_phy, 1); + phy_power_off(dwc->usb2_generic_phy); + phy_power_off(dwc->usb3_generic_phy); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -799,6 +858,8 @@ static int dwc3_suspend(struct device *dev) usb_phy_shutdown(dwc->usb3_phy); usb_phy_shutdown(dwc->usb2_phy); + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); return 0; } @@ -807,9 +868,17 @@ static int dwc3_resume(struct device *dev) { struct dwc3 *dwc = dev_get_drvdata(dev); unsigned long flags; + int ret; usb_phy_init(dwc->usb3_phy); usb_phy_init(dwc->usb2_phy); + ret = phy_init(dwc->usb2_generic_phy); + if (ret < 0) + return ret; + + ret = phy_init(dwc->usb3_generic_phy); + if (ret < 0) + goto err_usb2phy_init; spin_lock_irqsave(&dwc->lock, flags); @@ -833,6 +902,11 @@ static int dwc3_resume(struct device *dev) pm_runtime_enable(dev); return 0; + +err_usb2phy_init: + phy_exit(dwc->usb2_generic_phy); + + return ret; } static const struct dev_pm_ops dwc3_dev_pm_ops = { -- cgit