summaryrefslogtreecommitdiffstats
path: root/drivers/usb/dwc3
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-01-09 12:09:47 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2012-01-09 12:09:47 -0800
commit55b81e6f2795484ea8edf5805c95c007cacfa736 (patch)
treec3724975107857fcc03b5dd649c462e4f72397be /drivers/usb/dwc3
parent5983faf942f260023e547f3c5f38c1033c35cc9b (diff)
parent08e87d0d773dc9ca5faf4c3306e238ed0ea129b0 (diff)
Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
* 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (232 commits) USB: Add USB-ID for Multiplex RC serial adapter to cp210x.c xhci: Clean up 32-bit build warnings. USB: update documentation for usbmon usb: usb-storage doesn't support dynamic id currently, the patch disables the feature to fix an oops drivers/usb/class/cdc-acm.c: clear dangling pointer drivers/usb/dwc3/dwc3-pci.c: introduce missing kfree drivers/usb/host/isp1760-if.c: introduce missing kfree usb: option: add ZD Incorporated HSPA modem usb: ch9: fix up MaxStreams helper USB: usb-skeleton.c: cleanup open_count USB: usb-skeleton.c: fix open/disconnect race xhci: Properly handle COMP_2ND_BW_ERR USB: remove dead code from suspend/resume path USB: add quirk for another camera drivers: usb: wusbcore: Fix dependency for USB_WUSB xhci: Better debugging for critical host errors. xhci: Be less verbose during URB cancellation. xhci: Remove debugging about ring structure allocation. xhci: Remove debugging about toggling cycle bits. xhci: Remove debugging for individual transfers. ...
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r--drivers/usb/dwc3/Kconfig5
-rw-r--r--drivers/usb/dwc3/Makefile6
-rw-r--r--drivers/usb/dwc3/core.c209
-rw-r--r--drivers/usb/dwc3/core.h62
-rw-r--r--drivers/usb/dwc3/debugfs.c83
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c43
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c51
-rw-r--r--drivers/usb/dwc3/ep0.c160
-rw-r--r--drivers/usb/dwc3/gadget.c440
-rw-r--r--drivers/usb/dwc3/gadget.h29
-rw-r--r--drivers/usb/dwc3/host.c102
-rw-r--r--drivers/usb/dwc3/io.h2
12 files changed, 871 insertions, 321 deletions
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 3c1d67d324fd..d8f741f9e56e 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -1,7 +1,10 @@
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
+ 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..900ae74357f1 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -4,10 +4,8 @@ ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG
obj-$(CONFIG_USB_DWC3) += dwc3.o
dwc3-y := core.o
-
-ifneq ($(CONFIG_USB_GADGET_DWC3),)
- dwc3-y += gadget.o ep0.o
-endif
+dwc3-y += host.o
+dwc3-y += gadget.o ep0.o
ifneq ($(CONFIG_DEBUG_FS),)
dwc3-y += debugfs.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 600d82348511..7c9df630dbe4 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -59,6 +59,60 @@
#include "debug.h"
+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);
+
+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
* @dwc: pointer to our context structure
@@ -150,7 +204,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);
@@ -162,17 +216,25 @@ 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;
+
+ 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;
@@ -198,7 +260,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,
@@ -221,7 +283,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);
@@ -285,8 +347,32 @@ 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);
+
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg &= ~DWC3_GCTL_SCALEDOWN(3);
+ reg &= ~DWC3_GCTL_DISSCRAMBLE;
+
+ switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
+ 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");
ret = -ENOMEM;
@@ -299,8 +385,6 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc)
goto err1;
}
- dwc3_cache_hwparams(dwc);
-
return 0;
err1:
@@ -320,15 +404,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");
@@ -343,6 +429,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) {
@@ -370,6 +458,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);
@@ -380,13 +479,44 @@ 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_DEVICE:
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_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;
+ 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");
+ goto err4;
+ }
+ 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");
+ goto err4;
+ }
+
+ ret = dwc3_gadget_init(dwc);
+ if (ret) {
+ 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) {
@@ -399,8 +529,21 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
return 0;
err5:
- if (features & DWC3_HAS_PERIPHERAL)
+ switch (mode) {
+ case DWC3_MODE_DEVICE:
+ dwc3_gadget_exit(dwc);
+ break;
+ case DWC3_MODE_HOST:
+ dwc3_host_exit(dwc);
+ break;
+ case DWC3_MODE_DRD:
+ dwc3_host_exit(dwc);
dwc3_gadget_exit(dwc);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
err4:
dwc3_core_exit(dwc);
@@ -420,10 +563,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);
@@ -432,8 +573,21 @@ static int __devexit dwc3_remove(struct platform_device *pdev)
dwc3_debugfs_exit(dwc);
- if (features & DWC3_HAS_PERIPHERAL)
+ switch (dwc->mode) {
+ case DWC3_MODE_DEVICE:
+ dwc3_gadget_exit(dwc);
+ break;
+ case DWC3_MODE_HOST:
+ dwc3_host_exit(dwc);
+ break;
+ case DWC3_MODE_DRD:
+ dwc3_host_exit(dwc);
dwc3_gadget_exit(dwc);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
dwc3_core_exit(dwc);
release_mem_region(res->start, resource_size(res));
@@ -443,30 +597,15 @@ 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_ALIAS("platform:dwc3");
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver");
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 29a8e1679e12..9e57f8e9bf17 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -41,6 +41,7 @@
#include <linux/device.h>
#include <linux/spinlock.h>
+#include <linux/ioport.h>
#include <linux/list.h>
#include <linux/dma-mapping.h>
#include <linux/mm.h>
@@ -52,7 +53,6 @@
/* Global constants */
#define DWC3_ENDPOINTS_NUM 32
-#define DWC3_EVENT_BUFFERS_NUM 2
#define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE
#define DWC3_EVENT_TYPE_MASK 0xfe
@@ -153,6 +153,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
@@ -347,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;
@@ -536,6 +538,31 @@ 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)
+
+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
@@ -549,19 +576,24 @@ 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
* @regs: base address for our registers
* @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
* @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
+ * @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
@@ -579,12 +611,15 @@ 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;
- struct dwc3_event_buffer *ev_buffs[DWC3_EVENT_BUFFERS_NUM];
+ struct platform_device *xhci;
+ struct resource *res;
+
+ struct dwc3_event_buffer **ev_buffs;
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
struct usb_gadget gadget;
@@ -595,7 +630,11 @@ struct dwc3 {
int irq;
+ u32 num_event_buffers;
+ u32 u1u2;
+ u32 maximum_speed;
u32 revision;
+ u32 mode;
#define DWC3_REVISION_173A 0x5533173a
#define DWC3_REVISION_175A 0x5533175a
@@ -607,10 +646,11 @@ 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;
+ unsigned setup_packet_pending:1;
+ unsigned delayed_status:1;
enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state;
@@ -765,4 +805,16 @@ union dwc3_event {
#define DWC3_HAS_XHCI BIT(1)
#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);
+
+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/debugfs.c b/drivers/usb/dwc3/debugfs.c
index fcfa91517ea1..433c97c15fc5 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -44,12 +44,12 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/delay.h>
-
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "core.h"
#include "gadget.h"
#include "io.h"
+#include "debug.h"
#define dump_register(nm) \
{ \
@@ -395,6 +395,75 @@ 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 mode = 0;
+ char buf[32];
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ if (!strncmp(buf, "host", 4))
+ mode |= DWC3_GCTL_PRTCAP_HOST;
+
+ if (!strncmp(buf, "device", 6))
+ mode |= DWC3_GCTL_PRTCAP_DEVICE;
+
+ if (!strncmp(buf, "otg", 3))
+ 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;
+}
+
+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;
@@ -402,7 +471,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;
}
@@ -415,6 +484,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:
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index 062552b5fc8a..3274ac8f1200 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -48,6 +48,7 @@
#include <linux/io.h>
#include <linux/module.h>
+#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-omap", -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);
@@ -384,18 +394,9 @@ static struct platform_driver dwc3_omap_driver = {
},
};
+module_platform_driver(dwc3_omap_driver);
+
+MODULE_ALIAS("platform:omap-dwc3");
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
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);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index f77c00042685..64e1f7c67b08 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -42,52 +42,17 @@
#include <linux/pci.h>
#include <linux/platform_device.h>
+#include "core.h"
+
/* FIXME define these in <linux/pci_ids.h> */
#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,11 +79,11 @@ 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;
- 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;
@@ -163,13 +128,13 @@ err4:
platform_device_put(dwc3);
err3:
- dwc3_pci_put_device_id(glue, devid);
+ dwc3_put_device_id(devid);
err2:
pci_disable_device(pci);
err1:
- kfree(pci);
+ kfree(glue);
err0:
return ret;
@@ -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);
@@ -196,7 +161,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),
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 69a4e43ddf59..2f51de57593a 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -48,13 +48,13 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
#include "core.h"
#include "gadget.h"
#include "io.h"
-static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
- const struct dwc3_event_depevt *event);
+static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum);
static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
{
@@ -125,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;
@@ -143,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);
@@ -165,6 +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->delayed_status = false;
+
+ 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;
@@ -190,9 +197,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) {
+ if (!list_empty(&dep->request_list)) {
ret = -EBUSY;
goto out;
}
@@ -214,8 +219,9 @@ 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;
+ dwc->delayed_status = false;
if (!list_empty(&dep->request_list)) {
struct dwc3_request *req;
@@ -254,17 +260,14 @@ 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
*/
-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;
@@ -291,7 +294,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;
@@ -302,10 +305,14 @@ 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;
+ 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 __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
}
static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
@@ -396,8 +403,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);
@@ -422,8 +428,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);
@@ -473,8 +486,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)
@@ -537,6 +552,9 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
else
ret = dwc3_ep0_delegate_req(dwc, ctrl);