aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r--drivers/usb/dwc3/Kconfig31
-rw-r--r--drivers/usb/dwc3/Makefile10
-rw-r--r--drivers/usb/dwc3/core.c31
-rw-r--r--drivers/usb/dwc3/core.h24
-rw-r--r--drivers/usb/dwc3/debugfs.c38
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c57
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c152
-rw-r--r--drivers/usb/dwc3/gadget.c292
-rw-r--r--drivers/usb/dwc3/host.c2
9 files changed, 396 insertions, 241 deletions
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index f6a6e070c2ac..77e3f40f5cea 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)
+ depends on (USB || USB_GADGET)
select USB_OTG_UTILS
select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD
help
@@ -12,6 +12,35 @@ config USB_DWC3
if USB_DWC3
+choice
+ bool "DWC3 Mode Selection"
+ default USB_DWC3_DUAL_ROLE if (USB && USB_GADGET)
+ default USB_DWC3_HOST if (USB && !USB_GADGET)
+ default USB_DWC3_GADGET if (!USB && USB_GADGET)
+
+config USB_DWC3_HOST
+ bool "Host only mode"
+ depends on USB
+ help
+ Select this when you want to use DWC3 in host mode only,
+ thereby the gadget feature will be regressed.
+
+config USB_DWC3_GADGET
+ bool "Gadget only mode"
+ depends on USB_GADGET
+ help
+ Select this when you want to use DWC3 in gadget mode only,
+ thereby the host feature will be regressed.
+
+config USB_DWC3_DUAL_ROLE
+ bool "Dual Role mode"
+ depends on (USB && USB_GADGET)
+ help
+ This is the default mode of working of DWC3 controller where
+ both host and gadget features are enabled.
+
+endchoice
+
config USB_DWC3_DEBUG
bool "Enable Debugging Messages"
help
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 4502648b8171..0c7ac92582be 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -4,8 +4,14 @@ ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG
obj-$(CONFIG_USB_DWC3) += dwc3.o
dwc3-y := core.o
-dwc3-y += host.o
-dwc3-y += gadget.o ep0.o
+
+ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
+ dwc3-y += host.o
+endif
+
+ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
+ dwc3-y += gadget.o ep0.o
+endif
ifneq ($(CONFIG_DEBUG_FS),)
dwc3-y += debugfs.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 3a4004a620ad..999909451e37 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -420,18 +420,27 @@ static int dwc3_probe(struct platform_device *pdev)
return -ENOMEM;
}
- dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+ 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_OR_NULL(dwc->usb2_phy)) {
dev_err(dev, "no usb2 phy configured\n");
return -EPROBE_DEFER;
}
- dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
if (IS_ERR_OR_NULL(dwc->usb3_phy)) {
dev_err(dev, "no usb3 phy configured\n");
return -EPROBE_DEFER;
}
+ usb_phy_set_suspend(dwc->usb2_phy, 0);
+ usb_phy_set_suspend(dwc->usb3_phy, 0);
+
spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc);
@@ -450,8 +459,7 @@ static int dwc3_probe(struct platform_device *pdev)
else
dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
- if (of_get_property(node, "tx-fifo-resize", NULL))
- dwc->needs_fifo_resize = true;
+ dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize");
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
@@ -550,9 +558,9 @@ err0:
static int dwc3_remove(struct platform_device *pdev)
{
struct dwc3 *dwc = platform_get_drvdata(pdev);
- struct resource *res;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ usb_phy_set_suspend(dwc->usb2_phy, 1);
+ usb_phy_set_suspend(dwc->usb3_phy, 1);
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@@ -580,11 +588,22 @@ static int dwc3_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id of_dwc3_match[] = {
+ {
+ .compatible = "synopsys,dwc3"
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_dwc3_match);
+#endif
+
static struct platform_driver dwc3_driver = {
.probe = dwc3_probe,
.remove = dwc3_remove,
.driver = {
.name = "dwc3",
+ .of_match_table = of_match_ptr(of_dwc3_match),
},
};
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 499956344262..b41750660235 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -55,7 +55,9 @@
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
-#define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE
+#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)
#define DWC3_EVENT_TYPE_MASK 0xfe
#define DWC3_EVENT_TYPE_DEV 0
@@ -405,7 +407,6 @@ struct dwc3_event_buffer {
* @number: endpoint number (1 - 15)
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
* @resource_index: Resource transfer index
- * @current_uf: Current uf received through last event parameter
* @interval: the intervall on which the ISOC transfer is started
* @name: a human readable name e.g. ep1out-bulk
* @direction: true for TX, false for RX
@@ -439,7 +440,6 @@ struct dwc3_ep {
u8 number;
u8 type;
u8 resource_index;
- u16 current_uf;
u32 interval;
char name[20];
@@ -581,6 +581,7 @@ struct dwc3_request {
struct usb_request request;
struct list_head list;
struct dwc3_ep *dep;
+ u32 start_slot;
u8 epnum;
struct dwc3_trb *trb;
@@ -721,6 +722,7 @@ struct dwc3 {
struct dwc3_hwparams hwparams;
struct dentry *root;
+ struct debugfs_regset32 *regset;
u8 test_mode;
u8 test_mode_nr;
@@ -862,10 +864,24 @@ union dwc3_event {
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
+#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
-
+#else
+static inline int dwc3_host_init(struct dwc3 *dwc)
+{ return 0; }
+static inline void dwc3_host_exit(struct dwc3 *dwc)
+{ }
+#endif
+
+#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);
+#else
+static inline int dwc3_gadget_init(struct dwc3 *dwc)
+{ return 0; }
+static inline void dwc3_gadget_exit(struct dwc3 *dwc)
+{ }
+#endif
#endif /* __DRIVERS_USB_DWC3_CORE_H */
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 5945aadaa1c9..4a752e730c5f 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -59,7 +59,7 @@
.offset = DWC3_ ##nm - DWC3_GLOBALS_REGS_START, \
}
-static const struct debugfs_reg32 dwc3_regs[] = {
+static struct debugfs_reg32 dwc3_regs[] = {
dump_register(GSBUSCFG0),
dump_register(GSBUSCFG1),
dump_register(GTXTHRCFG),
@@ -376,27 +376,6 @@ static const struct debugfs_reg32 dwc3_regs[] = {
dump_register(OSTS),
};
-static int dwc3_regdump_show(struct seq_file *s, void *unused)
-{
- struct dwc3 *dwc = s->private;
-
- seq_printf(s, "DesignWare USB3 Core Register Dump\n");
- debugfs_print_regs32(s, dwc3_regs, ARRAY_SIZE(dwc3_regs),
- dwc->regs, "");
- return 0;
-}
-
-static int dwc3_regdump_open(struct inode *inode, struct file *file)
-{
- return single_open(file, dwc3_regdump_show, inode->i_private);
-}
-
-static const struct file_operations dwc3_regdump_fops = {
- .open = dwc3_regdump_open,
- .read = seq_read,
- .release = single_release,
-};
-
static int dwc3_mode_show(struct seq_file *s, void *unused)
{
struct dwc3 *dwc = s->private;
@@ -666,13 +645,23 @@ int dwc3_debugfs_init(struct dwc3 *dwc)
dwc->root = root;
- file = debugfs_create_file("regdump", S_IRUGO, root, dwc,
- &dwc3_regdump_fops);
+ dwc->regset = kzalloc(sizeof(*dwc->regset), GFP_KERNEL);
+ if (!dwc->regset) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ dwc->regset->regs = dwc3_regs;
+ dwc->regset->nregs = ARRAY_SIZE(dwc3_regs);
+ dwc->regset->base = dwc->regs;
+
+ file = debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset);
if (!file) {
ret = -ENOMEM;
goto err1;
}
+#if IS_ENABLED(CONFIG_USB_DWC3_GADGET)
file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
dwc, &dwc3_mode_fops);
if (!file) {
@@ -693,6 +682,7 @@ int dwc3_debugfs_init(struct dwc3 *dwc)
ret = -ENOMEM;
goto err1;
}
+#endif
return 0;
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index aae5328ac771..b50da53e9a52 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -42,7 +42,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
memset(&pdata, 0x00, sizeof(pdata));
- pdev = platform_device_alloc("nop_usb_xceiv", 0);
+ pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
if (!pdev)
return -ENOMEM;
@@ -53,7 +53,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
if (ret)
goto err1;
- pdev = platform_device_alloc("nop_usb_xceiv", 1);
+ pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
if (!pdev) {
ret = -ENOMEM;
goto err1;
@@ -95,13 +95,14 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
struct platform_device *dwc3;
struct dwc3_exynos *exynos;
struct clk *clk;
+ struct device *dev = &pdev->dev;
int ret = -ENOMEM;
- exynos = kzalloc(sizeof(*exynos), GFP_KERNEL);
+ exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL);
if (!exynos) {
- dev_err(&pdev->dev, "not enough memory\n");
- goto err0;
+ dev_err(dev, "not enough memory\n");
+ return -ENOMEM;
}
/*
@@ -116,30 +117,30 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
ret = dwc3_exynos_register_phys(exynos);
if (ret) {
- dev_err(&pdev->dev, "couldn't register PHYs\n");
- goto err1;
+ dev_err(dev, "couldn't register PHYs\n");
+ return ret;
}
dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
if (!dwc3) {
- dev_err(&pdev->dev, "couldn't allocate dwc3 device\n");
- goto err1;
+ dev_err(dev, "couldn't allocate dwc3 device\n");
+ return -ENOMEM;
}
- clk = clk_get(&pdev->dev, "usbdrd30");
+ clk = devm_clk_get(dev, "usbdrd30");
if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "couldn't get clock\n");
+ dev_err(dev, "couldn't get clock\n");
ret = -EINVAL;
- goto err3;
+ goto err1;
}
- dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask);
+ dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
- dwc3->dev.parent = &pdev->dev;
- dwc3->dev.dma_mask = pdev->dev.dma_mask;
- dwc3->dev.dma_parms = pdev->dev.dma_parms;
+ dwc3->dev.parent = dev;
+ dwc3->dev.dma_mask = dev->dma_mask;
+ dwc3->dev.dma_parms = dev->dma_parms;
exynos->dwc3 = dwc3;
- exynos->dev = &pdev->dev;
+ exynos->dev = dev;
exynos->clk = clk;
clk_enable(exynos->clk);
@@ -147,26 +148,23 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
ret = platform_device_add_resources(dwc3, pdev->resource,
pdev->num_resources);
if (ret) {
- dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n");
- goto err4;
+ dev_err(dev, "couldn't add resources to dwc3 device\n");
+ goto err2;
}
ret = platform_device_add(dwc3);
if (ret) {
- dev_err(&pdev->dev, "failed to register dwc3 device\n");
- goto err4;
+ dev_err(dev, "failed to register dwc3 device\n");
+ goto err2;
}
return 0;
-err4:
+err2:
clk_disable(clk);
- clk_put(clk);
-err3:
- platform_device_put(dwc3);
err1:
- kfree(exynos);
-err0:
+ platform_device_put(dwc3);
+
return ret;
}
@@ -179,16 +177,13 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
platform_device_unregister(exynos->usb3_phy);
clk_disable(exynos->clk);
- clk_put(exynos->clk);
-
- kfree(exynos);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id exynos_dwc3_match[] = {
- { .compatible = "samsung,exynos-dwc3" },
+ { .compatible = "samsung,exynos5250-dwusb3" },
{},
};
MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index f31867fd2574..22f337f57219 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -43,10 +43,13 @@
#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/usb/otg.h>
#include <linux/usb/nop-usb-xceiv.h>
@@ -78,23 +81,6 @@
/* SYSCONFIG REGISTER */
#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16)
-#define USBOTGSS_SYSCONFIG_STANDBYMODE(x) ((x) << 4)
-
-#define USBOTGSS_STANDBYMODE_FORCE_STANDBY 0
-#define USBOTGSS_STANDBYMODE_NO_STANDBY 1
-#define USBOTGSS_STANDBYMODE_SMART_STANDBY 2
-#define USBOTGSS_STANDBYMODE_SMART_WAKEUP 3
-
-#define USBOTGSS_STANDBYMODE_MASK (0x03 << 4)
-
-#define USBOTGSS_SYSCONFIG_IDLEMODE(x) ((x) << 2)
-
-#define USBOTGSS_IDLEMODE_FORCE_IDLE 0
-#define USBOTGSS_IDLEMODE_NO_IDLE 1
-#define USBOTGSS_IDLEMODE_SMART_IDLE 2
-#define USBOTGSS_IDLEMODE_SMART_WAKEUP 3
-
-#define USBOTGSS_IDLEMODE_MASK (0x03 << 2)
/* IRQ_EOI REGISTER */
#define USBOTGSS_IRQ_EOI_LINE_NUMBER (1 << 0)
@@ -133,7 +119,6 @@ struct dwc3_omap {
/* device lock */
spinlock_t lock;
- struct platform_device *dwc3;
struct platform_device *usb2_phy;
struct platform_device *usb3_phy;
struct device *dev;
@@ -147,6 +132,8 @@ struct dwc3_omap {
u32 dma_status:1;
};
+struct dwc3_omap *_omap;
+
static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset)
{
return readl(base + offset);
@@ -157,6 +144,57 @@ static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
writel(value, base + offset);
}
+void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
+{
+ u32 val;
+ struct dwc3_omap *omap = _omap;
+
+ switch (status) {
+ case OMAP_DWC3_ID_GROUND:
+ dev_dbg(omap->dev, "ID GND\n");
+
+ val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
+ val &= ~(USBOTGSS_UTMI_OTG_STATUS_IDDIG
+ | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
+ | USBOTGSS_UTMI_OTG_STATUS_SESSEND);
+ val |= USBOTGSS_UTMI_OTG_STATUS_SESSVALID
+ | USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
+ dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
+ break;
+
+ case OMAP_DWC3_VBUS_VALID:
+ dev_dbg(omap->dev, "VBUS Connect\n");
+
+ val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
+ val &= ~USBOTGSS_UTMI_OTG_STATUS_SESSEND;
+ val |= USBOTGSS_UTMI_OTG_STATUS_IDDIG
+ | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
+ | USBOTGSS_UTMI_OTG_STATUS_SESSVALID
+ | USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
+ dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
+ break;
+
+ case OMAP_DWC3_ID_FLOAT:
+ case OMAP_DWC3_VBUS_OFF:
+ dev_dbg(omap->dev, "VBUS Disconnect\n");
+
+ val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
+ val &= ~(USBOTGSS_UTMI_OTG_STATUS_SESSVALID
+ | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
+ | USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT);
+ val |= USBOTGSS_UTMI_OTG_STATUS_SESSEND
+ | USBOTGSS_UTMI_OTG_STATUS_IDDIG;
+ dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
+ break;
+
+ default:
+ dev_dbg(omap->dev, "ID float\n");
+ }
+
+ return;
+}
+EXPORT_SYMBOL_GPL(dwc3_omap_mailbox);
+
static int dwc3_omap_register_phys(struct dwc3_omap *omap)
{
struct nop_usb_xceiv_platform_data pdata;
@@ -165,7 +203,7 @@ static int dwc3_omap_register_phys(struct dwc3_omap *omap)
memset(&pdata, 0x00, sizeof(pdata));
- pdev = platform_device_alloc("nop_usb_xceiv", 0);
+ pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
if (!pdev)
return -ENOMEM;
@@ -176,7 +214,7 @@ static int dwc3_omap_register_phys(struct dwc3_omap *omap)
if (ret)
goto err1;
- pdev = platform_device_alloc("nop_usb_xceiv", 1);
+ pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
if (!pdev) {
ret = -ENOMEM;
goto err1;
@@ -262,12 +300,20 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
return IRQ_HANDLED;
}
+static int dwc3_omap_remove_core(struct device *dev, void *c)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+
+ return 0;
+}
+
static int dwc3_omap_probe(struct platform_device *pdev)
{
struct dwc3_omap_data *pdata = pdev->dev.platform_data;
struct device_node *node = pdev->dev.of_node;
- struct platform_device *dwc3;
struct dwc3_omap *omap;
struct resource *res;
struct device *dev = &pdev->dev;
@@ -314,30 +360,32 @@ static int dwc3_omap_probe(struct platform_device *pdev)
return ret;
}
- dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
- if (!dwc3) {
- dev_err(dev, "couldn't allocate dwc3 device\n");
- return -ENOMEM;
- }
-
context = devm_kzalloc(dev, resource_size(res), GFP_KERNEL);
if (!context) {
dev_err(dev, "couldn't allocate dwc3 context memory\n");
- goto err2;
+ return -ENOMEM;
}
spin_lock_init(&omap->lock);
- dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
- dwc3->dev.parent = dev;
- dwc3->dev.dma_mask = dev->dma_mask;
- dwc3->dev.dma_parms = dev->dma_parms;
omap->resource_size = resource_size(res);
omap->context = context;
omap->dev = dev;
omap->irq = irq;
omap->base = base;
- omap->dwc3 = dwc3;
+
+ /*
+ * 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) {
+ dev_err(dev, "get_sync failed with err %d\n", ret);
+ return ret;
+ }
reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
@@ -368,21 +416,12 @@ static int dwc3_omap_probe(struct platform_device *pdev)
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
- /* Set No-Idle and No-Standby */
- reg &= ~(USBOTGSS_STANDBYMODE_MASK
- | USBOTGSS_IDLEMODE_MASK);
-
- reg |= (USBOTGSS_SYSCONFIG_STANDBYMODE(USBOTGSS_STANDBYMODE_NO_STANDBY)
- | USBOTGSS_SYSCONFIG_IDLEMODE(USBOTGSS_IDLEMODE_NO_IDLE));
-
- dwc3_omap_writel(omap->base, USBOTGSS_SYSCONFIG, reg);
-
ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
"dwc3-omap", omap);
if (ret) {
dev_err(dev, "failed to request IRQ #%d --> %d\n",
omap->irq, ret);
- goto err2;
+ return ret;
}
/* enable all IRQs */
@@ -401,33 +440,28 @@ static int dwc3_omap_probe(struct platform_device *pdev)
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
- ret = platform_device_add_resources(dwc3, pdev->resource,
- pdev->num_resources);
- if (ret) {
- dev_err(dev, "couldn't add resources to dwc3 device\n");
- goto err2;
- }
-
- ret = platform_device_add(dwc3);
- if (ret) {
- dev_err(dev, "failed to register dwc3 device\n");
- goto err2;
+ if (node) {
+ ret = of_platform_populate(node, NULL, NULL, dev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to add create dwc3 core\n");
+ return ret;
+ }
}
return 0;
-
-err2:
- platform_device_put(dwc3);
- return ret;
}
static int dwc3_omap_remove(struct platform_device *pdev)
{
struct dwc3_omap *omap = platform_get_drvdata(pdev);
- platform_device_unregister(omap->dwc3);
platform_device_unregister(omap->usb2_phy);
platform_device_unregister(omap->usb3_phy);
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core);
+
return 0;
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 2fdd767f8fe8..a04342f6cbfa 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -241,21 +241,23 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status)
{
struct dwc3 *dwc = dep->dwc;
+ int i;
if (req->queued) {
- if (req->request.num_mapped_sgs)
- dep->busy_slot += req->request.num_mapped_sgs;
- else
+ i = 0;
+ do {
dep->busy_slot++;
-
- /*
- * Skip LINK TRB. We can't use req->trb and check for
- * DWC3_TRBCTL_LINK_TRB because it points the TRB we just
- * completed (not the LINK TRB).
- */
- if (((dep->busy_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
+ /*
+ * Skip LINK TRB. We can't use req->trb and check for
+ * DWC3_TRBCTL_LINK_TRB because it points the TRB we
+ * just completed (not the LINK TRB).
+ */
+ if (((dep->busy_slot & DWC3_TRB_MASK) ==
+ DWC3_TRB_NUM- 1) &&
usb_endpoint_xfer_isoc(dep->endpoint.desc))
- dep->busy_slot++;
+ dep->busy_slot++;
+ } while(++i < req->request.num_mapped_sgs);
+ req->queued = false;
}
list_del(&req->list);
req->trb = NULL;
@@ -749,33 +751,32 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
*/
static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
struct dwc3_request *req, dma_addr_t dma,
- unsigned length, unsigned last, unsigned chain)
+ unsigned length, unsigned last, unsigned chain, unsigned node)
{
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
- unsigned int cur_slot;
-
dev_vdbg(dwc->dev, "%s: req %p dma %08llx length %d%s%s\n",
dep->name, req, (unsigned long long) dma,
length, last ? " last" : "",
chain ? " chain" : "");
- trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
- cur_slot = dep->free_slot;
- dep->free_slot++;
-
/* Skip the LINK-TRB on ISOC */
- if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
+ if (((dep->free_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
usb_endpoint_xfer_isoc(dep->endpoint.desc))
- return;
+ dep->free_slot++;
+
+ trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
if (!req->trb) {
dwc3_gadget_move_request_queued(req);
req->trb = trb;
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
+ req->start_slot = dep->free_slot & DWC3_TRB_MASK;
}
+ dep->free_slot++;
+
trb->size = DWC3_TRB_SIZE_LENGTH(length);
trb->bpl = lower_32_bits(dma);
trb->bph = upper_32_bits(dma);
@@ -786,9 +787,12 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
break;
case USB_ENDPOINT_XFER_ISOC:
- trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
+ if (!node)
+ trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
+ else
+ trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
- if (!req->request.no_interrupt)
+ if (!req->request.no_interrupt && !chain)
trb->ctrl |= DWC3_TRB_CTRL_IOC;
break;
@@ -807,14 +811,13 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
trb->ctrl |= DWC3_TRB_CTRL_CSP;
- } else {
- if (chain)
- trb->ctrl |= DWC3_TRB_CTRL_CHN;
-
- if (last)
- trb->ctrl |= DWC3_TRB_CTRL_LST;
+ } else if (last) {
+ trb->ctrl |= DWC3_TRB_CTRL_LST;
}
+ if (chain)
+ trb->ctrl |= DWC3_TRB_CTRL_CHN;
+
if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id);
@@ -885,6 +888,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
list_for_each_entry_safe(req, n, &dep->request_list, list) {
unsigned length;
dma_addr_t dma;
+ last_one = false;
if (req->request.num_mapped_sgs > 0) {
struct usb_request *request = &req->request;
@@ -900,7 +904,9 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
if (i == (request->num_mapped_sgs - 1) ||
sg_is_last(s)) {
- last_one = true;
+ if (list_is_last(&req->list,
+ &dep->request_list))
+ last_one = true;
chain = false;
}
@@ -912,7 +918,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
chain = false;
dwc3_prepare_one_trb(dep, req, dma, length,
- last_one, chain);
+ last_one, chain, i);
if (last_one)
break;
@@ -930,7 +936,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
last_one = 1;
dwc3_prepare_one_trb(dep, req, dma, length,
- last_one, false);
+ last_one, false, 0);
if (last_one)
break;
@@ -977,13 +983,14 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
}
memset(&params, 0, sizeof(params));
- params.param0 = upper_32_bits(req->trb_dma);
- params.param1 = lower_32_bits(req->trb_dma);
- if (start_new)
+ if (start_new) {
+ params.param0 = upper_32_bits(req->trb_dma);
+ params.param1 = lower_32_bits(req->trb_dma);
cmd = DWC3_DEPCMD_STARTTRANSFER;
- else
+ } else {
cmd = DWC3_DEPCMD_UPDATETRANSFER;
+ }
cmd |= DWC3_DEPCMD_PARAM(cmd_param);
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
@@ -1082,8 +1089,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
*
*/
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
- int ret;
-
/*
* If xfernotready is already elapsed and it is a case
* of isoc transfer, then issue END TRANSFER, so that
@@ -1091,7 +1096,10 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* notion of current microframe.
*/
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- dwc3_stop_active_transfer(dwc, dep->number);
+ if (list_empty(&dep->req_queued)) {
+ dwc3_stop_active_transfer(dwc, dep->number);
+ dep->flags = DWC3_EP_ENABLED;
+ }
return 0;
}
@@ -1099,6 +1107,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
if (ret && ret != -EBUSY)
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
dep->name);
+ return ret;
}
/*
@@ -1115,16 +1124,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
if (ret && ret != -EBUSY)
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
dep->name);
- }
-
- /*
- * 3. Missed ISOC Handling. We need to start isoc transfer on the saved
- * uframe number.
- */
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- (dep->flags & DWC3_EP_MISSED_ISOC)) {
- __dwc3_gadget_start_isoc(dwc, dep, dep->current_uf);
- dep->flags &= ~DWC3_EP_MISSED_ISOC;
+ return ret;
}
return 0;
@@ -1652,76 +1652,134 @@ static void dwc3_gadget_release(struct device *dev)
}
/* -------------------------------------------------------------------------- */
-static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
+static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
+ struct dwc3_request *req, struct dwc3_trb *trb,
const struct dwc3_event_depevt *event, int status)
{
- struct dwc3_request *req;
- struct dwc3_trb *trb;
unsigned int count;
unsigned int s_pkt = 0;
unsigned int trb_status;
+ if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
+ /*
+ * We continue despite the error. There is not much we
+ * can do. If we don't clean it up we loop forever. If
+ * we skip the TRB then it gets overwritten after a
+ * while since we use them in a ring buffer. A BUG()
+ * would help. Lets hope that if this occurs, someone
+ * fixes the root cause instead of looking away :)
+ */
+ dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n",
+ dep->name, trb);
+ count = trb->size & DWC3_TRB_SIZE_MASK;
+
+ if (dep->direction) {
+ if (count) {
+ trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
+ if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
+ dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
+ dep->name);
+ /*
+ * If missed isoc occurred and there is
+ * no request queued then issue END
+ * TRANSFER, so that core generates
+ * next xfernotready and we will issue
+ * a fresh START TRANSFER.
+ * If there are still queued request
+ * then wait, do not issue either END
+ * or UPDATE TRANSFER, just attach next
+ * request in request_list during
+ * giveback.If any future queued request
+ * is successfully transferred then we
+ * will issue UPDATE TRANSFER for all
+ * request in the request_list.
+ */
+ dep->flags |= DWC3_EP_MISSED_ISOC;
+ } else {
+ dev_err(dwc->dev, "incomplete IN transfer %s\n",
+ dep->name);
+ status = -ECONNRESET;
+ }
+ } else {
+ dep->flags &= ~DWC3_EP_MISSED_ISOC;
+ }
+ } else {
+ if (count && (event->status & DEPEVT_STATUS_SHORT))
+ s_pkt = 1;
+ }
+
+ /*
+ * We assume here we will always receive the entire data block
+ * which we should receive. Meaning, if we program RX to
+ * receive 4K but we receive only 2K, we assume that's all we
+ * should receive and we simply bounce the request back to the
+ * gadget driver for further processing.
+ */
+ req->request.actual += req->request.length - count;
+ if (s_pkt)
+ return 1;
+ if ((event->status & DEPEVT_STATUS_LST) &&
+ (trb->ctrl & (DWC3_TRB_CTRL_LST |
+ DWC3_TRB_CTRL_HWO)))
+ return 1;
+ if ((event->status & DEPEVT_STATUS_IOC) &&
+ (trb->ctrl & DWC3_TRB_CTRL_IOC))
+ return 1;
+ return 0;
+}
+
+static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event, int status)
+{
+ struct dwc3_request *req;
+ struct dwc3_trb *trb;
+ unsigned int slot;
+ unsigned int i;
+ int ret;
+
do {
req = next_request(&dep->req_queued);
if (!req) {
WARN_ON_ONCE(1);
return 1;
}
+ i = 0;
+ do {
+ slot = req->start_slot + i;
+ if ((slot == DWC3_TRB_NUM - 1) &&
+ usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ slot++;
+ slot %= DWC3_TRB_NUM;
+ trb = &dep->trb_pool[slot];
- trb = req->trb;
+ ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
+ event, status);
+ if (ret)
+ break;
+ }while (++i < req->request.num_mapped_sgs);
- if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
+ dwc3_gadget_giveback(dep, req, status);
+
+ if (ret)
+ break;
+ } while (1);
+
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+ list_empty(&dep->req_queued)) {
+ if (list_empty(&dep->request_list)) {
/*
- * We continue despite the error. There is not much we
- * can do. If we don't clean it up we loop forever. If
- * we skip the TRB then it gets overwritten after a
- * while since we use them in a ring buffer. A BUG()
- * would help. Lets hope that if this occurs, someone
- * fixes the root cause instead of looking away :)
+ * If there is no entry in request list then do
+ * not issue END TRANSFER now. Just set PENDING
+ * flag, so that END TRANSFER is issued when an
+ * entry is added into request list.
*/
- dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n",
- dep->name, req->trb);
- count = trb->size & DWC3_TRB_SIZE_MASK;
-
- if (dep->direction) {
- if (count) {
- trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
- if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
- dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
- dep->name);
- dep->current_uf = event->parameters &
- ~(dep->interval - 1);
- dep->flags |= DWC3_EP_MISSED_ISOC;
- } else {
- dev_err(dwc->dev, "incomplete IN transfer %s\n",
- dep->name);
- status = -ECONNRESET;
- }
- }
+ dep->flags = DWC3_EP_PENDING_REQUEST;
} else {
- if (count && (event->status & DEPEVT_STATUS_SHORT))
- s_pkt = 1;
+ dwc3_stop_active_transfer(dwc, dep->number);
+ dep->flags = DWC3_EP_ENABLED;
}
-
- /*
- * We assume here we will always receive the entire data block
- * which we should receive. Meaning, if we program RX to
- * receive 4K but we receive only 2K, we assume that's all we
- * should receive and we simply bounce the request back to the
- * gadget driver for further processing.
- */
- req->request.actual += req->request.length - count;
- dwc3_gadget_giveback(dep, req, status);
- if (s_pkt)
- break;
- if ((event->status & DEPEVT_STATUS_LST) &&
- (trb->ctrl & (DWC3_TRB_CTRL_LST |
- DWC3_TRB_CTRL_HWO)))
- break;
- if ((event->status & DEPEVT_STATUS_IOC) &&
- (trb->ctrl & DWC3_TRB_CTRL_IOC))
- break;
- } while (1);
+ return 1;
+ }
if ((event->status & DEPEVT_STATUS_IOC) &&
(trb->ctrl & DWC3_TRB_CTRL_IOC))
@@ -2157,6 +2215,26 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
break;
}
+ /* Enable USB2 LPM Capability */
+
+ if ((dwc->revision > DWC3_REVISION_194A)
+ && (speed != DWC3_DCFG_SUPERSPEED)) {
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg |= DWC3_DCFG_LPM_CAP;
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
+
+ /*
+ * TODO: This should be configurable. For now using
+ * maximum allowed HIRD threshold value of 0b1100
+ */
+ 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 */
@@ -2463,20 +2541,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
DWC3_DEVTEN_DISCONNEVTEN);
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
- /* Enable USB2 LPM and automatic phy suspend only on recent versions */
+ /* automatic phy suspend only on recent versions */
if (dwc->revision >= DWC3_REVISION_194A) {
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
- reg |= DWC3_DCFG_LPM_CAP;
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
-
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
- reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
-
- /* TODO: This should be configurable */
- reg |= DWC3_DCTL_HIRD_THRES(28);
-
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
-
dwc3_gadget_usb2_phy_suspend(dwc, false);
dwc3_gadget_usb3_phy_suspend(dwc, false);
}
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index 56a62342884d..0fa1846eda4c 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -44,7 +44,7 @@ int dwc3_host_init(struct dwc3 *dwc)
struct platform_device *xhci;
int ret;
- xhci = platform_device_alloc("xhci-hcd", -1);
+ xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
if (!xhci) {
dev_err(dwc->dev, "couldn't allocate xHCI device\n");
ret = -ENOMEM;