diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-09 12:09:47 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-09 12:09:47 -0800 |
commit | 55b81e6f2795484ea8edf5805c95c007cacfa736 (patch) | |
tree | c3724975107857fcc03b5dd649c462e4f72397be /drivers/usb/dwc3 | |
parent | 5983faf942f260023e547f3c5f38c1033c35cc9b (diff) | |
parent | 08e87d0d773dc9ca5faf4c3306e238ed0ea129b0 (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/Kconfig | 5 | ||||
-rw-r--r-- | drivers/usb/dwc3/Makefile | 6 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.c | 209 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.h | 62 | ||||
-rw-r--r-- | drivers/usb/dwc3/debugfs.c | 83 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-omap.c | 43 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-pci.c | 51 | ||||
-rw-r--r-- | drivers/usb/dwc3/ep0.c | 160 | ||||
-rw-r--r-- | drivers/usb/dwc3/gadget.c | 440 | ||||
-rw-r--r-- | drivers/usb/dwc3/gadget.h | 29 | ||||
-rw-r--r-- | drivers/usb/dwc3/host.c | 102 | ||||
-rw-r--r-- | drivers/usb/dwc3/io.h | 2 |
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); |