diff options
Diffstat (limited to 'drivers/usb/cdns3/cdnsp-ring.c')
| -rw-r--r-- | drivers/usb/cdns3/cdnsp-ring.c | 110 | 
1 files changed, 60 insertions, 50 deletions
| diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c index b23e543b3a3d..07f6068342d4 100644 --- a/drivers/usb/cdns3/cdnsp-ring.c +++ b/drivers/usb/cdns3/cdnsp-ring.c @@ -1333,6 +1333,20 @@ static int cdnsp_handle_tx_event(struct cdnsp_device *pdev,  					 ep_ring->dequeue, td->last_trb,  					 ep_trb_dma); +		desc = td->preq->pep->endpoint.desc; + +		if (ep_seg) { +			ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) +					       / sizeof(*ep_trb)]; + +			trace_cdnsp_handle_transfer(ep_ring, +					(struct cdnsp_generic_trb *)ep_trb); + +			if (pep->skip && usb_endpoint_xfer_isoc(desc) && +			    td->last_trb != ep_trb) +				return -EAGAIN; +		} +  		/*  		 * Skip the Force Stopped Event. The event_trb(ep_trb_dma)  		 * of FSE is not in the current TD pointed by ep_ring->dequeue @@ -1347,7 +1361,6 @@ static int cdnsp_handle_tx_event(struct cdnsp_device *pdev,  			goto cleanup;  		} -		desc = td->preq->pep->endpoint.desc;  		if (!ep_seg) {  			if (!pep->skip || !usb_endpoint_xfer_isoc(desc)) {  				/* Something is busted, give up! */ @@ -1374,12 +1387,6 @@ static int cdnsp_handle_tx_event(struct cdnsp_device *pdev,  			goto cleanup;  		} -		ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) -				       / sizeof(*ep_trb)]; - -		trace_cdnsp_handle_transfer(ep_ring, -					    (struct cdnsp_generic_trb *)ep_trb); -  		if (cdnsp_trb_is_noop(ep_trb))  			goto cleanup; @@ -1726,11 +1733,6 @@ static unsigned int count_sg_trbs_needed(struct cdnsp_request *preq)  	return num_trbs;  } -static unsigned int count_isoc_trbs_needed(struct cdnsp_request *preq) -{ -	return cdnsp_count_trbs(preq->request.dma, preq->request.length); -} -  static void cdnsp_check_trb_math(struct cdnsp_request *preq, int running_total)  {  	if (running_total != preq->request.length) @@ -2192,28 +2194,48 @@ static unsigned int  }  /* Queue function isoc transfer */ -static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev, -			       struct cdnsp_request *preq) +int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev, +			struct cdnsp_request *preq)  { -	int trb_buff_len, td_len, td_remain_len, ret; +	unsigned int trb_buff_len, td_len, td_remain_len, block_len;  	unsigned int burst_count, last_burst_pkt;  	unsigned int total_pkt_count, max_pkt;  	struct cdnsp_generic_trb *start_trb; +	struct scatterlist *sg = NULL;  	bool more_trbs_coming = true;  	struct cdnsp_ring *ep_ring; +	unsigned int num_sgs = 0;  	int running_total = 0;  	u32 field, length_field; +	u64 addr, send_addr;  	int start_cycle;  	int trbs_per_td; -	u64 addr; -	int i; +	int i, sent_len, ret;  	ep_ring = preq->pep->ring; + +	td_len = preq->request.length; + +	if (preq->request.num_sgs) { +		num_sgs = preq->request.num_sgs; +		sg = preq->request.sg; +		addr = (u64)sg_dma_address(sg); +		block_len = sg_dma_len(sg); +		trbs_per_td = count_sg_trbs_needed(preq); +	} else { +		addr = (u64)preq->request.dma; +		block_len = td_len; +		trbs_per_td = count_trbs_needed(preq); +	} + +	ret = cdnsp_prepare_transfer(pdev, preq, trbs_per_td); +	if (ret) +		return ret; +  	start_trb = &ep_ring->enqueue->generic;  	start_cycle = ep_ring->cycle_state; -	td_len = preq->request.length; -	addr = (u64)preq->request.dma;  	td_remain_len = td_len; +	send_addr = addr;  	max_pkt = usb_endpoint_maxp(preq->pep->endpoint.desc);  	total_pkt_count = DIV_ROUND_UP(td_len, max_pkt); @@ -2225,11 +2247,6 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,  	burst_count = cdnsp_get_burst_count(pdev, preq, total_pkt_count);  	last_burst_pkt = cdnsp_get_last_burst_packet_count(pdev, preq,  							   total_pkt_count); -	trbs_per_td = count_isoc_trbs_needed(preq); - -	ret = cdnsp_prepare_transfer(pdev, preq, trbs_per_td); -	if (ret) -		goto cleanup;  	/*  	 * Set isoc specific data for the first TRB in a TD. @@ -2248,6 +2265,7 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,  		/* Calculate TRB length. */  		trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(addr); +		trb_buff_len = min(trb_buff_len, block_len);  		if (trb_buff_len > td_remain_len)  			trb_buff_len = td_remain_len; @@ -2256,7 +2274,8 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,  					       trb_buff_len, td_len, preq,  					       more_trbs_coming, 0); -		length_field = TRB_LEN(trb_buff_len) | TRB_INTR_TARGET(0); +		length_field = TRB_LEN(trb_buff_len) | TRB_TD_SIZE(remainder) | +			TRB_INTR_TARGET(0);  		/* Only first TRB is isoc, overwrite otherwise. */  		if (i) { @@ -2281,12 +2300,27 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,  		}  		cdnsp_queue_trb(pdev, ep_ring, more_trbs_coming, -				lower_32_bits(addr), upper_32_bits(addr), +				lower_32_bits(send_addr), upper_32_bits(send_addr),  				length_field, field);  		running_total += trb_buff_len;  		addr += trb_buff_len;  		td_remain_len -= trb_buff_len; + +		sent_len = trb_buff_len; +		while (sg && sent_len >= block_len) { +			/* New sg entry */ +			--num_sgs; +			sent_len -= block_len; +			if (num_sgs != 0) { +				sg = sg_next(sg); +				block_len = sg_dma_len(sg); +				addr = (u64)sg_dma_address(sg); +				addr += sent_len; +			} +		} +		block_len -= sent_len; +		send_addr = addr;  	}  	/* Check TD length */ @@ -2324,30 +2358,6 @@ cleanup:  	return ret;  } -int cdnsp_queue_isoc_tx_prepare(struct cdnsp_device *pdev, -				struct cdnsp_request *preq) -{ -	struct cdnsp_ring *ep_ring; -	u32 ep_state; -	int num_trbs; -	int ret; - -	ep_ring = preq->pep->ring; -	ep_state = GET_EP_CTX_STATE(preq->pep->out_ctx); -	num_trbs = count_isoc_trbs_needed(preq); - -	/* -	 * Check the ring to guarantee there is enough room for the whole -	 * request. Do not insert any td of the USB Request to the ring if the -	 * check failed. -	 */ -	ret = cdnsp_prepare_ring(pdev, ep_ring, ep_state, num_trbs, GFP_ATOMIC); -	if (ret) -		return ret; - -	return cdnsp_queue_isoc_tx(pdev, preq); -} -  /****		Command Ring Operations		****/  /*   * Generic function for queuing a command TRB on the command ring. | 
