From cc27c96c2bee93068bfc60ea6b09611d88cef429 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 27 Nov 2011 20:16:27 +0800 Subject: usb: convert drivers/usb/* to use module_platform_driver() This patch converts the drivers in drivers/usb/* to use the module_platform_driver() macro which makes the code smaller and a bit simpler. Cc: Felipe Balbi Cc: Li Yang Cc: Kuninori Morimoto Cc: Sarah Sharp Cc: Jiri Kosina Cc: Lucas De Marchi Cc: Alan Stern Signed-off-by: Axel Lin Acked-by: Peter Korsgaard Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-omap.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 062552b5fc8a..4e27d5bf40ad 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -384,18 +384,8 @@ static struct platform_driver dwc3_omap_driver = { }, }; +module_platform_driver(dwc3_omap_driver); + MODULE_AUTHOR("Felipe Balbi "); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer"); - -static int __devinit dwc3_omap_init(void) -{ - return platform_driver_register(&dwc3_omap_driver); -} -module_init(dwc3_omap_init); - -static void __exit dwc3_omap_exit(void) -{ - platform_driver_unregister(&dwc3_omap_driver); -} -module_exit(dwc3_omap_exit); -- cgit v1.2.2 From d327ab5b6d660d6fe22b073b743fde1668e593bb Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Sat, 19 Nov 2011 18:27:37 +0100 Subject: usb: gadget: replace usb_gadget::is_dualspeed with max_speed This commit replaces usb_gadget's is_dualspeed field with a max_speed field. [ balbi@ti.com : Fixed DWC3 driver ] Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 25dbd8614e72..580272042a33 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1986,7 +1986,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) dev_set_name(&dwc->gadget.dev, "gadget"); dwc->gadget.ops = &dwc3_gadget_ops; - dwc->gadget.is_dualspeed = true; + dwc->gadget.max_speed = USB_SPEED_SUPER; dwc->gadget.speed = USB_SPEED_UNKNOWN; dwc->gadget.dev.parent = dwc->dev; -- cgit v1.2.2 From b2c2271c826589c5c5b285a5a32e158d36d263d9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 7 Oct 2011 22:40:41 +0300 Subject: usb: dwc3: gadget: don't disable endpoints on exit when we remove the gadget driver, it will already do that for us. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 25dbd8614e72..46ed15bca19f 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2076,7 +2076,6 @@ err0: void dwc3_gadget_exit(struct dwc3 *dwc) { int irq; - int i; usb_del_gadget_udc(&dwc->gadget); irq = platform_get_irq(to_platform_device(dwc->dev), 0); @@ -2084,9 +2083,6 @@ void dwc3_gadget_exit(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); free_irq(irq, dwc); - for (i = 0; i < ARRAY_SIZE(dwc->eps); i++) - __dwc3_gadget_ep_disable(dwc->eps[i]); - dwc3_gadget_free_endpoints(dwc); dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce, -- cgit v1.2.2 From 6c167fc9b0c23ead791edb94cf4debb6b8e534b5 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 7 Oct 2011 22:55:04 +0300 Subject: usb: dwc3: allow forcing a maximum speed this is mainly for testing. In order to be able to test if we're enumerating correctly on all speeds, let that be controlled by a module parameter. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 15 +++++++++++++++ drivers/usb/dwc3/core.h | 2 ++ drivers/usb/dwc3/gadget.c | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 717ebc9ff941..ca3b01f5fffa 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -59,6 +59,10 @@ #include "debug.h" +static char *maximum_speed = "super"; +module_param(maximum_speed, charp, 0); +MODULE_PARM_DESC(maximum_speed, "Maximum supported speed."); + /** * dwc3_core_soft_reset - Issues core soft reset and PHY reset * @dwc: pointer to our context structure @@ -370,6 +374,17 @@ static int __devinit dwc3_probe(struct platform_device *pdev) dwc->dev = &pdev->dev; dwc->irq = irq; + 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; + pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); pm_runtime_forbid(&pdev->dev); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 29a8e1679e12..b7d56c3a1fbb 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -555,6 +555,7 @@ struct dwc3_hwparams { * @regs: base address for our registers * @regs_size: address space size * @irq: IRQ number + * @maximum_speed: maximum speed requested (mainly for testing purposes) * @revision: revision register contents * @is_selfpowered: true when we are selfpowered * @three_stage_setup: set if we perform a three phase setup @@ -595,6 +596,7 @@ struct dwc3 { int irq; + u32 maximum_speed; u32 revision; #define DWC3_REVISION_173A 0x5533173a diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 46ed15bca19f..807737aec00e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1183,7 +1183,7 @@ static int dwc3_gadget_start(struct usb_gadget *g, reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg &= ~(DWC3_DCFG_SPEED_MASK); - reg |= DWC3_DCFG_SUPERSPEED; + reg |= dwc->maximum_speed; dwc3_writel(dwc->regs, DWC3_DCFG, reg); dwc->start_config_issued = false; -- cgit v1.2.2 From 9f622b2a407d8b34a5a7f5b4abd8b29b25cf4f32 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 12 Oct 2011 10:31:04 +0300 Subject: usb: dwc3: calculate number of event buffers dynamically This will allow us to only allocate memory when we actually need. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 21 +++++++++++---------- drivers/usb/dwc3/core.h | 8 ++++++-- drivers/usb/dwc3/gadget.c | 2 +- 3 files changed, 18 insertions(+), 13 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index ca3b01f5fffa..1f67606fd4fb 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -154,7 +154,7 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc) struct dwc3_event_buffer *evt; int i; - for (i = 0; i < DWC3_EVENT_BUFFERS_NUM; i++) { + for (i = 0; i < dwc->num_event_buffers; i++) { evt = dwc->ev_buffs[i]; if (evt) { dwc3_free_one_event_buffer(dwc, evt); @@ -166,17 +166,19 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc) /** * dwc3_alloc_event_buffers - Allocates @num event buffers of size @length * @dwc: Pointer to out controller context structure - * @num: number of event buffers to allocate * @length: size of event buffer * * Returns 0 on success otherwise negative errno. In error the case, dwc * may contain some buffers allocated but not all which were requested. */ -static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned num, - unsigned length) +static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) { + int num; int i; + num = DWC3_NUM_INT(dwc->hwparams.hwparams1); + dwc->num_event_buffers = num; + for (i = 0; i < num; i++) { struct dwc3_event_buffer *evt; @@ -202,7 +204,7 @@ static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc) struct dwc3_event_buffer *evt; int n; - for (n = 0; n < DWC3_EVENT_BUFFERS_NUM; n++) { + for (n = 0; n < dwc->num_event_buffers; n++) { evt = dwc->ev_buffs[n]; dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n", evt->buf, (unsigned long long) evt->dma, @@ -225,7 +227,7 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) struct dwc3_event_buffer *evt; int n; - for (n = 0; n < DWC3_EVENT_BUFFERS_NUM; n++) { + for (n = 0; n < dwc->num_event_buffers; n++) { evt = dwc->ev_buffs[n]; dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0); dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0); @@ -289,8 +291,9 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) cpu_relax(); } while (true); - ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_NUM, - DWC3_EVENT_BUFFERS_SIZE); + dwc3_cache_hwparams(dwc); + + ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); if (ret) { dev_err(dwc->dev, "failed to allocate event buffers\n"); ret = -ENOMEM; @@ -303,8 +306,6 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) goto err1; } - dwc3_cache_hwparams(dwc); - return 0; err1: diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index b7d56c3a1fbb..4b6c673ed608 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -52,7 +52,7 @@ /* Global constants */ #define DWC3_ENDPOINTS_NUM 32 -#define DWC3_EVENT_BUFFERS_NUM 2 +#define DWC3_EVENT_BUFFERS_MAX 2 #define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE #define DWC3_EVENT_TYPE_MASK 0xfe @@ -536,6 +536,8 @@ struct dwc3_hwparams { u32 hwparams8; }; +#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) + /** * struct dwc3 - representation of our controller * @ctrl_req: usb control request which is used for ep0 @@ -555,6 +557,7 @@ struct dwc3_hwparams { * @regs: base address for our registers * @regs_size: address space size * @irq: IRQ number + * @num_event_buffers: calculated number of event buffers * @maximum_speed: maximum speed requested (mainly for testing purposes) * @revision: revision register contents * @is_selfpowered: true when we are selfpowered @@ -585,7 +588,7 @@ struct dwc3 { spinlock_t lock; struct device *dev; - struct dwc3_event_buffer *ev_buffs[DWC3_EVENT_BUFFERS_NUM]; + struct dwc3_event_buffer *ev_buffs[DWC3_EVENT_BUFFERS_MAX]; struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; struct usb_gadget gadget; @@ -596,6 +599,7 @@ struct dwc3 { int irq; + u32 num_event_buffers; u32 maximum_speed; u32 revision; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 807737aec00e..b84418e88774 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1925,7 +1925,7 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc) spin_lock(&dwc->lock); - for (i = 0; i < DWC3_EVENT_BUFFERS_NUM; i++) { + for (i = 0; i < dwc->num_event_buffers; i++) { irqreturn_t status; status = dwc3_process_event_buf(dwc, i); -- cgit v1.2.2 From 0949e99b05736946cf0ac78e37194be0807e497e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 12 Oct 2011 10:44:56 +0300 Subject: usb: dwc3: fetch mode of operation from HW There's no need to add driver_data for something we can fetch from HW. This also makes our id_table unnecessary - at least for now -, so we also remove it on the same patch. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 57 +++++++++++++++++++++++++------------------- drivers/usb/dwc3/core.h | 11 +++++++++ drivers/usb/dwc3/dwc3-omap.c | 2 +- drivers/usb/dwc3/dwc3-pci.c | 4 ++-- 4 files changed, 46 insertions(+), 28 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 1f67606fd4fb..df151992e49d 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -325,15 +325,17 @@ static void dwc3_core_exit(struct dwc3 *dwc) static int __devinit dwc3_probe(struct platform_device *pdev) { - const struct platform_device_id *id = platform_get_device_id(pdev); struct resource *res; struct dwc3 *dwc; - void __iomem *regs; - unsigned int features = id->driver_data; + int ret = -ENOMEM; int irq; + + void __iomem *regs; void *mem; + u8 mode; + mem = kzalloc(sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL); if (!mem) { dev_err(&pdev->dev, "not enough memory\n"); @@ -396,13 +398,22 @@ static int __devinit dwc3_probe(struct platform_device *pdev) goto err3; } - if (features & DWC3_HAS_PERIPHERAL) { + mode = DWC3_MODE(dwc->hwparams.hwparams0); + + switch (mode) { + case DWC3_MODE_DRD: + case DWC3_MODE_DEVICE: ret = dwc3_gadget_init(dwc); if (ret) { - dev_err(&pdev->dev, "failed to initialized gadget\n"); + dev_err(&pdev->dev, "failed to initialize gadget\n"); goto err4; } + break; + default: + dev_err(&pdev->dev, "Unsupported mode of operation %d\n", mode); + goto err4; } + dwc->mode = mode; ret = dwc3_debugfs_init(dwc); if (ret) { @@ -415,8 +426,15 @@ static int __devinit dwc3_probe(struct platform_device *pdev) return 0; err5: - if (features & DWC3_HAS_PERIPHERAL) + switch (mode) { + case DWC3_MODE_DRD: + case DWC3_MODE_DEVICE: dwc3_gadget_exit(dwc); + break; + default: + /* do nothing */ + break; + } err4: dwc3_core_exit(dwc); @@ -436,10 +454,8 @@ err0: static int __devexit dwc3_remove(struct platform_device *pdev) { - const struct platform_device_id *id = platform_get_device_id(pdev); struct dwc3 *dwc = platform_get_drvdata(pdev); struct resource *res; - unsigned int features = id->driver_data; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -448,8 +464,15 @@ static int __devexit dwc3_remove(struct platform_device *pdev) dwc3_debugfs_exit(dwc); - if (features & DWC3_HAS_PERIPHERAL) + switch (dwc->mode) { + case DWC3_MODE_DRD: + case DWC3_MODE_DEVICE: dwc3_gadget_exit(dwc); + break; + default: + /* do nothing */ + break; + } dwc3_core_exit(dwc); release_mem_region(res->start, resource_size(res)); @@ -459,28 +482,12 @@ static int __devexit dwc3_remove(struct platform_device *pdev) return 0; } -static const struct platform_device_id dwc3_id_table[] __devinitconst = { - { - .name = "dwc3-omap", - .driver_data = (DWC3_HAS_PERIPHERAL - | DWC3_HAS_XHCI - | DWC3_HAS_OTG), - }, - { - .name = "dwc3-pci", - .driver_data = DWC3_HAS_PERIPHERAL, - }, - { }, /* Terminating Entry */ -}; -MODULE_DEVICE_TABLE(platform, dwc3_id_table); - static struct platform_driver dwc3_driver = { .probe = dwc3_probe, .remove = __devexit_p(dwc3_remove), .driver = { .name = "dwc3", }, - .id_table = dwc3_id_table, }; MODULE_AUTHOR("Felipe Balbi "); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 4b6c673ed608..a3ef8f34bf77 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -536,6 +536,15 @@ struct dwc3_hwparams { u32 hwparams8; }; +/* 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 + +/* HWPARAMS1 */ #define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) /** @@ -560,6 +569,7 @@ struct dwc3_hwparams { * @num_event_buffers: calculated number of event buffers * @maximum_speed: maximum speed requested (mainly for testing purposes) * @revision: revision register contents + * @mode: mode of operation * @is_selfpowered: true when we are selfpowered * @three_stage_setup: set if we perform a three phase setup * @ep0_status_pending: ep0 status response without a req is pending @@ -602,6 +612,7 @@ struct dwc3 { u32 num_event_buffers; u32 maximum_speed; u32 revision; + u32 mode; #define DWC3_REVISION_173A 0x5533173a #define DWC3_REVISION_175A 0x5533175a diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 062552b5fc8a..7bcf6775a1a1 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -236,7 +236,7 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev) goto err1; } - dwc3 = platform_device_alloc("dwc3-omap", -1); + dwc3 = platform_device_alloc("dwc3", -1); if (!dwc3) { dev_err(&pdev->dev, "couldn't allocate dwc3 device\n"); goto err2; diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index f77c00042685..193f1bd90d59 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -118,7 +118,7 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci, if (devid < 0) goto err2; - dwc3 = platform_device_alloc("dwc3-pci", devid); + dwc3 = platform_device_alloc("dwc3", devid); if (!dwc3) { dev_err(&pci->dev, "couldn't allocate dwc3 device\n"); goto err3; @@ -196,7 +196,7 @@ static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = { MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table); static struct pci_driver dwc3_pci_driver = { - .name = "pci-dwc3", + .name = "dwc3-pci", .id_table = dwc3_pci_id_table, .probe = dwc3_pci_probe, .remove = __devexit_p(dwc3_pci_remove), -- cgit v1.2.2 From d07e8819a03dc2d1f03f725194ae56544e6c680b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 12 Oct 2011 14:08:26 +0300 Subject: usb: dwc3: add xHCI Host support The Designware USB3 IP can be configured with an internal xHCI. If we're running on such a version, let's start the xHCI stack. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/Kconfig | 1 + drivers/usb/dwc3/Makefile | 2 + drivers/usb/dwc3/core.c | 39 ++++++++++++++-- drivers/usb/dwc3/core.h | 9 ++++ drivers/usb/dwc3/host.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 164 insertions(+), 3 deletions(-) create mode 100644 drivers/usb/dwc3/host.c (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 3c1d67d324fd..2c05ec9cf352 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -2,6 +2,7 @@ config USB_DWC3 tristate "DesignWare USB3 DRD Core Support" depends on (USB || USB_GADGET) select USB_OTG_UTILS + select USB_XHCI_PLATFORM help Say Y or M here if your system has a Dual Role SuperSpeed USB controller based on the DesignWare USB3 IP Core. diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 593d1dbc465b..0926e71142b9 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -4,11 +4,13 @@ ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_DWC3) += dwc3.o dwc3-y := core.o +dwc3-y += host.o ifneq ($(CONFIG_USB_GADGET_DWC3),) dwc3-y += gadget.o ep0.o endif + ifneq ($(CONFIG_DEBUG_FS),) dwc3-y += debugfs.o endif diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index df151992e49d..410835e28cf6 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -350,6 +350,8 @@ static int __devinit dwc3_probe(struct platform_device *pdev) goto err1; } + dwc->res = res; + res = request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev)); if (!res) { @@ -401,8 +403,27 @@ static int __devinit dwc3_probe(struct platform_device *pdev) mode = DWC3_MODE(dwc->hwparams.hwparams0); switch (mode) { - case DWC3_MODE_DRD: case DWC3_MODE_DEVICE: + ret = dwc3_gadget_init(dwc); + if (ret) { + dev_err(&pdev->dev, "failed to initialize gadget\n"); + goto err4; + } + break; + case DWC3_MODE_HOST: + ret = dwc3_host_init(dwc); + if (ret) { + dev_err(&pdev->dev, "failed to initialize host\n"); + goto err4; + } + break; + case DWC3_MODE_DRD: + ret = dwc3_host_init(dwc); + if (ret) { + dev_err(&pdev->dev, "failed to initialize host\n"); + goto err4; + } + ret = dwc3_gadget_init(dwc); if (ret) { dev_err(&pdev->dev, "failed to initialize gadget\n"); @@ -427,10 +448,16 @@ static int __devinit dwc3_probe(struct platform_device *pdev) err5: switch (mode) { - case DWC3_MODE_DRD: 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; @@ -465,10 +492,16 @@ static int __devexit dwc3_remove(struct platform_device *pdev) dwc3_debugfs_exit(dwc); switch (dwc->mode) { - case DWC3_MODE_DRD: 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; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index a3ef8f34bf77..a77554373224 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -41,6 +41,7 @@ #include #include +#include #include #include #include @@ -560,6 +561,7 @@ struct dwc3_hwparams { * @ep0_bounce_addr: dma address of ep0_bounce * @lock: for synchronizing * @dev: pointer to our struct device + * @xhci: pointer to our xHCI child * @event_buffer_list: a list of event buffers * @gadget: device side representation of the peripheral controller * @gadget_driver: pointer to the gadget driver @@ -598,6 +600,9 @@ struct dwc3 { spinlock_t lock; struct device *dev; + struct platform_device *xhci; + struct resource *res; + struct dwc3_event_buffer *ev_buffs[DWC3_EVENT_BUFFERS_MAX]; struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; @@ -782,4 +787,8 @@ union dwc3_event { #define DWC3_HAS_XHCI BIT(1) #define DWC3_HAS_OTG BIT(3) +/* prototypes */ +int dwc3_host_init(struct dwc3 *dwc); +void dwc3_host_exit(struct dwc3 *dwc); + #endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c new file mode 100644 index 000000000000..9b300a26fceb --- /dev/null +++ b/drivers/usb/dwc3/host.c @@ -0,0 +1,116 @@ +/** + * host.c - DesignWare USB3 DRD Controller Host Glue + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi , + * + * 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. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "io.h" + +#include "debug.h" + +static struct resource generic_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, + { + .flags = IORESOURCE_MEM, + }, +}; + +int dwc3_host_init(struct dwc3 *dwc) +{ + struct platform_device *xhci; + int ret; + + xhci = platform_device_alloc("xhci", -1); + if (!xhci) { + dev_err(dwc->dev, "couldn't allocate xHCI device\n"); + ret = -ENOMEM; + goto err0; + } + + dma_set_coherent_mask(&xhci->dev, dwc->dev->coherent_dma_mask); + + xhci->dev.parent = dwc->dev; + xhci->dev.dma_mask = dwc->dev->dma_mask; + xhci->dev.dma_parms = dwc->dev->dma_parms; + + dwc->xhci = xhci; + + /* setup resources */ + generic_resources[0].start = dwc->irq; + + generic_resources[1].start = dwc->res->start; + generic_resources[1].end = dwc->res->start + 0x7fff; + + ret = platform_device_add_resources(xhci, generic_resources, + ARRAY_SIZE(generic_resources)); + if (ret) { + dev_err(dwc->dev, "couldn't add resources to xHCI device\n"); + goto err1; + } + + ret = platform_device_add(xhci); + if (ret) { + dev_err(dwc->dev, "failed to register xHCI device\n"); + goto err1; + } + + return 0; + +err1: + platform_device_put(xhci); + +err0: + return ret; +} + +void dwc3_host_exit(struct dwc3 *dwc) +{ + platform_device_unregister(dwc->xhci); +} -- cgit v1.2.2 From 67920bd7c984c7f3a73305ad11cbb9fd3d5e239c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 12 Oct 2011 14:15:10 +0300 Subject: usb: dwc3: always compile gadget side too We can decide in runtime if that will be used or not. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/Kconfig | 2 ++ drivers/usb/dwc3/Makefile | 6 +----- drivers/usb/dwc3/gadget.h | 10 ---------- 3 files changed, 3 insertions(+), 15 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 2c05ec9cf352..dca0b51435e9 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -2,6 +2,8 @@ config USB_DWC3 tristate "DesignWare USB3 DRD Core Support" depends on (USB || USB_GADGET) select USB_OTG_UTILS + select USB_GADGET_DUALSPEED + select USB_GADGET_SUPERSPEED select USB_XHCI_PLATFORM help Say Y or M here if your system has a Dual Role SuperSpeed diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 0926e71142b9..900ae74357f1 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -5,11 +5,7 @@ obj-$(CONFIG_USB_DWC3) += dwc3.o dwc3-y := core.o dwc3-y += host.o - -ifneq ($(CONFIG_USB_GADGET_DWC3),) - dwc3-y += gadget.o ep0.o -endif - +dwc3-y += gadget.o ep0.o ifneq ($(CONFIG_DEBUG_FS),) dwc3-y += debugfs.o diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 71145a449d99..6dec6d94e547 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -110,18 +110,8 @@ static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req) list_move_tail(&req->list, &dep->req_queued); } -#if defined(CONFIG_USB_GADGET_DWC3) || defined(CONFIG_USB_GADGET_DWC3_MODULE) 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) { } -static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, - unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) -{ - return 0; -} -#endif void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int status); -- cgit v1.2.2 From f80b45e75e112e78b2b439dd3401bb3dff7cf6a1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 12 Oct 2011 14:15:49 +0300 Subject: usb: dwc3: move gadget prototypes to core.h host prototypes are there, let's move gadget's closer. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 3 +++ drivers/usb/dwc3/gadget.h | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index a77554373224..513a7391099f 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -791,4 +791,7 @@ union dwc3_event { int dwc3_host_init(struct dwc3 *dwc); void dwc3_host_exit(struct dwc3 *dwc); +int dwc3_gadget_init(struct dwc3 *dwc); +void dwc3_gadget_exit(struct dwc3 *dwc); + #endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 6dec6d94e547..5c4a56f055e9 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -110,9 +110,6 @@ static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req) list_move_tail(&req->list, &dep->req_queued); } -int dwc3_gadget_init(struct dwc3 *dwc); -void dwc3_gadget_exit(struct dwc3 *dwc); - void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int status); -- cgit v1.2.2 From c4da177f1f5560dfaf2e057cbf85411cd65c5426 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Oct 2011 10:16:50 +0300 Subject: usb: dwc3: depend on both Host and Gadget stacks now that we have host support, we must depend on both sides. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index dca0b51435e9..d8f741f9e56e 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_GADGET_DUALSPEED select USB_GADGET_SUPERSPEED -- cgit v1.2.2 From f96a6ec1db1ac730b5a031f2c2f7fcc6b07459d8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 15 Oct 2011 21:37:35 +0300 Subject: usb: dwc3: ep0: SetAddress() won't be issued while Configured I have talked to USB-IF about USB30CV issuing SetAddres() with a device on Configured state and they have agreed on changing USB30CV not to do so. Adding back the STALL reply in such case and while at that, also add a debugging message for an address which is too large. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 69a4e43ddf59..24b447e40bc7 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -422,8 +422,15 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) u32 reg; addr = le16_to_cpu(ctrl->wValue); - if (addr > 127) + if (addr > 127) { + dev_dbg(dwc->dev, "invalid device address %d\n", addr); return -EINVAL; + } + + if (dwc->dev_state == DWC3_CONFIGURED_STATE) { + dev_dbg(dwc->dev, "trying to set address when configured\n"); + return -EINVAL; + } reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg &= ~(DWC3_DCFG_DEVADDR_MASK); -- cgit v1.2.2 From 0b9fe32deece53c9bc6d1e6d17a85ef1eb2e294b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 17 Oct 2011 08:50:39 +0300 Subject: usb: dwc3: debugfs: add support for changing port mode This makes testing a lot easier when trying to switch between host and device modes. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/debugfs.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 513a7391099f..6f1e8c98d78d 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -154,6 +154,7 @@ #define DWC3_GCTL_CLK_PIPEHALF (2) #define DWC3_GCTL_CLK_MASK (3) +#define DWC3_GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12) #define DWC3_GCTL_PRTCAPDIR(n) (n << 12) #define DWC3_GCTL_PRTCAP_HOST 1 #define DWC3_GCTL_PRTCAP_DEVICE 2 diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index da1ad77d8d51..fc49334a01cf 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -405,6 +405,80 @@ static const struct file_operations dwc3_regdump_fops = { .release = single_release, }; +static int dwc3_mode_show(struct seq_file *s, void *unused) +{ + struct dwc3 *dwc = s->private; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&dwc->lock, flags); + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + spin_unlock_irqrestore(&dwc->lock, flags); + + switch (DWC3_GCTL_PRTCAP(reg)) { + case DWC3_GCTL_PRTCAP_HOST: + seq_printf(s, "host\n"); + break; + case DWC3_GCTL_PRTCAP_DEVICE: + seq_printf(s, "device\n"); + break; + case DWC3_GCTL_PRTCAP_OTG: + seq_printf(s, "OTG\n"); + break; + default: + seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg)); + } + + return 0; +} + +static int dwc3_mode_open(struct inode *inode, struct file *file) +{ + return single_open(file, dwc3_mode_show, inode->i_private); +} + +static ssize_t dwc3_mode_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct dwc3 *dwc = s->private; + unsigned long flags; + u32 reg; + char buf[32]; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + spin_lock_irqsave(&dwc->lock, flags); + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + spin_unlock_irqrestore(&dwc->lock, flags); + + reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); + + if (!strncmp(buf, "host", 4)) + reg |= DWC3_GCTL_PRTCAP(DWC3_GCTL_PRTCAP_HOST); + + if (!strncmp(buf, "device", 6)) + reg |= DWC3_GCTL_PRTCAP(DWC3_GCTL_PRTCAP_DEVICE); + + if (!strncmp(buf, "otg", 3)) + reg |= DWC3_GCTL_PRTCAP(DWC3_GCTL_PRTCAP_OTG); + + spin_lock_irqsave(&dwc->lock, flags); + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + spin_unlock_irqrestore(&dwc->lock, flags); + + return count; +} + +static const struct file_operations dwc3_mode_fops = { + .open = dwc3_mode_open, + .write = dwc3_mode_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + int __devinit dwc3_debugfs_init(struct dwc3 *dwc) { struct dentry *root; @@ -425,6 +499,14 @@ int __devinit dwc3_debugfs_init(struct dwc3 *dwc) ret = PTR_ERR(file); goto err1; } + + file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, + dwc, &dwc3_mode_fops); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto err1; + } + return 0; err1: -- cgit v1.2.2 From 9cc9bcd5b3e8efa45accf2ccb59f13c8de85a0ce Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 18 Oct 2011 18:00:26 +0300 Subject: usb: dwc3: ep0: handle unexpected XferNotReady events Sometimes the host might be trying to initiate Data or Status phase for an older Control transfer. In such situations we must STALL that transfer and restart the state machine rather than letting such situation go through the wire. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 24b447e40bc7..36482f45b3b1 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -733,6 +733,41 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc, static void dwc3_ep0_xfernotready(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { + /* + * This part is very tricky: If we has just handled + * XferNotReady(Setup) and we're now expecting a + * XferComplete but, instead, we receive another + * XferNotReady(Setup), we should STALL and restart + * the state machine. + * + * In all other cases, we just continue waiting + * for the XferComplete event. + * + * We are a little bit unsafe here because we're + * not trying to ensure that last event was, indeed, + * XferNotReady(Setup). + * + * Still, we don't expect any condition where that + * should happen and, even if it does, it would be + * another error condition. + */ + if (dwc->ep0_next_event == DWC3_EP0_COMPLETE) { + switch (event->status) { + case DEPEVT_STATUS_CONTROL_SETUP: + dev_vdbg(dwc->dev, "Unexpected XferNotReady(Setup)\n"); + dwc3_ep0_stall_and_restart(dwc); + break; + case DEPEVT_STATUS_CONTROL_DATA: + /* FALLTHROUGH */ + case DEPEVT_STATUS_CONTROL_STATUS: + /* FALLTHROUGH */ + default: + dev_vdbg(dwc->dev, "waiting for XferComplete\n"); + } + + return; + } + switch (event->status) { case DEPEVT_STATUS_CONTROL_SETUP: dev_vdbg(dwc->dev, "Control Setup\n"); -- cgit v1.2.2 From 7ae4fc4dc835033067096639bd26416b3df744c7 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 19 Oct 2011 19:39:50 +0200 Subject: usb: dwc3: add a platform device alias We can have three modules here: dwc3.ko, dwc3-omap.ko and dwc3-pci.ko. The later have already ids-aliases for probing and is fine. The omap module has alias for DT but lacks alias for the "native" platform_device. Maybe we should get rid of it and stick to the DT name? Both glue modules create a new device for which the dwc3.ko module is responsible and that one lacks the platform alias. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 1 + drivers/usb/dwc3/dwc3-omap.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 410835e28cf6..d55d84db2be7 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -523,6 +523,7 @@ static struct platform_driver dwc3_driver = { }, }; +MODULE_ALIAS("platform:dwc3"); MODULE_AUTHOR("Felipe Balbi "); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver"); diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 7bcf6775a1a1..0bdc5e9095b7 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -384,6 +384,7 @@ static struct platform_driver dwc3_omap_driver = { }, }; +MODULE_ALIAS("platform:omap-dwc3"); MODULE_AUTHOR("Felipe Balbi "); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer"); -- cgit v1.2.2 From c5537ea531d83b7d02ce2d52a5e2b90f526449d0 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 20 Oct 2011 18:43:07 +0200 Subject: usb: dwc3: debugfs: hold the lock in during mode change The read and write operation is atomic and we need no locking around this operations. What we need however is a lock that is held which ensures that the content of the DWC3_GCTL has not been changed. With this, the conten may have been change changed after the first but before our write back. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debugfs.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index fc49334a01cf..b5370e781500 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -451,7 +451,6 @@ static ssize_t dwc3_mode_write(struct file *file, spin_lock_irqsave(&dwc->lock, flags); reg = dwc3_readl(dwc->regs, DWC3_GCTL); - spin_unlock_irqrestore(&dwc->lock, flags); reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); @@ -464,7 +463,6 @@ static ssize_t dwc3_mode_write(struct file *file, if (!strncmp(buf, "otg", 3)) reg |= DWC3_GCTL_PRTCAP(DWC3_GCTL_PRTCAP_OTG); - spin_lock_irqsave(&dwc->lock, flags); dwc3_writel(dwc->regs, DWC3_GCTL, reg); spin_unlock_irqrestore(&dwc->lock, flags); -- cgit v1.2.2 From bd178f2d62e8e2feb6b544c08d7d1dd92254201d Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 20 Oct 2011 18:43:10 +0200 Subject: usb: dwc3: host: remove unused includes None of these are required atm. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/host.c | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 9b300a26fceb..7cfe211b6c37 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -35,23 +35,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include #include "core.h" -#include "io.h" - -#include "debug.h" static struct resource generic_resources[] = { { -- cgit v1.2.2 From 0cc7a519c424c8f07d5ef5e3928b7a07446c5303 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 18 Oct 2011 19:13:28 +0200 Subject: usb: dwc3: reset pending status flag in error case If we stall and restart we have to reset also this flag to 0 as there is nothing pending anymore. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 36482f45b3b1..900627cf8603 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -224,6 +224,7 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) dwc3_gadget_giveback(dep, req, -ECONNRESET); } + dwc->ep0_status_pending = 0; dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); } -- cgit v1.2.2 From 8ee6270c7f0aeba07355eee82d687efcd8ca9d39 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 18 Oct 2011 19:13:29 +0200 Subject: usb: dwc3: remove special status request handling in ep0 The GetStatus (STD)-request is handled the driver and uses a tiny hack to send the two bytes long answer. This patch removes the custom hack uses the normal usb_ep_queue() for that. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 -- drivers/usb/dwc3/ep0.c | 31 ++++++++----------------------- 2 files changed, 8 insertions(+), 25 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 6f1e8c98d78d..bebb5e1530a0 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -575,7 +575,6 @@ struct dwc3_hwparams { * @mode: mode of operation * @is_selfpowered: true when we are selfpowered * @three_stage_setup: set if we perform a three phase setup - * @ep0_status_pending: ep0 status response without a req is pending * @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 @@ -630,7 +629,6 @@ struct dwc3 { unsigned is_selfpowered:1; unsigned three_stage_setup:1; - unsigned ep0_status_pending:1; unsigned ep0_bounced:1; unsigned ep0_expect_in:1; unsigned start_config_issued:1; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 900627cf8603..13c898b4cc1d 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -191,8 +191,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, /* we share one TRB for ep0/1 */ if (!list_empty(&dwc->eps[0]->request_list) || - !list_empty(&dwc->eps[1]->request_list) || - dwc->ep0_status_pending) { + !list_empty(&dwc->eps[1]->request_list)) { ret = -EBUSY; goto out; } @@ -224,7 +223,6 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) dwc3_gadget_giveback(dep, req, -ECONNRESET); } - dwc->ep0_status_pending = 0; dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); } @@ -255,13 +253,9 @@ static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le) return NULL; } -static void dwc3_ep0_send_status_response(struct dwc3 *dwc) +static void dwc3_ep0_status_cmpl(struct usb_ep *ep, struct usb_request *req) { - dwc3_ep0_start_trans(dwc, 1, dwc->setup_buf_addr, - dwc->ep0_usb_req.length, - DWC3_TRBCTL_CONTROL_DATA); } - /* * ch 9.4.5 */ @@ -304,9 +298,10 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl response_pkt = (__le16 *) dwc->setup_buf; *response_pkt = cpu_to_le16(usb_status); dwc->ep0_usb_req.length = sizeof(*response_pkt); - dwc->ep0_status_pending = 1; - - return 0; + dwc->ep0_usb_req.dma = dwc->setup_buf_addr; + dwc->ep0_usb_req.complete = dwc3_ep0_status_cmpl; + return usb_ep_queue(&dwc->eps[1]->endpoint, &dwc->ep0_usb_req, + GFP_ATOMIC); } static int dwc3_ep0_handle_feature(struct dwc3 *dwc, @@ -567,13 +562,8 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; - if (!dwc->ep0_status_pending) { - r = next_request(&dwc->eps[0]->request_list); - ur = &r->request; - } else { - ur = &dwc->ep0_usb_req; - dwc->ep0_status_pending = 0; - } + r = next_request(&dwc->eps[0]->request_list); + ur = &r->request; dwc3_trb_to_nat(dwc->ep0_trb, &trb); @@ -665,11 +655,6 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc, dep = dwc->eps[0]; dwc->ep0state = EP0_DATA_PHASE; - if (dwc->ep0_status_pending) { - dwc3_ep0_send_status_response(dwc); - return; - } - if (list_empty(&dep->request_list)) { dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n"); dep->flags |= DWC3_EP_PENDING_REQUEST; -- cgit v1.2.2 From 8300dd236e957429acfb36be0ce8fe276dbe823c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 18 Oct 2011 13:54:01 +0300 Subject: usb: dwc3: move dwc3 device ID bitmap to core.c if we want to support situations where we have both SoC and PCIe versions of the IP on the same platform, we need to have sequential numbers between them, otherwise we will still have name collisions. Because of that, we need to move dwc3_get/put_device_id() to core.c and export that symbol to be used by glue layers. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 42 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/dwc3/core.h | 3 +++ drivers/usb/dwc3/dwc3-pci.c | 45 +++++---------------------------------------- 3 files changed, 50 insertions(+), 40 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index d55d84db2be7..83e382b4ae28 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -63,6 +63,48 @@ static char *maximum_speed = "super"; module_param(maximum_speed, charp, 0); MODULE_PARM_DESC(maximum_speed, "Maximum supported speed."); +/* -------------------------------------------------------------------------- */ + +#define DWC3_DEVS_POSSIBLE 32 + +static DECLARE_BITMAP(dwc3_devs, DWC3_DEVS_POSSIBLE); + +int dwc3_get_device_id(void) +{ + int id; + +again: + id = find_first_zero_bit(dwc3_devs, DWC3_DEVS_POSSIBLE); + if (id < DWC3_DEVS_POSSIBLE) { + int old; + + old = test_and_set_bit(id, dwc3_devs); + if (old) + goto again; + } else { + pr_err("dwc3: no space for new device\n"); + id = -ENOMEM; + } + + return 0; +} +EXPORT_SYMBOL_GPL(dwc3_get_device_id); + +void dwc3_put_device_id(int id) +{ + int ret; + + if (id < 0) + return; + + ret = test_bit(id, dwc3_devs); + WARN(!ret, "dwc3: ID %d not in use\n", id); + clear_bit(id, dwc3_devs); +} +EXPORT_SYMBOL_GPL(dwc3_put_device_id); + +/* -------------------------------------------------------------------------- */ + /** * dwc3_core_soft_reset - Issues core soft reset and PHY reset * @dwc: pointer to our context structure diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index bebb5e1530a0..95d5a87b4091 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -793,4 +793,7 @@ void dwc3_host_exit(struct dwc3 *dwc); int dwc3_gadget_init(struct dwc3 *dwc); void dwc3_gadget_exit(struct dwc3 *dwc); +extern int dwc3_get_device_id(void); +extern void dwc3_put_device_id(int id); + #endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 193f1bd90d59..cd1429f168c2 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -42,52 +42,17 @@ #include #include +#include "core.h" + /* FIXME define these in */ #define PCI_VENDOR_ID_SYNOPSYS 0x16c3 #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd -#define DWC3_PCI_DEVS_POSSIBLE 32 - struct dwc3_pci { struct device *dev; struct platform_device *dwc3; }; -static DECLARE_BITMAP(dwc3_pci_devs, DWC3_PCI_DEVS_POSSIBLE); - -static int dwc3_pci_get_device_id(struct dwc3_pci *glue) -{ - int id; - -again: - id = find_first_zero_bit(dwc3_pci_devs, DWC3_PCI_DEVS_POSSIBLE); - if (id < DWC3_PCI_DEVS_POSSIBLE) { - int old; - - old = test_and_set_bit(id, dwc3_pci_devs); - if (old) - goto again; - } else { - dev_err(glue->dev, "no space for new device\n"); - id = -ENOMEM; - } - - return 0; -} - -static void dwc3_pci_put_device_id(struct dwc3_pci *glue, int id) -{ - int ret; - - if (id < 0) - return; - - ret = test_bit(id, dwc3_pci_devs); - WARN(!ret, "Device: %s\nID %d not in use\n", - dev_driver_string(glue->dev), id); - clear_bit(id, dwc3_pci_devs); -} - static int __devinit dwc3_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) { @@ -114,7 +79,7 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci, pci_set_power_state(pci, PCI_D0); pci_set_master(pci); - devid = dwc3_pci_get_device_id(glue); + devid = dwc3_get_device_id(); if (devid < 0) goto err2; @@ -163,7 +128,7 @@ err4: platform_device_put(dwc3); err3: - dwc3_pci_put_device_id(glue, devid); + dwc3_put_device_id(devid); err2: pci_disable_device(pci); @@ -179,7 +144,7 @@ static void __devexit dwc3_pci_remove(struct pci_dev *pci) { struct dwc3_pci *glue = pci_get_drvdata(pci); - dwc3_pci_put_device_id(glue, glue->dwc3->id); + dwc3_put_device_id(glue->dwc3->id); platform_device_unregister(glue->dwc3); pci_set_drvdata(pci, NULL); pci_disable_device(pci); -- cgit v1.2.2 From 5ddcee27c19e36711992a0e6ed3249fd06faa0e7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 18 Oct 2011 13:58:30 +0300 Subject: usb: dwc3: omap: add multiple instances support to OMAP if we ever have an omap with multiple instances of the DWC3 IP, we need unique names for them. In order to achieve that, let's use the dwc3_get/put_device_id() calls to give us an unique device identifier. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 0bdc5e9095b7..c8a1bc53ed61 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -48,6 +48,7 @@ #include #include +#include "core.h" #include "io.h" /* @@ -200,6 +201,7 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev) struct dwc3_omap *omap; struct resource *res; + int devid; int ret = -ENOMEM; int irq; @@ -236,16 +238,20 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev) goto err1; } - dwc3 = platform_device_alloc("dwc3", -1); + devid = dwc3_get_device_id(); + if (devid < 0) + goto err2; + + dwc3 = platform_device_alloc("dwc3", devid); if (!dwc3) { dev_err(&pdev->dev, "couldn't allocate dwc3 device\n"); - goto err2; + goto err3; } context = kzalloc(resource_size(res), GFP_KERNEL); if (!context) { dev_err(&pdev->dev, "couldn't allocate dwc3 context memory\n"); - goto err3; + goto err4; } spin_lock_init(&omap->lock); @@ -299,7 +305,7 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "failed to request IRQ #%d --> %d\n", omap->irq, ret); - goto err4; + goto err5; } /* enable all IRQs */ @@ -322,26 +328,29 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev) pdev->num_resources); if (ret) { dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n"); - goto err5; + goto err6; } ret = platform_device_add(dwc3); if (ret) { dev_err(&pdev->dev, "failed to register dwc3 device\n"); - goto err5; + goto err6; } return 0; -err5: +err6: free_irq(omap->irq, omap); -err4: +err5: kfree(omap->context); -err3: +err4: platform_device_put(dwc3); +err3: + dwc3_put_device_id(devid); + err2: iounmap(base); @@ -358,6 +367,7 @@ static int __devexit dwc3_omap_remove(struct platform_device *pdev) platform_device_unregister(omap->dwc3); + dwc3_put_device_id(omap->dwc3->id); free_irq(omap->irq, omap); iounmap(omap->base); -- cgit v1.2.2 From 457d3f214f97783c392dd4d64e2427c121b1a4d6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 24 Oct 2011 12:03:13 +0300 Subject: usb: dwc3: core: drop DWC3_EVENT_BUFFERS_MAX hardware will tell us how many event buffers we need to support, so let's allocate the array dynamically too. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 6 ++++++ drivers/usb/dwc3/core.h | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 83e382b4ae28..a2db41162575 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -221,6 +221,12 @@ static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) num = DWC3_NUM_INT(dwc->hwparams.hwparams1); dwc->num_event_buffers = num; + dwc->ev_buffs = kzalloc(sizeof(*dwc->ev_buffs) * num, GFP_KERNEL); + if (!dwc->ev_buffs) { + dev_err(dwc->dev, "can't allocate event buffers array\n"); + return -ENOMEM; + } + for (i = 0; i < num; i++) { struct dwc3_event_buffer *evt; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 95d5a87b4091..d6f1b793cd04 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -53,7 +53,6 @@ /* Global constants */ #define DWC3_ENDPOINTS_NUM 32 -#define DWC3_EVENT_BUFFERS_MAX 2 #define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE #define DWC3_EVENT_TYPE_MASK 0xfe @@ -603,7 +602,7 @@ struct dwc3 { struct platform_device *xhci; struct resource *res; - struct dwc3_event_buffer *ev_buffs[DWC3_EVENT_BUFFERS_MAX]; + struct dwc3_event_buffer **ev_buffs; struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; struct usb_gadget gadget; -- cgit v1.2.2 From 1e7618d8a1ad7aac6904c3a3915bf63f411344c2 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 24 Oct 2011 12:09:39 +0300 Subject: usb: dwc3: ep0: use proper endianess in SetFeature for wIndex The first access was correct, the second was wrong. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 13c898b4cc1d..7760d00cb902 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -392,8 +392,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, case USB_RECIP_ENDPOINT: switch (wValue) { case USB_ENDPOINT_HALT: - - dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex); + dep = dwc3_wIndex_to_dep(dwc, wIndex); if (!dep) return -EINVAL; ret = __dwc3_gadget_ep_set_halt(dep, set); -- cgit v1.2.2 From c2da2ff00606ae008f0e233bd29c3307d0c3ce85 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 20 Oct 2011 19:04:16 +0200 Subject: usb: dwc3: ep0: don't use ep0in for transfers In "usb: dwc3: remove special status request handling in ep0" I simplied a few things and used the generic API for the status transfers. The bug I introcuded here is that we queue now requests to dep[1] but we don't clear that list in the stall+start case. Actually we don't need to use dep[1] at all. We only did in the past to talk to the correct endpoint (i.e. in or out). This is now take care of in a diffent place within the ep0 code. So we could queue the in transfers to dep[0] and don't use dep[1] at all. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 7760d00cb902..7da25e181889 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -190,8 +190,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, } /* we share one TRB for ep0/1 */ - if (!list_empty(&dwc->eps[0]->request_list) || - !list_empty(&dwc->eps[1]->request_list)) { + if (!list_empty(&dep->request_list)) { ret = -EBUSY; goto out; } @@ -213,8 +212,8 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) struct dwc3_ep *dep = dwc->eps[0]; /* stall is always issued on EP0 */ - __dwc3_gadget_ep_set_halt(dwc->eps[0], 1); - dwc->eps[0]->flags = DWC3_EP_ENABLED; + __dwc3_gadget_ep_set_halt(dep, 1); + dep->flags = DWC3_EP_ENABLED; if (!list_empty(&dep->request_list)) { struct dwc3_request *req; @@ -300,7 +299,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl dwc->ep0_usb_req.length = sizeof(*response_pkt); dwc->ep0_usb_req.dma = dwc->setup_buf_addr; dwc->ep0_usb_req.complete = dwc3_ep0_status_cmpl; - return usb_ep_queue(&dwc->eps[1]->endpoint, &dwc->ep0_usb_req, + return usb_ep_queue(&dwc->eps[0]->endpoint, &dwc->ep0_usb_req, GFP_ATOMIC); } @@ -552,22 +551,21 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, struct dwc3_request *r = NULL; struct usb_request *ur; struct dwc3_trb trb; - struct dwc3_ep *dep; + struct dwc3_ep *ep0; u32 transferred; u8 epnum; epnum = event->endpoint_number; - dep = dwc->eps[epnum]; + ep0 = dwc->eps[0]; dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; - r = next_request(&dwc->eps[0]->request_list); + r = next_request(&ep0->request_list); ur = &r->request; dwc3_trb_to_nat(dwc->ep0_trb, &trb); if (dwc->ep0_bounced) { - struct dwc3_ep *ep0 = dwc->eps[0]; transferred = min_t(u32, ur->length, ep0->endpoint.maxpacket - trb.length); @@ -588,7 +586,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, * seems to be case when req.length > maxpacket. Could it be? */ if (r) - dwc3_gadget_giveback(dep, r, 0); + dwc3_gadget_giveback(ep0, r, 0); } } -- cgit v1.2.2 From 3140e8cbfec18ecb9c9ef856933fdb98c09af1e8 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 31 Oct 2011 22:25:40 +0100 Subject: usb: dwc3: use a helper function for operation mode setting There are two where need to set operational mode: - during initialization while we decide to run in host,device or DRD mode - at runtime via the debugfs interface. This patch provides a new function which sets the operational mode and moves its initialiation to the mode switch instead in the gadget code itself. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 13 ++++++++++++- drivers/usb/dwc3/core.h | 2 ++ drivers/usb/dwc3/debugfs.c | 21 +++++++++------------ drivers/usb/dwc3/gadget.c | 2 -- 4 files changed, 23 insertions(+), 15 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index a2db41162575..217547514faa 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -103,7 +103,15 @@ void dwc3_put_device_id(int id) } EXPORT_SYMBOL_GPL(dwc3_put_device_id); -/* -------------------------------------------------------------------------- */ +void dwc3_set_mode(struct dwc3 *dwc, u32 mode) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); + reg |= DWC3_GCTL_PRTCAPDIR(mode); + dwc3_writel(dwc->regs, DWC3_GCTL, reg); +} /** * dwc3_core_soft_reset - Issues core soft reset and PHY reset @@ -452,6 +460,7 @@ static int __devinit dwc3_probe(struct platform_device *pdev) switch (mode) { case DWC3_MODE_DEVICE: + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) { dev_err(&pdev->dev, "failed to initialize gadget\n"); @@ -459,6 +468,7 @@ static int __devinit dwc3_probe(struct platform_device *pdev) } break; case DWC3_MODE_HOST: + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); ret = dwc3_host_init(dwc); if (ret) { dev_err(&pdev->dev, "failed to initialize host\n"); @@ -466,6 +476,7 @@ static int __devinit dwc3_probe(struct platform_device *pdev) } break; case DWC3_MODE_DRD: + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); ret = dwc3_host_init(dwc); if (ret) { dev_err(&pdev->dev, "failed to initialize host\n"); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index d6f1b793cd04..cecff5624af3 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -786,6 +786,8 @@ union dwc3_event { #define DWC3_HAS_OTG BIT(3) /* prototypes */ +void dwc3_set_mode(struct dwc3 *dwc, u32 mode); + int dwc3_host_init(struct dwc3 *dwc); void dwc3_host_exit(struct dwc3 *dwc); diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index b5370e781500..ca4be0afc33d 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -443,29 +443,26 @@ static ssize_t dwc3_mode_write(struct file *file, struct seq_file *s = file->private_data; struct dwc3 *dwc = s->private; unsigned long flags; - u32 reg; + u32 mode = 0; char buf[32]; if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; - spin_lock_irqsave(&dwc->lock, flags); - reg = dwc3_readl(dwc->regs, DWC3_GCTL); - - reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); - if (!strncmp(buf, "host", 4)) - reg |= DWC3_GCTL_PRTCAP(DWC3_GCTL_PRTCAP_HOST); + mode |= DWC3_GCTL_PRTCAP_HOST; if (!strncmp(buf, "device", 6)) - reg |= DWC3_GCTL_PRTCAP(DWC3_GCTL_PRTCAP_DEVICE); + mode |= DWC3_GCTL_PRTCAP_DEVICE; if (!strncmp(buf, "otg", 3)) - reg |= DWC3_GCTL_PRTCAP(DWC3_GCTL_PRTCAP_OTG); - - dwc3_writel(dwc->regs, DWC3_GCTL, reg); - spin_unlock_irqrestore(&dwc->lock, flags); + mode |= DWC3_GCTL_PRTCAP_OTG; + if (mode) { + spin_lock_irqsave(&dwc->lock, flags); + dwc3_set_mode(dwc, mode); + spin_unlock_irqrestore(&dwc->lock, flags); + } return count; } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index b84418e88774..fab4ee0082e2 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1158,9 +1158,7 @@ static int dwc3_gadget_start(struct usb_gadget *g, reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~DWC3_GCTL_SCALEDOWN(3); - reg &= ~DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG); reg &= ~DWC3_GCTL_DISSCRAMBLE; - reg |= DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE); switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams0)) { case DWC3_GHWPARAMS1_EN_PWROPT_CLK: -- cgit v1.2.2 From 4878a02898bab1a988206341e529997cb46e5f29 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 31 Oct 2011 22:25:41 +0100 Subject: usb: dwc3: move generic dwc3 code from gadget into core A few inits like the scale value or the removal of the DISSCRAMBLE is done in the gadget code however it touches a general register. Move this piece to the core.c file since it is likely to be requied by both, parts of the core (device and host). Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 23 +++++++++++++++++++++++ drivers/usb/dwc3/gadget.c | 24 ------------------------ 2 files changed, 23 insertions(+), 24 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 217547514faa..6910a2d14d93 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -349,6 +349,29 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) dwc3_cache_hwparams(dwc); + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~DWC3_GCTL_SCALEDOWN(3); + reg &= ~DWC3_GCTL_DISSCRAMBLE; + + switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams0)) { + case DWC3_GHWPARAMS1_EN_PWROPT_CLK: + reg &= ~DWC3_GCTL_DSBLCLKGTNG; + break; + default: + dev_dbg(dwc->dev, "No power optimization available\n"); + } + + /* + * WORKAROUND: DWC3 revisions <1.90a have a bug + * when The device fails to connect at SuperSpeed + * and falls back to high-speed mode which causes + * the device to enter in a Connect/Disconnect loop + */ + if (dwc->revision < DWC3_REVISION_190A) + reg |= DWC3_GCTL_U2RSTECN; + + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); if (ret) { dev_err(dwc->dev, "failed to allocate event buffers\n"); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index fab4ee0082e2..8aff490a1ae1 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1155,30 +1155,6 @@ static int dwc3_gadget_start(struct usb_gadget *g, dwc->gadget_driver = driver; dwc->gadget.dev.driver = &driver->driver; - reg = dwc3_readl(dwc->regs, DWC3_GCTL); - - reg &= ~DWC3_GCTL_SCALEDOWN(3); - reg &= ~DWC3_GCTL_DISSCRAMBLE; - - switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams0)) { - case DWC3_GHWPARAMS1_EN_PWROPT_CLK: - reg &= ~DWC3_GCTL_DSBLCLKGTNG; - break; - default: - dev_dbg(dwc->dev, "No power optimization available\n"); - } - - /* - * WORKAROUND: DWC3 revisions <1.90a have a bug - * when The device fails to connect at SuperSpeed - * and falls back to high-speed mode which causes - * the device to enter in a Connect/Disconnect loop - */ - if (dwc->revision < DWC3_REVISION_190A) - reg |= DWC3_GCTL_U2RSTECN; - - dwc3_writel(dwc->regs, DWC3_GCTL, reg); - reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg &= ~(DWC3_DCFG_SPEED_MASK); reg |= dwc->maximum_speed; -- cgit v1.2.2 From 25b8ff68bf1d4954d4a9dcb4862c6b6a53cb09e2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 4 Nov 2011 12:32:47 +0200 Subject: usb: dwc3: fix few coding style problems There were a few coding style issues with this driver which are now fixed: drivers/usb/dwc3/debugfs.c:48: WARNING: Use #include \ instead of drivers/usb/dwc3/debugfs.c:484: ERROR: space required \ before the open brace '{' drivers/usb/dwc3/ep0.c:261: WARNING: line over 80 characters drivers/usb/dwc3/ep0.c:287: WARNING: suspect code indent \ for conditional statements (16, 23) drivers/usb/dwc3/gadget.c:749: WARNING: line over 80 characters drivers/usb/dwc3/gadget.c:1267: WARNING: line over 80 characters drivers/usb/dwc3/gadget.h:116: WARNING: line over 80 characters drivers/usb/dwc3/io.h:42: WARNING: Use #include \ instead of Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debugfs.c | 5 ++--- drivers/usb/dwc3/ep0.c | 5 +++-- drivers/usb/dwc3/gadget.c | 10 +++++----- drivers/usb/dwc3/gadget.h | 3 ++- drivers/usb/dwc3/io.h | 2 +- 5 files changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index ca4be0afc33d..e78abb438b4b 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -44,8 +44,7 @@ #include #include #include - -#include +#include #include "core.h" #include "gadget.h" @@ -481,7 +480,7 @@ int __devinit dwc3_debugfs_init(struct dwc3 *dwc) int ret; root = debugfs_create_dir(dev_name(dwc->dev), NULL); - if (IS_ERR(root)){ + if (IS_ERR(root)) { ret = PTR_ERR(root); goto err0; } diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 7da25e181889..861a41aa87d2 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -258,7 +258,8 @@ static void dwc3_ep0_status_cmpl(struct usb_ep *ep, struct usb_request *req) /* * ch 9.4.5 */ -static int dwc3_ep0_handle_status(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +static int dwc3_ep0_handle_status(struct dwc3 *dwc, + struct usb_ctrlrequest *ctrl) { struct dwc3_ep *dep; u32 recip; @@ -285,7 +286,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl case USB_RECIP_ENDPOINT: dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex); if (!dep) - return -EINVAL; + return -EINVAL; if (dep->flags & DWC3_EP_STALL) usb_status = 1 << USB_ENDPOINT_HALT; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8aff490a1ae1..9497fa5e3921 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -745,8 +745,9 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, dep->flags |= DWC3_EP_BUSY; dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, dep->number); - if (!dep->res_trans_idx) - printk_once(KERN_ERR "%s() res_trans_idx is invalid\n", __func__); + + WARN_ON_ONCE(!dep->res_trans_idx); + return 0; } @@ -1264,11 +1265,10 @@ static int __devinit dwc3_gadget_init_endpoints(struct dwc3 *dwc) &dwc->gadget.ep_list); ret = dwc3_alloc_trb_pool(dep); - if (ret) { - dev_err(dwc->dev, "%s: failed to allocate TRB pool\n", dep->name); + if (ret) return ret; - } } + INIT_LIST_HEAD(&dep->request_list); INIT_LIST_HEAD(&dep->req_queued); } diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 5c4a56f055e9..4cdaf02ead5d 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -113,7 +113,8 @@ 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); -void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event); +void dwc3_ep0_interrupt(struct dwc3 *dwc, + const struct dwc3_event_depevt *event); void dwc3_ep0_out_start(struct dwc3 *dwc); int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, gfp_t gfp_flags); diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index bc957db1ea4b..071d561f3e68 100644 --- a/drivers/usb/dwc3/io.h +++ b/drivers/usb/dwc3/io.h @@ -39,7 +39,7 @@ #ifndef __DRIVERS_USB_DWC3_IO_H #define __DRIVERS_USB_DWC3_IO_H -#include +#include static inline u32 dwc3_readl(void __iomem *base, u32 offset) { -- cgit v1.2.2 From 8becf2704415d2bf471a0a73ae84c3cc24da8a90 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 4 Nov 2011 12:40:05 +0200 Subject: usb: dwc3: fix sparse errors sparse caught three mistakes on this driver, fix them: drivers/usb/dwc3/ep0.c:806:29: warning: duplicate const drivers/usb/dwc3/debugfs.c:481:15: warning: symbol 'dwc3_debugfs_init' \ was not declared. Should it be static? drivers/usb/dwc3/debugfs.c:518:16: warning: symbol 'dwc3_debugfs_exit' \ was not declared. Should it be static? Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debugfs.c | 1 + drivers/usb/dwc3/ep0.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index e78abb438b4b..87d403df1f3f 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -49,6 +49,7 @@ #include "core.h" #include "gadget.h" #include "io.h" +#include "debug.h" struct dwc3_register { const char *name; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 861a41aa87d2..86d7b6de68f9 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -803,7 +803,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, } void dwc3_ep0_interrupt(struct dwc3 *dwc, - const const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { u8 epnum = event->endpoint_number; -- cgit v1.2.2 From f0f2b2a2db85f99637376caf25e46623af56acad Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 2 Nov 2011 13:30:44 +0100 Subject: usb: dwc3: ep0: push ep0state into xfernotready processing We wait for the XferNotReady before we start the transfer and by then we know ep0 state in which we supposed to be. This is some cleanup work for the following patch in which we require to know the ep0 state before the transfer completes. While here, also change the argument to dwc3_ep0_do_control_status() so we don't require the complete event structure but only the required piece of information. Inspired-by: Felipe Balbi Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 86d7b6de68f9..333278c56690 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -53,9 +53,6 @@ #include "gadget.h" #include "io.h" -static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, - const struct dwc3_event_depevt *event); - static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) { switch (state) { @@ -639,7 +636,6 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, static void dwc3_ep0_do_control_setup(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { - dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); } @@ -651,7 +647,6 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc, int ret; dep = dwc->eps[0]; - dwc->ep0state = EP0_DATA_PHASE; if (list_empty(&dep->request_list)) { dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n"); @@ -665,7 +660,6 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc, req = next_request(&dep->request_list); req->direction = !!event->endpoint_number; - dwc->ep0state = EP0_DATA_PHASE; if (req->request.length == 0) { ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, dwc->ctrl_req_addr, 0, @@ -697,21 +691,23 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc, WARN_ON(ret < 0); } -static void dwc3_ep0_do_control_status(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) +static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) { + struct dwc3 *dwc = dep->dwc; u32 type; - int ret; - - dwc->ep0state = EP0_STATUS_PHASE; type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3 : DWC3_TRBCTL_CONTROL_STATUS2; - ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, + return dwc3_ep0_start_trans(dwc, dep->number, dwc->ctrl_req_addr, 0, type); +} - WARN_ON(ret < 0); +static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum) +{ + struct dwc3_ep *dep = dwc->eps[epnum]; + + WARN_ON(dwc3_ep0_start_control_status(dep)); } static void dwc3_ep0_xfernotready(struct dwc3 *dwc, @@ -755,12 +751,17 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, switch (event->status) { case DEPEVT_STATUS_CONTROL_SETUP: dev_vdbg(dwc->dev, "Control Setup\n"); + + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_do_control_setup(dwc, event); break; case DEPEVT_STATUS_CONTROL_DATA: dev_vdbg(dwc->dev, "Control Data\n"); + dwc->ep0state = EP0_DATA_PHASE; + if (dwc->ep0_next_event != DWC3_EP0_NRDY_DATA) { dev_vdbg(dwc->dev, "Expected %d got %d\n", dwc->ep0_next_event, @@ -790,6 +791,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, case DEPEVT_STATUS_CONTROL_STATUS: dev_vdbg(dwc->dev, "Control Status\n"); + dwc->ep0state = EP0_STATUS_PHASE; + if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) { dev_vdbg(dwc->dev, "Expected %d got %d\n", dwc->ep0_next_event, @@ -798,7 +801,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, dwc3_ep0_stall_and_restart(dwc); return; } - dwc3_ep0_do_control_status(dwc, event); + dwc3_ep0_do_control_status(dwc, event->endpoint_number); } } -- cgit v1.2.2 From 5bdb1dcc63304a407e70020c1118fca1642bebaa Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 2 Nov 2011 13:30:45 +0100 Subject: usb: dwc3: ep0: handle delayed_status again Since the re-worked ep0 handling (which uses HW's hints to recognize the ep0 status) we lost the delayed status handling. This is used by the file and mass storage gadget to gain some extra time so setup its internal status before it can proceed further requests. In particular the storage gadget does nothing on USB_REQ_SET_CONFIGURATION but wakes up a thread which handles the request. If the udc driver continues ep0 handling before the thread did its work then then endpoint is not yet configured and further requests will fail. Once the gadget is ready, it will enqueue an empty packet which is used for synchronization. In order to fix this issue, the patch does the following: Set ->delayed_status once the delayed_status has been notices and do not continue on the next XferNotReady event. We will continues ep0 processing once the gadget enqueued the zero packet for synchronization. A cleaner approach would be to enforce the gadget to enqueue an empty (zero) request even for the status phase but this would do for now. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/ep0.c | 26 +++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index cecff5624af3..dc2db165412b 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -631,6 +631,7 @@ struct dwc3 { unsigned ep0_bounced:1; unsigned ep0_expect_in:1; unsigned start_config_issued:1; + unsigned delayed_status:1; enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 333278c56690..314acb289d23 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -48,11 +48,14 @@ #include #include +#include #include "core.h" #include "gadget.h" #include "io.h" +static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum); + static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) { switch (state) { @@ -122,6 +125,8 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, struct dwc3_request *req) { + struct dwc3 *dwc = dep->dwc; + u32 type; int ret = 0; req->request.actual = 0; @@ -140,9 +145,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * IRQ we were waiting for is long gone. */ if (dep->flags & DWC3_EP_PENDING_REQUEST) { - struct dwc3 *dwc = dep->dwc; unsigned direction; - u32 type; direction = !!(dep->flags & DWC3_EP0_DIR_IN); @@ -162,6 +165,10 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, req->request.dma, req->request.length, type); dep->flags &= ~(DWC3_EP_PENDING_REQUEST | DWC3_EP0_DIR_IN); + + } else if (dwc->delayed_status && (dwc->ep0state == EP0_STATUS_PHASE)) { + dwc->delayed_status = false; + dwc3_ep0_do_control_status(dwc, 1); } return ret; @@ -211,6 +218,7 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) /* stall is always issued on EP0 */ __dwc3_gadget_ep_set_halt(dep, 1); dep->flags = DWC3_EP_ENABLED; + dwc->delayed_status = false; if (!list_empty(&dep->request_list)) { struct dwc3_request *req; @@ -472,8 +480,10 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) if (!cfg) dwc->dev_state = DWC3_ADDRESS_STATE; break; + default: + ret = -EINVAL; } - return 0; + return ret; } static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) @@ -536,6 +546,9 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, else ret = dwc3_ep0_delegate_req(dwc, ctrl); + if (ret == USB_GADGET_DELAYED_STATUS) + dwc->delayed_status = true; + if (ret >= 0) return; @@ -801,6 +814,13 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, dwc3_ep0_stall_and_restart(dwc); return; } + + if (dwc->delayed_status) { + WARN_ON_ONCE(event->endpoint_number != 1); + dev_vdbg(dwc->dev, "Mass Storage delayed status\n"); + return; + } + dwc3_ep0_do_control_status(dwc, event->endpoint_number); } } -- cgit v1.2.2 From d39ee7be2aaf0a53d7b5f43c13571bac95f7cc0c Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 3 Nov 2011 10:32:20 +0100 Subject: usb: dwc3: gadget: return early in dwc3_cleanup_done_reqs() This patch avoids the compiler spitting out the following warning: |drivers/usb/dwc3/gadget.c:1304: warning: 'trb' is used uninitialized \ in this function This is only uninitialized if the list of to-cleanup TRBs is empty which should not be the case because we call this functions once a transfer completed so it should be on list. In order to make the warning disappear we return early. This should never happen and the WARN_ON_ONCE(1) is there in case it happens so we can investigate what went wrong. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 9497fa5e3921..85cf392365cb 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1308,8 +1308,10 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, do { req = next_request(&dep->req_queued); - if (!req) - break; + if (!req) { + WARN_ON_ONCE(1); + return 1; + } dwc3_trb_to_nat(req->trb, &trb); -- cgit v1.2.2 From fae2b904aa85beecd0950026de28921ae65fb3da Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 14 Oct 2011 13:00:30 +0300 Subject: usb: dwc3: workaround: U1/U2 -> U0 transiton RTL revisions <1.83a have an issue where, depending on the link partner, the USB link might do multiple entry/exit of low power states before a transfer takes place causing degraded throughput. The suggested workaround is to clear bits 12:9 of DCTL register if we see a transition from U1|U2 to U0 and only re-enable that on a transfer complete IRQ and we have no pending transfers on any of the enabled endpoints. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 ++ drivers/usb/dwc3/gadget.c | 76 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 76 insertions(+), 2 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index dc2db165412b..b901a4d3b068 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -569,6 +569,7 @@ struct dwc3_hwparams { * @regs_size: address space size * @irq: IRQ number * @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 @@ -614,6 +615,7 @@ struct dwc3 { int irq; u32 num_event_buffers; + u32 u1u2; u32 maximum_speed; u32 revision; u32 mode; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 85cf392365cb..0a6deeaf3377 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1376,6 +1376,31 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, dep->flags &= ~DWC3_EP_BUSY; dep->res_trans_idx = 0; } + + /* + * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround. + * See dwc3_gadget_linksts_change_interrupt() for 1st half. + */ + if (dwc->revision < DWC3_REVISION_183A) { + u32 reg; + int i; + + for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) { + struct dwc3_ep *dep = dwc->eps[i]; + + if (!(dep->flags & DWC3_EP_ENABLED)) + continue; + + if (!list_empty(&dep->req_queued)) + return; + } + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg |= dwc->u1u2; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + dwc->u1u2 = 0; + } } static void dwc3_gadget_start_isoc(struct dwc3 *dwc, @@ -1794,8 +1819,55 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, unsigned int evtinfo) { - /* The fith bit says SuperSpeed yes or no. */ - dwc->link_state = evtinfo & DWC3_LINK_STATE_MASK; + enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; + + /* + * WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending + * on the link partner, the USB session might do multiple entry/exit + * of low power states before a transfer takes place. + * + * Due to this problem, we might experience lower throughput. The + * suggested workaround is to disable DCTL[12:9] bits if we're + * transitioning from U1/U2 to U0 and enable those bits again + * after a transfer completes and there are no pending transfers + * on any of the enabled endpoints. + * + * This is the first half of that workaround. + * + * Refers to: + * + * STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us + * core send LGO_Ux entering U0 + */ + if (dwc->revision < DWC3_REVISION_183A) { + if (next == DWC3_LINK_STATE_U0) { + u32 u1u2; + u32 reg; + + switch (dwc->link_state) { + case DWC3_LINK_STATE_U1: + case DWC3_LINK_STATE_U2: + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + u1u2 = reg & (DWC3_DCTL_INITU2ENA + | DWC3_DCTL_ACCEPTU2ENA + | DWC3_DCTL_INITU1ENA + | DWC3_DCTL_ACCEPTU1ENA); + + if (!dwc->u1u2) + dwc->u1u2 = reg & u1u2; + + reg &= ~u1u2; + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + break; + default: + /* do nothing */ + break; + } + } + } + + dwc->link_state = next; dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state); } -- cgit v1.2.2 From 05870c5ba2002c7d49adf8875cca49ee062af894 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 14 Oct 2011 14:51:38 +0300 Subject: usb: dwc3: workaround: missing USB3 Reset event DWC3 revisions <1.90a have an issue which would cause a missing USB3 Reset event. In such cases, it's suggested that we follow the steps of a normal USB3 Reset on Connection Done Event. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0a6deeaf3377..6704a52c9f12 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1756,6 +1756,22 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) switch (speed) { case DWC3_DCFG_SUPERSPEED: + /* + * WORKAROUND: DWC3 revisions <1.90a have an issue which + * would cause a missing USB3 Reset event. + * + * In such situations, we should force a USB3 Reset + * event by calling our dwc3_gadget_reset_interrupt() + * routine. + * + * Refers to: + * + * STAR#9000483510: RTL: SS : USB3 reset event may + * not be generated always when the link enters poll + */ + if (dwc->revision < DWC3_REVISION_190A) + dwc3_gadget_reset_interrupt(dwc); + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); dwc->gadget.ep0->maxpacket = 512; dwc->gadget.speed = USB_SPEED_SUPER; -- cgit v1.2.2 From df62df56e13d73cb0dd4c54649d4fe13557128f8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 14 Oct 2011 15:11:49 +0300 Subject: usb: dwc3: workaround: missing disconnect event DWC3 revisions <1.88a have an issue which would case a missing Disconnect event if cable is disconnected while there's a Setup packet pending the FIFO. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 ++ drivers/usb/dwc3/ep0.c | 3 +++ drivers/usb/dwc3/gadget.c | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index b901a4d3b068..836cf9942a4f 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -578,6 +578,7 @@ struct dwc3_hwparams { * @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 * @ep0_next_event: hold the next expected event * @ep0state: state of endpoint zero * @link_state: link state @@ -633,6 +634,7 @@ struct dwc3 { unsigned ep0_bounced:1; unsigned ep0_expect_in:1; unsigned start_config_issued:1; + unsigned setup_packet_pending:1; unsigned delayed_status:1; enum dwc3_ep0_next ep0_next_event; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 314acb289d23..ed44525c8d62 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -625,6 +625,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; dep->flags &= ~DWC3_EP_BUSY; + dwc->setup_packet_pending = false; switch (dwc->ep0state) { case EP0_SETUP_PHASE: @@ -726,6 +727,8 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum) static void dwc3_ep0_xfernotready(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { + dwc->setup_packet_pending = true; + /* * This part is very tricky: If we has just handled * XferNotReady(Setup) and we're now expecting a diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 6704a52c9f12..7c98b3f2e6a7 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1640,6 +1640,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) dwc->start_config_issued = false; dwc->gadget.speed = USB_SPEED_UNKNOWN; + dwc->setup_packet_pending = false; } static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on) @@ -1676,6 +1677,37 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) dev_vdbg(dwc->dev, "%s\n", __func__); + /* + * WORKAROUND: DWC3 revisions <1.88a have an issue which + * would cause a missing Disconnect Event if there's a + * pending Setup Packet in the FIFO. + * + * There's no suggested workaround on the official Bug + * report, which states that "unless the driver/application + * is doing any special handling of a disconnect event, + * there is no functional issue". + * + * Unfortunately, it turns out that we _do_ some special + * handling of a disconnect event, namely complete all + * pending transfers, notify gadget driver of the + * disconnection, and so on. + * + * Our suggested workaround is to follow the Disconnect + * Event steps here, instead, based on a setup_packet_pending + * flag. Such flag gets set whenever we have a XferNotReady + * event on EP0 and gets cleared on XferComplete for the + * same endpoint. + * + * Refers to: + * + * STAR#9000466709: RTL: Device : Disconnect event not + * generated if setup packet pending in FIFO + */ + if (dwc->revision < DWC3_REVISION_188A) { + if (dwc->setup_packet_pending) + dwc3_gadget_disconnect_interrupt(dwc); + } + /* Enable PHYs */ dwc3_gadget_usb2_phy_power(dwc, true); dwc3_gadget_usb3_phy_power(dwc, true); -- cgit v1.2.2 From 68380876d674e8e0a810128971772e38201491ba Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 18 Nov 2011 21:31:14 +0200 Subject: usb: dwc3: omap: move to module_platform_driver the new module_platform_driver macro is a helper for modules which just register and unregister the platform_driver. It allows us to delete a few duplicated lines. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index c8a1bc53ed61..5809bf413d76 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -399,14 +399,4 @@ MODULE_AUTHOR("Felipe Balbi "); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer"); -static int __devinit dwc3_omap_init(void) -{ - return platform_driver_register(&dwc3_omap_driver); -} -module_init(dwc3_omap_init); - -static void __exit dwc3_omap_exit(void) -{ - platform_driver_unregister(&dwc3_omap_driver); -} -module_exit(dwc3_omap_exit); +module_platform_driver(dwc3_omap_driver); -- cgit v1.2.2 From 164d773168d7f09ecd46d9ce9b07f194ea97bf33 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 24 Nov 2011 11:22:05 +0100 Subject: usb: dwc3: use correct hwparam register for power mgm check We mask the correct bits within the wrong register. The power optimization mode is stored hwparam1 register and not in hwparam0. Reported-by: Partha Basak Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 6910a2d14d93..455bb1e748d7 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -353,7 +353,7 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) reg &= ~DWC3_GCTL_SCALEDOWN(3); reg &= ~DWC3_GCTL_DISSCRAMBLE; - switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams0)) { + switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) { case DWC3_GHWPARAMS1_EN_PWROPT_CLK: reg &= ~DWC3_GCTL_DSBLCLKGTNG; break; -- cgit v1.2.2 From e0ce0b0a0ae5a31ee96b38a7c5390f867634b4f6 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 25 Nov 2011 12:03:46 +0100 Subject: usb: dwc3: ep0: use dwc3_request for ep0 requsts instead of usb_request Instead of special functions and shortcuts for sending our internal answers to the host we started doing what the gadget does and used the public API for this. Since we only were using a few fields the usb_request was enough. Later added the list handling in order to synchronize the host / gadget events and now we require to have the dwc3_request struct around our usb_request or else we touch memory that does not belong to us. So this patch does this. Reported-by: Partha Basak Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 16 +++++++++++++++- drivers/usb/dwc3/ep0.c | 8 ++++---- drivers/usb/dwc3/gadget.h | 13 ------------- 3 files changed, 19 insertions(+), 18 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 836cf9942a4f..da523f5648a5 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -548,6 +548,20 @@ struct dwc3_hwparams { /* HWPARAMS1 */ #define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) +struct dwc3_request { + struct usb_request request; + struct list_head list; + struct dwc3_ep *dep; + + u8 epnum; + struct dwc3_trb_hw *trb; + dma_addr_t trb_dma; + + unsigned direction:1; + unsigned mapped:1; + unsigned queued:1; +}; + /** * struct dwc3 - representation of our controller * @ctrl_req: usb control request which is used for ep0 @@ -596,7 +610,7 @@ struct dwc3 { dma_addr_t ep0_trb_addr; dma_addr_t setup_buf_addr; dma_addr_t ep0_bounce_addr; - struct usb_request ep0_usb_req; + struct dwc3_request ep0_usb_req; /* device lock */ spinlock_t lock; struct device *dev; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index ed44525c8d62..1ba86a114655 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -302,10 +302,10 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, response_pkt = (__le16 *) dwc->setup_buf; *response_pkt = cpu_to_le16(usb_status); - dwc->ep0_usb_req.length = sizeof(*response_pkt); - dwc->ep0_usb_req.dma = dwc->setup_buf_addr; - dwc->ep0_usb_req.complete = dwc3_ep0_status_cmpl; - return usb_ep_queue(&dwc->eps[0]->endpoint, &dwc->ep0_usb_req, + dwc->ep0_usb_req.request.length = sizeof(*response_pkt); + dwc->ep0_usb_req.request.dma = dwc->setup_buf_addr; + dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl; + return usb_ep_queue(&dwc->eps[0]->endpoint, &dwc->ep0_usb_req.request, GFP_ATOMIC); } diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 4cdaf02ead5d..d97f467d41cc 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -79,19 +79,6 @@ struct dwc3_gadget_ep_cmd_params { /* -------------------------------------------------------------------------- */ -struct dwc3_request { - struct usb_request request; - struct list_head list; - struct dwc3_ep *dep; - - u8 epnum; - struct dwc3_trb_hw *trb; - dma_addr_t trb_dma; - - unsigned direction:1; - unsigned mapped:1; - unsigned queued:1; -}; #define to_dwc3_request(r) (container_of(r, struct dwc3_request, request)) static inline struct dwc3_request *next_request(struct list_head *list) -- cgit v1.2.2 From e2617796053437df586c53e462076f74bcf268b4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 29 Nov 2011 10:35:47 +0200 Subject: usb: dwc3: ep0: fix GetStatus handling (again) previous commit fixed part of it but it was still calling usb_ep_queue() from IRQ context without loosing locks. That cannot be done otherwise we will have a recursive locking. Also, we need to assign the 'dep' pointer on dwc->ep0_usb_req otherwise we will have a NULL pointer dereference on dwc3_map_buffer_to_dma(). Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 1ba86a114655..d6bfc73dedbd 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -302,11 +302,14 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, response_pkt = (__le16 *) dwc->setup_buf; *response_pkt = cpu_to_le16(usb_status); + + dep = dwc->eps[0]; + dwc->ep0_usb_req.dep = dep; dwc->ep0_usb_req.request.length = sizeof(*response_pkt); dwc->ep0_usb_req.request.dma = dwc->setup_buf_addr; dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl; - return usb_ep_queue(&dwc->eps[0]->endpoint, &dwc->ep0_usb_req.request, - GFP_ATOMIC); + + return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req); } static int dwc3_ep0_handle_feature(struct dwc3 *dwc, -- cgit v1.2.2 From c90bfaece97c18d1ad66b9d4c717b1cb55a647ad Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 29 Nov 2011 13:11:21 +0200 Subject: usb: dwc3: gadget: fix stream enable bit ep->max_streams is a mere hint to the gadget driver that 'ep' supports stream handling. Using that as a decision variable for enabling streams was my worst brain-fart to date. Instead, we should check from the Superspeed Endpoint Companion Descriptor if the endpoint has requested streams. For that we need a little re-factoring but it is now correct. Debugged-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/gadget.c | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index da523f5648a5..9e57f8e9bf17 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -348,6 +348,7 @@ struct dwc3_ep { u32 free_slot; u32 busy_slot; const struct usb_endpoint_descriptor *desc; + const struct usb_ss_ep_comp_descriptor *comp_desc; struct dwc3 *dwc; unsigned flags; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7c98b3f2e6a7..026c53cf1645 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -251,7 +251,8 @@ 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_endpoint_descriptor *desc, + const struct usb_ss_ep_comp_descriptor *comp_desc) { struct dwc3_gadget_ep_cmd_params params; @@ -264,7 +265,8 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN | DWC3_DEPCFG_XFER_NOT_READY_EN; - if (usb_endpoint_xfer_bulk(desc) && dep->endpoint.max_streams) { + if (comp_desc && USB_SS_MAX_STREAMS(comp_desc->bmAttributes) + && usb_endpoint_xfer_bulk(desc)) { params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE | DWC3_DEPCFG_STREAM_EVENT_EN; dep->stream_capable = true; @@ -317,7 +319,8 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep) * Caller should take care of locking */ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, - const struct usb_endpoint_descriptor *desc) + const struct usb_endpoint_descriptor *desc, + const struct usb_ss_ep_comp_descriptor *comp_desc) { struct dwc3 *dwc = dep->dwc; u32 reg; @@ -329,7 +332,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, return ret; } - ret = dwc3_gadget_set_ep_config(dwc, dep, desc); + ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc); if (ret) return ret; @@ -343,6 +346,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, return ret; dep->desc = desc; + dep->comp_desc = comp_desc; dep->type = usb_endpoint_type(desc); dep->flags |= DWC3_EP_ENABLED; @@ -405,6 +409,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) dep->stream_capable = false; dep->desc = NULL; + dep->comp_desc = NULL; dep->type = 0; dep->flags = 0; @@ -473,7 +478,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, dev_vdbg(dwc->dev, "Enabling %s\n", dep->name); spin_lock_irqsave(&dwc->lock, flags); - ret = __dwc3_gadget_ep_enable(dep, desc); + ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc); spin_unlock_irqrestore(&dwc->lock, flags); return ret; @@ -1167,14 +1172,14 @@ 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); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); goto err0; } dep = dwc->eps[1]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); goto err1; @@ -1830,14 +1835,14 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) dwc3_gadget_disable_phy(dwc, dwc->gadget.speed); dep = dwc->eps[0]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); 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); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); return; -- cgit v1.2.2 From 68d3e668d245bb8300c7c6ddbc8508ddfe352e0f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 8 Dec 2011 13:56:27 +0200 Subject: usb: dwc3: ep0: fix for possible early delayed_status There is a very small possibility (previously unimagined by us) that the whole Mass Storage delayed status happens rather early, before we even get our XferNotReady event. In that case, we will be queueing a request to ep0 while we're still on Setup Phase and we would be waiting for another usb_ep_queue() forever. Handle such cases by clearing dwc->delayed_status so that we start control status from the next XferNotReady like there was no wait for Delayed Status. Tested against Linux 3.2-rc3 and USB30CV tool from USB-IF (on a Windows XP with USB3 PCIe card). Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index d6bfc73dedbd..2f51de57593a 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -165,10 +165,13 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, req->request.dma, req->request.length, type); dep->flags &= ~(DWC3_EP_PENDING_REQUEST | DWC3_EP0_DIR_IN); - - } else if (dwc->delayed_status && (dwc->ep0state == EP0_STATUS_PHASE)) { + } else if (dwc->delayed_status) { dwc->delayed_status = false; - dwc3_ep0_do_control_status(dwc, 1); + + if (dwc->ep0state == EP0_STATUS_PHASE) + dwc3_ep0_do_control_status(dwc, 1); + else + dev_dbg(dwc->dev, "too early for delayed status\n"); } return ret; -- cgit v1.2.2 From 961906edb549c95f4cc33e4f3dbfd0fcc364954d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 20 Dec 2011 15:37:21 +0200 Subject: usb: dwc3: gadget: move us to Default State after reset After a bus reset, we should move our state to Default, in order to be able to re-enumerate again. I only managed to trigger this problem with g_ether by removing the cable after a few transfers had been completed. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 026c53cf1645..cb6870cbc49a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1713,6 +1713,9 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) dwc3_gadget_disconnect_interrupt(dwc); } + /* after reset -> Default State */ + dwc->dev_state = DWC3_DEFAULT_STATE; + /* Enable PHYs */ dwc3_gadget_usb2_phy_power(dwc, true); dwc3_gadget_usb3_phy_power(dwc, true); -- cgit v1.2.2 From c71fc37c191747ea1f00424e84f96c1f88e52bfc Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 22 Nov 2011 11:37:34 +0200 Subject: usb: dwc3: gadget: re-factor dwc3_prepare_trbs() In order to make it easier to add SG support, let's split the big loop out to its own function. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 146 +++++++++++++++++++++++++--------------------- 1 file changed, 78 insertions(+), 68 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 580272042a33..317fc7d04694 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -539,6 +539,78 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep, kfree(req); } +/** + * dwc3_prepare_one_trb - setup one TRB from one request + * @dep: endpoint for which this request is prepared + * @req: dwc3_request pointer + */ +static int dwc3_prepare_one_trb(struct dwc3_ep *dep, + struct dwc3_request *req, unsigned last) +{ + struct dwc3_trb_hw *trb_hw; + struct dwc3_trb trb; + + unsigned int cur_slot; + + trb_hw = &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) && + usb_endpoint_xfer_isoc(dep->desc)) + return 0; + + dwc3_gadget_move_request_queued(req); + memset(&trb, 0, sizeof(trb)); + + req->trb = trb_hw; + + if (usb_endpoint_xfer_isoc(dep->desc)) { + trb.isp_imi = true; + trb.csp = true; + } else { + trb.lst = last; + } + + if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable) + trb.sid_sofn = req->request.stream_id; + + switch (usb_endpoint_type(dep->desc)) { + case USB_ENDPOINT_XFER_CONTROL: + trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP; + break; + + case USB_ENDPOINT_XFER_ISOC: + trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; + + /* IOC every DWC3_TRB_NUM / 4 so we can refill */ + if (!(cur_slot % (DWC3_TRB_NUM / 4))) + trb.ioc = last; + break; + + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + trb.trbctl = DWC3_TRBCTL_NORMAL; + break; + default: + /* + * This is only possible with faulty memory because we + * checked it already :) + */ + BUG(); + } + + trb.length = req->request.length; + trb.bplh = req->request.dma; + trb.hwo = true; + + dwc3_trb_to_hw(&trb, trb_hw); + req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw); + + return 0; +} + /* * dwc3_prepare_trbs - setup TRBs from requests * @dep: endpoint for which requests are being prepared @@ -552,14 +624,14 @@ static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) { struct dwc3_request *req, *n, *ret = NULL; - struct dwc3_trb_hw *trb_hw; - struct dwc3_trb trb; u32 trbs_left; + unsigned int last_one = 0; BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); /* the first request must not be queued */ trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK; + /* * if busy & slot are equal than it is either full or empty. If we are * starting to proceed requests then we are empty. Otherwise we ar @@ -594,25 +666,11 @@ static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep, return NULL; list_for_each_entry_safe(req, n, &dep->request_list, list) { - unsigned int last_one = 0; - unsigned int cur_slot; - - trb_hw = &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) && - usb_endpoint_xfer_isoc(dep->desc)) - continue; - - dwc3_gadget_move_request_queued(req); - memset(&trb, 0, sizeof(trb)); trbs_left--; - /* Is our TRB pool empty? */ if (!trbs_left) last_one = 1; + /* Is this the last request? */ if (list_empty(&dep->request_list)) last_one = 1; @@ -625,57 +683,9 @@ static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep, * While we're debugging the problem, as a workaround to * multiple TRBs handling, use only one TRB at a time. */ - last_one = 1; - - req->trb = trb_hw; - if (!ret) - ret = req; - - trb.bplh = req->request.dma; - - if (usb_endpoint_xfer_isoc(dep->desc)) { - trb.isp_imi = true; - trb.csp = true; - } else { - trb.lst = last_one; - } - - if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable) - trb.sid_sofn = req->request.stream_id; - - switch (usb_endpoint_type(dep->desc)) { - case USB_ENDPOINT_XFER_CONTROL: - trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP; - break; - - case USB_ENDPOINT_XFER_ISOC: - trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; - - /* IOC every DWC3_TRB_NUM / 4 so we can refill */ - if (!(cur_slot % (DWC3_TRB_NUM / 4))) - trb.ioc = last_one; - break; - - case USB_ENDPOINT_XFER_BULK: - case USB_ENDPOINT_XFER_INT: - trb.trbctl = DWC3_TRBCTL_NORMAL; - break; - default: - /* - * This is only possible with faulty memory because we - * checked it already :) - */ - BUG(); - } - - trb.length = req->request.length; - trb.hwo = true; - - dwc3_trb_to_hw(&trb, trb_hw); - req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw); - - if (last_one) - break; + dwc3_prepare_one_trb(dep, req, true); + ret = req; + break; } return ret; -- cgit v1.2.2 From 68e823e24aea5227eaf20d6435485e733109d113 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 28 Nov 2011 12:25:01 +0200 Subject: usb: dwc3: gadget: don't return anything on prepare trbs all that function does is setup a TRB to be sent to HW later. There's no need to return anything actually. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 317fc7d04694..984580a18a38 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -544,7 +544,7 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep, * @dep: endpoint for which this request is prepared * @req: dwc3_request pointer */ -static int dwc3_prepare_one_trb(struct dwc3_ep *dep, +static void dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_request *req, unsigned last) { struct dwc3_trb_hw *trb_hw; @@ -559,7 +559,7 @@ static int dwc3_prepare_one_trb(struct dwc3_ep *dep, /* Skip the LINK-TRB on ISOC */ if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) && usb_endpoint_xfer_isoc(dep->desc)) - return 0; + return; dwc3_gadget_move_request_queued(req); memset(&trb, 0, sizeof(trb)); @@ -607,8 +607,6 @@ static int dwc3_prepare_one_trb(struct dwc3_ep *dep, dwc3_trb_to_hw(&trb, trb_hw); req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw); - - return 0; } /* @@ -620,10 +618,9 @@ static int dwc3_prepare_one_trb(struct dwc3_ep *dep, * transfers. The functions returns once there are not more TRBs available or * it run out of requests. */ -static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep, - bool starting) +static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) { - struct dwc3_request *req, *n, *ret = NULL; + struct dwc3_request *req, *n; u32 trbs_left; unsigned int last_one = 0; @@ -639,7 +636,7 @@ static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep, */ if (!trbs_left) { if (!starting) - return NULL; + return; trbs_left = DWC3_TRB_NUM; /* * In case we start from scratch, we queue the ISOC requests @@ -663,7 +660,7 @@ static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep, /* The last TRB is a link TRB, not used for xfer */ if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->desc)) - return NULL; + return; list_for_each_entry_safe(req, n, &dep->request_list, list) { trbs_left--; @@ -684,11 +681,8 @@ static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep, * multiple TRBs handling, use only one TRB at a time. */ dwc3_prepare_one_trb(dep, req, true); - ret = req; break; } - - return ret; } static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, @@ -717,11 +711,13 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, /* req points to the first request which will be sent */ req = next_request(&dep->req_queued); } else { + dwc3_prepare_trbs(dep, start_new); + /* * req points to the first request where HWO changed * from 0 to 1 */ - req = dwc3_prepare_trbs(dep, start_new); + req = next_request(&dep->req_queued); } if (!req) { dep->flags |= DWC3_EP_PENDING_REQUEST; -- cgit v1.2.2 From 42f8eb7a1087442e9710ce75b355c0f28aadbf96 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 28 Nov 2011 12:27:17 +0200 Subject: usb: dwc3: gadget: don't force 'LST' always the LST bit is to be set on the last of a series of consecutive TRBs. We had a workaround for a problem where data would get corrupted but that doesn't happen anymore. It's likely that it was caused by some FPGA instability during development phase. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 984580a18a38..0292b0617d72 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -672,16 +672,10 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) if (list_empty(&dep->request_list)) last_one = 1; - /* - * FIXME we shouldn't need to set LST bit always but we are - * facing some weird problem with the Hardware where it doesn't - * complete even though it has been previously started. - * - * While we're debugging the problem, as a workaround to - * multiple TRBs handling, use only one TRB at a time. - */ - dwc3_prepare_one_trb(dep, req, true); - break; + dwc3_prepare_one_trb(dep, req, last_one); + + if (last_one) + break; } } -- cgit v1.2.2 From eeb720fb21d61dfc3aac780e721150998ef603af Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 28 Nov 2011 12:46:59 +0200 Subject: usb: dwc3: gadget: add support for SG lists add support for SG lists on dwc3 driver. With this we can e.g. use VFS layer's SG lists on storage gadgets so that we can start bigger transfers and improve throughput. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 115 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 98 insertions(+), 17 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0292b0617d72..ddc7a43592c0 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -65,6 +65,22 @@ void dwc3_map_buffer_to_dma(struct dwc3_request *req) return; } + if (req->request.num_sgs) { + int mapped; + + mapped = dma_map_sg(dwc->dev, req->request.sg, + req->request.num_sgs, + req->direction ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + if (mapped < 0) { + dev_err(dwc->dev, "failed to map SGs\n"); + return; + } + + req->request.num_mapped_sgs = mapped; + return; + } + if (req->request.dma == DMA_ADDR_INVALID) { req->request.dma = dma_map_single(dwc->dev, req->request.buf, req->request.length, req->direction @@ -82,6 +98,17 @@ void dwc3_unmap_buffer_from_dma(struct dwc3_request *req) return; } + if (req->request.num_mapped_sgs) { + req->request.dma = DMA_ADDR_INVALID; + dma_unmap_sg(dwc->dev, req->request.sg, + req->request.num_sgs, + req->direction ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + + req->request.num_mapped_sgs = 0; + return; + } + if (req->mapped) { dma_unmap_single(dwc->dev, req->request.dma, req->request.length, req->direction @@ -97,7 +124,11 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, struct dwc3 *dwc = dep->dwc; if (req->queued) { - dep->busy_slot++; + if (req->request.num_mapped_sgs) + dep->busy_slot += req->request.num_mapped_sgs; + else + 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 @@ -108,6 +139,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, dep->busy_slot++; } list_del(&req->list); + req->trb = NULL; if (req->request.status == -EINPROGRESS) req->request.status = status; @@ -545,13 +577,20 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep, * @req: dwc3_request pointer */ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, - struct dwc3_request *req, unsigned last) + struct dwc3_request *req, dma_addr_t dma, + unsigned length, unsigned last, unsigned chain) { + struct dwc3 *dwc = dep->dwc; struct dwc3_trb_hw *trb_hw; 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_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK]; cur_slot = dep->free_slot; dep->free_slot++; @@ -561,15 +600,18 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, usb_endpoint_xfer_isoc(dep->desc)) return; - dwc3_gadget_move_request_queued(req); memset(&trb, 0, sizeof(trb)); - - req->trb = trb_hw; + if (!req->trb) { + dwc3_gadget_move_request_queued(req); + req->trb = trb_hw; + req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw); + } if (usb_endpoint_xfer_isoc(dep->desc)) { trb.isp_imi = true; trb.csp = true; } else { + trb.chn = chain; trb.lst = last; } @@ -601,12 +643,11 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, BUG(); } - trb.length = req->request.length; - trb.bplh = req->request.dma; + trb.length = length; + trb.bplh = dma; trb.hwo = true; dwc3_trb_to_hw(&trb, trb_hw); - req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw); } /* @@ -663,19 +704,58 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) return; list_for_each_entry_safe(req, n, &dep->request_list, list) { - trbs_left--; + unsigned length; + dma_addr_t dma; - if (!trbs_left) - last_one = 1; + if (req->request.num_mapped_sgs > 0) { + struct usb_request *request = &req->request; + struct scatterlist *sg = request->sg; + struct scatterlist *s; + int i; - /* Is this the last request? */ - if (list_empty(&dep->request_list)) - last_one = 1; + for_each_sg(sg, s, request->num_mapped_sgs, i) { + unsigned chain = true; - dwc3_prepare_one_trb(dep, req, last_one); + length = sg_dma_len(s); + dma = sg_dma_address(s); - if (last_one) - break; + if (i == (request->num_mapped_sgs - 1) + || sg_is_last(s)) { + last_one = true; + chain = false; + } + + trbs_left--; + if (!trbs_left) + last_one = true; + + if (last_one) + chain = false; + + dwc3_prepare_one_trb(dep, req, dma, length, + last_one, chain); + + if (last_one) + break; + } + } else { + dma = req->request.dma; + length = req->request.length; + trbs_left--; + + if (!trbs_left) + last_one = 1; + + /* Is this the last request? */ + if (list_is_last(&req->list, &dep->request_list)) + last_one = 1; + + dwc3_prepare_one_trb(dep, req, dma, length, + last_one, false); + + if (last_one) + break; + } } } @@ -1989,6 +2069,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) dwc->gadget.max_speed = USB_SPEED_SUPER; dwc->gadget.speed = USB_SPEED_UNKNOWN; dwc->gadget.dev.parent = dwc->dev; + dwc->gadget.sg_supported = true; dma_set_coherent_mask(&dwc->gadget.dev, dwc->dev->coherent_dma_mask); -- cgit v1.2.2 From 18b7ede5f7ee2092aedcb578d3ac30bd5d4fc23c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 2 Jan 2012 13:35:41 +0200 Subject: usb: ch9: fix up MaxStreams helper According to USB 3.0 Specification Table 9-22, if bmAttributes [4:0] are set to zero, it means "no streams supported", but the way this helper was defined on Linux, we will *always* have one stream which might cause several problems. For example on DWC3, we would tell the controller endpoint has streams enabled and yet start transfers with Stream ID set to 0, which would goof up the host side. While doing that, convert the macro to an inline function due to the different checks we now need. Signed-off-by: Felipe Balbi Signed-off-by: Sarah Sharp Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4c6bedad51fd..a696bde53222 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -297,8 +297,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN | DWC3_DEPCFG_XFER_NOT_READY_EN; - if (comp_desc && USB_SS_MAX_STREAMS(comp_desc->bmAttributes) - && usb_endpoint_xfer_bulk(desc)) { + if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) { params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE | DWC3_DEPCFG_STREAM_EVENT_EN; dep->stream_capable = true; -- cgit v1.2.2 From 5632c827cbd3617613530ba0e99344192d0a31ca Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 23 Dec 2011 18:39:28 +0100 Subject: drivers/usb/dwc3/dwc3-pci.c: introduce missing kfree Glue needs to be freed on exiting the function in an error case. Furthermore, pci, which is the first argument to the probe function should not be freed before leaveing the function, as it is reused at the call site. So the free of pci is changed to free glue instead. A simplified version of the semantic match that finds the problem is as follows: (http://coccinelle.lip6.fr) // @r exists@ local idexpression x; statement S; identifier f1; position p1,p2; expression *ptr != NULL; @@ x@p1 = \(kmalloc\|kzalloc\|kcalloc\)(...); ... if (x == NULL) S <... when != x when != if (...) { <+...x...+> } x->f1 ...> ( return \(0\|<+...x...+>\|ptr\); | return@p2 ...; ) @script:python@ p1 << r.p1; p2 << r.p2; @@ print "* file: %s kmalloc %s return %s" % (p1[0].file,p1[0].line,p2[0].line) // Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/dwc3') diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index cd1429f168c2..64e1f7c67b08 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -134,7 +134,7 @@ err2: pci_disable_device(pci); err1: - kfree(pci); + kfree(glue); err0: return ret; -- cgit v1.2.2