diff options
| author | Greg Kroah-Hartman <gregkh@suse.de> | 2011-12-12 15:19:40 -0800 | 
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-12-12 15:19:53 -0800 | 
| commit | 007d00d4c11b30b8fd7ff23b9d5aed3743e41f74 (patch) | |
| tree | d5c307ba2ee0e2f56fbc284db21b8bccf95d8a40 /drivers/usb/dwc3 | |
| parent | c91043adaf50ef13609003120f3471783460fb71 (diff) | |
| parent | 68d3e668d245bb8300c7c6ddbc8508ddfe352e0f (diff) | |
| download | olio-linux-3.10-007d00d4c11b30b8fd7ff23b9d5aed3743e41f74.tar.xz olio-linux-3.10-007d00d4c11b30b8fd7ff23b9d5aed3743e41f74.zip  | |
Merge branch 'for-next/dwc3' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
* 'for-next/dwc3' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb: (392 commits)
  usb: dwc3: ep0: fix for possible early delayed_status
  usb: dwc3: gadget: fix stream enable bit
  usb: dwc3: ep0: fix GetStatus handling (again)
  usb: dwc3: ep0: use dwc3_request for ep0 requsts instead of usb_request
  usb: dwc3: use correct hwparam register for power mgm check
  usb: dwc3: omap: move to module_platform_driver
  usb: dwc3: workaround: missing disconnect event
  usb: dwc3: workaround: missing USB3 Reset event
  usb: dwc3: workaround: U1/U2 -> U0 transiton
  usb: dwc3: gadget: return early in dwc3_cleanup_done_reqs()
  usb: dwc3: ep0: handle delayed_status again
  usb: dwc3: ep0: push ep0state into xfernotready processing
  usb: dwc3: fix sparse errors
  usb: dwc3: fix few coding style problems
  usb: dwc3: move generic dwc3 code from gadget into core
  usb: dwc3: use a helper function for operation mode setting
  usb: dwc3: ep0: don't use ep0in for transfers
  usb: dwc3: ep0: use proper endianess in SetFeature for wIndex
  usb: dwc3: core: drop DWC3_EVENT_BUFFERS_MAX
  usb: dwc3: omap: add multiple instances support to OMAP
  ...
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 | 29 | ||||
| -rw-r--r-- | drivers/usb/dwc3/dwc3-pci.c | 49 | ||||
| -rw-r--r-- | drivers/usb/dwc3/ep0.c | 160 | ||||
| -rw-r--r-- | drivers/usb/dwc3/gadget.c | 197 | ||||
| -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, 705 insertions, 228 deletions
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 3c1d67d324f..d8f741f9e56 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 593d1dbc465..900ae74357f 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 717ebc9ff94..455bb1e748d 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 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 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) { @@ -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 29a8e1679e1..9e57f8e9bf1 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 da1ad77d8d5..87d403df1f3 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"  struct dwc3_register {  	const char	*name; @@ -405,6 +405,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; @@ -412,7 +481,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;  	} @@ -425,6 +494,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 4e27d5bf40a..3274ac8f120 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); @@ -386,6 +396,7 @@ 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"); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index f77c0004268..cd1429f168c 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,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); @@ -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 69a4e43ddf5..2f51de57593 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); +	if (ret == USB_GADGET_DELAYED_STATUS) +		dwc->delayed_status = true; +  	if (ret >= 0)  		return; @@ -550,27 +568,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; -	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(&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); @@ -591,7 +603,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);  	}  } @@ -619,6 +631,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: @@ -643,7 +656,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);  } @@ -655,12 +667,6 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc,  	int			ret;  	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"); @@ -674,7 +680,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, @@ -706,35 +711,79 @@ 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,  		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 +	 * 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"); + +		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, @@ -764,6 +813,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, @@ -772,12 +823,19 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,  			dwc3_ep0_stall_and_restart(dwc);  			return;  		} -		dwc3_ep0_do_control_status(dwc, event); + +		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);  	}  }  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; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 25dbd8614e7..026c53cf164 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; @@ -745,8 +750,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;  } @@ -1155,35 +1161,9 @@ 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_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: -		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 |= DWC3_DCFG_SUPERSPEED; +	reg |= dwc->maximum_speed;  	dwc3_writel(dwc->regs, DWC3_DCFG, reg);  	dwc->start_config_issued = false; @@ -1192,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; @@ -1290,11 +1270,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);  	} @@ -1334,8 +1313,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); @@ -1400,6 +1381,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, @@ -1639,6 +1645,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) @@ -1675,6 +1682,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); @@ -1755,6 +1793,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; @@ -1781,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; @@ -1818,8 +1872,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);  } @@ -1925,7 +2026,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); @@ -2076,7 +2177,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 +2184,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, diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 71145a449d9..d97f467d41c 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) @@ -110,23 +97,11 @@ 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); -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/host.c b/drivers/usb/dwc3/host.c new file mode 100644 index 00000000000..7cfe211b6c3 --- /dev/null +++ b/drivers/usb/dwc3/host.c @@ -0,0 +1,102 @@ +/** + * host.c - DesignWare USB3 DRD Controller Host Glue + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions, and the following disclaimer, + *    without modification. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + *    to endorse or promote products derived from this software without + *    specific prior written permission. + * + * 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 <linux/platform_device.h> + +#include "core.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); +} diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index bc957db1ea4..071d561f3e6 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 <asm/io.h> +#include <linux/io.h>  static inline u32 dwc3_readl(void __iomem *base, u32 offset)  {  |