Restore the dwc3 source files to the state of the original import in
commit 85d5e7075f33 ("usb: dwc3: add dwc3 folder from linux kernel to
u-boot").The following files are preserved accross the import: Makefile Kconfig dwc3-meson-g12a.c dwc3-meson-gxl.c dwc3-omap.c dwc3-uniphier.c dwc3-generic.h dwc3-generic.c dwc3-generic-sti.c dwc3-layerscape.c ti_usb_phy.c Note that this is a raw import and doesn't build. A fixup commit at the end of the series fixes that. Signed-off-by: Jens Wiklander <[email protected]> --- drivers/usb/dwc3/core.c | 1002 +++++++++++++--------------- drivers/usb/dwc3/core.h | 171 ++--- drivers/usb/dwc3/debug.c | 32 + drivers/usb/dwc3/debug.h | 228 +++++++ drivers/usb/dwc3/dwc3-am62.c | 125 ---- drivers/usb/dwc3/ep0.c | 217 +++--- drivers/usb/dwc3/gadget.c | 482 +++++++------ drivers/usb/dwc3/gadget.h | 16 +- drivers/usb/dwc3/io.h | 54 +- drivers/usb/dwc3/linux-compat.h | 16 - drivers/usb/dwc3/platform_data.h | 47 ++ drivers/usb/dwc3/samsung_usb_phy.c | 77 --- 12 files changed, 1211 insertions(+), 1256 deletions(-) create mode 100644 drivers/usb/dwc3/debug.c create mode 100644 drivers/usb/dwc3/debug.h delete mode 100644 drivers/usb/dwc3/dwc3-am62.c delete mode 100644 drivers/usb/dwc3/linux-compat.h create mode 100644 drivers/usb/dwc3/platform_data.h delete mode 100644 drivers/usb/dwc3/samsung_usb_phy.c diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 847fa1f82c37..25ddc39efad8 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1,48 +1,55 @@ -// SPDX-License-Identifier: GPL-2.0 /** * core.c - DesignWare USB3 DRD Controller Core file * - * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com * * Authors: Felipe Balbi <[email protected]>, * Sebastian Andrzej Siewior <[email protected]> * - * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/core.c) and ported - * to uboot. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * - * commit cd72f890d2 : usb: dwc3: core: enable phy suspend quirk on non-FPGA + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <clk.h> -#include <cpu_func.h> -#include <malloc.h> -#include <dwc3-uboot.h> -#include <dm/device_compat.h> -#include <dm/devres.h> -#include <linux/bug.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/list.h> #include <linux/delay.h> #include <linux/dma-mapping.h> -#include <linux/err.h> -#include <linux/iopoll.h> -#include <linux/ioport.h> -#include <dm.h> -#include <generic-phy.h> +#include <linux/of.h> +#include <linux/acpi.h> + #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> -#include <linux/bitfield.h> -#include <linux/math64.h> -#include <linux/time.h> +#include <linux/usb/of.h> +#include <linux/usb/otg.h> +#include "platform_data.h" #include "core.h" #include "gadget.h" #include "io.h" -#include "linux-compat.h" +#include "debug.h" -static LIST_HEAD(dwc3_list); /* -------------------------------------------------------------------------- */ -static void dwc3_set_mode(struct dwc3 *dwc, u32 mode) +void dwc3_set_mode(struct dwc3 *dwc, u32 mode) { u32 reg; @@ -59,6 +66,7 @@ static void dwc3_set_mode(struct dwc3 *dwc, u32 mode) 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); @@ -75,6 +83,17 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST; dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + 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 */ @@ -97,94 +116,6 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) return 0; } -/* - * dwc3_frame_length_adjustment - Adjusts frame length if required - * @dwc3: Pointer to our controller context structure - * @fladj: Value of GFLADJ_30MHZ to adjust frame length - */ -static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj) -{ - u32 reg; - - if (dwc->revision < DWC3_REVISION_250A) - return; - - if (fladj == 0) - return; - - reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); - reg &= ~DWC3_GFLADJ_30MHZ_MASK; - reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | fladj; - dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); -} - -/** - * dwc3_ref_clk_period - Reference clock period configuration - * Default reference clock period depends on hardware - * configuration. For systems with reference clock that differs - * from the default, this will set clock period in DWC3_GUCTL - * register. - * @dwc: Pointer to our controller context structure - * @ref_clk_per: reference clock period in ns - */ -static void dwc3_ref_clk_period(struct dwc3 *dwc) -{ - unsigned long period; - unsigned long fladj; - unsigned long decr; - unsigned long rate; - u32 reg; - - if (dwc->ref_clk) { - rate = clk_get_rate(dwc->ref_clk); - if (!rate) - return; - period = NSEC_PER_SEC / rate; - } else { - return; - } - - reg = dwc3_readl(dwc->regs, DWC3_GUCTL); - reg &= ~DWC3_GUCTL_REFCLKPER_MASK; - reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period); - dwc3_writel(dwc->regs, DWC3_GUCTL, reg); - - if (dwc->revision <= DWC3_REVISION_250A) - return; - - /* - * The calculation below is - * - * 125000 * (NSEC_PER_SEC / (rate * period) - 1) - * - * but rearranged for fixed-point arithmetic. The division must be - * 64-bit because 125000 * NSEC_PER_SEC doesn't fit in 32 bits (and - * neither does rate * period). - * - * Note that rate * period ~= NSEC_PER_SECOND, minus the number of - * nanoseconds of error caused by the truncation which happened during - * the division when calculating rate or period (whichever one was - * derived from the other). We first calculate the relative error, then - * scale it to units of 8 ppm. - */ - fladj = div64_u64(125000ULL * NSEC_PER_SEC, (u64)rate * period); - fladj -= 125000; - - /* - * The documented 240MHz constant is scaled by 2 to get PLS1 as well. - */ - decr = 480000000 / rate; - - reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); - reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK - & ~DWC3_GFLADJ_240MHZDECR - & ~DWC3_GFLADJ_240MHZDECR_PLS1; - reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj) - | FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1) - | FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, decr & 1); - dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); -} - /** * dwc3_free_one_event_buffer - Frees one event buffer * @dwc: Pointer to our controller context structure @@ -193,7 +124,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc) static void dwc3_free_one_event_buffer(struct dwc3 *dwc, struct dwc3_event_buffer *evt) { - dma_free_coherent(evt->buf); + dma_free_coherent(dwc->dev, evt->length, evt->buf, evt->dma); } /** @@ -209,20 +140,17 @@ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc, { struct dwc3_event_buffer *evt; - evt = devm_kzalloc((struct udevice *)dwc->dev, sizeof(*evt), - GFP_KERNEL); + evt = devm_kzalloc(dwc->dev, sizeof(*evt), GFP_KERNEL); if (!evt) return ERR_PTR(-ENOMEM); evt->dwc = dwc; evt->length = length; - evt->buf = dma_alloc_coherent(length, - (unsigned long *)&evt->dma); + evt->buf = dma_alloc_coherent(dwc->dev, length, + &evt->dma, GFP_KERNEL); if (!evt->buf) return ERR_PTR(-ENOMEM); - dwc3_flush_cache((uintptr_t)evt->buf, evt->length); - return evt; } @@ -258,8 +186,8 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) num = DWC3_NUM_INT(dwc->hwparams.hwparams1); dwc->num_event_buffers = num; - dwc->ev_buffs = memalign(CONFIG_SYS_CACHELINE_SIZE, - sizeof(*dwc->ev_buffs) * num); + dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs) * num, + GFP_KERNEL); if (!dwc->ev_buffs) return -ENOMEM; @@ -354,9 +282,13 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc) if (!dwc->nr_scratch) return 0; - scratch_addr = dma_map_single(dwc->scratchbuf, - dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE, - DMA_BIDIRECTIONAL); + /* 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; @@ -382,8 +314,8 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc) return 0; err1: - dma_unmap_single(scratch_addr, dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE, - DMA_BIDIRECTIONAL); + dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch * + DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); err0: return ret; @@ -397,8 +329,12 @@ static void dwc3_free_scratch_buffers(struct dwc3 *dwc) if (!dwc->nr_scratch) return; - dma_unmap_single(dwc->scratch_addr, dwc->nr_scratch * - DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); + /* 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); } @@ -428,34 +364,6 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8); } -static void dwc3_hsphy_mode_setup(struct dwc3 *dwc) -{ - enum usb_phy_interface hsphy_mode = dwc->hsphy_mode; - u32 reg; - - /* Set dwc3 usb2 phy config */ - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - - switch (hsphy_mode) { - case USBPHY_INTERFACE_MODE_UTMI: - reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK | - DWC3_GUSB2PHYCFG_USBTRDTIM_MASK); - reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_8_BIT) | - DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_8_BIT); - break; - case USBPHY_INTERFACE_MODE_UTMIW: - reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK | - DWC3_GUSB2PHYCFG_USBTRDTIM_MASK); - reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_16_BIT) | - DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_16_BIT); - break; - default: - break; - } - - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); -} - /** * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core * @dwc: Pointer to our controller context structure @@ -499,13 +407,8 @@ static void dwc3_phy_setup(struct dwc3 *dwc) if (dwc->dis_u3_susphy_quirk) reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; - if (dwc->dis_del_phy_power_chg_quirk) - reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); - dwc3_hsphy_mode_setup(dwc); - mdelay(100); reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); @@ -522,64 +425,11 @@ static void dwc3_phy_setup(struct dwc3 *dwc) if (dwc->dis_u2_susphy_quirk) reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; - if (dwc->dis_enblslpm_quirk) - reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; - - if (dwc->dis_u2_freeclk_exists_quirk) - reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); mdelay(100); } -/* set global incr burst type configuration registers */ -static void dwc3_set_incr_burst_type(struct dwc3 *dwc) -{ - struct udevice *dev = dwc->dev; - u32 cfg; - - if (!dwc->incrx_size) - return; - - cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0); - - /* Enable Undefined Length INCR Burst and Enable INCRx Burst */ - cfg &= ~DWC3_GSBUSCFG0_INCRBRST_MASK; - if (dwc->incrx_mode) - cfg |= DWC3_GSBUSCFG0_INCRBRSTENA; - switch (dwc->incrx_size) { - case 256: - cfg |= DWC3_GSBUSCFG0_INCR256BRSTENA; - break; - case 128: - cfg |= DWC3_GSBUSCFG0_INCR128BRSTENA; - break; - case 64: - cfg |= DWC3_GSBUSCFG0_INCR64BRSTENA; - break; - case 32: - cfg |= DWC3_GSBUSCFG0_INCR32BRSTENA; - break; - case 16: - cfg |= DWC3_GSBUSCFG0_INCR16BRSTENA; - break; - case 8: - cfg |= DWC3_GSBUSCFG0_INCR8BRSTENA; - break; - case 4: - cfg |= DWC3_GSBUSCFG0_INCR4BRSTENA; - break; - case 1: - break; - default: - dev_err(dev, "Invalid property\n"); - break; - } - - dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg); -} - /** * dwc3_core_init - Low-level initialization of DWC3 Core * @dwc: Pointer to our controller context structure @@ -588,20 +438,26 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) */ static int dwc3_core_init(struct dwc3 *dwc) { + unsigned long timeout; u32 hwparams4 = dwc->hwparams.hwparams4; u32 reg; int ret; reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); /* This should read as U3 followed by revision number */ - if ((reg & DWC3_GSNPSID_MASK) != 0x55330000 && - (reg & DWC3_GSNPSID_MASK) != 0x33310000) { + if ((reg & DWC3_GSNPSID_MASK) != 0x55330000) { dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); ret = -ENODEV; goto err0; } dwc->revision = reg; + /* + * 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) { @@ -610,17 +466,21 @@ static int dwc3_core_init(struct dwc3 *dwc) } /* issue device SoftReset too */ + timeout = jiffies + msecs_to_jiffies(500); dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); - ret = read_poll_timeout(dwc3_readl, reg, - !(reg & DWC3_DCTL_CSFTRST), - 1, 5000, dwc->regs, DWC3_DCTL); - if (ret) { - dev_err(dwc->dev, "Reset Timed Out\n"); - ret = -ETIMEDOUT; - goto err0; - } + do { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + break; - dwc3_phy_setup(dwc); + if (time_after(jiffies, timeout)) { + dev_err(dwc->dev, "Reset Timed Out\n"); + ret = -ETIMEDOUT; + goto err0; + } + + cpu_relax(); + } while (true); ret = dwc3_core_soft_reset(dwc); if (ret) @@ -671,9 +531,8 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc->is_fpga = true; } - if(dwc->disable_scramble_quirk && !dwc->is_fpga) - WARN(true, - "disable_scramble cannot be used on non-FPGA builds\n"); + WARN_ONCE(dwc->disable_scramble_quirk && !dwc->is_fpga, + "disable_scramble cannot be used on non-FPGA builds\n"); if (dwc->disable_scramble_quirk && dwc->is_fpga) reg |= DWC3_GCTL_DISSCRAMBLE; @@ -696,27 +555,27 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GCTL, reg); + dwc3_phy_setup(dwc); + ret = dwc3_alloc_scratch_buffers(dwc); if (ret) - goto err0; + goto err1; ret = dwc3_setup_scratch_buffers(dwc); if (ret) - goto err1; - - /* Adjust Frame Length */ - dwc3_frame_length_adjustment(dwc, dwc->fladj); - - /* Adjust Reference Clock Period */ - dwc3_ref_clk_period(dwc); - - dwc3_set_incr_burst_type(dwc); + goto err2; return 0; -err1: +err2: dwc3_free_scratch_buffers(dwc); +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; } @@ -724,10 +583,82 @@ err0: 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); +} + +static int dwc3_core_get_phy(struct dwc3 *dwc) +{ + struct device *dev = dwc->dev; + struct device_node *node = dev->of_node; + int ret; + + if (node) { + dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); + dwc->usb3_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 1); + } else { + dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3); + } + + if (IS_ERR(dwc->usb2_phy)) { + ret = PTR_ERR(dwc->usb2_phy); + if (ret == -ENXIO || ret == -ENODEV) { + dwc->usb2_phy = NULL; + } else if (ret == -EPROBE_DEFER) { + return ret; + } else { + dev_err(dev, "no usb2 phy configured\n"); + return ret; + } + } + + if (IS_ERR(dwc->usb3_phy)) { + ret = PTR_ERR(dwc->usb3_phy); + if (ret == -ENXIO || ret == -ENODEV) { + dwc->usb3_phy = NULL; + } else if (ret == -EPROBE_DEFER) { + return ret; + } else { + dev_err(dev, "no usb3 phy configured\n"); + return ret; + } + } + + 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; + } + } + + return 0; } static int dwc3_core_init_mode(struct dwc3 *dwc) { + struct device *dev = dwc->dev; int ret; switch (dwc->dr_mode) { @@ -735,7 +666,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) { - dev_err(dwc->dev, "failed to initialize gadget\n"); + dev_err(dev, "failed to initialize gadget\n"); return ret; } break; @@ -743,7 +674,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); ret = dwc3_host_init(dwc); if (ret) { - dev_err(dwc->dev, "failed to initialize host\n"); + dev_err(dev, "failed to initialize host\n"); return ret; } break; @@ -751,39 +682,24 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); ret = dwc3_host_init(dwc); if (ret) { - dev_err(dwc->dev, "failed to initialize host\n"); + dev_err(dev, "failed to initialize host\n"); return ret; } ret = dwc3_gadget_init(dwc); if (ret) { - dev_err(dwc->dev, "failed to initialize gadget\n"); + dev_err(dev, "failed to initialize gadget\n"); return ret; } break; default: - dev_err(dwc->dev, - "Unsupported mode of operation %d\n", dwc->dr_mode); + dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode); return -EINVAL; } return 0; } -static void dwc3_gadget_run(struct dwc3 *dwc) -{ - dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_RUN_STOP); - mdelay(100); -} - -static void dwc3_core_stop(struct dwc3 *dwc) -{ - u32 reg; - - reg = dwc3_readl(dwc->regs, DWC3_DCTL); - dwc3_writel(dwc->regs, DWC3_DCTL, reg & ~(DWC3_DCTL_RUN_STOP)); -} - static void dwc3_core_exit_mode(struct dwc3 *dwc) { switch (dwc->dr_mode) { @@ -801,50 +717,74 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc) /* do nothing */ break; } - - /* - * switch back to peripheral mode - * This enables the phy to enter idle and then, if enabled, suspend. - */ - dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); - dwc3_gadget_run(dwc); } #define DWC3_ALIGN_MASK (16 - 1) -/** - * dwc3_uboot_init - dwc3 core uboot initialization code - * @dwc3_dev: struct dwc3_device containing initialization data - * - * Entry point for dwc3 driver (equivalent to dwc3_probe in linux - * kernel driver). Pointer to dwc3_device should be passed containing - * base address and other initialization data. Returns '0' on success and - * a negative value on failure. - * - * Generally called from board_usb_init() implemented in board file. - */ -int dwc3_uboot_init(struct dwc3_device *dwc3_dev) +static int dwc3_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct dwc3_platform_data *pdata = dev_get_platdata(dev); + struct device_node *node = dev->of_node; + struct resource *res; struct dwc3 *dwc; - struct device *dev = NULL; u8 lpm_nyet_threshold; u8 tx_de_emphasis; u8 hird_threshold; int ret; + void __iomem *regs; void *mem; - mem = devm_kzalloc((struct udevice *)dev, - sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL); + 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_IRQ, 0); + if (!res) { + dev_err(dev, "missing IRQ\n"); + return -ENODEV; + } + dwc->xhci_resources[1].start = res->start; + dwc->xhci_resources[1].end = res->end; + dwc->xhci_resources[1].flags = res->flags; + dwc->xhci_resources[1].name = res->name; + + 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; - dwc->regs = (void *)(uintptr_t)(dwc3_dev->base + - DWC3_GLOBALS_REGS_START); + 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)) + return PTR_ERR(regs); + + dwc->regs = regs; + dwc->regs_size = resource_size(res); + /* + * restore res->start back to its original value so that, + * in case the probe is deferred, we don't end up getting error in + * request the memory region the next time probe is called. + */ + res->start -= DWC3_GLOBALS_REGS_START; /* default to highest possible threshold */ lpm_nyet_threshold = 0xff; @@ -858,35 +798,73 @@ int dwc3_uboot_init(struct dwc3_device *dwc3_dev) */ hird_threshold = 12; - dwc->maximum_speed = dwc3_dev->maximum_speed; - dwc->has_lpm_erratum = dwc3_dev->has_lpm_erratum; - if (dwc3_dev->lpm_nyet_threshold) - lpm_nyet_threshold = dwc3_dev->lpm_nyet_threshold; - dwc->is_utmi_l1_suspend = dwc3_dev->is_utmi_l1_suspend; - if (dwc3_dev->hird_threshold) - hird_threshold = dwc3_dev->hird_threshold; - - dwc->needs_fifo_resize = dwc3_dev->tx_fifo_resize; - dwc->dr_mode = dwc3_dev->dr_mode; - - dwc->disable_scramble_quirk = dwc3_dev->disable_scramble_quirk; - dwc->u2exit_lfps_quirk = dwc3_dev->u2exit_lfps_quirk; - dwc->u2ss_inp3_quirk = dwc3_dev->u2ss_inp3_quirk; - dwc->req_p1p2p3_quirk = dwc3_dev->req_p1p2p3_quirk; - dwc->del_p1p2p3_quirk = dwc3_dev->del_p1p2p3_quirk; - dwc->del_phy_power_chg_quirk = dwc3_dev->del_phy_power_chg_quirk; - dwc->lfps_filter_quirk = dwc3_dev->lfps_filter_quirk; - dwc->rx_detect_poll_quirk = dwc3_dev->rx_detect_poll_quirk; - dwc->dis_u3_susphy_quirk = dwc3_dev->dis_u3_susphy_quirk; - dwc->dis_u2_susphy_quirk = dwc3_dev->dis_u2_susphy_quirk; - dwc->dis_del_phy_power_chg_quirk = dwc3_dev->dis_del_phy_power_chg_quirk; - dwc->dis_tx_ipgap_linecheck_quirk = dwc3_dev->dis_tx_ipgap_linecheck_quirk; - dwc->dis_enblslpm_quirk = dwc3_dev->dis_enblslpm_quirk; - dwc->dis_u2_freeclk_exists_quirk = dwc3_dev->dis_u2_freeclk_exists_quirk; - - dwc->tx_de_emphasis_quirk = dwc3_dev->tx_de_emphasis_quirk; - if (dwc3_dev->tx_de_emphasis) - tx_de_emphasis = dwc3_dev->tx_de_emphasis; + if (node) { + dwc->maximum_speed = of_usb_get_maximum_speed(node); + dwc->has_lpm_erratum = of_property_read_bool(node, + "snps,has-lpm-erratum"); + of_property_read_u8(node, "snps,lpm-nyet-threshold", + &lpm_nyet_threshold); + dwc->is_utmi_l1_suspend = of_property_read_bool(node, + "snps,is-utmi-l1-suspend"); + of_property_read_u8(node, "snps,hird-threshold", + &hird_threshold); + + dwc->needs_fifo_resize = of_property_read_bool(node, + "tx-fifo-resize"); + dwc->dr_mode = of_usb_get_dr_mode(node); + + dwc->disable_scramble_quirk = of_property_read_bool(node, + "snps,disable_scramble_quirk"); + dwc->u2exit_lfps_quirk = of_property_read_bool(node, + "snps,u2exit_lfps_quirk"); + dwc->u2ss_inp3_quirk = of_property_read_bool(node, + "snps,u2ss_inp3_quirk"); + dwc->req_p1p2p3_quirk = of_property_read_bool(node, + "snps,req_p1p2p3_quirk"); + dwc->del_p1p2p3_quirk = of_property_read_bool(node, + "snps,del_p1p2p3_quirk"); + dwc->del_phy_power_chg_quirk = of_property_read_bool(node, + "snps,del_phy_power_chg_quirk"); + dwc->lfps_filter_quirk = of_property_read_bool(node, + "snps,lfps_filter_quirk"); + dwc->rx_detect_poll_quirk = of_property_read_bool(node, + "snps,rx_detect_poll_quirk"); + dwc->dis_u3_susphy_quirk = of_property_read_bool(node, + "snps,dis_u3_susphy_quirk"); + dwc->dis_u2_susphy_quirk = of_property_read_bool(node, + "snps,dis_u2_susphy_quirk"); + + dwc->tx_de_emphasis_quirk = of_property_read_bool(node, + "snps,tx_de_emphasis_quirk"); + of_property_read_u8(node, "snps,tx_de_emphasis", + &tx_de_emphasis); + } else if (pdata) { + dwc->maximum_speed = pdata->maximum_speed; + dwc->has_lpm_erratum = pdata->has_lpm_erratum; + if (pdata->lpm_nyet_threshold) + lpm_nyet_threshold = pdata->lpm_nyet_threshold; + dwc->is_utmi_l1_suspend = pdata->is_utmi_l1_suspend; + if (pdata->hird_threshold) + hird_threshold = pdata->hird_threshold; + + dwc->needs_fifo_resize = pdata->tx_fifo_resize; + dwc->dr_mode = pdata->dr_mode; + + dwc->disable_scramble_quirk = pdata->disable_scramble_quirk; + dwc->u2exit_lfps_quirk = pdata->u2exit_lfps_quirk; + dwc->u2ss_inp3_quirk = pdata->u2ss_inp3_quirk; + dwc->req_p1p2p3_quirk = pdata->req_p1p2p3_quirk; + dwc->del_p1p2p3_quirk = pdata->del_p1p2p3_quirk; + dwc->del_phy_power_chg_quirk = pdata->del_phy_power_chg_quirk; + dwc->lfps_filter_quirk = pdata->lfps_filter_quirk; + dwc->rx_detect_poll_quirk = pdata->rx_detect_poll_quirk; + dwc->dis_u3_susphy_quirk = pdata->dis_u3_susphy_quirk; + dwc->dis_u2_susphy_quirk = pdata->dis_u2_susphy_quirk; + + dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk; + if (pdata->tx_de_emphasis) + tx_de_emphasis = pdata->tx_de_emphasis; + } /* default to superspeed if no maximum_speed passed */ if (dwc->maximum_speed == USB_SPEED_UNKNOWN) @@ -898,21 +876,35 @@ int dwc3_uboot_init(struct dwc3_device *dwc3_dev) dwc->hird_threshold = hird_threshold | (dwc->is_utmi_l1_suspend << 4); - dwc->hsphy_mode = dwc3_dev->hsphy_mode; + ret = dwc3_core_get_phy(dwc); + if (ret) + return ret; + + spin_lock_init(&dwc->lock); + platform_set_drvdata(pdev, dwc); - dwc->index = dwc3_dev->index; + 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_enable(dev); + pm_runtime_get_sync(dev); + pm_runtime_forbid(dev); dwc3_cache_hwparams(dwc); ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); if (ret) { dev_err(dwc->dev, "failed to allocate event buffers\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err0; } - if (!IS_ENABLED(CONFIG_USB_DWC3_GADGET)) + if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) dwc->dr_mode = USB_DR_MODE_HOST; - else if (!IS_ENABLED(CONFIG_USB_HOST)) + else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) dwc->dr_mode = USB_DR_MODE_PERIPHERAL; if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) @@ -920,28 +912,55 @@ int dwc3_uboot_init(struct dwc3_device *dwc3_dev) ret = dwc3_core_init(dwc); if (ret) { - dev_err(dwc->dev, "failed to initialize core\n"); + dev_err(dev, "failed to initialize core\n"); goto err0; } + 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; } ret = dwc3_core_init_mode(dwc); if (ret) goto err2; - list_add_tail(&dwc->list, &dwc3_list); + ret = dwc3_debugfs_init(dwc); + if (ret) { + dev_err(dev, "failed to initialize debugfs\n"); + goto err3; + } + + pm_runtime_allow(dev); return 0; +err3: + dwc3_core_exit_mode(dwc); + 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); dwc3_core_exit(dwc); err0: @@ -950,276 +969,151 @@ err0: return ret; } -/** - * dwc3_uboot_exit - dwc3 core uboot cleanup code - * @index: index of this controller - * - * Performs cleanup of memory allocated in dwc3_uboot_init and other misc - * cleanups (equivalent to dwc3_remove in linux). index of _this_ controller - * should be passed and should match with the index passed in - * dwc3_device during init. - * - * Generally called from board file. - */ -void dwc3_uboot_exit(int index) -{ - struct dwc3 *dwc; - - list_for_each_entry(dwc, &dwc3_list, list) { - if (dwc->index != index) - continue; - - dwc3_core_exit_mode(dwc); - dwc3_event_buffers_cleanup(dwc); - dwc3_free_event_buffers(dwc); - dwc3_core_exit(dwc); - list_del(&dwc->list); - kfree(dwc->mem); - break; - } -} - -MODULE_ALIAS("platform:dwc3"); -MODULE_AUTHOR("Felipe Balbi <[email protected]>"); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver"); - -#if !CONFIG_IS_ENABLED(DM_USB_GADGET) -__weak int dwc3_uboot_interrupt_status(struct udevice *dev) +static int dwc3_remove(struct platform_device *pdev) { - return 1; -} + struct dwc3 *dwc = platform_get_drvdata(pdev); -/** - * dm_usb_gadget_handle_interrupts - handle dwc3 core interrupt - * @dev: device of this controller - * - * Invokes dwc3 gadget interrupts. - * - * Generally called from board file. - */ -int dm_usb_gadget_handle_interrupts(struct udevice *dev) -{ - struct dwc3 *dwc = NULL; + dwc3_debugfs_exit(dwc); + dwc3_core_exit_mode(dwc); + dwc3_event_buffers_cleanup(dwc); + dwc3_free_event_buffers(dwc); - if (!dwc3_uboot_interrupt_status(dev)) - return 0; + 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); - list_for_each_entry(dwc, &dwc3_list, list) { - if (dwc->dev != dev) - continue; + dwc3_core_exit(dwc); - dwc3_gadget_uboot_handle_interrupt(dwc); - break; - } + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); return 0; } -#endif -#if CONFIG_IS_ENABLED(PHY) && CONFIG_IS_ENABLED(DM_USB) -int dwc3_setup_phy(struct udevice *dev, struct phy_bulk *phys) +#ifdef CONFIG_PM_SLEEP +static int dwc3_suspend(struct device *dev) { - int ret; - - ret = generic_phy_get_bulk(dev, phys); - if (ret) - return ret; + struct dwc3 *dwc = dev_get_drvdata(dev); + unsigned long flags; - ret = generic_phy_init_bulk(phys); - if (ret) - return ret; + spin_lock_irqsave(&dwc->lock, flags); - ret = generic_phy_power_on_bulk(phys); - if (ret) - generic_phy_exit_bulk(phys); + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + case USB_DR_MODE_OTG: + dwc3_gadget_suspend(dwc); + /* FALLTHROUGH */ + case USB_DR_MODE_HOST: + default: + dwc3_event_buffers_cleanup(dwc); + break; + } - return ret; -} + dwc->gctl = dwc3_readl(dwc->regs, DWC3_GCTL); + spin_unlock_irqrestore(&dwc->lock, flags); -int dwc3_shutdown_phy(struct udevice *dev, struct phy_bulk *phys) -{ - int ret; + usb_phy_shutdown(dwc->usb3_phy); + usb_phy_shutdown(dwc->usb2_phy); + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); - ret = generic_phy_power_off_bulk(phys); - ret |= generic_phy_exit_bulk(phys); - return ret; + return 0; } -#endif -#if CONFIG_IS_ENABLED(DM_USB) -void dwc3_of_parse(struct dwc3 *dwc) +static int dwc3_resume(struct device *dev) { - const u8 *tmp; - struct udevice *dev = dwc->dev; - u8 lpm_nyet_threshold; - u8 tx_de_emphasis; - u8 hird_threshold; - u32 val; - int i; + struct dwc3 *dwc = dev_get_drvdata(dev); + unsigned long flags; + int ret; - /* default to highest possible threshold */ - lpm_nyet_threshold = 0xff; - - /* default to -3.5dB de-emphasis */ - tx_de_emphasis = 1; - - /* - * default to assert utmi_sleep_n and use maximum allowed HIRD - * threshold value of 0b1100 - */ - hird_threshold = 12; - - dwc->hsphy_mode = usb_get_phy_mode(dev_ofnode(dev)); - - dwc->has_lpm_erratum = dev_read_bool(dev, - "snps,has-lpm-erratum"); - tmp = dev_read_u8_array_ptr(dev, "snps,lpm-nyet-threshold", 1); - if (tmp) - lpm_nyet_threshold = *tmp; - - dwc->is_utmi_l1_suspend = dev_read_bool(dev, - "snps,is-utmi-l1-suspend"); - tmp = dev_read_u8_array_ptr(dev, "snps,hird-threshold", 1); - if (tmp) - hird_threshold = *tmp; - - dwc->disable_scramble_quirk = dev_read_bool(dev, - "snps,disable_scramble_quirk"); - dwc->u2exit_lfps_quirk = dev_read_bool(dev, - "snps,u2exit_lfps_quirk"); - dwc->u2ss_inp3_quirk = dev_read_bool(dev, - "snps,u2ss_inp3_quirk"); - dwc->req_p1p2p3_quirk = dev_read_bool(dev, - "snps,req_p1p2p3_quirk"); - dwc->del_p1p2p3_quirk = dev_read_bool(dev, - "snps,del_p1p2p3_quirk"); - dwc->del_phy_power_chg_quirk = dev_read_bool(dev, - "snps,del_phy_power_chg_quirk"); - dwc->lfps_filter_quirk = dev_read_bool(dev, - "snps,lfps_filter_quirk"); - dwc->rx_detect_poll_quirk = dev_read_bool(dev, - "snps,rx_detect_poll_quirk"); - dwc->dis_u3_susphy_quirk = dev_read_bool(dev, - "snps,dis_u3_susphy_quirk"); - dwc->dis_u2_susphy_quirk = dev_read_bool(dev, - "snps,dis_u2_susphy_quirk"); - dwc->dis_del_phy_power_chg_quirk = dev_read_bool(dev, - "snps,dis-del-phy-power-chg-quirk"); - dwc->dis_tx_ipgap_linecheck_quirk = dev_read_bool(dev, - "snps,dis-tx-ipgap-linecheck-quirk"); - dwc->dis_enblslpm_quirk = dev_read_bool(dev, - "snps,dis_enblslpm_quirk"); - dwc->dis_u2_freeclk_exists_quirk = dev_read_bool(dev, - "snps,dis-u2-freeclk-exists-quirk"); - dwc->tx_de_emphasis_quirk = dev_read_bool(dev, - "snps,tx_de_emphasis_quirk"); - tmp = dev_read_u8_array_ptr(dev, "snps,tx_de_emphasis", 1); - if (tmp) - tx_de_emphasis = *tmp; - - dwc->lpm_nyet_threshold = lpm_nyet_threshold; - dwc->tx_de_emphasis = tx_de_emphasis; - - dwc->hird_threshold = hird_threshold - | (dwc->is_utmi_l1_suspend << 4); - - dev_read_u32(dev, "snps,quirk-frame-length-adjustment", &dwc->fladj); - - /* - * Handle property "snps,incr-burst-type-adjustment". - * Get the number of value from this property: - * result <= 0, means this property is not supported. - * result = 1, means INCRx burst mode supported. - * result > 1, means undefined length burst mode supported. - */ - dwc->incrx_mode = INCRX_BURST_MODE; - dwc->incrx_size = 0; - for (i = 0; i < 8; i++) { - if (dev_read_u32_index(dev, "snps,incr-burst-type-adjustment", - i, &val)) - break; - - dwc->incrx_mode = INCRX_UNDEF_LENGTH_BURST_MODE; - dwc->incrx_size = max(dwc->incrx_size, val); - } -} - -int dwc3_init(struct dwc3 *dwc) -{ - int ret; - u32 reg; + usb_phy_init(dwc->usb3_phy); + usb_phy_init(dwc->usb2_phy); + ret = phy_init(dwc->usb2_generic_phy); + if (ret < 0) + return ret; - dwc3_cache_hwparams(dwc); + ret = phy_init(dwc->usb3_generic_phy); + if (ret < 0) + goto err_usb2phy_init; - ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); - if (ret) { - dev_err(dwc->dev, "failed to allocate event buffers\n"); - return -ENOMEM; - } + spin_lock_irqsave(&dwc->lock, flags); - ret = dwc3_core_init(dwc); - if (ret) { - dev_err(dwc->dev, "failed to initialize core\n"); - goto core_fail; - } + dwc3_event_buffers_setup(dwc); + dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl); - ret = dwc3_event_buffers_setup(dwc); - if (ret) { - dev_err(dwc->dev, "failed to setup event buffers\n"); - goto event_fail; + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + case USB_DR_MODE_OTG: + dwc3_gadget_resume(dwc); + /* FALLTHROUGH */ + case USB_DR_MODE_HOST: + default: + /* do nothing */ + break; } - if (dwc->revision >= DWC3_REVISION_250A) { - reg = dwc3_readl(dwc->regs, DWC3_GUCTL1); + spin_unlock_irqrestore(&dwc->lock, flags); - /* - * 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_GUCTL1_DEV_L1_EXIT_BY_HW; + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); - if (dwc->dis_tx_ipgap_linecheck_quirk) - reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS; + return 0; - dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); - } +err_usb2phy_init: + phy_exit(dwc->usb2_generic_phy); - if (dwc->dr_mode == USB_DR_MODE_HOST || - dwc->dr_mode == USB_DR_MODE_OTG) { - reg = dwc3_readl(dwc->regs, DWC3_GUCTL); + return ret; +} - reg |= DWC3_GUCTL_HSTINAUTORETRY; +static const struct dev_pm_ops dwc3_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume) +}; - dwc3_writel(dwc->regs, DWC3_GUCTL, reg); - } +#define DWC3_PM_OPS &(dwc3_dev_pm_ops) +#else +#define DWC3_PM_OPS NULL +#endif - ret = dwc3_core_init_mode(dwc); - if (ret) - goto mode_fail; +#ifdef CONFIG_OF +static const struct of_device_id of_dwc3_match[] = { + { + .compatible = "snps,dwc3" + }, + { + .compatible = "synopsys,dwc3" + }, + { }, +}; +MODULE_DEVICE_TABLE(of, of_dwc3_match); +#endif - return 0; +#ifdef CONFIG_ACPI -mode_fail: - dwc3_event_buffers_cleanup(dwc); +#define ACPI_ID_INTEL_BSW "808622B7" -event_fail: - dwc3_core_exit(dwc); +static const struct acpi_device_id dwc3_acpi_match[] = { + { ACPI_ID_INTEL_BSW, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match); +#endif -core_fail: - dwc3_free_event_buffers(dwc); +static struct platform_driver dwc3_driver = { + .probe = dwc3_probe, + .remove = dwc3_remove, + .driver = { + .name = "dwc3", + .of_match_table = of_match_ptr(of_dwc3_match), + .acpi_match_table = ACPI_PTR(dwc3_acpi_match), + .pm = DWC3_PM_OPS, + }, +}; - return ret; -} +module_platform_driver(dwc3_driver); -void dwc3_remove(struct dwc3 *dwc) -{ - dwc3_core_exit_mode(dwc); - dwc3_event_buffers_cleanup(dwc); - dwc3_free_event_buffers(dwc); - dwc3_core_stop(dwc); - dwc3_core_exit(dwc); - kfree(dwc->mem); -} -#endif +MODULE_ALIAS("platform:dwc3"); +MODULE_AUTHOR("Felipe Balbi <[email protected]>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver"); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index b572ea340c8c..4bb9aa696ede 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1,28 +1,37 @@ -/* SPDX-License-Identifier: GPL-2.0 */ /** * core.h - DesignWare USB3 DRD Core Header * - * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com * * Authors: Felipe Balbi <[email protected]>, * Sebastian Andrzej Siewior <[email protected]> * - * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/core.h) and ported - * to uboot. - * - * commit 460d098cb6 : usb: dwc3: make HIRD threshold configurable + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #ifndef __DRIVERS_USB_DWC3_CORE_H #define __DRIVERS_USB_DWC3_CORE_H -#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/spinlock.h> #include <linux/ioport.h> +#include <linux/list.h> +#include <linux/dma-mapping.h> +#include <linux/mm.h> +#include <linux/debugfs.h> #include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> #include <linux/usb/otg.h> -#include <linux/usb/phy.h> + +#include <linux/phy/phy.h> #define DWC3_MSG_MAX 500 @@ -75,7 +84,6 @@ #define DWC3_GCTL 0xc110 #define DWC3_GEVTEN 0xc114 #define DWC3_GSTS 0xc118 -#define DWC3_GUCTL1 0xc11c #define DWC3_GSNPSID 0xc120 #define DWC3_GGPIO 0xc124 #define DWC3_GUID 0xc128 @@ -115,7 +123,6 @@ #define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10)) #define DWC3_GHWPARAMS8 0xc600 -#define DWC3_GFLADJ 0xc630 /* Device Registers */ #define DWC3_DCFG 0xc700 @@ -139,17 +146,6 @@ /* Bit fields */ -/* Global SoC Bus Configuration INCRx Register 0 */ -#define DWC3_GSBUSCFG0_INCR256BRSTENA (1 << 7) /* INCR256 burst */ -#define DWC3_GSBUSCFG0_INCR128BRSTENA (1 << 6) /* INCR128 burst */ -#define DWC3_GSBUSCFG0_INCR64BRSTENA (1 << 5) /* INCR64 burst */ -#define DWC3_GSBUSCFG0_INCR32BRSTENA (1 << 4) /* INCR32 burst */ -#define DWC3_GSBUSCFG0_INCR16BRSTENA (1 << 3) /* INCR16 burst */ -#define DWC3_GSBUSCFG0_INCR8BRSTENA (1 << 2) /* INCR8 burst */ -#define DWC3_GSBUSCFG0_INCR4BRSTENA (1 << 1) /* INCR4 burst */ -#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */ -#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff - /* Global Configuration Register */ #define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19) #define DWC3_GCTL_U2RSTECN (1 << 16) @@ -174,26 +170,9 @@ #define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1) #define DWC3_GCTL_DSBLCLKGTNG (1 << 0) -/* Global User Control Register */ -#define DWC3_GUCTL_HSTINAUTORETRY BIT(14) - -/* Global User Control 1 Register */ -#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28) -#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) - /* Global USB2 PHY Configuration Register */ #define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) -#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS (1 << 30) #define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) -#define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8) -#define DWC3_GUSB2PHYCFG_PHYIF(n) ((n) << 3) -#define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1) -#define DWC3_GUSB2PHYCFG_USBTRDTIM(n) ((n) << 10) -#define DWC3_GUSB2PHYCFG_USBTRDTIM_MASK DWC3_GUSB2PHYCFG_USBTRDTIM(0xf) -#define USBTRDTIM_UTMI_8_BIT 9 -#define USBTRDTIM_UTMI_16_BIT 5 -#define UTMI_PHYIF_16_BIT 1 -#define UTMI_PHYIF_8_BIT 0 /* Global USB3 PIPE Control Register */ #define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) @@ -245,17 +224,6 @@ /* Global HWPARAMS6 Register */ #define DWC3_GHWPARAMS6_EN_FPGA (1 << 7) -/* Global Frame Length Adjustment Register */ -#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7) -#define DWC3_GFLADJ_30MHZ_MASK 0x3f -#define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8) -#define DWC3_GFLADJ_240MHZDECR GENMASK(30, 24) -#define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31) - -/* Global User Control Register*/ -#define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000 -#define DWC3_GUCTL_REFCLKPER_SEL 22 - /* Device Configuration Register */ #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) @@ -405,8 +373,6 @@ #define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0) #define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0) -#define DWC3_DEPCMD_CMD(x) ((x) & 0xf) - /* The EP number goes 0..31 so ep0 is always out and ep1 is always in */ #define DWC3_DALEPENA_EP(n) (1 << n) @@ -436,7 +402,7 @@ struct dwc3_event_buffer { unsigned int count; unsigned int flags; -#define DWC3_EVENT_PENDING (1UL << 0) +#define DWC3_EVENT_PENDING BIT(0) dma_addr_t dma; @@ -670,7 +636,6 @@ struct dwc3_scratchpad_array { * @ep0_trb: dma address of ep0_trb * @ep0_usb_req: dummy req used while handling STD USB requests * @ep0_bounce_addr: dma address of ep0_bounce - * @setup_buf_addr: dma address of setup_buf * @scratch_addr: dma address of scratchbuf * @lock: for synchronizing * @dev: pointer to our struct device @@ -678,19 +643,18 @@ struct dwc3_scratchpad_array { * @event_buffer_list: a list of event buffers * @gadget: device side representation of the peripheral controller * @gadget_driver: pointer to the gadget driver - * @ref_clk: reference clock * @regs: base address for our registers * @regs_size: address space size - * @ref_clk_per: reference clock period configuration * @nr_scratch: number of scratch buffers * @num_event_buffers: calculated number of event buffers * @u1u2: only used on revisions <1.83a for workaround * @maximum_speed: maximum speed requested (mainly for testing purposes) * @revision: revision register contents * @dr_mode: requested mode of operation - * @hsphy_mode: UTMI phy mode, one of following: - * - USBPHY_INTERFACE_MODE_UTMI - * - USBPHY_INTERFACE_MODE_UTMIW + * @usb2_phy: pointer to USB2 PHY + * @usb3_phy: pointer to USB3 PHY + * @usb2_generic_phy: pointer to USB2 PHY + * @usb3_generic_phy: pointer to USB3 PHY * @dcfg: saved contents of DCFG register * @gctl: saved contents of GCTL register * @isoch_delay: wValue from Set Isochronous Delay request; @@ -719,8 +683,8 @@ struct dwc3_scratchpad_array { * @has_lpm_erratum: true when core was configured with LPM Erratum. Note that * there's now way for software to detect this in runtime. * @is_utmi_l1_suspend: the core asserts output signal - * 0 - utmi_sleep_n - * 1 - utmi_l1_suspend_n + * 0 - utmi_sleep_n + * 1 - utmi_l1_suspend_n * @is_selfpowered: true when we are selfpowered * @is_fpga: true when we are using the FPGA board * @needs_fifo_resize: not all users might want fifo resizing, flag it @@ -741,12 +705,10 @@ struct dwc3_scratchpad_array { * @dis_u2_susphy_quirk: set if we disable usb2 suspend phy * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk * @tx_de_emphasis: Tx de-emphasis value - * 0 - -6dB de-emphasis - * 1 - -3.5dB de-emphasis - * 2 - No de-emphasis - * 3 - Reserved - * @index: index of _this_ controller - * @list: to maintain the list of dwc3 controllers + * 0 - -6dB de-emphasis + * 1 - -3.5dB de-emphasis + * 2 - No de-emphasis + * 3 - Reserved */ struct dwc3 { struct usb_ctrlrequest *ctrl_req; @@ -758,17 +720,12 @@ struct dwc3 { dma_addr_t ep0_trb_addr; dma_addr_t ep0_bounce_addr; dma_addr_t scratch_addr; - dma_addr_t setup_buf_addr; struct dwc3_request ep0_usb_req; /* device lock */ spinlock_t lock; -#if defined(__UBOOT__) && CONFIG_IS_ENABLED(DM_USB) - struct udevice *dev; -#else struct device *dev; -#endif struct platform_device *xhci; struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM]; @@ -779,13 +736,16 @@ struct dwc3 { struct usb_gadget gadget; struct usb_gadget_driver *gadget_driver; - struct clk *ref_clk; + struct usb_phy *usb2_phy; + struct usb_phy *usb3_phy; + + struct phy *usb2_generic_phy; + struct phy *usb3_generic_phy; void __iomem *regs; size_t regs_size; enum usb_dr_mode dr_mode; - enum usb_phy_interface hsphy_mode; /* used for suspend/resume */ u32 dcfg; @@ -816,7 +776,6 @@ struct dwc3 { #define DWC3_REVISION_260A 0x5533260a #define DWC3_REVISION_270A 0x5533270a #define DWC3_REVISION_280A 0x5533280a -#define DWC3_REVISION_290A 0x5533290a enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; @@ -843,10 +802,6 @@ struct dwc3 { u8 test_mode_nr; u8 lpm_nyet_threshold; u8 hird_threshold; - u32 fladj; - u32 ref_clk_per; - u8 incrx_mode; - u32 incrx_size; unsigned delayed_status:1; unsigned ep0_bounced:1; @@ -873,20 +828,11 @@ struct dwc3 { unsigned rx_detect_poll_quirk:1; unsigned dis_u3_susphy_quirk:1; unsigned dis_u2_susphy_quirk:1; - unsigned dis_del_phy_power_chg_quirk:1; - unsigned dis_tx_ipgap_linecheck_quirk:1; - unsigned dis_enblslpm_quirk:1; - unsigned dis_u2_freeclk_exists_quirk:1; unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; - int index; - struct list_head list; }; -#define INCRX_BURST_MODE 0 -#define INCRX_UNDEF_LENGTH_BURST_MODE 1 - /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -904,30 +850,6 @@ struct dwc3_event_type { #define DWC3_DEPEVT_STREAMEVT 0x06 #define DWC3_DEPEVT_EPCMDCMPLT 0x07 -/** - * dwc3_ep_event_string - returns event name - * @event: then event code - */ -static inline const char *dwc3_ep_event_string(u8 event) -{ - switch (event) { - case DWC3_DEPEVT_XFERCOMPLETE: - return "Transfer Complete"; - case DWC3_DEPEVT_XFERINPROGRESS: - return "Transfer In-Progress"; - case DWC3_DEPEVT_XFERNOTREADY: - return "Transfer Not Ready"; - case DWC3_DEPEVT_RXTXFIFOEVT: - return "FIFO"; - case DWC3_DEPEVT_STREAMEVT: - return "Stream"; - case DWC3_DEPEVT_EPCMDCMPLT: - return "Endpoint Command Complete"; - } - - return "UNKNOWN"; -} - /** * struct dwc3_event_depvt - Device Endpoint Events * @one_bit: indicates this is an endpoint event (not used) @@ -1057,17 +979,20 @@ struct dwc3_gadget_ep_cmd_params { #define DWC3_HAS_OTG BIT(3) /* prototypes */ +void dwc3_set_mode(struct dwc3 *dwc, u32 mode); int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc); -void dwc3_of_parse(struct dwc3 *dwc); -int dwc3_init(struct dwc3 *dwc); -void dwc3_remove(struct dwc3 *dwc); +#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) +int dwc3_host_init(struct dwc3 *dwc); +void dwc3_host_exit(struct dwc3 *dwc); +#else static inline int dwc3_host_init(struct dwc3 *dwc) { return 0; } static inline void dwc3_host_exit(struct dwc3 *dwc) { } +#endif -#ifdef CONFIG_USB_DWC3_GADGET +#if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) int dwc3_gadget_init(struct dwc3 *dwc); void dwc3_gadget_exit(struct dwc3 *dwc); int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode); @@ -1097,4 +1022,20 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, { return 0; } #endif +/* power management interface */ +#if !IS_ENABLED(CONFIG_USB_DWC3_HOST) +int dwc3_gadget_suspend(struct dwc3 *dwc); +int dwc3_gadget_resume(struct dwc3 *dwc); +#else +static inline int dwc3_gadget_suspend(struct dwc3 *dwc) +{ + return 0; +} + +static inline int dwc3_gadget_resume(struct dwc3 *dwc) +{ + return 0; +} +#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */ + #endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/debug.c b/drivers/usb/dwc3/debug.c new file mode 100644 index 000000000000..0be6885bc370 --- /dev/null +++ b/drivers/usb/dwc3/debug.c @@ -0,0 +1,32 @@ +/** + * debug.c - DesignWare USB3 DRD Controller Debug/Trace Support + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Felipe Balbi <[email protected]> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "debug.h" + +void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + trace(&vaf); + + va_end(args); +} diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h new file mode 100644 index 000000000000..07fbc2d94fd4 --- /dev/null +++ b/drivers/usb/dwc3/debug.h @@ -0,0 +1,228 @@ +/** + * debug.h - DesignWare USB3 DRD Controller Debug Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <[email protected]>, + * Sebastian Andrzej Siewior <[email protected]> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __DWC3_DEBUG_H +#define __DWC3_DEBUG_H + +#include "core.h" + +/** + * dwc3_gadget_ep_cmd_string - returns endpoint command string + * @cmd: command code + */ +static inline const char * +dwc3_gadget_ep_cmd_string(u8 cmd) +{ + switch (cmd) { + case DWC3_DEPCMD_DEPSTARTCFG: + return "Start New Configuration"; + case DWC3_DEPCMD_ENDTRANSFER: + return "End Transfer"; + case DWC3_DEPCMD_UPDATETRANSFER: + return "Update Transfer"; + case DWC3_DEPCMD_STARTTRANSFER: + return "Start Transfer"; + case DWC3_DEPCMD_CLEARSTALL: + return "Clear Stall"; + case DWC3_DEPCMD_SETSTALL: + return "Set Stall"; + case DWC3_DEPCMD_GETEPSTATE: + return "Get Endpoint State"; + case DWC3_DEPCMD_SETTRANSFRESOURCE: + return "Set Endpoint Transfer Resource"; + case DWC3_DEPCMD_SETEPCONFIG: + return "Set Endpoint Configuration"; + default: + return "UNKNOWN command"; + } +} + +/** + * dwc3_gadget_generic_cmd_string - returns generic command string + * @cmd: command code + */ +static inline const char * +dwc3_gadget_generic_cmd_string(u8 cmd) +{ + switch (cmd) { + case DWC3_DGCMD_SET_LMP: + return "Set LMP"; + case DWC3_DGCMD_SET_PERIODIC_PAR: + return "Set Periodic Parameters"; + case DWC3_DGCMD_XMIT_FUNCTION: + return "Transmit Function Wake Device Notification"; + case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO: + return "Set Scratchpad Buffer Array Address Lo"; + case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI: + return "Set Scratchpad Buffer Array Address Hi"; + case DWC3_DGCMD_SELECTED_FIFO_FLUSH: + return "Selected FIFO Flush"; + case DWC3_DGCMD_ALL_FIFO_FLUSH: + return "All FIFO Flush"; + case DWC3_DGCMD_SET_ENDPOINT_NRDY: + return "Set Endpoint NRDY"; + case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK: + return "Run SoC Bus Loopback Test"; + default: + return "UNKNOWN"; + } +} + +/** + * dwc3_gadget_link_string - returns link name + * @link_state: link state code + */ +static inline const char * +dwc3_gadget_link_string(enum dwc3_link_state link_state) +{ + switch (link_state) { + case DWC3_LINK_STATE_U0: + return "U0"; + case DWC3_LINK_STATE_U1: + return "U1"; + case DWC3_LINK_STATE_U2: + return "U2"; + case DWC3_LINK_STATE_U3: + return "U3"; + case DWC3_LINK_STATE_SS_DIS: + return "SS.Disabled"; + case DWC3_LINK_STATE_RX_DET: + return "RX.Detect"; + case DWC3_LINK_STATE_SS_INACT: + return "SS.Inactive"; + case DWC3_LINK_STATE_POLL: + return "Polling"; + case DWC3_LINK_STATE_RECOV: + return "Recovery"; + case DWC3_LINK_STATE_HRESET: + return "Hot Reset"; + case DWC3_LINK_STATE_CMPLY: + return "Compliance"; + case DWC3_LINK_STATE_LPBK: + return "Loopback"; + case DWC3_LINK_STATE_RESET: + return "Reset"; + case DWC3_LINK_STATE_RESUME: + return "Resume"; + default: + return "UNKNOWN link state\n"; + } +} + +/** + * dwc3_gadget_event_string - returns event name + * @event: the event code + */ +static inline const char *dwc3_gadget_event_string(u8 event) +{ + switch (event) { + case DWC3_DEVICE_EVENT_DISCONNECT: + return "Disconnect"; + case DWC3_DEVICE_EVENT_RESET: + return "Reset"; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + return "Connection Done"; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + return "Link Status Change"; + case DWC3_DEVICE_EVENT_WAKEUP: + return "WakeUp"; + case DWC3_DEVICE_EVENT_EOPF: + return "End-Of-Frame"; + case DWC3_DEVICE_EVENT_SOF: + return "Start-Of-Frame"; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + return "Erratic Error"; + case DWC3_DEVICE_EVENT_CMD_CMPL: + return "Command Complete"; + case DWC3_DEVICE_EVENT_OVERFLOW: + return "Overflow"; + } + + return "UNKNOWN"; +} + +/** + * dwc3_ep_event_string - returns event name + * @event: then event code + */ +static inline const char *dwc3_ep_event_string(u8 event) +{ + switch (event) { + case DWC3_DEPEVT_XFERCOMPLETE: + return "Transfer Complete"; + case DWC3_DEPEVT_XFERINPROGRESS: + return "Transfer In-Progress"; + case DWC3_DEPEVT_XFERNOTREADY: + return "Transfer Not Ready"; + case DWC3_DEPEVT_RXTXFIFOEVT: + return "FIFO"; + case DWC3_DEPEVT_STREAMEVT: + return "Stream"; + case DWC3_DEPEVT_EPCMDCMPLT: + return "Endpoint Command Complete"; + } + + return "UNKNOWN"; +} + +/** + * dwc3_gadget_event_type_string - return event name + * @event: the event code + */ +static inline const char *dwc3_gadget_event_type_string(u8 event) +{ + switch (event) { + case DWC3_DEVICE_EVENT_DISCONNECT: + return "Disconnect"; + case DWC3_DEVICE_EVENT_RESET: + return "Reset"; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + return "Connect Done"; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + return "Link Status Change"; + case DWC3_DEVICE_EVENT_WAKEUP: + return "Wake-Up"; + case DWC3_DEVICE_EVENT_HIBER_REQ: + return "Hibernation"; + case DWC3_DEVICE_EVENT_EOPF: + return "End of Periodic Frame"; + case DWC3_DEVICE_EVENT_SOF: + return "Start of Frame"; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + return "Erratic Error"; + case DWC3_DEVICE_EVENT_CMD_CMPL: + return "Command Complete"; + case DWC3_DEVICE_EVENT_OVERFLOW: + return "Overflow"; + default: + return "UNKNOWN"; + } +} + +void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...); + +#ifdef CONFIG_DEBUG_FS +extern int dwc3_debugfs_init(struct dwc3 *); +extern void dwc3_debugfs_exit(struct dwc3 *); +#else +static inline int dwc3_debugfs_init(struct dwc3 *d) +{ return 0; } +static inline void dwc3_debugfs_exit(struct dwc3 *d) +{ } +#endif +#endif /* __DWC3_DEBUG_H */ diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c deleted file mode 100644 index 99519602eb2c..000000000000 --- a/drivers/usb/dwc3/dwc3-am62.c +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * TI AM62 specific glue layer for DWC3 - */ - -#include <dm.h> -#include <dm/device_compat.h> -#include <regmap.h> -#include <syscon.h> -#include <asm/io.h> - -#include "dwc3-generic.h" - -#define USBSS_MODE_CONTROL 0x1c -#define USBSS_PHY_CONFIG 0x8 -#define USBSS_PHY_VBUS_SEL_MASK GENMASK(2, 1) -#define USBSS_PHY_VBUS_SEL_SHIFT 1 -#define USBSS_MODE_VALID BIT(0) -#define PHY_PLL_REFCLK_MASK GENMASK(3, 0) -static const int dwc3_ti_am62_rate_table[] = { /* in KHZ */ - 9600, - 10000, - 12000, - 19200, - 20000, - 24000, - 25000, - 26000, - 38400, - 40000, - 58000, - 50000, - 52000, -}; - -static void dwc3_ti_am62_glue_configure(struct udevice *dev, int index, - enum usb_dr_mode mode) -{ - struct clk usb2_refclk; - int rate_code, i, ret; - unsigned long rate; - u32 reg; - void *usbss; - bool vbus_divider; - struct regmap *syscon; - struct ofnode_phandle_args args; - - usbss = dev_remap_addr_index(dev, 0); - if (IS_ERR(usbss)) { - dev_err(dev, "can't map IOMEM resource\n"); - return; - } - - ret = clk_get_by_name(dev, "ref", &usb2_refclk); - if (ret) { - dev_err(dev, "can't get usb2_refclk\n"); - return; - } - - /* Calculate the rate code */ - rate = clk_get_rate(&usb2_refclk); - rate /= 1000; /* To KHz */ - for (i = 0; i < ARRAY_SIZE(dwc3_ti_am62_rate_table); i++) { - if (dwc3_ti_am62_rate_table[i] == rate) - break; - } - - if (i == ARRAY_SIZE(dwc3_ti_am62_rate_table)) { - dev_err(dev, "unsupported usb2_refclk rate: %lu KHz\n", rate); - return; - } - - rate_code = i; - - /* Read the syscon property */ - syscon = syscon_regmap_lookup_by_phandle(dev, "ti,syscon-phy-pll-refclk"); - if (IS_ERR(syscon)) { - dev_err(dev, "unable to get ti,syscon-phy-pll-refclk regmap\n"); - return; - } - - ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "ti,syscon-phy-pll-refclk", NULL, 1, - 0, &args); - if (ret) - return; - - /* Program PHY PLL refclk by reading syscon property */ - ret = regmap_update_bits(syscon, args.args[0], PHY_PLL_REFCLK_MASK, rate_code); - if (ret) { - dev_err(dev, "failed to set phy pll reference clock rate\n"); - return; - } - - /* VBUS divider select */ - reg = readl(usbss + USBSS_PHY_CONFIG); - vbus_divider = dev_read_bool(dev, "ti,vbus-divider"); - if (vbus_divider) - reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT; - - writel(reg, usbss + USBSS_PHY_CONFIG); - - /* Set mode valid */ - reg = readl(usbss + USBSS_MODE_CONTROL); - reg |= USBSS_MODE_VALID; - writel(reg, usbss + USBSS_MODE_CONTROL); -} - -struct dwc3_glue_ops ti_am62_ops = { - .glue_configure = dwc3_ti_am62_glue_configure, -}; - -static const struct udevice_id dwc3_am62_match[] = { - { .compatible = "ti,am62-usb", .data = (ulong)&ti_am62_ops }, - { /* sentinel */ } -}; - -U_BOOT_DRIVER(dwc3_am62_wrapper) = { - .name = "dwc3-am62", - .id = UCLASS_SIMPLE_BUS, - .of_match = dwc3_am62_match, - .bind = dwc3_glue_bind, - .probe = dwc3_glue_probe, - .remove = dwc3_glue_remove, - .plat_auto = sizeof(struct dwc3_glue_data), -}; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 680756532f0d..1bc77a3b4997 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -1,34 +1,40 @@ -// SPDX-License-Identifier: GPL-2.0 /** * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling * - * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com * * Authors: Felipe Balbi <[email protected]>, * Sebastian Andrzej Siewior <[email protected]> * - * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/ep0.c) and ported - * to uboot. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * - * commit c00552ebaf : Merge 3.18-rc7 into usb-next + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ -#include <cpu_func.h> -#include <dm.h> -#include <dm/device_compat.h> -#include <linux/bug.h> + #include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/interrupt.h> +#include <linux/io.h> #include <linux/list.h> +#include <linux/dma-mapping.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/composite.h> #include "core.h" +#include "debug.h" #include "gadget.h" #include "io.h" -#include "linux-compat.h" - static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep); static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, struct dwc3_ep *dep, struct dwc3_request *req); @@ -50,7 +56,7 @@ static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) } static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, - u32 len, u32 type, unsigned chain) + u32 len, u32 type) { struct dwc3_gadget_ep_cmd_params params; struct dwc3_trb *trb; @@ -60,14 +66,11 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, dep = dwc->eps[epnum]; if (dep->flags & DWC3_EP_BUSY) { - dev_vdbg(dwc->dev, "%s still busy\n", dep->name); + dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name); return 0; } - trb = &dwc->ep0_trb[dep->free_slot]; - - if (chain) - dep->free_slot++; + trb = dwc->ep0_trb; trb->bpl = lower_32_bits(buf_dma); trb->bph = upper_32_bits(buf_dma); @@ -75,28 +78,21 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, trb->ctrl = type; trb->ctrl |= (DWC3_TRB_CTRL_HWO + | DWC3_TRB_CTRL_LST + | DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_ISP_IMI); - if (chain) - trb->ctrl |= DWC3_TRB_CTRL_CHN; - else - trb->ctrl |= (DWC3_TRB_CTRL_IOC - | DWC3_TRB_CTRL_LST); - - dwc3_flush_cache((uintptr_t)buf_dma, len); - dwc3_flush_cache((uintptr_t)trb, sizeof(*trb)); - - if (chain) - return 0; - memset(¶ms, 0, sizeof(params)); params.param0 = upper_32_bits(dwc->ep0_trb_addr); params.param1 = lower_32_bits(dwc->ep0_trb_addr); + trace_dwc3_prepare_trb(dep, trb); + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_STARTTRANSFER, ¶ms); if (ret < 0) { - dev_dbg(dwc->dev, "%s STARTTRANSFER failed", dep->name); + dwc3_trace(trace_dwc3_ep0, "%s STARTTRANSFER failed", + dep->name); return ret; } @@ -161,7 +157,8 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, if (dwc->ep0state == EP0_STATUS_PHASE) __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); else - dev_dbg(dwc->dev, "too early for delayed status"); + dwc3_trace(trace_dwc3_ep0, + "too early for delayed status"); return 0; } @@ -225,7 +222,8 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, spin_lock_irqsave(&dwc->lock, flags); if (!dep->endpoint.desc) { - dev_dbg(dwc->dev, "trying to queue request %p to disabled %s", + dwc3_trace(trace_dwc3_ep0, + "trying to queue request %p to disabled %s", request, dep->name); ret = -ESHUTDOWN; goto out; @@ -237,9 +235,10 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, goto out; } - dev_vdbg(dwc->dev, "queueing request %p to %s length %d state '%s\n'", - request, dep->name, request->length, - dwc3_ep0_state_string(dwc->ep0state)); + dwc3_trace(trace_dwc3_ep0, + "queueing request %p to %s length %d state '%s'", + request, dep->name, request->length, + dwc3_ep0_state_string(dwc->ep0state)); ret = __dwc3_gadget_ep0_queue(dep, req); @@ -286,6 +285,8 @@ int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) { + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; unsigned long flags; int ret; @@ -301,7 +302,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc) int ret; ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8, - DWC3_TRBCTL_CONTROL_SETUP, 0); + DWC3_TRBCTL_CONTROL_SETUP); WARN_ON(ret < 0); } @@ -380,7 +381,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, dep = dwc->eps[0]; dwc->ep0_usb_req.dep = dep; dwc->ep0_usb_req.request.length = sizeof(*response_pkt); - dwc->ep0_usb_req.request.buf = (void *)(uintptr_t)dwc->setup_buf_addr; + dwc->ep0_usb_req.request.buf = dwc->setup_buf; dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl; return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req); @@ -504,12 +505,13 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) addr = le16_to_cpu(ctrl->wValue); if (addr > 127) { - dev_dbg(dwc->dev, "invalid device address %d", addr); + dwc3_trace(trace_dwc3_ep0, "invalid device address %d", addr); return -EINVAL; } if (state == USB_STATE_CONFIGURED) { - dev_dbg(dwc->dev, "trying to set address when configured"); + dwc3_trace(trace_dwc3_ep0, + "trying to set address when configured"); return -EINVAL; } @@ -574,7 +576,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) dwc3_writel(dwc->regs, DWC3_DCTL, reg); dwc->resize_fifos = true; - dev_dbg(dwc->dev, "resize FIFOs flag SET"); + dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET"); } break; @@ -639,10 +641,12 @@ static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) struct dwc3_ep *dep; enum usb_device_state state = dwc->gadget.state; u16 wLength; + u16 wValue; if (state == USB_STATE_DEFAULT) return -EINVAL; + wValue = le16_to_cpu(ctrl->wValue); wLength = le16_to_cpu(ctrl->wLength); if (wLength != 6) { @@ -662,7 +666,7 @@ static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) dep = dwc->eps[0]; dwc->ep0_usb_req.dep = dep; dwc->ep0_usb_req.request.length = dep->endpoint.maxpacket; - dwc->ep0_usb_req.request.buf = (void *)(uintptr_t)dwc->setup_buf_addr; + dwc->ep0_usb_req.request.buf = dwc->setup_buf; dwc->ep0_usb_req.request.complete = dwc3_ep0_set_sel_cmpl; return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req); @@ -696,35 +700,35 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) switch (ctrl->bRequest) { case USB_REQ_GET_STATUS: - dev_vdbg(dwc->dev, "USB_REQ_GET_STATUS\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS"); ret = dwc3_ep0_handle_status(dwc, ctrl); break; case USB_REQ_CLEAR_FEATURE: - dev_vdbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE"); ret = dwc3_ep0_handle_feature(dwc, ctrl, 0); break; case USB_REQ_SET_FEATURE: - dev_vdbg(dwc->dev, "USB_REQ_SET_FEATURE\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE"); ret = dwc3_ep0_handle_feature(dwc, ctrl, 1); break; case USB_REQ_SET_ADDRESS: - dev_vdbg(dwc->dev, "USB_REQ_SET_ADDRESS\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS"); ret = dwc3_ep0_set_address(dwc, ctrl); break; case USB_REQ_SET_CONFIGURATION: - dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION"); ret = dwc3_ep0_set_config(dwc, ctrl); break; case USB_REQ_SET_SEL: - dev_vdbg(dwc->dev, "USB_REQ_SET_SEL\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL"); ret = dwc3_ep0_set_sel(dwc, ctrl); break; case USB_REQ_SET_ISOCH_DELAY: - dev_vdbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY"); ret = dwc3_ep0_set_isoch_delay(dwc, ctrl); break; default: - dev_vdbg(dwc->dev, "Forwarding to gadget driver\n"); + dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver"); ret = dwc3_ep0_delegate_req(dwc, ctrl); break; } @@ -742,7 +746,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, if (!dwc->gadget_driver) goto out; - dwc3_invalidate_cache((uintptr_t)ctrl, sizeof(*ctrl)); + trace_dwc3_ctrl_req(ctrl); len = le16_to_cpu(ctrl->wLength); if (!len) { @@ -775,10 +779,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, struct usb_request *ur; struct dwc3_trb *trb; struct dwc3_ep *ep0; - unsigned transfer_size = 0; - unsigned maxp; - void *buf; - u32 transferred = 0; + u32 transferred; u32 status; u32 length; u8 epnum; @@ -790,50 +791,34 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, trb = dwc->ep0_trb; + trace_dwc3_complete_trb(ep0, trb); + r = next_request(&ep0->request_list); if (!r) return; - dwc3_flush_cache((uintptr_t)trb, sizeof(*trb)); - status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) { - dev_dbg(dwc->dev, "Setup Pending received"); - dwc3_gadget_giveback(ep0, r, -ECONNRESET); + dwc3_trace(trace_dwc3_ep0, "Setup Pending received"); + + if (r) + dwc3_gadget_giveback(ep0, r, -ECONNRESET); + return; } ur = &r->request; - buf = ur->buf; length = trb->size & DWC3_TRB_SIZE_MASK; - maxp = ep0->endpoint.maxpacket; - if (dwc->ep0_bounced) { - /* - * Handle the first TRB before handling the bounce buffer if - * the request length is greater than the bounce buffer size. - */ - if (ur->length > DWC3_EP0_BOUNCE_SIZE) { - transfer_size = (ur->length / maxp) * maxp; - transferred = transfer_size - length; - buf = (u8 *)buf + transferred; - ur->actual += transferred; + unsigned transfer_size = ur->length; + unsigned maxp = ep0->endpoint.maxpacket; - trb++; - dwc3_flush_cache((uintptr_t)trb, sizeof(*trb)); - length = trb->size & DWC3_TRB_SIZE_MASK; - - ep0->free_slot = 0; - } - - transfer_size = roundup((ur->length - transfer_size), - maxp); - transferred = min_t(u32, ur->length - transferred, - transfer_size - length); - dwc3_flush_cache((uintptr_t)dwc->ep0_bounce, DWC3_EP0_BOUNCE_SIZE); - memcpy(buf, dwc->ep0_bounce, transferred); + transfer_size += (maxp - (transfer_size % maxp)); + transferred = min_t(u32, ur->length, + transfer_size - length); + memcpy(ur->buf, dwc->ep0_bounce, transferred); } else { transferred = ur->length - length; } @@ -855,7 +840,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, ret = dwc3_ep0_start_trans(dwc, epnum, dwc->ctrl_req_addr, 0, - DWC3_TRBCTL_CONTROL_DATA, 0); + DWC3_TRBCTL_CONTROL_DATA); WARN_ON(ret < 0); } } @@ -872,6 +857,8 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc, dep = dwc->eps[0]; trb = dwc->ep0_trb; + trace_dwc3_complete_trb(dep, trb); + if (!list_empty(&dep->request_list)) { r = next_request(&dep->request_list); @@ -883,7 +870,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc, ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr); if (ret < 0) { - dev_dbg(dwc->dev, "Invalid Test #%d", + dwc3_trace(trace_dwc3_ep0, "Invalid Test #%d", dwc->test_mode_nr); dwc3_ep0_stall_and_restart(dwc); return; @@ -892,7 +879,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc, status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) - dev_dbg(dwc->dev, "Setup Pending received"); + dwc3_trace(trace_dwc3_ep0, "Setup Pending received"); dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); @@ -909,17 +896,17 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, switch (dwc->ep0state) { case EP0_SETUP_PHASE: - dev_vdbg(dwc->dev, "Setup Phase\n"); + dwc3_trace(trace_dwc3_ep0, "Setup Phase"); dwc3_ep0_inspect_setup(dwc, event); break; case EP0_DATA_PHASE: - dev_vdbg(dwc->dev, "Data Phase\n"); + dwc3_trace(trace_dwc3_ep0, "Data Phase"); dwc3_ep0_complete_data(dwc, event); break; case EP0_STATUS_PHASE: - dev_vdbg(dwc->dev, "Status Phase\n"); + dwc3_trace(trace_dwc3_ep0, "Status Phase"); dwc3_ep0_complete_status(dwc, event); break; default: @@ -936,11 +923,11 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, if (req->request.length == 0) { ret = dwc3_ep0_start_trans(dwc, dep->number, - dwc->ctrl_req_addr, 0, - DWC3_TRBCTL_CONTROL_DATA, 0); - } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) && - (dep->number == 0)) { - u32 transfer_size = 0; + dwc->ctrl_req_addr, 0, + DWC3_TRBCTL_CONTROL_DATA); + } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) + && (dep->number == 0)) { + u32 transfer_size; u32 maxpacket; ret = usb_gadget_map_request(&dwc->gadget, &req->request, @@ -950,18 +937,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, return; } - maxpacket = dep->endpoint.maxpacket; - if (req->request.length > DWC3_EP0_BOUNCE_SIZE) { - transfer_size = (req->request.length / maxpacket) * - maxpacket; - ret = dwc3_ep0_start_trans(dwc, dep->number, - req->request.dma, - transfer_size, - DWC3_TRBCTL_CONTROL_DATA, 1); - } + WARN_ON(req->request.length > DWC3_EP0_BOUNCE_SIZE); - transfer_size = roundup((req->request.length - transfer_size), - maxpacket); + maxpacket = dep->endpoint.maxpacket; + transfer_size = roundup(req->request.length, maxpacket); dwc->ep0_bounced = true; @@ -971,8 +950,8 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, * TRBs to handle the transfer. */ ret = dwc3_ep0_start_trans(dwc, dep->number, - dwc->ep0_bounce_addr, transfer_size, - DWC3_TRBCTL_CONTROL_DATA, 0); + dwc->ep0_bounce_addr, transfer_size, + DWC3_TRBCTL_CONTROL_DATA); } else { ret = usb_gadget_map_request(&dwc->gadget, &req->request, dep->number); @@ -982,8 +961,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, } ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma, - req->request.length, - DWC3_TRBCTL_CONTROL_DATA, 0); + req->request.length, DWC3_TRBCTL_CONTROL_DATA); } WARN_ON(ret < 0); @@ -998,13 +976,13 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) : DWC3_TRBCTL_CONTROL_STATUS2; return dwc3_ep0_start_trans(dwc, dep->number, - dwc->ctrl_req_addr, 0, type, 0); + dwc->ctrl_req_addr, 0, type); } static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep) { if (dwc->resize_fifos) { - dev_dbg(dwc->dev, "Resizing FIFOs"); + dwc3_trace(trace_dwc3_ep0, "Resizing FIFOs"); dwc3_gadget_resize_tx_fifos(dwc); dwc->resize_fifos = 0; } @@ -1045,7 +1023,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, switch (event->status) { case DEPEVT_STATUS_CONTROL_DATA: - dev_vdbg(dwc->dev, "Control Data\n"); + dwc3_trace(trace_dwc3_ep0, "Control Data"); /* * We already have a DATA transfer in the controller's cache, @@ -1059,7 +1037,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, if (dwc->ep0_expect_in != event->endpoint_number) { struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in]; - dev_vdbg(dwc->dev, "Wrong direction for Data phase\n"); + dwc3_trace(trace_dwc3_ep0, + "Wrong direction for Data phase"); dwc3_ep0_end_control_data(dwc, dep); dwc3_ep0_stall_and_restart(dwc); return; @@ -1071,13 +1050,13 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) return; - dev_vdbg(dwc->dev, "Control Status\n"); + dwc3_trace(trace_dwc3_ep0, "Control Status"); dwc->ep0state = EP0_STATUS_PHASE; if (dwc->delayed_status) { WARN_ON_ONCE(event->endpoint_number != 1); - dev_vdbg(dwc->dev, "Delayed Status\n"); + dwc3_trace(trace_dwc3_ep0, "Delayed Status"); return; } @@ -1090,10 +1069,10 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc, { u8 epnum = event->endpoint_number; - dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n", - dwc3_ep_event_string(event->endpoint_event), - epnum >> 1, (epnum & 1) ? "in" : "out", - dwc3_ep0_state_string(dwc->ep0state)); + dwc3_trace(trace_dwc3_ep0, "%s while ep%d%s in state '%s'", + dwc3_ep_event_string(event->endpoint_event), + epnum >> 1, (epnum & 1) ? "in" : "out", + dwc3_ep0_state_string(dwc->ep0state)); switch (event->endpoint_event) { case DWC3_DEPEVT_XFERCOMPLETE: diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2b01113d54cd..f03b136ecfce 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1,39 +1,40 @@ -// SPDX-License-Identifier: GPL-2.0 /** * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link * - * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com * * Authors: Felipe Balbi <[email protected]>, * Sebastian Andrzej Siewior <[email protected]> * - * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.c) and ported - * to uboot. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * - * commit 8e74475b0e : usb: dwc3: gadget: use udc-core's reset notifier + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ -#include <cpu_func.h> -#include <log.h> -#include <malloc.h> -#include <dm.h> -#include <dm/device_compat.h> -#include <dm/devres.h> -#include <linux/bug.h> +#include <linux/kernel.h> #include <linux/delay.h> -#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/interrupt.h> +#include <linux/io.h> #include <linux/list.h> -#include <linux/printk.h> +#include <linux/dma-mapping.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include "debug.h" #include "core.h" #include "gadget.h" #include "io.h" -#include "linux-compat.h" - /** * dwc3_gadget_set_test_mode - Enables USB2 Test Modes * @dwc: pointer to our context structure @@ -167,6 +168,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc) { int last_fifo_depth = 0; + int ram1_depth; int fifo_size; int mdwidth; int num; @@ -174,6 +176,7 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc) if (!dwc->needs_fifo_resize) return 0; + ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); /* MDWIDTH is represented in bits, we need it in bytes */ @@ -231,38 +234,40 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int status) { struct dwc3 *dwc = dep->dwc; + int i; if (req->queued) { - dep->busy_slot++; - /* - * Skip LINK TRB. We can't use req->trb and check for - * DWC3_TRBCTL_LINK_TRB because it points the TRB we - * just completed (not the LINK TRB). - */ - if (((dep->busy_slot & DWC3_TRB_MASK) == - DWC3_TRB_NUM- 1) && - usb_endpoint_xfer_isoc(dep->endpoint.desc)) + i = 0; + do { dep->busy_slot++; + /* + * Skip LINK TRB. We can't use req->trb and check for + * DWC3_TRBCTL_LINK_TRB because it points the TRB we + * just completed (not the LINK TRB). + */ + if (((dep->busy_slot & DWC3_TRB_MASK) == + DWC3_TRB_NUM- 1) && + usb_endpoint_xfer_isoc(dep->endpoint.desc)) + dep->busy_slot++; + } while(++i < req->request.num_mapped_sgs); req->queued = false; } - list_del(&req->list); req->trb = NULL; - if (req->request.dma && req->request.length) - dwc3_flush_cache((uintptr_t)req->request.dma, req->request.length); if (req->request.status == -EINPROGRESS) req->request.status = status; if (dwc->ep0_bounced && dep->number == 0) dwc->ep0_bounced = false; - else if (req->request.dma) + else usb_gadget_unmap_request(&dwc->gadget, &req->request, req->direction); dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n", req, dep->name, req->request.actual, req->request.length, status); + trace_dwc3_gadget_giveback(req); spin_unlock(&dwc->lock); usb_gadget_giveback_request(&dep->endpoint, &req->request); @@ -274,6 +279,8 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) u32 timeout = 500; u32 reg; + trace_dwc3_gadget_generic_cmd(cmd, param); + dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param); dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT); @@ -299,38 +306,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) { + struct dwc3_ep *dep = dwc->eps[ep]; u32 timeout = 500; - u32 saved_config = 0; u32 reg; - int ret = -EINVAL; - - /* - * When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or - * GUSB2PHYCFG.SUSPHY is set, it must be cleared before issuing an - * endpoint command. - * - * Save and clear both GUSB2PHYCFG.ENBLSLPM and GUSB2PHYCFG.SUSPHY - * settings. Restore them after the command is completed. - * - * DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2 - */ - if (dwc->gadget.speed <= USB_SPEED_HIGH || - DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) { - saved_config |= DWC3_GUSB2PHYCFG_SUSPHY; - reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; - } - - if (reg & DWC3_GUSB2PHYCFG_ENBLSLPM) { - saved_config |= DWC3_GUSB2PHYCFG_ENBLSLPM; - reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; - } - - if (saved_config) - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); - } + trace_dwc3_gadget_ep_cmd(dep, cmd, params); dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0); dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1); @@ -342,8 +322,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, if (!(reg & DWC3_DEPCMD_CMDACT)) { dev_vdbg(dwc->dev, "Command Complete --> %d\n", DWC3_DEPCMD_STATUS(reg)); - ret = 0; - break; + return 0; } /* @@ -351,21 +330,11 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, * interrupt context. */ timeout--; - if (!timeout) { - ret = -ETIMEDOUT; - break; - } + if (!timeout) + return -ETIMEDOUT; udelay(1); } while (1); - - if (saved_config) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - reg |= saved_config; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); - } - - return ret; } static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, @@ -378,15 +347,17 @@ static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, static int dwc3_alloc_trb_pool(struct dwc3_ep *dep) { + struct dwc3 *dwc = dep->dwc; + if (dep->trb_pool) return 0; if (dep->number == 0 || dep->number == 1) return 0; - dep->trb_pool = dma_alloc_coherent(sizeof(struct dwc3_trb) * - DWC3_TRB_NUM, - (unsigned long *)&dep->trb_pool_dma); + dep->trb_pool = dma_alloc_coherent(dwc->dev, + sizeof(struct dwc3_trb) * DWC3_TRB_NUM, + &dep->trb_pool_dma, GFP_KERNEL); if (!dep->trb_pool) { dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n", dep->name); @@ -398,7 +369,10 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep) static void dwc3_free_trb_pool(struct dwc3_ep *dep) { - dma_free_coherent(dep->trb_pool); + struct dwc3 *dwc = dep->dwc; + + dma_free_coherent(dwc->dev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM, + dep->trb_pool, dep->trb_pool_dma); dep->trb_pool = NULL; dep->trb_pool_dma = 0; @@ -640,6 +614,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc) { struct dwc3_ep *dep; + struct dwc3 *dwc; unsigned long flags; int ret; @@ -654,9 +629,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, } dep = to_dwc3_ep(ep); + dwc = dep->dwc; if (dep->flags & DWC3_EP_ENABLED) { - WARN(true, "%s is already enabled\n", + dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n", dep->name); return 0; } @@ -675,7 +651,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, strlcat(dep->name, "-int", sizeof(dep->name)); break; default: - dev_err(dep->dwc->dev, "invalid endpoint transfer type\n"); + dev_err(dwc->dev, "invalid endpoint transfer type\n"); } spin_lock_irqsave(&dwc->lock, flags); @@ -688,6 +664,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, static int dwc3_gadget_ep_disable(struct usb_ep *ep) { struct dwc3_ep *dep; + struct dwc3 *dwc; unsigned long flags; int ret; @@ -697,9 +674,10 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep) } dep = to_dwc3_ep(ep); + dwc = dep->dwc; if (!(dep->flags & DWC3_EP_ENABLED)) { - WARN(true, "%s is already disabled\n", + dev_WARN_ONCE(dwc->dev, true, "%s is already disabled\n", dep->name); return 0; } @@ -728,6 +706,8 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep, req->epnum = dep->number; req->dep = dep; + trace_dwc3_alloc_request(req); + return &req->request; } @@ -736,6 +716,7 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep, { struct dwc3_request *req = to_dwc3_request(request); + trace_dwc3_free_request(req); kfree(req); } @@ -748,11 +729,14 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_request *req, dma_addr_t dma, unsigned length, unsigned last, unsigned chain, unsigned node) { + struct dwc3 *dwc = dep->dwc; struct dwc3_trb *trb; - dev_vdbg(dep->dwc->dev, "%s: req %p dma %08llx length %d%s%s\n", - dep->name, req, (unsigned long long)dma, - length, last ? " last" : "", chain ? " chain" : ""); + dev_vdbg(dwc->dev, "%s: req %p dma %08llx length %d%s%s\n", + dep->name, req, (unsigned long long) dma, + length, last ? " last" : "", + chain ? " chain" : ""); + trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK]; @@ -815,8 +799,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, trb->ctrl |= DWC3_TRB_CTRL_HWO; - dwc3_flush_cache((uintptr_t)dma, length); - dwc3_flush_cache((uintptr_t)trb, sizeof(*trb)); + trace_dwc3_prepare_trb(dep, trb); } /* @@ -833,6 +816,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) struct dwc3_request *req, *n; u32 trbs_left; u32 max; + unsigned int last_one = 0; BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); @@ -882,14 +866,59 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) list_for_each_entry_safe(req, n, &dep->request_list, list) { unsigned length; dma_addr_t dma; + last_one = false; - dma = req->request.dma; - length = req->request.length; + if (req->request.num_mapped_sgs > 0) { + struct usb_request *request = &req->request; + struct scatterlist *sg = request->sg; + struct scatterlist *s; + int i; - dwc3_prepare_one_trb(dep, req, dma, length, - true, false, 0); + for_each_sg(sg, s, request->num_mapped_sgs, i) { + unsigned chain = true; - break; + length = sg_dma_len(s); + dma = sg_dma_address(s); + + if (i == (request->num_mapped_sgs - 1) || + sg_is_last(s)) { + if (list_is_last(&req->list, + &dep->request_list)) + last_one = true; + chain = false; + } + + trbs_left--; + if (!trbs_left) + last_one = true; + + if (last_one) + chain = false; + + dwc3_prepare_one_trb(dep, req, dma, length, + last_one, chain, i); + + if (last_one) + break; + } + } else { + dma = req->request.dma; + length = req->request.length; + trbs_left--; + + if (!trbs_left) + last_one = 1; + + /* Is this the last request? */ + if (list_is_last(&req->list, &dep->request_list)) + last_one = 1; + + dwc3_prepare_one_trb(dep, req, dma, length, + last_one, false, 0); + + if (last_one) + break; + } } } @@ -1007,14 +1036,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) req->direction = dep->direction; req->epnum = dep->number; - /* - * DWC3 hangs on OUT requests smaller than maxpacket size, - * so HACK the request length - */ - if (dep->direction == 0 && - req->request.length < dep->endpoint.maxpacket) - req->request.length = dep->endpoint.maxpacket; - /* * We only add to our list of requests now and * start consuming the list once we get XferNotReady @@ -1094,6 +1115,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) ret = __dwc3_gadget_kick_transfer(dep, 0, true); if (ret && ret != -EBUSY) { + struct dwc3 *dwc = dep->dwc; + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", dep->name); } @@ -1107,6 +1130,7 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, { struct dwc3_request *req = to_dwc3_request(request); struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; unsigned long flags; @@ -1114,22 +1138,21 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, spin_lock_irqsave(&dwc->lock, flags); if (!dep->endpoint.desc) { - dev_dbg(dep->dwc->dev, - "trying to queue request %p to disabled %s\n", request, - ep->name); + dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", + request, ep->name); ret = -ESHUTDOWN; goto out; } - if (req->dep != dep) { - WARN(true, "request %p belongs to '%s'\n", request, - req->dep->name); + if (WARN(req->dep != dep, "request %p belongs to '%s'\n", + request, req->dep->name)) { ret = -EINVAL; goto out; } - dev_vdbg(dep->dwc->dev, "queing request %p to %s length %d\n", - request, ep->name, request->length); + dev_vdbg(dwc->dev, "queing request %p to %s length %d\n", + request, ep->name, request->length); + trace_dwc3_ep_queue(req); ret = __dwc3_gadget_ep_queue(dep, req); @@ -1151,6 +1174,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, unsigned long flags; int ret = 0; + trace_dwc3_ep_dequeue(req); + spin_lock_irqsave(&dwc->lock, flags); list_for_each_entry(r, &dep->request_list, list) { @@ -1229,6 +1254,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) { struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; unsigned long flags; @@ -1244,6 +1270,7 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) { struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; unsigned long flags; int ret; @@ -1359,9 +1386,9 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) } /* poll until Link State changes to ON */ - timeout = 1000; + timeout = jiffies + msecs_to_jiffies(100); - while (timeout--) { + while (!time_after(jiffies, timeout)) { reg = dwc3_readl(dwc->regs, DWC3_DSTS); /* in HS, means ON */ @@ -1486,6 +1513,9 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); } +static irqreturn_t dwc3_interrupt(int irq, void *_dwc); +static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc); + static int dwc3_gadget_start(struct usb_gadget *g, struct usb_gadget_driver *driver) { @@ -1493,14 +1523,24 @@ static int dwc3_gadget_start(struct usb_gadget *g, struct dwc3_ep *dep; unsigned long flags; int ret = 0; + int irq; u32 reg; + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, + IRQF_SHARED, "dwc3", dwc); + if (ret) { + dev_err(dwc->dev, "failed to request irq #%d --> %d\n", + irq, ret); + goto err0; + } + spin_lock_irqsave(&dwc->lock, flags); if (dwc->gadget_driver) { dev_err(dwc->dev, "%s is already bound to %s\n", dwc->gadget.name, - dwc->gadget_driver->function); + dwc->gadget_driver->driver.name); ret = -EBUSY; goto err1; } @@ -1584,6 +1624,9 @@ err2: err1: spin_unlock_irqrestore(&dwc->lock, flags); + free_irq(irq, dwc); + +err0: return ret; } @@ -1591,6 +1634,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g) { struct dwc3 *dwc = gadget_to_dwc(g); unsigned long flags; + int irq; spin_lock_irqsave(&dwc->lock, flags); @@ -1602,56 +1646,10 @@ static int dwc3_gadget_stop(struct usb_gadget *g) spin_unlock_irqrestore(&dwc->lock, flags); - return 0; -} - -static struct usb_ep *dwc3_find_ep(struct usb_gadget *gadget, const char *name) -{ - struct usb_ep *ep; - - list_for_each_entry(ep, &gadget->ep_list, ep_list) - if (!strcmp(ep->name, name)) - return ep; + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + free_irq(irq, dwc); - return NULL; -} - -static struct -usb_ep *dwc3_gadget_match_ep(struct usb_gadget *gadget, - struct usb_endpoint_descriptor *desc, - struct usb_ss_ep_comp_descriptor *comp_desc) -{ - /* - * First try standard, common configuration: ep1in-bulk, - * ep2out-bulk, ep3in-int to match other udc drivers to avoid - * confusion in already deployed software (endpoint numbers - * hardcoded in userspace software/drivers) - */ - if (usb_endpoint_is_bulk_in(desc)) - return dwc3_find_ep(gadget, "ep1in"); - if (usb_endpoint_is_bulk_out(desc)) - return dwc3_find_ep(gadget, "ep2out"); - if (usb_endpoint_is_int_in(desc)) { - /* - * Special workaround for NXP UUU tool in SPL. - * - * The tool expects the interrupt-in endpoint to be ep1in, - * otherwise it crashes. This is a result of the previous - * hard-coded EP setup in drivers/usb/gadget/epautoconf.c - * which did special-case EP allocation for SPL builds, - * and which was since converted to this callback, but - * without the special-case EP allocation in SPL part. - * - * This reinstates the SPL part in an isolated manner, - * only for NXP iMX SoCs, only for SPL builds, and only - * for the ep1in interrupt-in endpoint. - */ - if (IS_ENABLED(CONFIG_MACH_IMX) && IS_ENABLED(CONFIG_XPL_BUILD)) - return dwc3_find_ep(gadget, "ep1in"); - return dwc3_find_ep(gadget, "ep3in"); - } - - return NULL; + return 0; } static const struct usb_gadget_ops dwc3_gadget_ops = { @@ -1661,7 +1659,6 @@ static const struct usb_gadget_ops dwc3_gadget_ops = { .pullup = dwc3_gadget_pullup, .udc_start = dwc3_gadget_start, .udc_stop = dwc3_gadget_stop, - .match_ep = dwc3_gadget_match_ep, }; /* -------------------------------------------------------------------------- */ @@ -1700,7 +1697,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, } else { int ret; - usb_ep_set_maxpacket_limit(&dep->endpoint, 512); + usb_ep_set_maxpacket_limit(&dep->endpoint, 1024); dep->endpoint.max_streams = 15; dep->endpoint.ops = &dwc3_gadget_ep_ops; list_add_tail(&dep->endpoint.ep_list, @@ -1776,6 +1773,8 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, unsigned int s_pkt = 0; unsigned int trb_status; + trace_dwc3_complete_trb(dep, trb); + if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN) /* * We continue despite the error. There is not much we @@ -1850,23 +1849,35 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, struct dwc3_request *req; struct dwc3_trb *trb; unsigned int slot; + unsigned int i; + int ret; - req = next_request(&dep->req_queued); - if (!req) { - WARN_ON_ONCE(1); - return 1; - } + do { + req = next_request(&dep->req_queued); + if (!req) { + WARN_ON_ONCE(1); + return 1; + } + i = 0; + do { + slot = req->start_slot + i; + if ((slot == DWC3_TRB_NUM - 1) && + usb_endpoint_xfer_isoc(dep->endpoint.desc)) + slot++; + slot %= DWC3_TRB_NUM; + trb = &dep->trb_pool[slot]; + + ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb, + event, status); + if (ret) + break; + }while (++i < req->request.num_mapped_sgs); - slot = req->start_slot; - if ((slot == DWC3_TRB_NUM - 1) && - usb_endpoint_xfer_isoc(dep->endpoint.desc)) - slot++; - slot %= DWC3_TRB_NUM; - trb = &dep->trb_pool[slot]; + dwc3_gadget_giveback(dep, req, status); - dwc3_flush_cache((uintptr_t)trb, sizeof(*trb)); - __dwc3_cleanup_done_trbs(dwc, dep, req, trb, event, status); - dwc3_gadget_giveback(dep, req, status); + if (ret) + break; + } while (1); if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && list_empty(&dep->req_queued)) { @@ -2299,8 +2310,9 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) * BESL value in the LPM token is less than or equal to LPM * NYET threshold. */ - if (dwc->revision < DWC3_REVISION_240A && dwc->has_lpm_erratum) - WARN(true, "LPM Erratum not available on dwc3 revisisions < 2.40a\n"); + WARN_ONCE(dwc->revision < DWC3_REVISION_240A + && dwc->has_lpm_erratum, + "LPM Erratum not available on dwc3 revisisions < 2.40a\n"); if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A) reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold); @@ -2449,7 +2461,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc, unsigned int evtinfo) { - unsigned int is_ss = evtinfo & (1UL << 4); + unsigned int is_ss = evtinfo & BIT(4); /** * WORKAROUND: DWC3 revison 2.20a with hibernation support @@ -2487,10 +2499,10 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, dwc3_gadget_wakeup_interrupt(dwc); break; case DWC3_DEVICE_EVENT_HIBER_REQ: - if (!dwc->has_hibernation) { - WARN(1 ,"unexpected hibernation event\n"); + if (dev_WARN_ONCE(dwc->dev, !dwc->has_hibernation, + "unexpected hibernation event\n")) break; - } + dwc3_gadget_hibernation_interrupt(dwc, event->event_info); break; case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: @@ -2519,6 +2531,8 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, static void dwc3_process_event_entry(struct dwc3 *dwc, const union dwc3_event *event) { + trace_dwc3_event(event->raw); + /* Endpoint IRQ, handle it and return early */ if (event->type.is_devspec == 0) { /* depevt */ @@ -2551,8 +2565,6 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) while (left > 0) { union dwc3_event event; - dwc3_invalidate_cache((uintptr_t)evt->buf, evt->length); - event.raw = *(u32 *) (evt->buf + evt->lpos); dwc3_process_event_entry(dwc, &event); @@ -2656,31 +2668,31 @@ int dwc3_gadget_init(struct dwc3 *dwc) { int ret; - dwc->ctrl_req = dma_alloc_coherent(sizeof(*dwc->ctrl_req), - (unsigned long *)&dwc->ctrl_req_addr); + dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req), + &dwc->ctrl_req_addr, GFP_KERNEL); if (!dwc->ctrl_req) { dev_err(dwc->dev, "failed to allocate ctrl request\n"); ret = -ENOMEM; goto err0; } - dwc->ep0_trb = dma_alloc_coherent(sizeof(*dwc->ep0_trb) * 2, - (unsigned long *)&dwc->ep0_trb_addr); + dwc->ep0_trb = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ep0_trb), + &dwc->ep0_trb_addr, GFP_KERNEL); if (!dwc->ep0_trb) { dev_err(dwc->dev, "failed to allocate ep0 trb\n"); ret = -ENOMEM; goto err1; } - dwc->setup_buf = dma_alloc_coherent(DWC3_EP0_BOUNCE_SIZE, - (unsigned long *)&dwc->setup_buf_addr); + dwc->setup_buf = kzalloc(DWC3_EP0_BOUNCE_SIZE, GFP_KERNEL); if (!dwc->setup_buf) { ret = -ENOMEM; goto err2; } - dwc->ep0_bounce = dma_alloc_coherent(DWC3_EP0_BOUNCE_SIZE, - (unsigned long *)&dwc->ep0_bounce_addr); + dwc->ep0_bounce = dma_alloc_coherent(dwc->dev, + DWC3_EP0_BOUNCE_SIZE, &dwc->ep0_bounce_addr, + GFP_KERNEL); if (!dwc->ep0_bounce) { dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n"); ret = -ENOMEM; @@ -2690,6 +2702,7 @@ int dwc3_gadget_init(struct dwc3 *dwc) dwc->gadget.ops = &dwc3_gadget_ops; dwc->gadget.max_speed = USB_SPEED_SUPER; dwc->gadget.speed = USB_SPEED_UNKNOWN; + dwc->gadget.sg_supported = true; dwc->gadget.name = "dwc3-gadget"; /* @@ -2707,7 +2720,7 @@ int dwc3_gadget_init(struct dwc3 *dwc) if (ret) goto err4; - ret = usb_add_gadget_udc((struct device *)dwc->dev, &dwc->gadget); + ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); if (ret) { dev_err(dwc->dev, "failed to register udc\n"); goto err4; @@ -2717,16 +2730,19 @@ int dwc3_gadget_init(struct dwc3 *dwc) err4: dwc3_gadget_free_endpoints(dwc); - dma_free_coherent(dwc->ep0_bounce); + dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE, + dwc->ep0_bounce, dwc->ep0_bounce_addr); err3: - dma_free_coherent(dwc->setup_buf); + kfree(dwc->setup_buf); err2: - dma_free_coherent(dwc->ep0_trb); + dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb), + dwc->ep0_trb, dwc->ep0_trb_addr); err1: - dma_free_coherent(dwc->ctrl_req); + dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req), + dwc->ctrl_req, dwc->ctrl_req_addr); err0: return ret; @@ -2740,37 +2756,69 @@ void dwc3_gadget_exit(struct dwc3 *dwc) dwc3_gadget_free_endpoints(dwc); - dma_free_coherent(dwc->ep0_bounce); + dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE, + dwc->ep0_bounce, dwc->ep0_bounce_addr); - dma_free_coherent(dwc->setup_buf); + kfree(dwc->setup_buf); - dma_free_coherent(dwc->ep0_trb); + dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb), + dwc->ep0_trb, dwc->ep0_trb_addr); - dma_free_coherent(dwc->ctrl_req); + dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req), + dwc->ctrl_req, dwc->ctrl_req_addr); } -/** - * dwc3_gadget_uboot_handle_interrupt - handle dwc3 gadget interrupt - * @dwc: struct dwce * - * - * Handles ep0 and gadget interrupt - * - * Should be called from dwc3 core. - */ -void dwc3_gadget_uboot_handle_interrupt(struct dwc3 *dwc) +int dwc3_gadget_suspend(struct dwc3 *dwc) { - int ret = dwc3_interrupt(0, dwc); + if (dwc->pullups_connected) { + dwc3_gadget_disable_irq(dwc); + dwc3_gadget_run_stop(dwc, true, true); + } - if (ret == IRQ_WAKE_THREAD) { - int i; - struct dwc3_event_buffer *evt; + __dwc3_gadget_ep_disable(dwc->eps[0]); + __dwc3_gadget_ep_disable(dwc->eps[1]); - dwc3_thread_interrupt(0, dwc); + dwc->dcfg = dwc3_readl(dwc->regs, DWC3_DCFG); - /* Clean + Invalidate the buffers after touching them */ - for (i = 0; i < dwc->num_event_buffers; i++) { - evt = dwc->ev_buffs[i]; - dwc3_flush_cache((uintptr_t)evt->buf, evt->length); - } + return 0; +} + +int dwc3_gadget_resume(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + int ret; + + /* Start with SuperSpeed Default */ + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); + + dep = dwc->eps[0]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, + false); + if (ret) + goto err0; + + dep = dwc->eps[1]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, + false); + if (ret) + goto err1; + + /* begin to receive SETUP packets */ + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); + + dwc3_writel(dwc->regs, DWC3_DCFG, dwc->dcfg); + + if (dwc->pullups_connected) { + dwc3_gadget_enable_irq(dwc); + dwc3_gadget_run_stop(dwc, true, false); } + + return 0; + +err1: + __dwc3_gadget_ep_disable(dwc->eps[0]); + +err0: + return ret; } diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index f28a9755dcb3..18ae3eaa8b6f 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -1,18 +1,19 @@ -/* SPDX-License-Identifier: GPL-2.0 */ /** * gadget.h - DesignWare USB3 DRD Gadget Header * - * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com * * Authors: Felipe Balbi <[email protected]>, * Sebastian Andrzej Siewior <[email protected]> * - * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.h) and ported - * to uboot. - * - * commit 7a60855972 : usb: dwc3: gadget: fix set_halt() bug with pending - transfers + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #ifndef __DRIVERS_USB_DWC3_GADGET_H @@ -86,7 +87,6 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, gfp_t gfp_flags); int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol); -void dwc3_gadget_uboot_handle_interrupt(struct dwc3 *dwc); /** * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index c1ab02881424..6a79c8e66bbc 100644 --- a/drivers/usb/dwc3/io.h +++ b/drivers/usb/dwc3/io.h @@ -1,29 +1,32 @@ -/* SPDX-License-Identifier: GPL-2.0 */ /** * io.h - DesignWare USB3 DRD IO Header * - * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com * * Authors: Felipe Balbi <[email protected]>, * Sebastian Andrzej Siewior <[email protected]> * - * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/io.h) and ported - * to uboot. - * - * commit 2c4cbe6e5a : usb: dwc3: add tracepoints to aid debugging + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #ifndef __DRIVERS_USB_DWC3_IO_H #define __DRIVERS_USB_DWC3_IO_H -#include <cpu_func.h> -#include <asm/io.h> +#include <linux/io.h> +#include "trace.h" +#include "debug.h" +#include "core.h" -#define CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE static inline u32 dwc3_readl(void __iomem *base, u32 offset) { - unsigned long offs = offset - DWC3_GLOBALS_REGS_START; + u32 offs = offset - DWC3_GLOBALS_REGS_START; u32 value; /* @@ -33,12 +36,20 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset) */ value = readl(base + offs); + /* + * When tracing we want to make it easy to find the correct address on + * documentation, so we revert it back to the proper addresses, the + * same way they are described on SNPS documentation + */ + dwc3_trace(trace_dwc3_readl, "addr %p value %08x", + base - DWC3_GLOBALS_REGS_START + offset, value); + return value; } static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value) { - unsigned long offs = offset - DWC3_GLOBALS_REGS_START; + u32 offs = offset - DWC3_GLOBALS_REGS_START; /* * We requested the mem region starting from the Globals address @@ -46,21 +57,14 @@ static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value) * However, the offsets are given starting from xHCI address space. */ writel(value, base + offs); -} - -static inline void dwc3_flush_cache(uintptr_t addr, int length) -{ - uintptr_t start_addr = (uintptr_t)addr & ~(CACHELINE_SIZE - 1); - uintptr_t end_addr = ALIGN((uintptr_t)addr + length, CACHELINE_SIZE); - flush_dcache_range((unsigned long)start_addr, (unsigned long)end_addr); + /* + * When tracing we want to make it easy to find the correct address on + * documentation, so we revert it back to the proper addresses, the + * same way they are described on SNPS documentation + */ + dwc3_trace(trace_dwc3_writel, "addr %p value %08x", + base - DWC3_GLOBALS_REGS_START + offset, value); } -static inline void dwc3_invalidate_cache(uintptr_t addr, int length) -{ - uintptr_t start_addr = (uintptr_t)addr & ~(CACHELINE_SIZE - 1); - uintptr_t end_addr = ALIGN((uintptr_t)addr + length, CACHELINE_SIZE); - - invalidate_dcache_range((unsigned long)start_addr, (unsigned long)end_addr); -} #endif /* __DRIVERS_USB_DWC3_IO_H */ diff --git a/drivers/usb/dwc3/linux-compat.h b/drivers/usb/dwc3/linux-compat.h deleted file mode 100644 index 563f8727cdde..000000000000 --- a/drivers/usb/dwc3/linux-compat.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/** - * linux-compat.h - DesignWare USB3 Linux Compatibiltiy Adapter Header - * - * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com - * - * Authors: Kishon Vijay Abraham I <[email protected]> - * - */ - -#ifndef __DWC3_LINUX_COMPAT__ -#define __DWC3_LINUX_COMPAT__ - -#define dev_WARN(dev, format, arg...) debug(format, ##arg) - -#endif diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h new file mode 100644 index 000000000000..a3a3b6d5668c --- /dev/null +++ b/drivers/usb/dwc3/platform_data.h @@ -0,0 +1,47 @@ +/** + * platform_data.h - USB DWC3 Platform Data Support + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * Author: Felipe Balbi <[email protected]> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/usb/ch9.h> +#include <linux/usb/otg.h> + +struct dwc3_platform_data { + enum usb_device_speed maximum_speed; + enum usb_dr_mode dr_mode; + bool tx_fifo_resize; + + unsigned is_utmi_l1_suspend:1; + u8 hird_threshold; + + u8 lpm_nyet_threshold; + + unsigned disable_scramble_quirk:1; + unsigned has_lpm_erratum:1; + unsigned u2exit_lfps_quirk:1; + unsigned u2ss_inp3_quirk:1; + unsigned req_p1p2p3_quirk:1; + unsigned del_p1p2p3_quirk:1; + unsigned del_phy_power_chg_quirk:1; + unsigned lfps_filter_quirk:1; + unsigned rx_detect_poll_quirk:1; + unsigned dis_u3_susphy_quirk:1; + unsigned dis_u2_susphy_quirk:1; + + unsigned tx_de_emphasis_quirk:1; + unsigned tx_de_emphasis:2; +}; diff --git a/drivers/usb/dwc3/samsung_usb_phy.c b/drivers/usb/dwc3/samsung_usb_phy.c deleted file mode 100644 index 3563070cb85d..000000000000 --- a/drivers/usb/dwc3/samsung_usb_phy.c +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * samsung_usb_phy.c - DesignWare USB3 (DWC3) PHY handling file - * - * Copyright (C) 2015 Samsung Electronics - * - * Author: Joonyoung Shim <[email protected]> - */ - -#include <asm/io.h> -#include <asm/arch/power.h> -#include <asm/arch/xhci-exynos.h> -#include <linux/delay.h> - -void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy) -{ - u32 reg; - - /* Reset USB 3.0 PHY */ - writel(0x0, &phy->phy_reg0); - - clrbits_le32(&phy->phy_param0, - /* Select PHY CLK source */ - PHYPARAM0_REF_USE_PAD | - /* Set Loss-of-Signal Detector sensitivity */ - PHYPARAM0_REF_LOSLEVEL_MASK); - setbits_le32(&phy->phy_param0, PHYPARAM0_REF_LOSLEVEL); - - writel(0x0, &phy->phy_resume); - - /* - * Setting the Frame length Adj value[6:1] to default 0x20 - * See xHCI 1.0 spec, 5.2.4 - */ - setbits_le32(&phy->link_system, - LINKSYSTEM_XHCI_VERSION_CONTROL | - LINKSYSTEM_FLADJ(0x20)); - - /* Set Tx De-Emphasis level */ - clrbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH_MASK); - setbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH); - - setbits_le32(&phy->phy_batchg, PHYBATCHG_UTMI_CLKSEL); - - /* PHYTEST POWERDOWN Control */ - clrbits_le32(&phy->phy_test, - PHYTEST_POWERDOWN_SSP | - PHYTEST_POWERDOWN_HSP); - - /* UTMI Power Control */ - writel(PHYUTMI_OTGDISABLE, &phy->phy_utmi); - - /* Use core clock from main PLL */ - reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK | - /* Default 24Mhz crystal clock */ - PHYCLKRST_FSEL(FSEL_CLKSEL_24M) | - PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | - PHYCLKRST_SSC_REFCLKSEL(0) | - /* Force PortReset of PHY */ - PHYCLKRST_PORTRESET | - /* Digital power supply in normal operating mode */ - PHYCLKRST_RETENABLEN | - /* Enable ref clock for SS function */ - PHYCLKRST_REF_SSP_EN | - /* Enable spread spectrum */ - PHYCLKRST_SSC_EN | - /* Power down HS Bias and PLL blocks in suspend mode */ - PHYCLKRST_COMMONONN; - - writel(reg, &phy->phy_clk_rst); - - /* giving time to Phy clock to settle before resetting */ - udelay(10); - - reg &= ~PHYCLKRST_PORTRESET; - writel(reg, &phy->phy_clk_rst); -} -- 2.43.0

