diff options
Diffstat (limited to 'drivers/usb/dwc2/gadget.c')
| -rw-r--r-- | drivers/usb/dwc2/gadget.c | 193 | 
1 files changed, 107 insertions, 86 deletions
| diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 837237e4bc96..11d85a6e0b0d 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -115,10 +115,16 @@ static inline bool using_desc_dma(struct dwc2_hsotg *hsotg)   */  static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep)  { +	struct dwc2_hsotg *hsotg = hs_ep->parent; +	u16 limit = DSTS_SOFFN_LIMIT; + +	if (hsotg->gadget.speed != USB_SPEED_HIGH) +		limit >>= 3; +  	hs_ep->target_frame += hs_ep->interval; -	if (hs_ep->target_frame > DSTS_SOFFN_LIMIT) { +	if (hs_ep->target_frame > limit) {  		hs_ep->frame_overrun = true; -		hs_ep->target_frame &= DSTS_SOFFN_LIMIT; +		hs_ep->target_frame &= limit;  	} else {  		hs_ep->frame_overrun = false;  	} @@ -136,10 +142,16 @@ static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep)   */  static inline void dwc2_gadget_dec_frame_num_by_one(struct dwc2_hsotg_ep *hs_ep)  { +	struct dwc2_hsotg *hsotg = hs_ep->parent; +	u16 limit = DSTS_SOFFN_LIMIT; + +	if (hsotg->gadget.speed != USB_SPEED_HIGH) +		limit >>= 3; +  	if (hs_ep->target_frame)  		hs_ep->target_frame -= 1;  	else -		hs_ep->target_frame = DSTS_SOFFN_LIMIT; +		hs_ep->target_frame = limit;  }  /** @@ -1018,6 +1030,12 @@ static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep)  	dwc2_writel(hsotg, ctrl, depctl);  } +static bool dwc2_gadget_target_frame_elapsed(struct dwc2_hsotg_ep *hs_ep); +static void dwc2_hsotg_complete_request(struct dwc2_hsotg *hsotg, +					struct dwc2_hsotg_ep *hs_ep, +				       struct dwc2_hsotg_req *hs_req, +				       int result); +  /**   * dwc2_hsotg_start_req - start a USB request from an endpoint's queue   * @hsotg: The controller state. @@ -1170,14 +1188,19 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,  		}  	} -	if (hs_ep->isochronous && hs_ep->interval == 1) { -		hs_ep->target_frame = dwc2_hsotg_read_frameno(hsotg); -		dwc2_gadget_incr_frame_num(hs_ep); - -		if (hs_ep->target_frame & 0x1) -			ctrl |= DXEPCTL_SETODDFR; -		else -			ctrl |= DXEPCTL_SETEVENFR; +	if (hs_ep->isochronous) { +		if (!dwc2_gadget_target_frame_elapsed(hs_ep)) { +			if (hs_ep->interval == 1) { +				if (hs_ep->target_frame & 0x1) +					ctrl |= DXEPCTL_SETODDFR; +				else +					ctrl |= DXEPCTL_SETEVENFR; +			} +			ctrl |= DXEPCTL_CNAK; +		} else { +			dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, -ENODATA); +			return; +		}  	}  	ctrl |= DXEPCTL_EPENA;	/* ensure ep enabled */ @@ -1325,12 +1348,16 @@ static bool dwc2_gadget_target_frame_elapsed(struct dwc2_hsotg_ep *hs_ep)  	u32 target_frame = hs_ep->target_frame;  	u32 current_frame = hsotg->frame_number;  	bool frame_overrun = hs_ep->frame_overrun; +	u16 limit = DSTS_SOFFN_LIMIT; + +	if (hsotg->gadget.speed != USB_SPEED_HIGH) +		limit >>= 3;  	if (!frame_overrun && current_frame >= target_frame)  		return true;  	if (frame_overrun && current_frame >= target_frame && -	    ((current_frame - target_frame) < DSTS_SOFFN_LIMIT / 2)) +	    ((current_frame - target_frame) < limit / 2))  		return true;  	return false; @@ -1713,11 +1740,9 @@ static struct dwc2_hsotg_req *get_ep_head(struct dwc2_hsotg_ep *hs_ep)   */  static void dwc2_gadget_start_next_request(struct dwc2_hsotg_ep *hs_ep)  { -	u32 mask;  	struct dwc2_hsotg *hsotg = hs_ep->parent;  	int dir_in = hs_ep->dir_in;  	struct dwc2_hsotg_req *hs_req; -	u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK;  	if (!list_empty(&hs_ep->queue)) {  		hs_req = get_ep_head(hs_ep); @@ -1733,9 +1758,6 @@ static void dwc2_gadget_start_next_request(struct dwc2_hsotg_ep *hs_ep)  	} else {  		dev_dbg(hsotg->dev, "%s: No more ISOC-OUT requests\n",  			__func__); -		mask = dwc2_readl(hsotg, epmsk_reg); -		mask |= DOEPMSK_OUTTKNEPDISMSK; -		dwc2_writel(hsotg, mask, epmsk_reg);  	}  } @@ -2306,19 +2328,6 @@ static void dwc2_hsotg_ep0_zlp(struct dwc2_hsotg *hsotg, bool dir_in)  	dwc2_hsotg_program_zlp(hsotg, hsotg->eps_out[0]);  } -static void dwc2_hsotg_change_ep_iso_parity(struct dwc2_hsotg *hsotg, -					    u32 epctl_reg) -{ -	u32 ctrl; - -	ctrl = dwc2_readl(hsotg, epctl_reg); -	if (ctrl & DXEPCTL_EOFRNUM) -		ctrl |= DXEPCTL_SETEVENFR; -	else -		ctrl |= DXEPCTL_SETODDFR; -	dwc2_writel(hsotg, ctrl, epctl_reg); -} -  /*   * dwc2_gadget_get_xfersize_ddma - get transferred bytes amount from desc   * @hs_ep - The endpoint on which transfer went @@ -2439,20 +2448,11 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)  			dwc2_hsotg_ep0_zlp(hsotg, true);  	} -	/* -	 * Slave mode OUT transfers do not go through XferComplete so -	 * adjust the ISOC parity here. -	 */ -	if (!using_dma(hsotg)) { -		if (hs_ep->isochronous && hs_ep->interval == 1) -			dwc2_hsotg_change_ep_iso_parity(hsotg, DOEPCTL(epnum)); -		else if (hs_ep->isochronous && hs_ep->interval > 1) -			dwc2_gadget_incr_frame_num(hs_ep); -	} -  	/* Set actual frame number for completed transfers */ -	if (!using_desc_dma(hsotg) && hs_ep->isochronous) -		req->frame_number = hsotg->frame_number; +	if (!using_desc_dma(hsotg) && hs_ep->isochronous) { +		req->frame_number = hs_ep->target_frame; +		dwc2_gadget_incr_frame_num(hs_ep); +	}  	dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, result);  } @@ -2766,6 +2766,12 @@ static void dwc2_hsotg_complete_in(struct dwc2_hsotg *hsotg,  		return;  	} +	/* Set actual frame number for completed transfers */ +	if (!using_desc_dma(hsotg) && hs_ep->isochronous) { +		hs_req->req.frame_number = hs_ep->target_frame; +		dwc2_gadget_incr_frame_num(hs_ep); +	} +  	dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);  } @@ -2826,23 +2832,18 @@ static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep)  		dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index); -		if (hs_ep->isochronous) { -			dwc2_hsotg_complete_in(hsotg, hs_ep); -			return; -		} -  		if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) {  			int dctl = dwc2_readl(hsotg, DCTL);  			dctl |= DCTL_CGNPINNAK;  			dwc2_writel(hsotg, dctl, DCTL);  		} -		return; -	} +	} else { -	if (dctl & DCTL_GOUTNAKSTS) { -		dctl |= DCTL_CGOUTNAK; -		dwc2_writel(hsotg, dctl, DCTL); +		if (dctl & DCTL_GOUTNAKSTS) { +			dctl |= DCTL_CGOUTNAK; +			dwc2_writel(hsotg, dctl, DCTL); +		}  	}  	if (!hs_ep->isochronous) @@ -2863,8 +2864,6 @@ static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep)  		/* Update current frame number value. */  		hsotg->frame_number = dwc2_hsotg_read_frameno(hsotg);  	} while (dwc2_gadget_target_frame_elapsed(hs_ep)); - -	dwc2_gadget_start_next_request(hs_ep);  }  /** @@ -2881,8 +2880,8 @@ static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep)  static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep)  {  	struct dwc2_hsotg *hsotg = ep->parent; +	struct dwc2_hsotg_req *hs_req;  	int dir_in = ep->dir_in; -	u32 doepmsk;  	if (dir_in || !ep->isochronous)  		return; @@ -2896,28 +2895,39 @@ static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep)  		return;  	} -	if (ep->interval > 1 && -	    ep->target_frame == TARGET_FRAME_INITIAL) { +	if (ep->target_frame == TARGET_FRAME_INITIAL) {  		u32 ctrl;  		ep->target_frame = hsotg->frame_number; -		dwc2_gadget_incr_frame_num(ep); +		if (ep->interval > 1) { +			ctrl = dwc2_readl(hsotg, DOEPCTL(ep->index)); +			if (ep->target_frame & 0x1) +				ctrl |= DXEPCTL_SETODDFR; +			else +				ctrl |= DXEPCTL_SETEVENFR; -		ctrl = dwc2_readl(hsotg, DOEPCTL(ep->index)); -		if (ep->target_frame & 0x1) -			ctrl |= DXEPCTL_SETODDFR; -		else -			ctrl |= DXEPCTL_SETEVENFR; +			dwc2_writel(hsotg, ctrl, DOEPCTL(ep->index)); +		} +	} + +	while (dwc2_gadget_target_frame_elapsed(ep)) { +		hs_req = get_ep_head(ep); +		if (hs_req) +			dwc2_hsotg_complete_request(hsotg, ep, hs_req, -ENODATA); -		dwc2_writel(hsotg, ctrl, DOEPCTL(ep->index)); +		dwc2_gadget_incr_frame_num(ep); +		/* Update current frame number value. */ +		hsotg->frame_number = dwc2_hsotg_read_frameno(hsotg);  	} -	dwc2_gadget_start_next_request(ep); -	doepmsk = dwc2_readl(hsotg, DOEPMSK); -	doepmsk &= ~DOEPMSK_OUTTKNEPDISMSK; -	dwc2_writel(hsotg, doepmsk, DOEPMSK); +	if (!ep->req) +		dwc2_gadget_start_next_request(ep); +  } +static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, +				   struct dwc2_hsotg_ep *hs_ep); +  /**   * dwc2_gadget_handle_nak - handle NAK interrupt   * @hs_ep: The endpoint on which interrupt is asserted. @@ -2935,7 +2945,9 @@ static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep)  static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep)  {  	struct dwc2_hsotg *hsotg = hs_ep->parent; +	struct dwc2_hsotg_req *hs_req;  	int dir_in = hs_ep->dir_in; +	u32 ctrl;  	if (!dir_in || !hs_ep->isochronous)  		return; @@ -2977,13 +2989,29 @@ static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep)  			dwc2_writel(hsotg, ctrl, DIEPCTL(hs_ep->index));  		} - -		dwc2_hsotg_complete_request(hsotg, hs_ep, -					    get_ep_head(hs_ep), 0);  	} -	if (!using_desc_dma(hsotg)) +	if (using_desc_dma(hsotg)) +		return; + +	ctrl = dwc2_readl(hsotg, DIEPCTL(hs_ep->index)); +	if (ctrl & DXEPCTL_EPENA) +		dwc2_hsotg_ep_stop_xfr(hsotg, hs_ep); +	else +		dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index); + +	while (dwc2_gadget_target_frame_elapsed(hs_ep)) { +		hs_req = get_ep_head(hs_ep); +		if (hs_req) +			dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, -ENODATA); +  		dwc2_gadget_incr_frame_num(hs_ep); +		/* Update current frame number value. */ +		hsotg->frame_number = dwc2_hsotg_read_frameno(hsotg); +	} + +	if (!hs_ep->req) +		dwc2_gadget_start_next_request(hs_ep);  }  /** @@ -3039,21 +3067,15 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,  		/* In DDMA handle isochronous requests separately */  		if (using_desc_dma(hsotg) && hs_ep->isochronous) { -			/* XferCompl set along with BNA */ -			if (!(ints & DXEPINT_BNAINTR)) -				dwc2_gadget_complete_isoc_request_ddma(hs_ep); +			dwc2_gadget_complete_isoc_request_ddma(hs_ep);  		} else if (dir_in) {  			/*  			 * We get OutDone from the FIFO, so we only  			 * need to look at completing IN requests here  			 * if operating slave mode  			 */ -			if (hs_ep->isochronous && hs_ep->interval > 1) -				dwc2_gadget_incr_frame_num(hs_ep); - -			dwc2_hsotg_complete_in(hsotg, hs_ep); -			if (ints & DXEPINT_NAKINTRPT) -				ints &= ~DXEPINT_NAKINTRPT; +			if (!hs_ep->isochronous || !(ints & DXEPINT_NAKINTRPT)) +				dwc2_hsotg_complete_in(hsotg, hs_ep);  			if (idx == 0 && !hs_ep->req)  				dwc2_hsotg_enqueue_setup(hsotg); @@ -3062,10 +3084,8 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,  			 * We're using DMA, we need to fire an OutDone here  			 * as we ignore the RXFIFO.  			 */ -			if (hs_ep->isochronous && hs_ep->interval > 1) -				dwc2_gadget_incr_frame_num(hs_ep); - -			dwc2_hsotg_handle_outdone(hsotg, idx); +			if (!hs_ep->isochronous || !(ints & DXEPINT_OUTTKNEPDIS)) +				dwc2_hsotg_handle_outdone(hsotg, idx);  		}  	} @@ -4085,6 +4105,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,  			mask |= DIEPMSK_NAKMSK;  			dwc2_writel(hsotg, mask, DIEPMSK);  		} else { +			epctrl |= DXEPCTL_SNAK;  			mask = dwc2_readl(hsotg, DOEPMSK);  			mask |= DOEPMSK_OUTTKNEPDISMSK;  			dwc2_writel(hsotg, mask, DOEPMSK); |