diff options
Diffstat (limited to 'drivers/usb/dwc3/gadget.c')
| -rw-r--r-- | drivers/usb/dwc3/gadget.c | 365 | 
1 files changed, 217 insertions, 148 deletions
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index ec70df7aba1..58fdfad96b4 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -100,6 +100,23 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)  	int		retries = 10000;  	u32		reg; +	/* +	 * Wait until device controller is ready. Only applies to 1.94a and +	 * later RTL. +	 */ +	if (dwc->revision >= DWC3_REVISION_194A) { +		while (--retries) { +			reg = dwc3_readl(dwc->regs, DWC3_DSTS); +			if (reg & DWC3_DSTS_DCNRD) +				udelay(5); +			else +				break; +		} + +		if (retries <= 0) +			return -ETIMEDOUT; +	} +  	reg = dwc3_readl(dwc->regs, DWC3_DCTL);  	reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; @@ -107,7 +124,15 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)  	reg |= DWC3_DCTL_ULSTCHNGREQ(state);  	dwc3_writel(dwc->regs, DWC3_DCTL, reg); +	/* +	 * The following code is racy when called from dwc3_gadget_wakeup, +	 * and is not needed, at least on newer versions +	 */ +	if (dwc->revision >= DWC3_REVISION_194A) +		return 0; +  	/* wait for a change in DSTS */ +	retries = 10000;  	while (--retries) {  		reg = dwc3_readl(dwc->regs, DWC3_DSTS); @@ -265,8 +290,8 @@ static const char *dwc3_gadget_ep_cmd_string(u8 cmd)  		return "Clear Stall";  	case DWC3_DEPCMD_SETSTALL:  		return "Set Stall"; -	case DWC3_DEPCMD_GETSEQNUMBER: -		return "Get Data Sequence Number"; +	case DWC3_DEPCMD_GETEPSTATE: +		return "Get Endpoint State";  	case DWC3_DEPCMD_SETTRANSFRESOURCE:  		return "Set Endpoint Transfer Resource";  	case DWC3_DEPCMD_SETEPCONFIG: @@ -414,7 +439,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,  	params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))  		| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc)) -		| DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst); +		| DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst - 1);  	params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN  		| DWC3_DEPCFG_XFER_NOT_READY_EN; @@ -530,9 +555,37 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)  {  	struct dwc3_request		*req; -	if (!list_empty(&dep->req_queued)) +	if (!list_empty(&dep->req_queued)) {  		dwc3_stop_active_transfer(dwc, dep->number); +		/* +		 * NOTICE: We are violating what the Databook says about the +		 * EndTransfer command. Ideally we would _always_ wait for the +		 * EndTransfer Command Completion IRQ, but that's causing too +		 * much trouble synchronizing between us and gadget driver. +		 * +		 * We have discussed this with the IP Provider and it was +		 * suggested to giveback all requests here, but give HW some +		 * extra time to synchronize with the interconnect. We're using +		 * an arbitraty 100us delay for that. +		 * +		 * Note also that a similar handling was tested by Synopsys +		 * (thanks a lot Paul) and nothing bad has come out of it. +		 * In short, what we're doing is: +		 * +		 * - Issue EndTransfer WITH CMDIOC bit set +		 * - Wait 100us +		 * - giveback all requests to gadget driver +		 */ +		udelay(100); + +		while (!list_empty(&dep->req_queued)) { +			req = next_request(&dep->req_queued); + +			dwc3_gadget_giveback(dep, req, -ESHUTDOWN); +		} +	} +  	while (!list_empty(&dep->request_list)) {  		req = next_request(&dep->request_list); @@ -741,8 +794,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,  	case USB_ENDPOINT_XFER_ISOC:  		trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; -		/* IOC every DWC3_TRB_NUM / 4 so we can refill */ -		if (!(cur_slot % (DWC3_TRB_NUM / 4))) +		if (!req->request.no_interrupt)  			trb->ctrl |= DWC3_TRB_CTRL_IOC;  		break; @@ -958,14 +1010,42 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,  	dep->flags |= DWC3_EP_BUSY;  	if (start_new) { -		dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, +		dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,  				dep->number); -		WARN_ON_ONCE(!dep->res_trans_idx); +		WARN_ON_ONCE(!dep->resource_index);  	}  	return 0;  } +static void __dwc3_gadget_start_isoc(struct dwc3 *dwc, +		struct dwc3_ep *dep, u32 cur_uf) +{ +	u32 uf; + +	if (list_empty(&dep->request_list)) { +		dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n", +			dep->name); +		return; +	} + +	/* 4 micro frames in the future */ +	uf = cur_uf + dep->interval * 4; + +	__dwc3_gadget_kick_transfer(dep, uf, 1); +} + +static void dwc3_gadget_start_isoc(struct dwc3 *dwc, +		struct dwc3_ep *dep, const struct dwc3_event_depevt *event) +{ +	u32 cur_uf, mask; + +	mask = ~(dep->interval - 1); +	cur_uf = event->parameters & mask; + +	__dwc3_gadget_start_isoc(dwc, dep, cur_uf); +} +  static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)  {  	struct dwc3		*dwc = dep->dwc; @@ -995,11 +1075,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)  	list_add_tail(&req->list, &dep->request_list); -	if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && (dep->flags & DWC3_EP_BUSY)) -		dep->flags |= DWC3_EP_PENDING_REQUEST; -  	/* -	 * There are two special cases: +	 * There are a few special cases:  	 *  	 * 1. XferNotReady with empty list of requests. We need to kick the  	 *    transfer here in that situation, otherwise we will be NAKing @@ -1008,31 +1085,46 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)  	 *    able to receive the data until the next request is queued.  	 *    The following code is handling exactly that.  	 * -	 * 2. XferInProgress on Isoc EP with an active transfer. We need to -	 *    kick the transfer here after queuing a request, otherwise the -	 *    core may not see the modified TRB(s).  	 */  	if (dep->flags & DWC3_EP_PENDING_REQUEST) {  		int	ret; -		int	start_trans = 1; -		u8	trans_idx = dep->res_trans_idx; -		if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && -				(dep->flags & DWC3_EP_BUSY)) { -			start_trans = 0; -			WARN_ON_ONCE(!trans_idx); -		} else { -			trans_idx = 0; +		ret = __dwc3_gadget_kick_transfer(dep, 0, true); +		if (ret && ret != -EBUSY) { +			struct dwc3	*dwc = dep->dwc; + +			dev_dbg(dwc->dev, "%s: failed to kick transfers\n", +					dep->name);  		} +	} -		ret = __dwc3_gadget_kick_transfer(dep, trans_idx, start_trans); +	/* +	 * 2. XferInProgress on Isoc EP with an active transfer. We need to +	 *    kick the transfer here after queuing a request, otherwise the +	 *    core may not see the modified TRB(s). +	 */ +	if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && +			(dep->flags & DWC3_EP_BUSY)) { +		WARN_ON_ONCE(!dep->resource_index); +		ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index, +				false);  		if (ret && ret != -EBUSY) {  			struct dwc3	*dwc = dep->dwc;  			dev_dbg(dwc->dev, "%s: failed to kick transfers\n",  					dep->name);  		} -	}; +	} + +	/* +	 * 3. Missed ISOC Handling. We need to start isoc transfer on the saved +	 * uframe number. +	 */ +	if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && +		(dep->flags & DWC3_EP_MISSED_ISOC)) { +			__dwc3_gadget_start_isoc(dwc, dep, dep->current_uf); +			dep->flags &= ~DWC3_EP_MISSED_ISOC; +	}  	return 0;  } @@ -1118,15 +1210,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)  	memset(¶ms, 0x00, sizeof(params));  	if (value) { -		if (dep->number == 0 || dep->number == 1) { -			/* -			 * Whenever EP0 is stalled, we will restart -			 * the state machine, thus moving back to -			 * Setup Phase -			 */ -			dwc->ep0state = EP0_SETUP_PHASE; -		} -  		ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,  			DWC3_DEPCMD_SETSTALL, ¶ms);  		if (ret) @@ -1186,7 +1269,10 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)  	dep->flags |= DWC3_EP_WEDGE;  	spin_unlock_irqrestore(&dwc->lock, flags); -	return dwc3_gadget_ep_set_halt(ep, 1); +	if (dep->number == 0 || dep->number == 1) +		return dwc3_gadget_ep0_set_halt(ep, 1); +	else +		return dwc3_gadget_ep_set_halt(ep, 1);  }  /* -------------------------------------------------------------------------- */ @@ -1204,7 +1290,7 @@ static const struct usb_ep_ops dwc3_gadget_ep0_ops = {  	.free_request	= dwc3_gadget_ep_free_request,  	.queue		= dwc3_gadget_ep0_queue,  	.dequeue	= dwc3_gadget_ep_dequeue, -	.set_halt	= dwc3_gadget_ep_set_halt, +	.set_halt	= dwc3_gadget_ep0_set_halt,  	.set_wedge	= dwc3_gadget_ep_set_wedge,  }; @@ -1280,9 +1366,13 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)  		goto out;  	} -	/* write zeroes to Link Change Request */ -	reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; -	dwc3_writel(dwc->regs, DWC3_DCTL, reg); +	/* Recent versions do this automatically */ +	if (dwc->revision < DWC3_REVISION_194A) { +		/* write zeroes to Link Change Request */ +		reg = dwc3_readl(dwc->regs, DWC3_DCTL); +		reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; +		dwc3_writel(dwc->regs, DWC3_DCTL, reg); +	}  	/* poll until Link State changes to ON */  	timeout = jiffies + msecs_to_jiffies(100); @@ -1319,16 +1409,21 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,  	return 0;  } -static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) +static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)  {  	u32			reg;  	u32			timeout = 500;  	reg = dwc3_readl(dwc->regs, DWC3_DCTL);  	if (is_on) { -		reg &= ~DWC3_DCTL_TRGTULST_MASK; -		reg |= (DWC3_DCTL_RUN_STOP -				| DWC3_DCTL_TRGTULST_RX_DET); +		if (dwc->revision <= DWC3_REVISION_187A) { +			reg &= ~DWC3_DCTL_TRGTULST_MASK; +			reg |= DWC3_DCTL_TRGTULST_RX_DET; +		} + +		if (dwc->revision >= DWC3_REVISION_194A) +			reg &= ~DWC3_DCTL_KEEP_CONNECT; +		reg |= DWC3_DCTL_RUN_STOP;  	} else {  		reg &= ~DWC3_DCTL_RUN_STOP;  	} @@ -1346,7 +1441,7 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)  		}  		timeout--;  		if (!timeout) -			break; +			return -ETIMEDOUT;  		udelay(1);  	} while (1); @@ -1354,20 +1449,23 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)  			dwc->gadget_driver  			? dwc->gadget_driver->function : "no-function",  			is_on ? "connect" : "disconnect"); + +	return 0;  }  static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)  {  	struct dwc3		*dwc = gadget_to_dwc(g);  	unsigned long		flags; +	int			ret;  	is_on = !!is_on;  	spin_lock_irqsave(&dwc->lock, flags); -	dwc3_gadget_run_stop(dwc, is_on); +	ret = dwc3_gadget_run_stop(dwc, is_on);  	spin_unlock_irqrestore(&dwc->lock, flags); -	return 0; +	return ret;  }  static int dwc3_gadget_start(struct usb_gadget *g, @@ -1468,6 +1566,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g,  	return 0;  } +  static const struct usb_gadget_ops dwc3_gadget_ops = {  	.get_frame		= dwc3_gadget_get_frame,  	.wakeup			= dwc3_gadget_wakeup, @@ -1558,6 +1657,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,  	struct dwc3_trb		*trb;  	unsigned int		count;  	unsigned int		s_pkt = 0; +	unsigned int		trb_status;  	do {  		req = next_request(&dep->req_queued); @@ -1583,9 +1683,18 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,  		if (dep->direction) {  			if (count) { -				dev_err(dwc->dev, "incomplete IN transfer %s\n", -						dep->name); -				status = -ECONNRESET; +				trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size); +				if (trb_status == DWC3_TRBSTS_MISSED_ISOC) { +					dev_dbg(dwc->dev, "incomplete IN transfer %s\n", +							dep->name); +					dep->current_uf = event->parameters & +						~(dep->interval - 1); +					dep->flags |= DWC3_EP_MISSED_ISOC; +				} else { +					dev_err(dwc->dev, "incomplete IN transfer %s\n", +							dep->name); +					status = -ECONNRESET; +				}  			}  		} else {  			if (count && (event->status & DEPEVT_STATUS_SHORT)) @@ -1604,7 +1713,8 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,  		if (s_pkt)  			break;  		if ((event->status & DEPEVT_STATUS_LST) && -				(trb->ctrl & DWC3_TRB_CTRL_LST)) +				(trb->ctrl & (DWC3_TRB_CTRL_LST | +						DWC3_TRB_CTRL_HWO)))  			break;  		if ((event->status & DEPEVT_STATUS_IOC) &&  				(trb->ctrl & DWC3_TRB_CTRL_IOC)) @@ -1657,65 +1767,6 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,  	}  } -static void dwc3_gadget_start_isoc(struct dwc3 *dwc, -		struct dwc3_ep *dep, const struct dwc3_event_depevt *event) -{ -	u32 uf, mask; - -	if (list_empty(&dep->request_list)) { -		dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n", -			dep->name); -		return; -	} - -	mask = ~(dep->interval - 1); -	uf = event->parameters & mask; -	/* 4 micro frames in the future */ -	uf += dep->interval * 4; - -	__dwc3_gadget_kick_transfer(dep, uf, 1); -} - -static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep, -		const struct dwc3_event_depevt *event) -{ -	struct dwc3 *dwc = dep->dwc; -	struct dwc3_event_depevt mod_ev = *event; - -	/* -	 * We were asked to remove one request. It is possible that this -	 * request and a few others were started together and have the same -	 * transfer index. Since we stopped the complete endpoint we don't -	 * know how many requests were already completed (and not yet) -	 * reported and how could be done (later). We purge them all until -	 * the end of the list. -	 */ -	mod_ev.status = DEPEVT_STATUS_LST; -	dwc3_cleanup_done_reqs(dwc, dep, &mod_ev, -ESHUTDOWN); -	dep->flags &= ~DWC3_EP_BUSY; -	/* pending requests are ignored and are queued on XferNotReady */ -} - -static void dwc3_ep_cmd_compl(struct dwc3_ep *dep, -		const struct dwc3_event_depevt *event) -{ -	u32 param = event->parameters; -	u32 cmd_type = (param >> 8) & ((1 << 5) - 1); - -	switch (cmd_type) { -	case DWC3_DEPCMD_ENDTRANSFER: -		dwc3_process_ep_cmd_complete(dep, event); -		break; -	case DWC3_DEPCMD_STARTTRANSFER: -		dep->res_trans_idx = param & 0x7f; -		break; -	default: -		printk(KERN_ERR "%s() unknown /unexpected type: %d\n", -				__func__, cmd_type); -		break; -	}; -} -  static void dwc3_endpoint_interrupt(struct dwc3 *dwc,  		const struct dwc3_event_depevt *event)  { @@ -1724,6 +1775,9 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,  	dep = dwc->eps[epnum]; +	if (!(dep->flags & DWC3_EP_ENABLED)) +		return; +  	dev_vdbg(dwc->dev, "%s: %s\n", dep->name,  			dwc3_ep_event_string(event->endpoint_event)); @@ -1734,7 +1788,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,  	switch (event->endpoint_event) {  	case DWC3_DEPEVT_XFERCOMPLETE: -		dep->res_trans_idx = 0; +		dep->resource_index = 0;  		if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {  			dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n", @@ -1797,7 +1851,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,  		dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);  		break;  	case DWC3_DEPEVT_EPCMDCMPLT: -		dwc3_ep_cmd_compl(dep, event); +		dev_vdbg(dwc->dev, "Endpoint Command Complete\n");  		break;  	}  } @@ -1820,16 +1874,16 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)  	dep = dwc->eps[epnum]; -	WARN_ON(!dep->res_trans_idx); -	if (dep->res_trans_idx) { -		cmd = DWC3_DEPCMD_ENDTRANSFER; -		cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC; -		cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx); -		memset(¶ms, 0, sizeof(params)); -		ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); -		WARN_ON_ONCE(ret); -		dep->res_trans_idx = 0; -	} +	if (!dep->resource_index) +		return; + +	cmd = DWC3_DEPCMD_ENDTRANSFER; +	cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC; +	cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); +	memset(¶ms, 0, sizeof(params)); +	ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); +	WARN_ON_ONCE(ret); +	dep->resource_index = 0;  }  static void dwc3_stop_active_transfers(struct dwc3 *dwc) @@ -1872,11 +1926,9 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)  static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)  { +	int			reg; +  	dev_vdbg(dwc->dev, "%s\n", __func__); -#if 0 -	XXX -	U1/U2 is powersave optimization. Skip it for now. Anyway we need to -	enable it before we can disable it.  	reg = dwc3_readl(dwc->regs, DWC3_DCTL);  	reg &= ~DWC3_DCTL_INITU1ENA; @@ -1884,9 +1936,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)  	reg &= ~DWC3_DCTL_INITU2ENA;  	dwc3_writel(dwc->regs, DWC3_DCTL, reg); -#endif -	dwc3_stop_active_transfers(dwc);  	dwc3_disconnect_gadget(dwc);  	dwc->start_config_issued = false; @@ -1894,30 +1944,30 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)  	dwc->setup_packet_pending = false;  } -static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on) +static void dwc3_gadget_usb3_phy_suspend(struct dwc3 *dwc, int suspend)  {  	u32			reg;  	reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); -	if (on) -		reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; -	else +	if (suspend)  		reg |= DWC3_GUSB3PIPECTL_SUSPHY; +	else +		reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;  	dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);  } -static void dwc3_gadget_usb2_phy_power(struct dwc3 *dwc, int on) +static void dwc3_gadget_usb2_phy_suspend(struct dwc3 *dwc, int suspend)  {  	u32			reg;  	reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); -	if (on) -		reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; -	else +	if (suspend)  		reg |= DWC3_GUSB2PHYCFG_SUSPHY; +	else +		reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;  	dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);  } @@ -1962,16 +2012,18 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)  	/* after reset -> Default State */  	dwc->dev_state = DWC3_DEFAULT_STATE; -	/* Enable PHYs */ -	dwc3_gadget_usb2_phy_power(dwc, true); -	dwc3_gadget_usb3_phy_power(dwc, true); +	/* Recent versions support automatic phy suspend and don't need this */ +	if (dwc->revision < DWC3_REVISION_194A) { +		/* Resume PHYs */ +		dwc3_gadget_usb2_phy_suspend(dwc, false); +		dwc3_gadget_usb3_phy_suspend(dwc, false); +	}  	if (dwc->gadget.speed != USB_SPEED_UNKNOWN)  		dwc3_disconnect_gadget(dwc);  	reg = dwc3_readl(dwc->regs, DWC3_DCTL);  	reg &= ~DWC3_DCTL_TSTCTRL_MASK; -	reg &= ~(DWC3_DCTL_INITU1ENA | DWC3_DCTL_INITU2ENA);  	dwc3_writel(dwc->regs, DWC3_DCTL, reg);  	dwc->test_mode = false; @@ -2010,16 +2062,16 @@ static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)  	dwc3_writel(dwc->regs, DWC3_GCTL, reg);  } -static void dwc3_gadget_disable_phy(struct dwc3 *dwc, u8 speed) +static void dwc3_gadget_phy_suspend(struct dwc3 *dwc, u8 speed)  {  	switch (speed) {  	case USB_SPEED_SUPER: -		dwc3_gadget_usb2_phy_power(dwc, false); +		dwc3_gadget_usb2_phy_suspend(dwc, true);  		break;  	case USB_SPEED_HIGH:  	case USB_SPEED_FULL:  	case USB_SPEED_LOW: -		dwc3_gadget_usb3_phy_power(dwc, false); +		dwc3_gadget_usb3_phy_suspend(dwc, true);  		break;  	}  } @@ -2082,8 +2134,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)  		break;  	} -	/* Disable unneded PHY */ -	dwc3_gadget_disable_phy(dwc, dwc->gadget.speed); +	/* Recent versions support automatic phy suspend and don't need this */ +	if (dwc->revision < DWC3_REVISION_194A) { +		/* Suspend unneeded PHY */ +		dwc3_gadget_phy_suspend(dwc, dwc->gadget.speed); +	}  	dep = dwc->eps[0];  	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); @@ -2373,10 +2428,6 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)  	reg |= DWC3_DCFG_LPM_CAP;  	dwc3_writel(dwc->regs, DWC3_DCFG, reg); -	reg = dwc3_readl(dwc->regs, DWC3_DCTL); -	reg |= DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA; -	dwc3_writel(dwc->regs, DWC3_DCTL, reg); -  	/* Enable all but Start and End of Frame IRQs */  	reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |  			DWC3_DEVTEN_EVNTOVERFLOWEN | @@ -2389,6 +2440,24 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)  			DWC3_DEVTEN_DISCONNEVTEN);  	dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); +	/* Enable USB2 LPM and automatic phy suspend only on recent versions */ +	if (dwc->revision >= DWC3_REVISION_194A) { +		reg = dwc3_readl(dwc->regs, DWC3_DCFG); +		reg |= DWC3_DCFG_LPM_CAP; +		dwc3_writel(dwc->regs, DWC3_DCFG, reg); + +		reg = dwc3_readl(dwc->regs, DWC3_DCTL); +		reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN); + +		/* TODO: This should be configurable */ +		reg |= DWC3_DCTL_HIRD_THRES(28); + +		dwc3_writel(dwc->regs, DWC3_DCTL, reg); + +		dwc3_gadget_usb2_phy_suspend(dwc, false); +		dwc3_gadget_usb3_phy_suspend(dwc, false); +	} +  	ret = device_register(&dwc->gadget.dev);  	if (ret) {  		dev_err(dwc->dev, "failed to register gadget device\n");  |