aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r--drivers/usb/dwc3/Kconfig44
-rw-r--r--drivers/usb/dwc3/Makefile14
-rw-r--r--drivers/usb/dwc3/core.c596
-rw-r--r--drivers/usb/dwc3/core.h162
-rw-r--r--drivers/usb/dwc3/debug.h34
-rw-r--r--drivers/usb/dwc3/debugfs.c34
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c91
-rw-r--r--drivers/usb/dwc3/dwc3-keystone.c202
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c200
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c76
-rw-r--r--drivers/usb/dwc3/ep0.c57
-rw-r--r--drivers/usb/dwc3/gadget.c561
-rw-r--r--drivers/usb/dwc3/gadget.h46
-rw-r--r--drivers/usb/dwc3/host.c34
-rw-r--r--drivers/usb/dwc3/io.h34
-rw-r--r--drivers/usb/dwc3/platform_data.h27
16 files changed, 1404 insertions, 808 deletions
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 757aa18027d..261c3b42822 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -1,6 +1,6 @@
config USB_DWC3
tristate "DesignWare USB3 DRD Core Support"
- depends on (USB || USB_GADGET) && GENERIC_HARDIRQS
+ depends on (USB || USB_GADGET) && HAS_DMA
select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD
help
Say Y or M here if your system has a Dual Role SuperSpeed
@@ -40,6 +40,48 @@ config USB_DWC3_DUAL_ROLE
endchoice
+comment "Platform Glue Driver Support"
+
+config USB_DWC3_OMAP
+ tristate "Texas Instruments OMAP5 and similar Platforms"
+ depends on EXTCON && (ARCH_OMAP2PLUS || COMPILE_TEST)
+ depends on OF
+ default USB_DWC3
+ help
+ Some platforms from Texas Instruments like OMAP5, DRA7xxx and
+ AM437x use this IP for USB2/3 functionality.
+
+ Say 'Y' or 'M' here if you have one such device
+
+config USB_DWC3_EXYNOS
+ tristate "Samsung Exynos Platform"
+ depends on ARCH_EXYNOS || COMPILE_TEST
+ default USB_DWC3
+ help
+ Recent Exynos5 SoCs ship with one DesignWare Core USB3 IP inside,
+ say 'Y' or 'M' if you have one such device.
+
+config USB_DWC3_PCI
+ tristate "PCIe-based Platforms"
+ depends on PCI
+ default USB_DWC3
+ help
+ If you're using the DesignWare Core IP with a PCIe, please say
+ 'Y' or 'M' here.
+
+ One such PCIe-based platform is Synopsys' PCIe HAPS model of
+ this IP.
+
+config USB_DWC3_KEYSTONE
+ tristate "Texas Instruments Keystone2 Platforms"
+ depends on ARCH_KEYSTONE || COMPILE_TEST
+ default USB_DWC3
+ help
+ Support of USB2/3 functionality in TI Keystone2 platforms.
+ Say 'Y' or 'M' here if you have one such device
+
+comment "Debugging features"
+
config USB_DWC3_DEBUG
bool "Enable Debugging Messages"
help
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 0c7ac92582b..10ac3e72482 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -27,15 +27,9 @@ endif
# the entire driver (with all its glue layers) on several architectures
# and make sure it compiles fine. This will also help with allmodconfig
# and allyesconfig builds.
-#
-# The only exception is the PCI glue layer, but that's only because
-# PCI doesn't provide nops if CONFIG_PCI isn't enabled.
##
-obj-$(CONFIG_USB_DWC3) += dwc3-omap.o
-obj-$(CONFIG_USB_DWC3) += dwc3-exynos.o
-
-ifneq ($(CONFIG_PCI),)
- obj-$(CONFIG_USB_DWC3) += dwc3-pci.o
-endif
-
+obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o
+obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o
+obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
+obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index c35d49d39b7..eb69eb9f06c 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -6,34 +6,17 @@
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
+ * 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.
*
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, 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.
*
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 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/module.h>
@@ -50,20 +33,18 @@
#include <linux/dma-mapping.h>
#include <linux/of.h>
-#include <linux/usb/otg.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.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 "debug.h"
-static char *maximum_speed = "super";
-module_param(maximum_speed, charp, 0);
-MODULE_PARM_DESC(maximum_speed, "Maximum supported speed.");
-
/* -------------------------------------------------------------------------- */
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
@@ -80,9 +61,10 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
* dwc3_core_soft_reset - Issues core soft reset and PHY reset
* @dwc: pointer to our context structure
*/
-static void dwc3_core_soft_reset(struct dwc3 *dwc)
+static int dwc3_core_soft_reset(struct dwc3 *dwc)
{
u32 reg;
+ int ret;
/* Before Resetting PHY, put Core in Reset */
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
@@ -101,6 +83,15 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
usb_phy_init(dwc->usb2_phy);
usb_phy_init(dwc->usb3_phy);
+ ret = phy_init(dwc->usb2_generic_phy);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_init(dwc->usb3_generic_phy);
+ if (ret < 0) {
+ phy_exit(dwc->usb2_generic_phy);
+ return ret;
+ }
mdelay(100);
/* Clear USB3 PHY reset */
@@ -119,6 +110,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_CORESOFTRESET;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+ return 0;
}
/**
@@ -236,7 +229,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n),
upper_32_bits(evt->dma));
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n),
- evt->length & 0xffff);
+ DWC3_GEVNTSIZ_SIZE(evt->length));
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0);
}
@@ -255,11 +248,96 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0);
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0);
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0);
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), DWC3_GEVNTSIZ_INTMASK
+ | DWC3_GEVNTSIZ_SIZE(0));
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0);
}
}
+static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
+{
+ if (!dwc->has_hibernation)
+ return 0;
+
+ if (!dwc->nr_scratch)
+ return 0;
+
+ dwc->scratchbuf = kmalloc_array(dwc->nr_scratch,
+ DWC3_SCRATCHBUF_SIZE, GFP_KERNEL);
+ if (!dwc->scratchbuf)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
+{
+ dma_addr_t scratch_addr;
+ u32 param;
+ int ret;
+
+ if (!dwc->has_hibernation)
+ return 0;
+
+ if (!dwc->nr_scratch)
+ return 0;
+
+ /* should never fall here */
+ if (!WARN_ON(dwc->scratchbuf))
+ return 0;
+
+ scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf,
+ dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dwc->dev, scratch_addr)) {
+ dev_err(dwc->dev, "failed to map scratch buffer\n");
+ ret = -EFAULT;
+ goto err0;
+ }
+
+ dwc->scratch_addr = scratch_addr;
+
+ param = lower_32_bits(scratch_addr);
+
+ ret = dwc3_send_gadget_generic_command(dwc,
+ DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, param);
+ if (ret < 0)
+ goto err1;
+
+ param = upper_32_bits(scratch_addr);
+
+ ret = dwc3_send_gadget_generic_command(dwc,
+ DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, param);
+ if (ret < 0)
+ goto err1;
+
+ return 0;
+
+err1:
+ dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
+ DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
+
+err0:
+ return ret;
+}
+
+static void dwc3_free_scratch_buffers(struct dwc3 *dwc)
+{
+ if (!dwc->has_hibernation)
+ return;
+
+ if (!dwc->nr_scratch)
+ return;
+
+ /* should never fall here */
+ if (!WARN_ON(dwc->scratchbuf))
+ return;
+
+ dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
+ DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
+ kfree(dwc->scratchbuf);
+}
+
static void dwc3_core_num_eps(struct dwc3 *dwc)
{
struct dwc3_hwparams *parms = &dwc->hwparams;
@@ -295,6 +373,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
static int dwc3_core_init(struct dwc3 *dwc)
{
unsigned long timeout;
+ u32 hwparams4 = dwc->hwparams.hwparams4;
u32 reg;
int ret;
@@ -324,7 +403,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
cpu_relax();
} while (true);
- dwc3_core_soft_reset(dwc);
+ ret = dwc3_core_soft_reset(dwc);
+ if (ret)
+ goto err0;
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
@@ -332,7 +413,29 @@ static int dwc3_core_init(struct dwc3 *dwc)
switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
- reg &= ~DWC3_GCTL_DSBLCLKGTNG;
+ /**
+ * WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an
+ * issue which would cause xHCI compliance tests to fail.
+ *
+ * Because of that we cannot enable clock gating on such
+ * configurations.
+ *
+ * Refers to:
+ *
+ * STAR#9000588375: Clock Gating, SOF Issues when ref_clk-Based
+ * SOF/ITP Mode Used
+ */
+ if ((dwc->dr_mode == USB_DR_MODE_HOST ||
+ dwc->dr_mode == USB_DR_MODE_OTG) &&
+ (dwc->revision >= DWC3_REVISION_210A &&
+ dwc->revision <= DWC3_REVISION_250A))
+ reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC;
+ else
+ reg &= ~DWC3_GCTL_DSBLCLKGTNG;
+ break;
+ case DWC3_GHWPARAMS1_EN_PWROPT_HIB:
+ /* enable hibernation here */
+ dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4);
break;
default:
dev_dbg(dwc->dev, "No power optimization available\n");
@@ -351,34 +454,183 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ ret = dwc3_alloc_scratch_buffers(dwc);
+ if (ret)
+ goto err1;
+
+ ret = dwc3_setup_scratch_buffers(dwc);
+ if (ret)
+ goto err2;
+
return 0;
+err2:
+ dwc3_free_scratch_buffers(dwc);
+
+err1:
+ usb_phy_shutdown(dwc->usb2_phy);
+ usb_phy_shutdown(dwc->usb3_phy);
+ phy_exit(dwc->usb2_generic_phy);
+ phy_exit(dwc->usb3_generic_phy);
+
err0:
return ret;
}
static void dwc3_core_exit(struct dwc3 *dwc)
{
+ dwc3_free_scratch_buffers(dwc);
usb_phy_shutdown(dwc->usb2_phy);
usb_phy_shutdown(dwc->usb3_phy);
+ 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) {
+ case USB_DR_MODE_PERIPHERAL:
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+ ret = dwc3_gadget_init(dwc);
+ if (ret) {
+ dev_err(dev, "failed to initialize gadget\n");
+ return ret;
+ }
+ break;
+ case USB_DR_MODE_HOST:
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
+ ret = dwc3_host_init(dwc);
+ if (ret) {
+ dev_err(dev, "failed to initialize host\n");
+ return ret;
+ }
+ break;
+ case USB_DR_MODE_OTG:
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+ ret = dwc3_host_init(dwc);
+ if (ret) {
+ dev_err(dev, "failed to initialize host\n");
+ return ret;
+ }
+
+ ret = dwc3_gadget_init(dwc);
+ if (ret) {
+ dev_err(dev, "failed to initialize gadget\n");
+ return ret;
+ }
+ break;
+ default:
+ dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void dwc3_core_exit_mode(struct dwc3 *dwc)
+{
+ switch (dwc->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ dwc3_gadget_exit(dwc);
+ break;
+ case USB_DR_MODE_HOST:
+ dwc3_host_exit(dwc);
+ break;
+ case USB_DR_MODE_OTG:
+ dwc3_host_exit(dwc);
+ dwc3_gadget_exit(dwc);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
}
#define DWC3_ALIGN_MASK (16 - 1)
static int dwc3_probe(struct platform_device *pdev)
{
- struct device_node *node = pdev->dev.of_node;
+ 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 = &pdev->dev;
- int ret = -ENOMEM;
+ int ret;
void __iomem *regs;
void *mem;
- u8 mode;
-
mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
if (!mem) {
dev_err(dev, "not enough memory\n");
@@ -386,6 +638,7 @@ static int dwc3_probe(struct platform_device *pdev)
}
dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
dwc->mem = mem;
+ dwc->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
@@ -402,95 +655,53 @@ static int dwc3_probe(struct platform_device *pdev)
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;
-
- /*
- * Request memory region but exclude xHCI regs,
- * since it will be requested by the xhci-plat driver.
- */
- res = devm_request_mem_region(dev, res->start + DWC3_GLOBALS_REGS_START,
- resource_size(res) - DWC3_GLOBALS_REGS_START,
- dev_name(dev));
- if (!res) {
- dev_err(dev, "can't request mem region\n");
- return -ENOMEM;
- }
-
- regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
- if (!regs) {
- dev_err(dev, "ioremap failed\n");
- return -ENOMEM;
- }
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);
- }
+ dwc->maximum_speed = of_usb_get_maximum_speed(node);
- if (IS_ERR(dwc->usb2_phy)) {
- ret = PTR_ERR(dwc->usb2_phy);
-
- /*
- * if -ENXIO is returned, it means PHY layer wasn't
- * enabled, so it makes no sense to return -EPROBE_DEFER
- * in that case, since no PHY driver will ever probe.
- */
- if (ret == -ENXIO)
- return ret;
+ dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize");
+ dwc->dr_mode = of_usb_get_dr_mode(node);
+ } else if (pdata) {
+ dwc->maximum_speed = pdata->maximum_speed;
- dev_err(dev, "no usb2 phy configured\n");
- return -EPROBE_DEFER;
+ dwc->needs_fifo_resize = pdata->tx_fifo_resize;
+ dwc->dr_mode = pdata->dr_mode;
}
- if (IS_ERR(dwc->usb3_phy)) {
- ret = PTR_ERR(dwc->usb2_phy);
+ /* default to superspeed if no maximum_speed passed */
+ if (dwc->maximum_speed == USB_SPEED_UNKNOWN)
+ dwc->maximum_speed = USB_SPEED_SUPER;
- /*
- * if -ENXIO is returned, it means PHY layer wasn't
- * enabled, so it makes no sense to return -EPROBE_DEFER
- * in that case, since no PHY driver will ever probe.
- */
- if (ret == -ENXIO)
- return ret;
+ ret = dwc3_core_get_phy(dwc);
+ if (ret)
+ return ret;
- dev_err(dev, "no usb3 phy configured\n");
- return -EPROBE_DEFER;
- }
+ 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;
- usb_phy_set_suspend(dwc->usb2_phy, 0);
- usb_phy_set_suspend(dwc->usb3_phy, 0);
+ 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);
spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc);
dwc->regs = regs;
dwc->regs_size = resource_size(res);
- dwc->dev = dev;
dev->dma_mask = dev->parent->dma_mask;
dev->dma_parms = dev->parent->dma_parms;
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
- if (!strncmp("super", maximum_speed, 5))
- dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
- else if (!strncmp("high", maximum_speed, 4))
- dwc->maximum_speed = DWC3_DCFG_HIGHSPEED;
- else if (!strncmp("full", maximum_speed, 4))
- dwc->maximum_speed = DWC3_DCFG_FULLSPEED1;
- else if (!strncmp("low", maximum_speed, 3))
- dwc->maximum_speed = DWC3_DCFG_LOWSPEED;
- else
- dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
-
- dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize");
-
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
pm_runtime_forbid(dev);
@@ -504,61 +715,39 @@ static int dwc3_probe(struct platform_device *pdev)
goto err0;
}
+ if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
+ dwc->dr_mode = USB_DR_MODE_HOST;
+ else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
+ dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
+
+ if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
+ dwc->dr_mode = USB_DR_MODE_OTG;
+
ret = dwc3_core_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize core\n");
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;
}
- if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
- mode = DWC3_MODE_HOST;
- else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
- mode = DWC3_MODE_DEVICE;
- else
- mode = DWC3_MODE_DRD;
-
- switch (mode) {
- case DWC3_MODE_DEVICE:
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
- ret = dwc3_gadget_init(dwc);
- if (ret) {
- dev_err(dev, "failed to initialize gadget\n");
- goto err2;
- }
- break;
- case DWC3_MODE_HOST:
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
- ret = dwc3_host_init(dwc);
- if (ret) {
- dev_err(dev, "failed to initialize host\n");
- goto err2;
- }
- break;
- case DWC3_MODE_DRD:
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
- ret = dwc3_host_init(dwc);
- if (ret) {
- dev_err(dev, "failed to initialize host\n");
- goto err2;
- }
-
- ret = dwc3_gadget_init(dwc);
- if (ret) {
- dev_err(dev, "failed to initialize gadget\n");
- goto err2;
- }
- break;
- default:
- dev_err(dev, "Unsupported mode of operation %d\n", mode);
+ ret = dwc3_core_init_mode(dwc);
+ if (ret)
goto err2;
- }
- dwc->mode = mode;
ret = dwc3_debugfs_init(dwc);
if (ret) {
@@ -571,26 +760,20 @@ static int dwc3_probe(struct platform_device *pdev)
return 0;
err3:
- switch (mode) {
- case DWC3_MODE_DEVICE:
- dwc3_gadget_exit(dwc);
- break;
- case DWC3_MODE_HOST:
- dwc3_host_exit(dwc);
- break;
- case DWC3_MODE_DRD:
- dwc3_host_exit(dwc);
- dwc3_gadget_exit(dwc);
- break;
- default:
- /* do nothing */
- break;
- }
+ 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:
@@ -605,28 +788,14 @@ static int dwc3_remove(struct platform_device *pdev)
usb_phy_set_suspend(dwc->usb2_phy, 1);
usb_phy_set_suspend(dwc->usb3_phy, 1);
+ phy_power_off(dwc->usb2_generic_phy);
+ phy_power_off(dwc->usb3_generic_phy);
- pm_runtime_put(&pdev->dev);
+ pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
dwc3_debugfs_exit(dwc);
-
- switch (dwc->mode) {
- case DWC3_MODE_DEVICE:
- dwc3_gadget_exit(dwc);
- break;
- case DWC3_MODE_HOST:
- dwc3_host_exit(dwc);
- break;
- case DWC3_MODE_DRD:
- dwc3_host_exit(dwc);
- dwc3_gadget_exit(dwc);
- break;
- default:
- /* do nothing */
- break;
- }
-
+ dwc3_core_exit_mode(dwc);
dwc3_event_buffers_cleanup(dwc);
dwc3_free_event_buffers(dwc);
dwc3_core_exit(dwc);
@@ -642,12 +811,12 @@ static int dwc3_prepare(struct device *dev)
spin_lock_irqsave(&dwc->lock, flags);
- switch (dwc->mode) {
- case DWC3_MODE_DEVICE:
- case DWC3_MODE_DRD:
+ switch (dwc->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ case USB_DR_MODE_OTG:
dwc3_gadget_prepare(dwc);
/* FALLTHROUGH */
- case DWC3_MODE_HOST:
+ case USB_DR_MODE_HOST:
default:
dwc3_event_buffers_cleanup(dwc);
break;
@@ -665,14 +834,14 @@ static void dwc3_complete(struct device *dev)
spin_lock_irqsave(&dwc->lock, flags);
- switch (dwc->mode) {
- case DWC3_MODE_DEVICE:
- case DWC3_MODE_DRD:
+ dwc3_event_buffers_setup(dwc);
+ switch (dwc->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ case USB_DR_MODE_OTG:
dwc3_gadget_complete(dwc);
/* FALLTHROUGH */
- case DWC3_MODE_HOST:
+ case USB_DR_MODE_HOST:
default:
- dwc3_event_buffers_setup(dwc);
break;
}
@@ -686,12 +855,12 @@ static int dwc3_suspend(struct device *dev)
spin_lock_irqsave(&dwc->lock, flags);
- switch (dwc->mode) {
- case DWC3_MODE_DEVICE:
- case DWC3_MODE_DRD:
+ switch (dwc->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ case USB_DR_MODE_OTG:
dwc3_gadget_suspend(dwc);
/* FALLTHROUGH */
- case DWC3_MODE_HOST:
+ case USB_DR_MODE_HOST:
default:
/* do nothing */
break;
@@ -702,6 +871,8 @@ static int dwc3_suspend(struct device *dev)
usb_phy_shutdown(dwc->usb3_phy);
usb_phy_shutdown(dwc->usb2_phy);
+ phy_exit(dwc->usb2_generic_phy);
+ phy_exit(dwc->usb3_generic_phy);
return 0;
}
@@ -710,21 +881,28 @@ static int dwc3_resume(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
unsigned long flags;
+ int ret;
usb_phy_init(dwc->usb3_phy);
usb_phy_init(dwc->usb2_phy);
- msleep(100);
+ ret = phy_init(dwc->usb2_generic_phy);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_init(dwc->usb3_generic_phy);
+ if (ret < 0)
+ goto err_usb2phy_init;
spin_lock_irqsave(&dwc->lock, flags);
dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl);
- switch (dwc->mode) {
- case DWC3_MODE_DEVICE:
- case DWC3_MODE_DRD:
+ switch (dwc->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ case USB_DR_MODE_OTG:
dwc3_gadget_resume(dwc);
/* FALLTHROUGH */
- case DWC3_MODE_HOST:
+ case USB_DR_MODE_HOST:
default:
/* do nothing */
break;
@@ -737,6 +915,11 @@ static int dwc3_resume(struct device *dev)
pm_runtime_enable(dev);
return 0;
+
+err_usb2phy_init:
+ phy_exit(dwc->usb2_generic_phy);
+
+ return ret;
}
static const struct dev_pm_ops dwc3_dev_pm_ops = {
@@ -754,6 +937,9 @@ static const struct dev_pm_ops dwc3_dev_pm_ops = {
#ifdef CONFIG_OF
static const struct of_device_id of_dwc3_match[] = {
{
+ .compatible = "snps,dwc3"
+ },
+ {
.compatible = "synopsys,dwc3"
},
{ },
@@ -775,5 +961,5 @@ module_platform_driver(dwc3_driver);
MODULE_ALIAS("platform:dwc3");
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
-MODULE_LICENSE("Dual BSD/GPL");
+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 b69d322e3ca..57332e3768e 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -6,34 +6,14 @@
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
+ * 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.
*
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 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
@@ -49,12 +29,16 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+
+#include <linux/phy/phy.h>
/* Global constants */
#define DWC3_EP0_BOUNCE_SIZE 512
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
+#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
#define DWC3_EVENT_SIZE 4 /* bytes */
#define DWC3_EVENT_MAX_NUM 64 /* 2 events/endpoint */
#define DWC3_EVENT_BUFFERS_SIZE (DWC3_EVENT_SIZE * DWC3_EVENT_MAX_NUM)
@@ -176,6 +160,7 @@
#define DWC3_GCTL_PRTCAP_OTG 3
#define DWC3_GCTL_CORESOFTRESET (1 << 11)
+#define DWC3_GCTL_SOFITPSYNC (1 << 10)
#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
@@ -194,6 +179,10 @@
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
+/* Global Event Size Registers */
+#define DWC3_GEVNTSIZ_INTMASK (1 << 31)
+#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)
+
/* Global HWPARAMS1 Register */
#define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24)
#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0
@@ -207,7 +196,6 @@
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
/* Device Configuration Register */
-#define DWC3_DCFG_LPM_CAP (1 << 22)
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
@@ -334,7 +322,7 @@
/* Device Endpoint Command Register */
#define DWC3_DEPCMD_PARAM_SHIFT 16
#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT)
-#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
+#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
#define DWC3_DEPCMD_STATUS(x) (((x) >> 15) & 1)
#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
#define DWC3_DEPCMD_CMDACT (1 << 10)
@@ -367,7 +355,6 @@ struct dwc3_trb;
/**
* struct dwc3_event_buffer - Software event buffer representation
- * @list: a list of event buffers
* @buf: _THE_ buffer
* @length: size of this buffer
* @lpos: event offset
@@ -410,12 +397,13 @@ struct dwc3_event_buffer {
* @busy_slot: first slot which is owned by HW
* @desc: usb_endpoint_descriptor pointer
* @dwc: pointer to DWC controller
+ * @saved_state: ep state saved during hibernation
* @flags: endpoint flags (wedged, stalled, ...)
* @current_trb: index of current used trb
* @number: endpoint number (1 - 15)
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
* @resource_index: Resource transfer index
- * @interval: the intervall on which the ISOC transfer is started
+ * @interval: the interval on which the ISOC transfer is started
* @name: a human readable name e.g. ep1out-bulk
* @direction: true for TX, false for RX
* @stream_capable: true when streams are enabled
@@ -432,6 +420,7 @@ struct dwc3_ep {
const struct usb_ss_ep_comp_descriptor *comp_desc;
struct dwc3 *dwc;
+ u32 saved_state;
unsigned flags;
#define DWC3_EP_ENABLED (1 << 0)
#define DWC3_EP_STALL (1 << 1)
@@ -566,11 +555,6 @@ struct dwc3_hwparams {
/* HWPARAMS0 */
#define DWC3_MODE(n) ((n) & 0x7)
-#define DWC3_MODE_DEVICE 0
-#define DWC3_MODE_HOST 1
-#define DWC3_MODE_DRD 2
-#define DWC3_MODE_HUB 3
-
#define DWC3_MDWIDTH(n) (((n) & 0xff00) >> 8)
/* HWPARAMS1 */
@@ -620,6 +604,7 @@ 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
+ * @scratch_addr: dma address of scratchbuf
* @lock: for synchronizing
* @dev: pointer to our struct device
* @xhci: pointer to our xHCI child
@@ -628,23 +613,18 @@ struct dwc3_scratchpad_array {
* @gadget_driver: pointer to the gadget driver
* @regs: base address for our registers
* @regs_size: address space size
+ * @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
- * @mode: mode of operation
+ * @dr_mode: requested mode of operation
* @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
- * @is_selfpowered: true when we are selfpowered
- * @three_stage_setup: set if we perform a three phase setup
- * @ep0_bounced: true when we used bounce buffer
- * @ep0_expect_in: true when we expect a DATA IN transfer
- * @start_config_issued: true when StartConfig command has been issued
- * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
- * @needs_fifo_resize: not all users might want fifo resizing, flag it
- * @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
* @isoch_delay: wValue from Set Isochronous Delay request;
* @u2sel: parameter from Set SEL request.
* @u2pel: parameter from Set SEL request.
@@ -659,15 +639,31 @@ struct dwc3_scratchpad_array {
* @mem: points to start of memory which is used for this struct.
* @hwparams: copy of hwparams registers
* @root: debugfs root folder pointer
+ * @regset: debugfs pointer to regdump file
+ * @test_mode: true when we're entering a USB test mode
+ * @test_mode_nr: test feature selector
+ * @delayed_status: true when gadget driver asks for delayed status
+ * @ep0_bounced: true when we used bounce buffer
+ * @ep0_expect_in: true when we expect a DATA IN transfer
+ * @has_hibernation: true when dwc3 was configured with Hibernation
+ * @is_selfpowered: true when we are selfpowered
+ * @needs_fifo_resize: not all users might want fifo resizing, flag it
+ * @pullups_connected: true when Run/Stop bit is set
+ * @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
+ * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
+ * @start_config_issued: true when StartConfig command has been issued
+ * @three_stage_setup: set if we perform a three phase setup
*/
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
struct dwc3_trb *ep0_trb;
void *ep0_bounce;
+ void *scratchbuf;
u8 *setup_buf;
dma_addr_t ctrl_req_addr;
dma_addr_t ep0_trb_addr;
dma_addr_t ep0_bounce_addr;
+ dma_addr_t scratch_addr;
struct dwc3_request ep0_usb_req;
/* device lock */
@@ -687,18 +683,23 @@ struct dwc3 {
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;
+
/* used for suspend/resume */
u32 dcfg;
u32 gctl;
+ u32 nr_scratch;
u32 num_event_buffers;
u32 u1u2;
u32 maximum_speed;
u32 revision;
- u32 mode;
#define DWC3_REVISION_173A 0x5533173a
#define DWC3_REVISION_175A 0x5533175a
@@ -716,17 +717,9 @@ struct dwc3 {
#define DWC3_REVISION_230A 0x5533230a
#define DWC3_REVISION_240A 0x5533240a
#define DWC3_REVISION_250A 0x5533250a
-
- unsigned is_selfpowered:1;
- unsigned three_stage_setup:1;
- unsigned ep0_bounced:1;
- unsigned ep0_expect_in:1;
- unsigned start_config_issued:1;
- unsigned setup_packet_pending:1;
- unsigned delayed_status:1;
- unsigned needs_fifo_resize:1;
- unsigned resize_fifos:1;
- unsigned pullups_connected:1;
+#define DWC3_REVISION_260A 0x5533260a
+#define DWC3_REVISION_270A 0x5533270a
+#define DWC3_REVISION_280A 0x5533280a
enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state;
@@ -751,6 +744,18 @@ struct dwc3 {
u8 test_mode;
u8 test_mode_nr;
+
+ unsigned delayed_status:1;
+ unsigned ep0_bounced:1;
+ unsigned ep0_expect_in:1;
+ unsigned has_hibernation:1;
+ unsigned is_selfpowered:1;
+ unsigned needs_fifo_resize:1;
+ unsigned pullups_connected:1;
+ unsigned resize_fifos:1;
+ unsigned setup_packet_pending:1;
+ unsigned start_config_issued:1;
+ unsigned three_stage_setup:1;
};
/* -------------------------------------------------------------------------- */
@@ -759,8 +764,8 @@ struct dwc3 {
struct dwc3_event_type {
u32 is_devspec:1;
- u32 type:6;
- u32 reserved8_31:25;
+ u32 type:7;
+ u32 reserved8_31:24;
} __packed;
#define DWC3_DEPEVT_XFERCOMPLETE 0x01
@@ -836,15 +841,15 @@ struct dwc3_event_depevt {
* 12 - VndrDevTstRcved
* @reserved15_12: Reserved, not used
* @event_info: Information about this event
- * @reserved31_24: Reserved, not used
+ * @reserved31_25: Reserved, not used
*/
struct dwc3_event_devt {
u32 one_bit:1;
u32 device_event:7;
u32 type:4;
u32 reserved15_12:4;
- u32 event_info:8;
- u32 reserved31_24:8;
+ u32 event_info:9;
+ u32 reserved31_25:7;
} __packed;
/**
@@ -877,6 +882,19 @@ union dwc3_event {
struct dwc3_event_gevt gevt;
};
+/**
+ * struct dwc3_gadget_ep_cmd_params - representation of endpoint command
+ * parameters
+ * @param2: third parameter
+ * @param1: second parameter
+ * @param0: first parameter
+ */
+struct dwc3_gadget_ep_cmd_params {
+ u32 param2;
+ u32 param1;
+ u32 param0;
+};
+
/*
* DWC3 Features to be used as Driver Data
*/
@@ -902,11 +920,31 @@ static inline void dwc3_host_exit(struct dwc3 *dwc)
#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);
+int dwc3_gadget_get_link_state(struct dwc3 *dwc);
+int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
+int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
+ unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
#else
static inline int dwc3_gadget_init(struct dwc3 *dwc)
{ return 0; }
static inline void dwc3_gadget_exit(struct dwc3 *dwc)
{ }
+static inline int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
+{ return 0; }
+static inline int dwc3_gadget_get_link_state(struct dwc3 *dwc)
+{ return 0; }
+static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc,
+ enum dwc3_link_state state)
+{ return 0; }
+
+static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
+ unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
+{ return 0; }
+static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
+ int cmd, u32 param)
+{ return 0; }
#endif
/* power management interface */
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 5894ee8222a..fceb39dc4bb 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -6,34 +6,14 @@
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
+ * 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.
*
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 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 "core.h"
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 9e9f122162f..9ac37fe1b6a 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -6,34 +6,14 @@
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
+ * 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.
*
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 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 <linux/kernel.h>
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index 8ce9d7fd6cf..f9fb8adb785 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -6,10 +6,14 @@
*
* Author: Anton Tikhomirov <av.tikhomirov@samsung.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * 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 <linux/module.h>
@@ -20,9 +24,10 @@
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <linux/usb/otg.h>
-#include <linux/usb/nop-usb-xceiv.h>
+#include <linux/usb/usb_phy_generic.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#include <linux/regulator/consumer.h>
struct dwc3_exynos {
struct platform_device *usb2_phy;
@@ -30,28 +35,31 @@ struct dwc3_exynos {
struct device *dev;
struct clk *clk;
+ struct regulator *vdd33;
+ struct regulator *vdd10;
};
static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
{
- struct nop_usb_xceiv_platform_data pdata;
+ struct usb_phy_generic_platform_data pdata;
struct platform_device *pdev;
int ret;
memset(&pdata, 0x00, sizeof(pdata));
- pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
+ pdev = platform_device_alloc("usb_phy_generic", PLATFORM_DEVID_AUTO);
if (!pdev)
return -ENOMEM;
exynos->usb2_phy = pdev;
pdata.type = USB_PHY_TYPE_USB2;
+ pdata.gpio_reset = -1;
ret = platform_device_add_data(exynos->usb2_phy, &pdata, sizeof(pdata));
if (ret)
goto err1;
- pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
+ pdev = platform_device_alloc("usb_phy_generic", PLATFORM_DEVID_AUTO);
if (!pdev) {
ret = -ENOMEM;
goto err1;
@@ -102,12 +110,12 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
- int ret = -ENOMEM;
+ int ret;
exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL);
if (!exynos) {
dev_err(dev, "not enough memory\n");
- goto err1;
+ return -ENOMEM;
}
/*
@@ -115,24 +123,22 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
* Since shared usb code relies on it, set it here for now.
* Once we move to full device tree support this will vanish off.
*/
- if (!dev->dma_mask)
- dev->dma_mask = &dev->coherent_dma_mask;
- if (!dev->coherent_dma_mask)
- dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
platform_set_drvdata(pdev, exynos);
ret = dwc3_exynos_register_phys(exynos);
if (ret) {
dev_err(dev, "couldn't register PHYs\n");
- goto err1;
+ return ret;
}
clk = devm_clk_get(dev, "usbdrd30");
if (IS_ERR(clk)) {
dev_err(dev, "couldn't get clock\n");
- ret = -EINVAL;
- goto err1;
+ return -EINVAL;
}
exynos->dev = dev;
@@ -140,23 +146,48 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
clk_prepare_enable(exynos->clk);
+ exynos->vdd33 = devm_regulator_get(dev, "vdd33");
+ if (IS_ERR(exynos->vdd33)) {
+ ret = PTR_ERR(exynos->vdd33);
+ goto err2;
+ }
+ ret = regulator_enable(exynos->vdd33);
+ if (ret) {
+ dev_err(dev, "Failed to enable VDD33 supply\n");
+ goto err2;
+ }
+
+ exynos->vdd10 = devm_regulator_get(dev, "vdd10");
+ if (IS_ERR(exynos->vdd10)) {
+ ret = PTR_ERR(exynos->vdd10);
+ goto err3;
+ }
+ ret = regulator_enable(exynos->vdd10);
+ if (ret) {
+ dev_err(dev, "Failed to enable VDD10 supply\n");
+ goto err3;
+ }
+
if (node) {
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
dev_err(dev, "failed to add dwc3 core\n");
- goto err2;
+ goto err4;
}
} else {
dev_err(dev, "no device node, failed to add dwc3 core\n");
ret = -ENODEV;
- goto err2;
+ goto err4;
}
return 0;
+err4:
+ regulator_disable(exynos->vdd10);
+err3:
+ regulator_disable(exynos->vdd33);
err2:
clk_disable_unprepare(clk);
-err1:
return ret;
}
@@ -170,6 +201,9 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
clk_disable_unprepare(exynos->clk);
+ regulator_disable(exynos->vdd33);
+ regulator_disable(exynos->vdd10);
+
return 0;
}
@@ -188,12 +222,27 @@ static int dwc3_exynos_suspend(struct device *dev)
clk_disable(exynos->clk);
+ regulator_disable(exynos->vdd33);
+ regulator_disable(exynos->vdd10);
+
return 0;
}
static int dwc3_exynos_resume(struct device *dev)
{
struct dwc3_exynos *exynos = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_enable(exynos->vdd33);
+ if (ret) {
+ dev_err(dev, "Failed to enable VDD33 supply\n");
+ return ret;
+ }
+ ret = regulator_enable(exynos->vdd10);
+ if (ret) {
+ dev_err(dev, "Failed to enable VDD10 supply\n");
+ return ret;
+ }
clk_enable(exynos->clk);
@@ -228,5 +277,5 @@ module_platform_driver(dwc3_exynos_driver);
MODULE_ALIAS("platform:exynos-dwc3");
MODULE_AUTHOR("Anton Tikhomirov <av.tikhomirov@samsung.com>");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("DesignWare USB3 EXYNOS Glue Layer");
diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c
new file mode 100644
index 00000000000..1fad1618df6
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-keystone.c
@@ -0,0 +1,202 @@
+/**
+ * dwc3-keystone.c - Keystone Specific Glue layer
+ *
+ * Copyright (C) 2010-2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: WingMan Kwok <w-kwok2@ti.com>
+ *
+ * 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 <linux/clk.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+
+/* USBSS register offsets */
+#define USBSS_REVISION 0x0000
+#define USBSS_SYSCONFIG 0x0010
+#define USBSS_IRQ_EOI 0x0018
+#define USBSS_IRQSTATUS_RAW_0 0x0020
+#define USBSS_IRQSTATUS_0 0x0024
+#define USBSS_IRQENABLE_SET_0 0x0028
+#define USBSS_IRQENABLE_CLR_0 0x002c
+
+/* IRQ register bits */
+#define USBSS_IRQ_EOI_LINE(n) BIT(n)
+#define USBSS_IRQ_EVENT_ST BIT(0)
+#define USBSS_IRQ_COREIRQ_EN BIT(0)
+#define USBSS_IRQ_COREIRQ_CLR BIT(0)
+
+static u64 kdwc3_dma_mask;
+
+struct dwc3_keystone {
+ struct device *dev;
+ struct clk *clk;
+ void __iomem *usbss;
+};
+
+static inline u32 kdwc3_readl(void __iomem *base, u32 offset)
+{
+ return readl(base + offset);
+}
+
+static inline void kdwc3_writel(void __iomem *base, u32 offset, u32 value)
+{
+ writel(value, base + offset);
+}
+
+static void kdwc3_enable_irqs(struct dwc3_keystone *kdwc)
+{
+ u32 val;
+
+ val = kdwc3_readl(kdwc->usbss, USBSS_IRQENABLE_SET_0);
+ val |= USBSS_IRQ_COREIRQ_EN;
+ kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, val);
+}
+
+static void kdwc3_disable_irqs(struct dwc3_keystone *kdwc)
+{
+ u32 val;
+
+ val = kdwc3_readl(kdwc->usbss, USBSS_IRQENABLE_SET_0);
+ val &= ~USBSS_IRQ_COREIRQ_EN;
+ kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, val);
+}
+
+static irqreturn_t dwc3_keystone_interrupt(int irq, void *_kdwc)
+{
+ struct dwc3_keystone *kdwc = _kdwc;
+
+ kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_CLR_0, USBSS_IRQ_COREIRQ_CLR);
+ kdwc3_writel(kdwc->usbss, USBSS_IRQSTATUS_0, USBSS_IRQ_EVENT_ST);
+ kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, USBSS_IRQ_COREIRQ_EN);
+ kdwc3_writel(kdwc->usbss, USBSS_IRQ_EOI, USBSS_IRQ_EOI_LINE(0));
+
+ return IRQ_HANDLED;
+}
+
+static int kdwc3_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = pdev->dev.of_node;
+ struct dwc3_keystone *kdwc;
+ struct resource *res;
+ int error, irq;
+
+ kdwc = devm_kzalloc(dev, sizeof(*kdwc), GFP_KERNEL);
+ if (!kdwc)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, kdwc);
+
+ kdwc->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "missing usbss resource\n");
+ return -EINVAL;
+ }
+
+ kdwc->usbss = devm_ioremap_resource(dev, res);
+ if (IS_ERR(kdwc->usbss))
+ return PTR_ERR(kdwc->usbss);
+
+ kdwc3_dma_mask = dma_get_mask(dev);
+ dev->dma_mask = &kdwc3_dma_mask;
+
+ kdwc->clk = devm_clk_get(kdwc->dev, "usb");
+
+ error = clk_prepare_enable(kdwc->clk);
+ if (error < 0) {
+ dev_dbg(kdwc->dev, "unable to enable usb clock, err %d\n",
+ error);
+ return error;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "missing irq\n");
+ goto err_irq;
+ }
+
+ error = devm_request_irq(dev, irq, dwc3_keystone_interrupt, IRQF_SHARED,
+ dev_name(dev), kdwc);
+ if (error) {
+ dev_err(dev, "failed to request IRQ #%d --> %d\n",
+ irq, error);
+ goto err_irq;
+ }
+
+ kdwc3_enable_irqs(kdwc);
+
+ error = of_platform_populate(node, NULL, NULL, dev);
+ if (error) {
+ dev_err(&pdev->dev, "failed to create dwc3 core\n");
+ goto err_core;
+ }
+
+ return 0;
+
+err_core:
+ kdwc3_disable_irqs(kdwc);
+err_irq:
+ clk_disable_unprepare(kdwc->clk);
+
+ return error;
+}
+
+static int kdwc3_remove_core(struct device *dev, void *c)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+
+ return 0;
+}
+
+static int kdwc3_remove(struct platform_device *pdev)
+{
+ struct dwc3_keystone *kdwc = platform_get_drvdata(pdev);
+
+ kdwc3_disable_irqs(kdwc);
+ device_for_each_child(&pdev->dev, NULL, kdwc3_remove_core);
+ clk_disable_unprepare(kdwc->clk);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id kdwc3_of_match[] = {
+ { .compatible = "ti,keystone-dwc3", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, kdwc3_of_match);
+
+static struct platform_driver kdwc3_driver = {
+ .probe = kdwc3_probe,
+ .remove = kdwc3_remove,
+ .driver = {
+ .name = "keystone-dwc3",
+ .owner = THIS_MODULE,
+ .of_match_table = kdwc3_of_match,
+ },
+};
+
+module_platform_driver(kdwc3_driver);
+
+MODULE_ALIAS("platform:keystone-dwc3");
+MODULE_AUTHOR("WingMan Kwok <w-kwok2@ti.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DesignWare USB3 KEYSTONE Glue Layer");
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index 077f110bd74..07a736acd0f 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -6,50 +6,30 @@
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
+ * 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.
*
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
-#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/platform_data/dwc3-omap.h>
-#include <linux/usb/dwc3-omap.h>
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#include <linux/extcon.h>
+#include <linux/regulator/consumer.h>
#include <linux/usb/otg.h>
@@ -138,9 +118,6 @@
#define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID (1 << 1)
struct dwc3_omap {
- /* device lock */
- spinlock_t lock;
-
struct device *dev;
int irq;
@@ -155,9 +132,21 @@ struct dwc3_omap {
u32 revision;
u32 dma_status:1;
+
+ struct extcon_specific_cable_nb extcon_vbus_dev;
+ struct extcon_specific_cable_nb extcon_id_dev;
+ struct notifier_block vbus_nb;
+ struct notifier_block id_nb;
+
+ struct regulator *vbus_reg;
};
-static struct dwc3_omap *_omap;
+enum omap_dwc3_vbus_id_status {
+ OMAP_DWC3_ID_FLOAT,
+ OMAP_DWC3_ID_GROUND,
+ OMAP_DWC3_VBUS_OFF,
+ OMAP_DWC3_VBUS_VALID,
+};
static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset)
{
@@ -221,18 +210,24 @@ static void dwc3_omap_write_irq0_set(struct dwc3_omap *omap, u32 value)
omap->irq0_offset, value);
}
-int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
+static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
+ enum omap_dwc3_vbus_id_status status)
{
- u32 val;
- struct dwc3_omap *omap = _omap;
-
- if (!omap)
- return -EPROBE_DEFER;
+ int ret;
+ u32 val;
switch (status) {
case OMAP_DWC3_ID_GROUND:
dev_dbg(omap->dev, "ID GND\n");
+ if (omap->vbus_reg) {
+ ret = regulator_enable(omap->vbus_reg);
+ if (ret) {
+ dev_dbg(omap->dev, "regulator enable failed\n");
+ return;
+ }
+ }
+
val = dwc3_omap_read_utmi_status(omap);
val &= ~(USBOTGSS_UTMI_OTG_STATUS_IDDIG
| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
@@ -255,6 +250,9 @@ int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
break;
case OMAP_DWC3_ID_FLOAT:
+ if (omap->vbus_reg)
+ regulator_disable(omap->vbus_reg);
+
case OMAP_DWC3_VBUS_OFF:
dev_dbg(omap->dev, "VBUS Disconnect\n");
@@ -268,20 +266,15 @@ int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
break;
default:
- dev_dbg(omap->dev, "ID float\n");
+ dev_dbg(omap->dev, "invalid state\n");
}
-
- return 0;
}
-EXPORT_SYMBOL_GPL(dwc3_omap_mailbox);
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
{
struct dwc3_omap *omap = _omap;
u32 reg;
- spin_lock(&omap->lock);
-
reg = dwc3_omap_read_irqmisc_status(omap);
if (reg & USBOTGSS_IRQMISC_DMADISABLECLR) {
@@ -322,8 +315,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
dwc3_omap_write_irq0_status(omap, reg);
- spin_unlock(&omap->lock);
-
return IRQ_HANDLED;
}
@@ -331,7 +322,7 @@ static int dwc3_omap_remove_core(struct device *dev, void *c)
{
struct platform_device *pdev = to_platform_device(dev);
- platform_device_unregister(pdev);
+ of_device_unregister(pdev);
return 0;
}
@@ -366,6 +357,32 @@ static void dwc3_omap_disable_irqs(struct dwc3_omap *omap)
static u64 dwc3_omap_dma_mask = DMA_BIT_MASK(32);
+static int dwc3_omap_id_notifier(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct dwc3_omap *omap = container_of(nb, struct dwc3_omap, id_nb);
+
+ if (event)
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
+ else
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_FLOAT);
+
+ return NOTIFY_DONE;
+}
+
+static int dwc3_omap_vbus_notifier(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct dwc3_omap *omap = container_of(nb, struct dwc3_omap, vbus_nb);
+
+ if (event)
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
+ else
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_OFF);
+
+ return NOTIFY_DONE;
+}
+
static int dwc3_omap_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -373,8 +390,10 @@ static int dwc3_omap_probe(struct platform_device *pdev)
struct dwc3_omap *omap;
struct resource *res;
struct device *dev = &pdev->dev;
+ struct extcon_dev *edev;
+ struct regulator *vbus_reg = NULL;
- int ret = -ENOMEM;
+ int ret;
int irq;
int utmi_mode = 0;
@@ -404,30 +423,24 @@ static int dwc3_omap_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "missing memory base resource\n");
- return -EINVAL;
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ if (of_property_read_bool(node, "vbus-supply")) {
+ vbus_reg = devm_regulator_get(dev, "vbus");
+ if (IS_ERR(vbus_reg)) {
+ dev_err(dev, "vbus init failed\n");
+ return PTR_ERR(vbus_reg);
+ }
}
- base = devm_ioremap_nocache(dev, res->start, resource_size(res));
- if (!base) {
- dev_err(dev, "ioremap failed\n");
- return -ENOMEM;
- }
-
- spin_lock_init(&omap->lock);
-
omap->dev = dev;
omap->irq = irq;
omap->base = base;
+ omap->vbus_reg = vbus_reg;
dev->dma_mask = &dwc3_omap_dma_mask;
- /*
- * REVISIT if we ever have two instances of the wrapper, we will be
- * in big trouble
- */
- _omap = omap;
-
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
@@ -502,14 +515,46 @@ static int dwc3_omap_probe(struct platform_device *pdev)
dwc3_omap_enable_irqs(omap);
+ if (of_property_read_bool(node, "extcon")) {
+ edev = extcon_get_edev_by_phandle(dev, 0);
+ if (IS_ERR(edev)) {
+ dev_vdbg(dev, "couldn't get extcon device\n");
+ ret = -EPROBE_DEFER;
+ goto err2;
+ }
+
+ omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier;
+ ret = extcon_register_interest(&omap->extcon_vbus_dev,
+ edev->name, "USB", &omap->vbus_nb);
+ if (ret < 0)
+ dev_vdbg(dev, "failed to register notifier for USB\n");
+ omap->id_nb.notifier_call = dwc3_omap_id_notifier;
+ ret = extcon_register_interest(&omap->extcon_id_dev, edev->name,
+ "USB-HOST", &omap->id_nb);
+ if (ret < 0)
+ dev_vdbg(dev,
+ "failed to register notifier for USB-HOST\n");
+
+ if (extcon_get_cable_state(edev, "USB") == true)
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
+ if (extcon_get_cable_state(edev, "USB-HOST") == true)
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
+ }
+
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
dev_err(&pdev->dev, "failed to create dwc3 core\n");
- goto err2;
+ goto err3;
}
return 0;
+err3:
+ if (omap->extcon_vbus_dev.edev)
+ extcon_unregister_interest(&omap->extcon_vbus_dev);
+ if (omap->extcon_id_dev.edev)
+ extcon_unregister_interest(&omap->extcon_id_dev);
+
err2:
dwc3_omap_disable_irqs(omap);
@@ -526,6 +571,10 @@ static int dwc3_omap_remove(struct platform_device *pdev)
{
struct dwc3_omap *omap = platform_get_drvdata(pdev);
+ if (omap->extcon_vbus_dev.edev)
+ extcon_unregister_interest(&omap->extcon_vbus_dev);
+ if (omap->extcon_id_dev.edev)
+ extcon_unregister_interest(&omap->extcon_id_dev);
dwc3_omap_disable_irqs(omap);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@@ -550,7 +599,7 @@ static int dwc3_omap_prepare(struct device *dev)
{
struct dwc3_omap *omap = dev_get_drvdata(dev);
- dwc3_omap_disable_irqs(omap);
+ dwc3_omap_write_irqmisc_set(omap, 0x00);
return 0;
}
@@ -558,8 +607,19 @@ static int dwc3_omap_prepare(struct device *dev)
static void dwc3_omap_complete(struct device *dev)
{
struct dwc3_omap *omap = dev_get_drvdata(dev);
+ u32 reg;
- dwc3_omap_enable_irqs(omap);
+ reg = (USBOTGSS_IRQMISC_OEVT |
+ USBOTGSS_IRQMISC_DRVVBUS_RISE |
+ USBOTGSS_IRQMISC_CHRGVBUS_RISE |
+ USBOTGSS_IRQMISC_DISCHRGVBUS_RISE |
+ USBOTGSS_IRQMISC_IDPULLUP_RISE |
+ USBOTGSS_IRQMISC_DRVVBUS_FALL |
+ USBOTGSS_IRQMISC_CHRGVBUS_FALL |
+ USBOTGSS_IRQMISC_DISCHRGVBUS_FALL |
+ USBOTGSS_IRQMISC_IDPULLUP_FALL);
+
+ dwc3_omap_write_irqmisc_set(omap, reg);
}
static int dwc3_omap_suspend(struct device *dev)
@@ -610,5 +670,5 @@ module_platform_driver(dwc3_omap_driver);
MODULE_ALIAS("platform:omap-dwc3");
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
-MODULE_LICENSE("Dual BSD/GPL");
+MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer");
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index ed07ec04a96..a60bab7dfa0 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -6,34 +6,14 @@
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
+ * 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.
*
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 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 <linux/kernel.h>
@@ -43,11 +23,13 @@
#include <linux/platform_device.h>
#include <linux/usb/otg.h>
-#include <linux/usb/nop-usb-xceiv.h>
+#include <linux/usb/usb_phy_generic.h>
/* FIXME define these in <linux/pci_ids.h> */
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd
+#define PCI_DEVICE_ID_INTEL_BYT 0x0f37
+#define PCI_DEVICE_ID_INTEL_MRFLD 0x119e
struct dwc3_pci {
struct device *dev;
@@ -58,24 +40,25 @@ struct dwc3_pci {
static int dwc3_pci_register_phys(struct dwc3_pci *glue)
{
- struct nop_usb_xceiv_platform_data pdata;
+ struct usb_phy_generic_platform_data pdata;
struct platform_device *pdev;
int ret;
memset(&pdata, 0x00, sizeof(pdata));
- pdev = platform_device_alloc("nop_usb_xceiv", 0);
+ pdev = platform_device_alloc("usb_phy_generic", 0);
if (!pdev)
return -ENOMEM;
glue->usb2_phy = pdev;
pdata.type = USB_PHY_TYPE_USB2;
+ pdata.gpio_reset = -1;
ret = platform_device_add_data(glue->usb2_phy, &pdata, sizeof(pdata));
if (ret)
goto err1;
- pdev = platform_device_alloc("nop_usb_xceiv", 1);
+ pdev = platform_device_alloc("usb_phy_generic", 1);
if (!pdev) {
ret = -ENOMEM;
goto err1;
@@ -116,7 +99,7 @@ static int dwc3_pci_probe(struct pci_dev *pci,
struct resource res[2];
struct platform_device *dwc3;
struct dwc3_pci *glue;
- int ret = -ENOMEM;
+ int ret;
struct device *dev = &pci->dev;
glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
@@ -127,7 +110,7 @@ static int dwc3_pci_probe(struct pci_dev *pci,
glue->dev = dev;
- ret = pci_enable_device(pci);
+ ret = pcim_enable_device(pci);
if (ret) {
dev_err(dev, "failed to enable pci device\n");
return -ENODEV;
@@ -144,8 +127,7 @@ static int dwc3_pci_probe(struct pci_dev *pci,
dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
if (!dwc3) {
dev_err(dev, "couldn't allocate dwc3 device\n");
- ret = -ENOMEM;
- goto err1;
+ return -ENOMEM;
}
memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
@@ -162,7 +144,7 @@ static int dwc3_pci_probe(struct pci_dev *pci,
ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
if (ret) {
dev_err(dev, "couldn't add resources to dwc3 device\n");
- goto err1;
+ return ret;
}
pci_set_drvdata(pci, glue);
@@ -183,11 +165,7 @@ static int dwc3_pci_probe(struct pci_dev *pci,
return 0;
err3:
- pci_set_drvdata(pci, NULL);
platform_device_put(dwc3);
-err1:
- pci_disable_device(pci);
-
return ret;
}
@@ -198,20 +176,20 @@ static void dwc3_pci_remove(struct pci_dev *pci)
platform_device_unregister(glue->dwc3);
platform_device_unregister(glue->usb2_phy);
platform_device_unregister(glue->usb3_phy);
- pci_set_drvdata(pci, NULL);
- pci_disable_device(pci);
}
-static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = {
+static const struct pci_device_id dwc3_pci_id_table[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3),
},
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT), },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), },
{ } /* Terminating Entry */
};
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int dwc3_pci_suspend(struct device *dev)
{
struct pci_dev *pci = to_pci_dev(dev);
@@ -236,28 +214,24 @@ static int dwc3_pci_resume(struct device *dev)
return 0;
}
+#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops dwc3_pci_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_suspend, dwc3_pci_resume)
};
-#define DEV_PM_OPS (&dwc3_pci_dev_pm_ops)
-#else
-#define DEV_PM_OPS NULL
-#endif /* CONFIG_PM */
-
static struct pci_driver dwc3_pci_driver = {
.name = "dwc3-pci",
.id_table = dwc3_pci_id_table,
.probe = dwc3_pci_probe,
.remove = dwc3_pci_remove,
.driver = {
- .pm = DEV_PM_OPS,
+ .pm = &dwc3_pci_dev_pm_ops,
},
};
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
-MODULE_LICENSE("Dual BSD/GPL");
+MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer");
module_pci_driver(dwc3_pci_driver);
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 5acbb948b70..21a352079bc 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -6,34 +6,14 @@
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
+ * 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.
*
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 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 <linux/kernel.h>
@@ -168,6 +148,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
direction = !dwc->ep0_expect_in;
dwc->delayed_status = false;
+ usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED);
if (dwc->ep0state == EP0_STATUS_PHASE)
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
@@ -371,7 +352,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
break;
default:
return -EINVAL;
- };
+ }
response_pkt = (__le16 *) dwc->setup_buf;
*response_pkt = cpu_to_le16(usb_status);
@@ -478,6 +459,8 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
dep = dwc3_wIndex_to_dep(dwc, wIndex);
if (!dep)
return -EINVAL;
+ if (set == 0 && (dep->flags & DWC3_EP_WEDGE))
+ break;
ret = __dwc3_gadget_ep_set_halt(dep, set);
if (ret)
return -EINVAL;
@@ -489,7 +472,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
default:
return -EINVAL;
- };
+ }
return 0;
}
@@ -553,8 +536,16 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
ret = dwc3_ep0_delegate_req(dwc, ctrl);
/* if the cfg matches and the cfg is non zero */
if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
- usb_gadget_set_state(&dwc->gadget,
- USB_STATE_CONFIGURED);
+
+ /*
+ * only change state if set_config has already
+ * been processed. If gadget driver returns
+ * USB_GADGET_DELAYED_STATUS, we will wait
+ * to change the state on the next usb_ep_queue()
+ */
+ if (ret == 0)
+ usb_gadget_set_state(&dwc->gadget,
+ USB_STATE_CONFIGURED);
/*
* Enable transition to U1/U2 state when
@@ -571,7 +562,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
case USB_STATE_CONFIGURED:
ret = dwc3_ep0_delegate_req(dwc, ctrl);
- if (!cfg)
+ if (!cfg && !ret)
usb_gadget_set_state(&dwc->gadget,
USB_STATE_ADDRESS);
break;
@@ -720,7 +711,7 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
dev_vdbg(dwc->dev, "Forwarding to gadget driver\n");
ret = dwc3_ep0_delegate_req(dwc, ctrl);
break;
- };
+ }
return ret;
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index b5e5b35df49..dab7927d100 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -6,34 +6,14 @@
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
+ * 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.
*
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 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 <linux/kernel.h>
@@ -88,6 +68,22 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
}
/**
+ * dwc3_gadget_get_link_state - Gets current state of USB Link
+ * @dwc: pointer to our context structure
+ *
+ * Caller should take care of locking. This function will
+ * return the link state on success (>= 0) or -ETIMEDOUT.
+ */
+int dwc3_gadget_get_link_state(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+
+ return DWC3_DSTS_USBLNKST(reg);
+}
+
+/**
* dwc3_gadget_set_link_state - Sets USB Link to a particular State
* @dwc: pointer to our context structure
* @state: the state to put link into
@@ -191,15 +187,12 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
* improve this algorithm so that we better use the internal
* FIFO space
*/
- for (num = 0; num < DWC3_ENDPOINTS_NUM; num++) {
- struct dwc3_ep *dep = dwc->eps[num];
- int fifo_number = dep->number >> 1;
+ for (num = 0; num < dwc->num_in_eps; num++) {
+ /* bit0 indicates direction; 1 means IN ep */
+ struct dwc3_ep *dep = dwc->eps[(num << 1) | 1];
int mult = 1;
int tmp;
- if (!(dep->number & 1))
- continue;
-
if (!(dep->flags & DWC3_EP_ENABLED))
continue;
@@ -228,8 +221,7 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
dev_vdbg(dwc->dev, "%s: Fifo Addr %04x Size %d\n",
dep->name, last_fifo_depth, fifo_size & 0xffff);
- dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(fifo_number),
- fifo_size);
+ dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size);
last_fifo_depth += (fifo_size & 0xffff);
}
@@ -306,11 +298,76 @@ static const char *dwc3_gadget_ep_cmd_string(u8 cmd)
}
}
+static 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";
+ }
+}
+
+static 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";
+ }
+}
+
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param)
{
u32 timeout = 500;
u32 reg;
+ dev_vdbg(dwc->dev, "generic cmd '%s' [%d] param %08x\n",
+ dwc3_gadget_generic_cmd_string(cmd), cmd, param);
+
dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
@@ -340,9 +397,9 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
u32 timeout = 500;
u32 reg;
- dev_vdbg(dwc->dev, "%s: cmd '%s' params %08x %08x %08x\n",
+ dev_vdbg(dwc->dev, "%s: cmd '%s' [%d] params %08x %08x %08x\n",
dep->name,
- dwc3_gadget_ep_cmd_string(cmd), params->param0,
+ dwc3_gadget_ep_cmd_string(cmd), cmd, params->param0,
params->param1, params->param2);
dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0);
@@ -437,7 +494,7 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
const struct usb_endpoint_descriptor *desc,
const struct usb_ss_ep_comp_descriptor *comp_desc,
- bool ignore)
+ bool ignore, bool restore)
{
struct dwc3_gadget_ep_cmd_params params;
@@ -456,6 +513,11 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
if (ignore)
params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM;
+ if (restore) {
+ params.param0 |= DWC3_DEPCFG_ACTION_RESTORE;
+ params.param2 |= dep->saved_state;
+ }
+
params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
| DWC3_DEPCFG_XFER_NOT_READY_EN;
@@ -514,11 +576,13 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
const struct usb_endpoint_descriptor *desc,
const struct usb_ss_ep_comp_descriptor *comp_desc,
- bool ignore)
+ bool ignore, bool restore)
{
struct dwc3 *dwc = dep->dwc;
u32 reg;
- int ret = -ENOMEM;
+ int ret;
+
+ dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
if (!(dep->flags & DWC3_EP_ENABLED)) {
ret = dwc3_gadget_start_config(dwc, dep);
@@ -526,7 +590,8 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
return ret;
}
- ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore);
+ ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore,
+ restore);
if (ret)
return ret;
@@ -566,13 +631,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
return 0;
}
-static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum);
+static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force);
static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_request *req;
if (!list_empty(&dep->req_queued)) {
- dwc3_stop_active_transfer(dwc, dep->number);
+ dwc3_stop_active_transfer(dwc, dep->number, true);
/* - giveback all requests to gadget driver */
while (!list_empty(&dep->req_queued)) {
@@ -604,6 +669,10 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
dwc3_remove_requests(dwc, dep);
+ /* make sure HW endpoint isn't stalled */
+ if (dep->flags & DWC3_EP_STALL)
+ __dwc3_gadget_ep_set_halt(dep, 0);
+
reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
reg &= ~DWC3_DALEPENA_EP(dep->number);
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
@@ -676,10 +745,8 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
dev_err(dwc->dev, "invalid endpoint transfer type\n");
}
- dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
-
spin_lock_irqsave(&dwc->lock, flags);
- ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false);
+ ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -761,10 +828,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
length, last ? " last" : "",
chain ? " chain" : "");
- /* Skip the LINK-TRB on ISOC */
- if (((dep->free_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
- usb_endpoint_xfer_isoc(dep->endpoint.desc))
- dep->free_slot++;
trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
@@ -776,6 +839,10 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
}
dep->free_slot++;
+ /* Skip the LINK-TRB on ISOC */
+ if (((dep->free_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
+ usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ dep->free_slot++;
trb->size = DWC3_TRB_SIZE_LENGTH(length);
trb->bpl = lower_32_bits(dma);
@@ -791,9 +858,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
else
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
-
- if (!req->request.no_interrupt && !chain)
- trb->ctrl |= DWC3_TRB_CTRL_IOC;
break;
case USB_ENDPOINT_XFER_BULK:
@@ -808,6 +872,9 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
BUG();
}
+ if (!req->request.no_interrupt && !chain)
+ trb->ctrl |= DWC3_TRB_CTRL_IOC;
+
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
trb->ctrl |= DWC3_TRB_CTRL_CSP;
@@ -1097,7 +1164,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
*/
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
if (list_empty(&dep->req_queued)) {
- dwc3_stop_active_transfer(dwc, dep->number);
+ dwc3_stop_active_transfer(dwc, dep->number, true);
dep->flags = DWC3_EP_ENABLED;
}
return 0;
@@ -1127,6 +1194,23 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
return ret;
}
+ /*
+ * 4. Stream Capable Bulk Endpoints. We need to start the transfer
+ * right away, otherwise host will not know we have streams to be
+ * handled.
+ */
+ if (dep->stream_capable) {
+ int ret;
+
+ 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);
+ }
+ }
+
return 0;
}
@@ -1183,7 +1267,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
}
if (r == req) {
/* wait until it is processed */
- dwc3_stop_active_transfer(dwc, dep->number);
+ dwc3_stop_active_transfer(dwc, dep->number, true);
goto out1;
}
dev_err(dwc->dev, "request %p was not queued to %s\n",
@@ -1214,23 +1298,18 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
DWC3_DEPCMD_SETSTALL, &params);
if (ret)
- dev_err(dwc->dev, "failed to %s STALL on %s\n",
- value ? "set" : "clear",
+ dev_err(dwc->dev, "failed to set STALL on %s\n",
dep->name);
else
dep->flags |= DWC3_EP_STALL;
} else {
- if (dep->flags & DWC3_EP_WEDGE)
- return 0;
-
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
DWC3_DEPCMD_CLEARSTALL, &params);
if (ret)
- dev_err(dwc->dev, "failed to %s STALL on %s\n",
- value ? "set" : "clear",
+ dev_err(dwc->dev, "failed to clear STALL on %s\n",
dep->name);
else
- dep->flags &= ~DWC3_EP_STALL;
+ dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
}
return ret;
@@ -1410,7 +1489,7 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
return 0;
}
-static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
+static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
{
u32 reg;
u32 timeout = 500;
@@ -1425,9 +1504,17 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
if (dwc->revision >= DWC3_REVISION_194A)
reg &= ~DWC3_DCTL_KEEP_CONNECT;
reg |= DWC3_DCTL_RUN_STOP;
+
+ if (dwc->has_hibernation)
+ reg |= DWC3_DCTL_KEEP_CONNECT;
+
dwc->pullups_connected = true;
} else {
reg &= ~DWC3_DCTL_RUN_STOP;
+
+ if (dwc->has_hibernation && !suspend)
+ reg &= ~DWC3_DCTL_KEEP_CONNECT;
+
dwc->pullups_connected = false;
}
@@ -1465,7 +1552,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
is_on = !!is_on;
spin_lock_irqsave(&dwc->lock, flags);
- ret = dwc3_gadget_run_stop(dwc, is_on);
+ ret = dwc3_gadget_run_stop(dwc, is_on, false);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -1508,6 +1595,15 @@ static int dwc3_gadget_start(struct usb_gadget *g,
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) {
@@ -1515,7 +1611,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
dwc->gadget.name,
dwc->gadget_driver->driver.name);
ret = -EBUSY;
- goto err0;
+ goto err1;
}
dwc->gadget_driver = driver;
@@ -1536,10 +1632,25 @@ static int dwc3_gadget_start(struct usb_gadget *g,
* STAR#9000525659: Clock Domain Crossing on DCTL in
* USB 2.0 Mode
*/
- if (dwc->revision < DWC3_REVISION_220A)
+ if (dwc->revision < DWC3_REVISION_220A) {
reg |= DWC3_DCFG_SUPERSPEED;
- else
- reg |= dwc->maximum_speed;
+ } else {
+ switch (dwc->maximum_speed) {
+ case USB_SPEED_LOW:
+ reg |= DWC3_DSTS_LOWSPEED;
+ break;
+ case USB_SPEED_FULL:
+ reg |= DWC3_DSTS_FULLSPEED1;
+ break;
+ case USB_SPEED_HIGH:
+ reg |= DWC3_DSTS_HIGHSPEED;
+ break;
+ case USB_SPEED_SUPER: /* FALLTHROUGH */
+ case USB_SPEED_UNKNOWN: /* FALTHROUGH */
+ default:
+ reg |= DWC3_DSTS_SUPERSPEED;
+ }
+ }
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
dwc->start_config_issued = false;
@@ -1548,44 +1659,43 @@ static int dwc3_gadget_start(struct usb_gadget *g,
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);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
+ false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
- goto err0;
+ goto err2;
}
dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
+ false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
- goto err1;
+ goto err3;
}
/* begin to receive SETUP packets */
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
- irq = platform_get_irq(to_platform_device(dwc->dev), 0);
- ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
- IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc);
- if (ret) {
- dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
- irq, ret);
- goto err1;
- }
-
dwc3_gadget_enable_irq(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
-err1:
+err3:
__dwc3_gadget_ep_disable(dwc->eps[0]);
-err0:
+err2:
+ dwc->gadget_driver = NULL;
+
+err1:
spin_unlock_irqrestore(&dwc->lock, flags);
+ free_irq(irq, dwc);
+
+err0:
return ret;
}
@@ -1599,9 +1709,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g,
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_disable_irq(dwc);
- irq = platform_get_irq(to_platform_device(dwc->dev), 0);
- free_irq(irq, dwc);
-
__dwc3_gadget_ep_disable(dwc->eps[0]);
__dwc3_gadget_ep_disable(dwc->eps[1]);
@@ -1609,6 +1716,9 @@ static int dwc3_gadget_stop(struct usb_gadget *g,
spin_unlock_irqrestore(&dwc->lock, flags);
+ irq = platform_get_irq(to_platform_device(dwc->dev), 0);
+ free_irq(irq, dwc);
+
return 0;
}
@@ -1641,16 +1751,18 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->dwc = dwc;
dep->number = epnum;
+ dep->direction = !!direction;
dwc->eps[epnum] = dep;
snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,
(epnum & 1) ? "in" : "out");
dep->endpoint.name = dep->name;
- dep->direction = (epnum & 1);
+
+ dev_vdbg(dwc->dev, "initializing %s\n", dep->name);
if (epnum == 0 || epnum == 1) {
- dep->endpoint.maxpacket = 512;
+ usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
dep->endpoint.maxburst = 1;
dep->endpoint.ops = &dwc3_gadget_ep0_ops;
if (!epnum)
@@ -1658,7 +1770,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
} else {
int ret;
- dep->endpoint.maxpacket = 1024;
+ 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,
@@ -1849,15 +1961,12 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
*/
dep->flags = DWC3_EP_PENDING_REQUEST;
} else {
- dwc3_stop_active_transfer(dwc, dep->number);
+ dwc3_stop_active_transfer(dwc, dep->number, true);
dep->flags = DWC3_EP_ENABLED;
}
return 1;
}
- if ((event->status & DEPEVT_STATUS_IOC) &&
- (trb->ctrl & DWC3_TRB_CTRL_IOC))
- return 0;
return 1;
}
@@ -1999,7 +2108,25 @@ static void dwc3_disconnect_gadget(struct dwc3 *dwc)
}
}
-static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
+static void dwc3_suspend_gadget(struct dwc3 *dwc)
+{
+ if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
+ spin_unlock(&dwc->lock);
+ dwc->gadget_driver->suspend(&dwc->gadget);
+ spin_lock(&dwc->lock);
+ }
+}
+
+static void dwc3_resume_gadget(struct dwc3 *dwc)
+{
+ if (dwc->gadget_driver && dwc->gadget_driver->resume) {
+ spin_unlock(&dwc->lock);
+ dwc->gadget_driver->resume(&dwc->gadget);
+ spin_lock(&dwc->lock);
+ }
+}
+
+static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
{
struct dwc3_ep *dep;
struct dwc3_gadget_ep_cmd_params params;
@@ -2031,7 +2158,8 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
*/
cmd = DWC3_DEPCMD_ENDTRANSFER;
- cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
+ cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
+ cmd |= DWC3_DEPCMD_CMDIOC;
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
memset(&params, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
@@ -2104,34 +2232,6 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
dwc->setup_packet_pending = false;
}
-static void dwc3_gadget_usb3_phy_suspend(struct dwc3 *dwc, int suspend)
-{
- u32 reg;
-
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
-
- if (suspend)
- reg |= DWC3_GUSB3PIPECTL_SUSPHY;
- else
- reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
-
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
-}
-
-static void dwc3_gadget_usb2_phy_suspend(struct dwc3 *dwc, int suspend)
-{
- u32 reg;
-
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
-
- if (suspend)
- reg |= DWC3_GUSB2PHYCFG_SUSPHY;
- else
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
-
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
-}
-
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
{
u32 reg;
@@ -2172,13 +2272,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
/* after reset -> Default State */
usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
- /* Recent versions support automatic phy suspend and don't need this */
- if (dwc->revision < DWC3_REVISION_194A) {
- /* Resume PHYs */
- dwc3_gadget_usb2_phy_suspend(dwc, false);
- dwc3_gadget_usb3_phy_suspend(dwc, false);
- }
-
if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
dwc3_disconnect_gadget(dwc);
@@ -2222,20 +2315,6 @@ static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
}
-static void dwc3_gadget_phy_suspend(struct dwc3 *dwc, u8 speed)
-{
- switch (speed) {
- case USB_SPEED_SUPER:
- dwc3_gadget_usb2_phy_suspend(dwc, true);
- break;
- case USB_SPEED_HIGH:
- case USB_SPEED_FULL:
- case USB_SPEED_LOW:
- dwc3_gadget_usb3_phy_suspend(dwc, true);
- break;
- }
-}
-
static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
{
struct dwc3_ep *dep;
@@ -2309,23 +2388,23 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
reg |= DWC3_DCTL_HIRD_THRES(12);
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
- }
-
- /* Recent versions support automatic phy suspend and don't need this */
- if (dwc->revision < DWC3_REVISION_194A) {
- /* Suspend unneeded PHY */
- dwc3_gadget_phy_suspend(dwc, dwc->gadget.speed);
+ } else {
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= ~DWC3_DCTL_HIRD_THRES_MASK;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
}
dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true,
+ false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
}
dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true,
+ false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
@@ -2431,9 +2510,52 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
}
}
+ switch (next) {
+ case DWC3_LINK_STATE_U1:
+ if (dwc->speed == USB_SPEED_SUPER)
+ dwc3_suspend_gadget(dwc);
+ break;
+ case DWC3_LINK_STATE_U2:
+ case DWC3_LINK_STATE_U3:
+ dwc3_suspend_gadget(dwc);
+ break;
+ case DWC3_LINK_STATE_RESUME:
+ dwc3_resume_gadget(dwc);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ dev_vdbg(dwc->dev, "link change: %s [%d] -> %s [%d]\n",
+ dwc3_gadget_link_string(dwc->link_state),
+ dwc->link_state, dwc3_gadget_link_string(next), next);
+
dwc->link_state = next;
+}
+
+static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
+ unsigned int evtinfo)
+{
+ unsigned int is_ss = evtinfo & BIT(4);
+
+ /**
+ * WORKAROUND: DWC3 revison 2.20a with hibernation support
+ * have a known issue which can cause USB CV TD.9.23 to fail
+ * randomly.
+ *
+ * Because of this issue, core could generate bogus hibernation
+ * events which SW needs to ignore.
+ *
+ * Refers to:
+ *
+ * STAR#9000546576: Device Mode Hibernation: Issue in USB 2.0
+ * Device Fallback from SuperSpeed
+ */
+ if (is_ss ^ (dwc->speed == USB_SPEED_SUPER))
+ return;
- dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state);
+ /* enter hibernation here */
}
static void dwc3_gadget_interrupt(struct dwc3 *dwc,
@@ -2452,6 +2574,13 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
case DWC3_DEVICE_EVENT_WAKEUP:
dwc3_gadget_wakeup_interrupt(dwc);
break;
+ case DWC3_DEVICE_EVENT_HIBER_REQ:
+ 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:
dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
break;
@@ -2494,61 +2623,75 @@ static void dwc3_process_event_entry(struct dwc3 *dwc,
}
}
-static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)
+static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
{
- struct dwc3 *dwc = _dwc;
- unsigned long flags;
+ struct dwc3_event_buffer *evt;
irqreturn_t ret = IRQ_NONE;
- int i;
+ int left;
+ u32 reg;
- spin_lock_irqsave(&dwc->lock, flags);
+ evt = dwc->ev_buffs[buf];
+ left = evt->count;
- for (i = 0; i < dwc->num_event_buffers; i++) {
- struct dwc3_event_buffer *evt;
- int left;
+ if (!(evt->flags & DWC3_EVENT_PENDING))
+ return IRQ_NONE;
- evt = dwc->ev_buffs[i];
- left = evt->count;
+ while (left > 0) {
+ union dwc3_event event;
- if (!(evt->flags & DWC3_EVENT_PENDING))
- continue;
+ event.raw = *(u32 *) (evt->buf + evt->lpos);
- while (left > 0) {
- union dwc3_event event;
+ dwc3_process_event_entry(dwc, &event);
- event.raw = *(u32 *) (evt->buf + evt->lpos);
+ /*
+ * FIXME we wrap around correctly to the next entry as
+ * almost all entries are 4 bytes in size. There is one
+ * entry which has 12 bytes which is a regular entry
+ * followed by 8 bytes data. ATM I don't know how
+ * things are organized if we get next to the a
+ * boundary so I worry about that once we try to handle
+ * that.
+ */
+ evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
+ left -= 4;
- dwc3_process_event_entry(dwc, &event);
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4);
+ }
- /*
- * FIXME we wrap around correctly to the next entry as
- * almost all entries are 4 bytes in size. There is one
- * entry which has 12 bytes which is a regular entry
- * followed by 8 bytes data. ATM I don't know how
- * things are organized if we get next to the a
- * boundary so I worry about that once we try to handle
- * that.
- */
- evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
- left -= 4;
+ evt->count = 0;
+ evt->flags &= ~DWC3_EVENT_PENDING;
+ ret = IRQ_HANDLED;
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(i), 4);
- }
+ /* Unmask interrupt */
+ reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf));
+ reg &= ~DWC3_GEVNTSIZ_INTMASK;
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg);
- evt->count = 0;
- evt->flags &= ~DWC3_EVENT_PENDING;
- ret = IRQ_HANDLED;
- }
+ return ret;
+}
+
+static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)
+{
+ struct dwc3 *dwc = _dwc;
+ unsigned long flags;
+ irqreturn_t ret = IRQ_NONE;
+ int i;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ for (i = 0; i < dwc->num_event_buffers; i++)
+ ret |= dwc3_process_event_buf(dwc, i);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
}
-static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
+static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc, u32 buf)
{
struct dwc3_event_buffer *evt;
u32 count;
+ u32 reg;
evt = dwc->ev_buffs[buf];
@@ -2560,6 +2703,11 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
evt->count = count;
evt->flags |= DWC3_EVENT_PENDING;
+ /* Mask interrupt */
+ reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf));
+ reg |= DWC3_GEVNTSIZ_INTMASK;
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg);
+
return IRQ_WAKE_THREAD;
}
@@ -2574,7 +2722,7 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
for (i = 0; i < dwc->num_event_buffers; i++) {
irqreturn_t status;
- status = dwc3_process_event_buf(dwc, i);
+ status = dwc3_check_event_buf(dwc, i);
if (status == IRQ_WAKE_THREAD)
ret = status;
}
@@ -2592,7 +2740,6 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
*/
int dwc3_gadget_init(struct dwc3 *dwc)
{
- u32 reg;
int ret;
dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
@@ -2634,6 +2781,12 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget.name = "dwc3-gadget";
/*
+ * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
+ * on ep out.
+ */
+ dwc->gadget.quirk_ep_out_aligned_size = true;
+
+ /*
* REVISIT: Here we should clear all pending IRQs to be
* sure we're starting from a well known location.
*/
@@ -2642,28 +2795,16 @@ int dwc3_gadget_init(struct dwc3 *dwc)
if (ret)
goto err4;
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
- reg |= DWC3_DCFG_LPM_CAP;
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
-
- /* Enable USB2 LPM and automatic phy suspend only on recent versions */
- if (dwc->revision >= DWC3_REVISION_194A) {
- dwc3_gadget_usb2_phy_suspend(dwc, false);
- dwc3_gadget_usb3_phy_suspend(dwc, false);
- }
-
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
- goto err5;
+ goto err4;
}
return 0;
-err5:
- dwc3_gadget_free_endpoints(dwc);
-
err4:
+ dwc3_gadget_free_endpoints(dwc);
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
dwc->ep0_bounce, dwc->ep0_bounce_addr);
@@ -2704,8 +2845,10 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
int dwc3_gadget_prepare(struct dwc3 *dwc)
{
- if (dwc->pullups_connected)
+ if (dwc->pullups_connected) {
dwc3_gadget_disable_irq(dwc);
+ dwc3_gadget_run_stop(dwc, true, true);
+ }
return 0;
}
@@ -2714,7 +2857,7 @@ void dwc3_gadget_complete(struct dwc3 *dwc)
{
if (dwc->pullups_connected) {
dwc3_gadget_enable_irq(dwc);
- dwc3_gadget_run_stop(dwc, true);
+ dwc3_gadget_run_stop(dwc, true, false);
}
}
@@ -2737,12 +2880,14 @@ int dwc3_gadget_resume(struct dwc3 *dwc)
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);
+ 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);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
+ false);
if (ret)
goto err1;
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 99e6d724882..a0ee75b68a8 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -6,34 +6,14 @@
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
+ * 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.
*
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 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
@@ -76,12 +56,6 @@ struct dwc3;
/* DEPXFERCFG parameter 0 */
#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff)
-struct dwc3_gadget_ep_cmd_params {
- u32 param2;
- u32 param1;
- u32 param0;
-};
-
/* -------------------------------------------------------------------------- */
#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
@@ -105,9 +79,6 @@ static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req)
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status);
-int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
-int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
-
void dwc3_ep0_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event);
void dwc3_ep0_out_start(struct dwc3 *dwc);
@@ -115,9 +86,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 dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
- unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
-int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
/**
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index 0fa1846eda4..32db328cc76 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -5,34 +5,14 @@
*
* Authors: Felipe Balbi <balbi@ti.com>,
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
+ * 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.
*
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 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 <linux/platform_device.h>
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index a50f76b9d19..d94441c14d8 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -6,34 +6,14 @@
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
+ * 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.
*
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 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
diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h
new file mode 100644
index 00000000000..7db34f00b89
--- /dev/null
+++ b/drivers/usb/dwc3/platform_data.h
@@ -0,0 +1,27 @@
+/**
+ * platform_data.h - USB DWC3 Platform Data Support
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Felipe Balbi <balbi@ti.com>
+ *
+ * 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;
+};