Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8c42bfb6 authored by Manu Gautam's avatar Manu Gautam
Browse files

USB: ci13xxx: Prime EP with first active TD if queue not empty



Driver is using hardware queuing feature to add TDs to an active
or primed EP QH at runtime. If enqueue requires priming the EP
again then there is requirement to first check for any pending
TDs with ACTIVE bit set. If there is one then prime EP with that
TD instead of the new one. Driver is currently checking only first
TD in software list but there is possibility that first few TDs
were completed by hardware but they were not reclaimed by software
yet due to interrupt moderation or ITC.
Not checking the whole list may cause software to skip one TD
which will never be completed and stay at the head of list causing
data stall.

Change-Id: Ic64023a383c2a1f7dd438b6a254b4fad10efac3a
Signed-off-by: default avatarManu Gautam <mgautam@codeaurora.org>
parent d70d0441
Loading
Loading
Loading
Loading
+26 −13
Original line number Diff line number Diff line
@@ -1622,15 +1622,14 @@ static ssize_t print_dtds(struct device *dev,
		mEp = &udc->ci13xxx_ep[ep_num];

	n = hw_ep_bit(mEp->num, mEp->dir);
	pr_info("%s: prime:%08x stat:%08x ep#%d dir:%s"
			"dTD_update_fail_count: %lu "
			"mEp->dTD_update_fail_count: %lu"
			"mEp->prime_fail_count: %lu\n", __func__,
	pr_info("%s: prime:%08x stat:%08x ep#%d dir:%s dTD_update_fail_count: %lu mEp->dTD_update_fail_count: %lu mEp->dTD_active_re_q_count: %lu mEp->prime_fail_count: %lu\n",
			__func__,
			hw_cread(CAP_ENDPTPRIME, ~0),
			hw_cread(CAP_ENDPTSTAT, ~0),
			mEp->num, mEp->dir ? "IN" : "OUT",
			udc->dTD_update_fail_count,
			mEp->dTD_update_fail_count,
			mEp->dTD_active_re_q_count,
			mEp->prime_fail_count);

	pr_info("QH: cap:%08x cur:%08x next:%08x token:%08x\n",
@@ -2027,19 +2026,33 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
			goto done;
	}

	/*  QH configuration */
	/* Hardware may leave few TDs unprocessed, check and reprime with 1st */
	if (!list_empty(&mEp->qh.queue)) {
		struct ci13xxx_req *mReq = \
			list_entry(mEp->qh.queue.next,
				   struct ci13xxx_req, queue);
		struct ci13xxx_req *mReq_active, *mReq_next;
		u32 i = 0;

		/* Iterate forward to find first TD with ACTIVE bit set */
		mReq_active = mReq;
		list_for_each_entry(mReq_next, &mEp->qh.queue, queue) {
			i++;
			mEp->dTD_active_re_q_count++;
			if (TD_STATUS_ACTIVE & mReq_next->ptr->token) {
				mReq_active = mReq_next;
				dbg_event(_usb_addr(mEp), "ReQUE",
					  mReq_next->ptr->token);
				pr_debug("!!ReQ(%u-%u-%x)-%u!!\n", mEp->num,
					 mEp->dir, mReq_next->ptr->token, i);
				break;
			}
		}

		if (TD_STATUS_ACTIVE & mReq->ptr->token) {
			mEp->qh.ptr->td.next   = mReq->dma;
		/*  QH configuration */
		mEp->qh.ptr->td.next = mReq_active->dma;
		mEp->qh.ptr->td.token &= ~TD_STATUS;
		goto prime;
	}
	}

	/*  QH configuration */
	mEp->qh.ptr->td.next   = mReq->dma;    /* TERMINATE = 0 */

	if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+2 −1
Original line number Diff line number Diff line
@@ -119,6 +119,7 @@ struct ci13xxx_ep {
	struct ci13xxx_td                     *last_zptr;
	dma_addr_t                            last_zdma;
	unsigned long                         dTD_update_fail_count;
	unsigned long                         dTD_active_re_q_count;
	unsigned long			      prime_fail_count;
	int				      prime_timer_count;
	struct timer_list		      prime_timer;