[PATCH 01/12] usb: chipidea: udc: use {read,write}l to handle mapped data

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The udc uses an shared dma memory space between hard and software. This
memory layout is described in ci13xxx_qh and ci13xxx_td which are marked
with the attribute ((packed)).

The packed attribute leads the compiler to generate one byte operations
for addressing the mapped memory as it believes this memory has no
alignement issues as common streaming data. This appeares on armv5
machines where the hardware does not support unaligned 32bit operations.
Compilers for newer ARMs will probably still generate 32bit operations,
as the hardware supports it.

The Datasheet of the synopsys core describes, that some operations on
the mapped memory need to be atomic double word operations. I.e. the
next pointer addressing in the qhead, as otherwise the hardware could
read wrong data and totally stuck.

This patch fixes that issue by addressing every mapped area operation
with explicit readl and writel operations where needed. It also adds an
wmb() for the prepared TD data before it gets enqueued into the qhead.

We also remove the packed attribute as other patches could not address
this memory handling correct and leak new causes for the issue to come
back.

Signed-off-by: Michael Grzeschik <m.grzeschik@xxxxxxxxxxxxxx>
---
 drivers/usb/chipidea/debug.c |  2 +-
 drivers/usb/chipidea/udc.c   | 34 +++++++++++++++++-----------------
 drivers/usb/chipidea/udc.h   |  4 ++--
 3 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c
index 36a7063..768fea4 100644
--- a/drivers/usb/chipidea/debug.c
+++ b/drivers/usb/chipidea/debug.c
@@ -180,7 +180,7 @@ static int ci_requests_show(struct seq_file *s, void *data)
 
 			for (j = 0; j < qsize; j++)
 				seq_printf(s, " %04X:    %08X\n", j,
-					   *((u32 *)req->ptr + j));
+					   readl(&req->ptr + j));
 		}
 	spin_unlock_irqrestore(&ci->lock, flags);
 
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 4a7b356..fd702e9 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -446,6 +446,8 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
 		mReq->ptr->page[i] =
 			(mReq->req.dma + i * CI13XXX_PAGE_SIZE) & ~TD_RESERVED_MASK;
 
+	wmb();
+
 	if (!list_empty(&mEp->qh.queue)) {
 		struct ci13xxx_req *mReqPrev;
 		int n = hw_ep_bit(mEp->num, mEp->dir);
@@ -454,9 +456,9 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
 		mReqPrev = list_entry(mEp->qh.queue.prev,
 				struct ci13xxx_req, queue);
 		if (mReqPrev->zptr)
-			mReqPrev->zptr->next = mReq->dma & TD_ADDR_MASK;
+			writel(mReq->dma & TD_ADDR_MASK, &mReqPrev->zptr->next);
 		else
-			mReqPrev->ptr->next = mReq->dma & TD_ADDR_MASK;
+			writel(mReq->dma & TD_ADDR_MASK, &mReqPrev->ptr->next);
 		wmb();
 		if (hw_read(ci, OP_ENDPTPRIME, BIT(n)))
 			goto done;
@@ -468,11 +470,10 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
 		if (tmp_stat)
 			goto done;
 	}
-
 	/*  QH configuration */
-	mEp->qh.ptr->td.next   = mReq->dma;    /* TERMINATE = 0 */
-	mEp->qh.ptr->td.token &= ~TD_STATUS;   /* clear status */
-	mEp->qh.ptr->cap |=  QH_ZLT;
+	writel(mReq->dma, &mEp->qh.ptr->td.next); /* TERMINATE = 0 */
+	writel(readl(&mEp->qh.ptr->td.token) & ~TD_STATUS, &mEp->qh.ptr->td.token);
+	writel(readl(&mEp->qh.ptr->cap) | QH_ZLT, &mEp->qh.ptr->cap);
 
 	wmb();   /* synchronize before ep prime */
 
@@ -494,11 +495,11 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
 	if (mReq->req.status != -EALREADY)
 		return -EINVAL;
 
-	if ((TD_STATUS_ACTIVE & mReq->ptr->token) != 0)
+	if ((TD_STATUS_ACTIVE & readl(&mReq->ptr->token)) != 0)
 		return -EBUSY;
 
 	if (mReq->zptr) {
-		if ((TD_STATUS_ACTIVE & mReq->zptr->token) != 0)
+		if ((TD_STATUS_ACTIVE & readl(&mReq->zptr->token)) != 0)
 			return -EBUSY;
 		dma_pool_free(mEp->td_pool, mReq->zptr, mReq->zdma);
 		mReq->zptr = NULL;
@@ -508,7 +509,7 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
 
 	usb_gadget_unmap_request(&mEp->ci->gadget, &mReq->req, mEp->dir);
 
-	mReq->req.status = mReq->ptr->token & TD_STATUS;
+	mReq->req.status = readl(&mReq->ptr->token) & TD_STATUS;
 	if ((TD_STATUS_HALTED & mReq->req.status) != 0)
 		mReq->req.status = -1;
 	else if ((TD_STATUS_DT_ERR & mReq->req.status) != 0)
@@ -516,7 +517,7 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
 	else if ((TD_STATUS_TR_ERR & mReq->req.status) != 0)
 		mReq->req.status = -1;
 
-	mReq->req.actual   = mReq->ptr->token & TD_TOTAL_BYTES;
+	mReq->req.actual   = readl(&mReq->ptr->token) & TD_TOTAL_BYTES;
 	mReq->req.actual >>= __ffs(TD_TOTAL_BYTES);
 	mReq->req.actual   = mReq->req.length - mReq->req.actual;
 	mReq->req.actual   = mReq->req.status ? 0 : mReq->req.actual;
@@ -786,7 +787,7 @@ __acquires(mEp->lock)
 		if (retval < 0)
 			break;
 		list_del_init(&mReq->queue);
-		trace_ci_ep_complete_req(mEp, mReq->ptr->token, retval);
+		trace_ci_ep_complete_req(mEp, readl(&mReq->ptr->token), retval);
 		if (mReq->req.complete != NULL) {
 			spin_unlock(mEp->lock);
 			if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) &&
@@ -1026,18 +1027,17 @@ static int ep_enable(struct usb_ep *ep,
 
 	trace_ci_ep_enable(mEp, 0);
 
-	mEp->qh.ptr->cap = 0;
+	writel(0, &mEp->qh.ptr->cap);
 
 	if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
-		mEp->qh.ptr->cap |=  QH_IOS;
+		writel(readl(&mEp->qh.ptr->cap) | QH_IOS, &mEp->qh.ptr->cap);
 	else if (mEp->type == USB_ENDPOINT_XFER_ISOC)
 		mEp->qh.ptr->cap &= ~QH_MULT;
 	else
-		mEp->qh.ptr->cap &= ~QH_ZLT;
+		writel(readl(&mEp->qh.ptr->cap) & ~QH_ZLT, &mEp->qh.ptr->cap);
 
-	mEp->qh.ptr->cap |=
-		(mEp->ep.maxpacket << __ffs(QH_MAX_PKT)) & QH_MAX_PKT;
-	mEp->qh.ptr->td.next |= TD_TERMINATE;   /* needed? */
+	writel(readl(&mEp->qh.ptr->cap) | ((mEp->ep.maxpacket << __ffs(QH_MAX_PKT)) & QH_MAX_PKT), &mEp->qh.ptr->cap);
+	writel(readl(&mEp->qh.ptr->td.next) | TD_TERMINATE, &mEp->qh.ptr->td.next);   /* needed? */
 
 	/*
 	 * Enable endpoints in the HW other than ep0 as ep0
diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h
index d644574..be60464 100644
--- a/drivers/usb/chipidea/udc.h
+++ b/drivers/usb/chipidea/udc.h
@@ -38,7 +38,7 @@ struct ci13xxx_td {
 #define TD_CURR_OFFSET        (0x0FFFUL <<  0)
 #define TD_FRAME_NUM          (0x07FFUL <<  0)
 #define TD_RESERVED_MASK      (0x0FFFUL <<  0)
-} __attribute__ ((packed));
+};
 
 /* DMA layout of queue heads */
 struct ci13xxx_qh {
@@ -55,7 +55,7 @@ struct ci13xxx_qh {
 	/* 9 */
 	u32 RESERVED;
 	struct usb_ctrlrequest   setup;
-} __attribute__ ((packed));
+};
 
 /**
  * struct ci13xxx_req - usb request representation
-- 
1.8.2.rc2

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux