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/gadget.c | |
| 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/gadget.c')
| -rw-r--r-- | drivers/usb/dwc3/gadget.c | 197 | 
1 files changed, 147 insertions, 50 deletions
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,  |