[PATCH] usb: host: ehci: distinguish DMA addresses

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

 



This patch adds translation from CPU to DMA addresses, which is required for
proper operation on certain architectures like MIPS.

This patch also fixes the bug introduced by 4350744bf5 "usb: ehci-hcd: port
periodic transactions implementation from the u-boot", which is still present
in the original U-Boot code:

	td->qt_buffer[0] =
		cpu_to_hc32((unsigned long)buffer + i * elementsize);
	td->qt_buffer[1] =
		cpu_to_hc32((td->qt_buffer[0] + 0x1000) & ~0xfff);
	...

In case of a big-endian CPU 0x1000 is added to the byte-swapped value
`td->qt_buffer[0]`, the result is byte swapped once again and stored to
`td->qt_buffer[1]`. This results in erroneous values being stored in
`td->qt_buffer[1..4]`.

N.B.: This patch needs some testing on architectures different from MIPS.

Signed-off-by: Peter Mamonov <pmamonov@xxxxxxxxx>
---
 drivers/usb/host/ehci-hcd.c | 101 ++++++++++++++++++++++++------------
 1 file changed, 68 insertions(+), 33 deletions(-)

diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 34b22bcb44..6a9e3863c4 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -42,7 +42,9 @@ struct ehci_host {
 	struct ehci_hcor *hcor;
 	struct usb_host host;
 	struct QH *qh_list;
+	dma_addr_t qh_list_dma;
 	struct qTD *td;
+	dma_addr_t td_dma;
 	int portreset;
 	unsigned long flags;
 
@@ -51,7 +53,9 @@ struct ehci_host {
 	void *drvdata;
 	int periodic_schedules;
 	struct QH *periodic_queue;
+	dma_addr_t periodic_queue_dma;
 	uint32_t *periodic_list;
+	dma_addr_t periodic_list_dma;
 };
 
 struct int_queue {
@@ -59,9 +63,11 @@ struct int_queue {
 	int queuesize;
 	unsigned long pipe;
 	struct QH *first;
+	dma_addr_t first_dma;
 	struct QH *current;
 	struct QH *last;
 	struct qTD *tds;
+	dma_addr_t tds_dma;
 };
 
 #define to_ehci(ptr) container_of(ptr, struct ehci_host, host)
@@ -137,6 +143,29 @@ static struct descriptor {
 
 #define ehci_is_TDI()	(ehci->flags & EHCI_HAS_TT)
 
+#define EHCI_DMA(dma0, ptr0, ptr) \
+	((dma0) + ((ptr) - (ptr0)) * sizeof(*(ptr)))
+
+static inline uint32_t ehci_qh_dma(struct ehci_host *ehci, struct QH *qh)
+{
+	return EHCI_DMA(ehci->qh_list_dma, ehci->qh_list, qh);
+}
+
+static inline uint32_t ehci_td_dma(struct ehci_host *ehci, struct qTD *td)
+{
+	return EHCI_DMA(ehci->td_dma, ehci->td, td);
+}
+
+static inline uint32_t ehci_int_qh_dma(struct int_queue *intq, struct QH *qh)
+{
+	return EHCI_DMA(intq->first_dma, intq->first, qh);
+}
+
+static inline uint32_t ehci_int_td_dma(struct int_queue *intq, struct qTD *td)
+{
+	return EHCI_DMA(intq->tds_dma, intq->tds, td);
+}
+
 static void memzero32(void *ptr, size_t size)
 {
 	uint32_t *ptr32 = ptr;
@@ -346,7 +375,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
 			dev_dbg(ehci->dev, "unable construct SETUP td\n");
 			return ret;
 		}
-		*tdp = cpu_to_hc32((uint32_t) td);
+		*tdp = cpu_to_hc32(ehci_td_dma(ehci, td));
 		tdp = &td->qt_next;
 
 		toggle = 1;
@@ -385,7 +414,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
 			dev_err(ehci->dev, "unable construct DATA td\n");
 			return ret;
 		}
-		*tdp = cpu_to_hc32((uint32_t) td);
+		*tdp = cpu_to_hc32(ehci_td_dma(ehci, td));
 		tdp = &td->qt_next;
 	}
 
@@ -399,7 +428,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
 					      QT_TOKEN_PID_IN),
 				 NULL, 0,
 				 NULL, DMA_NONE);
-		*tdp = cpu_to_hc32((uint32_t)td);
+		*tdp = cpu_to_hc32(ehci_td_dma(ehci, td));
 		tdp = &td->qt_next;
 	}
 
@@ -841,7 +870,7 @@ static int ehci_init(struct usb_host *host)
 			return ret;
 	}
 
-	ehci->qh_list[0].qh_link = cpu_to_hc32((uint32_t)&ehci->qh_list[1] |
+	ehci->qh_list[0].qh_link = cpu_to_hc32(ehci_qh_dma(ehci, &ehci->qh_list[1]) |
 					       QH_LINK_TYPE_QH);
 	ehci->qh_list[0].qh_endpt1 = cpu_to_hc32(QH_ENDPT1_H(1) |
 						 QH_ENDPT1_EPS(USB_SPEED_HIGH));
@@ -850,12 +879,13 @@ static int ehci_init(struct usb_host *host)
 	ehci->qh_list[0].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
 	ehci->qh_list[0].qt_token = cpu_to_hc32(QT_TOKEN_STATUS_HALTED);
 
-	ehci->qh_list[1].qh_link = cpu_to_hc32((uint32_t)&ehci->qh_list[0] |
+	ehci->qh_list[1].qh_link = cpu_to_hc32(ehci_qh_dma(ehci,
+							   &ehci->qh_list[0]) |
 					       QH_LINK_TYPE_QH);
 	ehci->qh_list[1].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
 
 	/* Set async. queue head pointer. */
-	ehci_writel(&ehci->hcor->or_asynclistaddr, (uint32_t)ehci->qh_list);
+	ehci_writel(&ehci->hcor->or_asynclistaddr, (uint32_t)ehci->qh_list_dma);
 
 	/*
 	 * Set up periodic list
@@ -885,15 +915,15 @@ static int ehci_init(struct usb_host *host)
 		 * PAGE_SIZE less then 4k will break this code.
 		 */
 		ehci->periodic_list = dma_alloc_coherent(1024 * 4,
-							 DMA_ADDRESS_BROKEN);
+						&ehci->periodic_list_dma);
 	for (i = 0; i < 1024; i++) {
-		ehci->periodic_list[i] = cpu_to_hc32((unsigned long)periodic
+		ehci->periodic_list[i] = cpu_to_hc32((unsigned long)ehci->periodic_queue_dma
 						| QH_LINK_TYPE_QH);
 	}
 
 	/* Set periodic list base address */
 	ehci_writel(&ehci->hcor->or_periodiclistbase,
-		(unsigned long)ehci->periodic_list);
+		    (uint32_t)ehci->periodic_list_dma);
 
 	reg = ehci_readl(&ehci->hccr->cr_hcsparams);
 	descriptor.hub.bNbrPorts = HCS_N_PORTS(reg);
@@ -986,7 +1016,10 @@ disable_periodic(struct ehci_host *ehci)
 	return 0;
 }
 
-#define NEXT_QH(qh) (struct QH *)((unsigned long)hc32_to_cpu((qh)->qh_link) & ~0x1f)
+#define NEXT_QH(queue, qh) (struct QH *)(			\
+	((unsigned long)hc32_to_cpu((qh)->qh_link) & ~0x1f) -	\
+	(queue)->first_dma +					\
+	(unsigned long)(queue)->first)
 
 static int
 enable_periodic(struct ehci_host *ehci)
@@ -1051,7 +1084,7 @@ static void ehci_update_endpt2_dev_n_port(struct usb_device *udev,
 
 static struct int_queue *ehci_create_int_queue(struct usb_device *dev,
 			unsigned long pipe, int queuesize, int elementsize,
-			void *buffer, int interval)
+			void *buffer, dma_addr_t buffer_dma, int interval)
 {
 	struct usb_host *host = dev->host;
 	struct ehci_host *ehci = to_ehci(host);
@@ -1100,22 +1133,23 @@ static struct int_queue *ehci_create_int_queue(struct usb_device *dev,
 	result->queuesize = queuesize;
 	result->pipe = pipe;
 	result->first = dma_alloc_coherent(sizeof(struct QH) * queuesize,
-					   DMA_ADDRESS_BROKEN);
+					   &result->first_dma);
 	result->current = result->first;
 	result->last = result->first + queuesize - 1;
 	result->tds = dma_alloc_coherent(sizeof(struct qTD) * queuesize,
-					 DMA_ADDRESS_BROKEN);
+					 &result->tds_dma);
 
 	for (i = 0; i < queuesize; i++) {
 		struct QH *qh = result->first + i;
 		struct qTD *td = result->tds + i;
 		void **buf = &qh->buffer;
 
-		qh->qh_link = cpu_to_hc32((unsigned long)(qh+1) | QH_LINK_TYPE_QH);
+		qh->qh_link = cpu_to_hc32(ehci_int_qh_dma(result, qh + 1) |
+					  QH_LINK_TYPE_QH);
 		if (i == queuesize - 1)
 			qh->qh_link = cpu_to_hc32(QH_LINK_TERMINATE);
 
-		qh->qt_next = cpu_to_hc32((unsigned long)td);
+		qh->qt_next = cpu_to_hc32(ehci_int_td_dma(result, td));
 		qh->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
 		qh->qh_endpt1 =
 			cpu_to_hc32((0 << 28) | /* No NAK reload (ehci 4.9) */
@@ -1142,15 +1176,15 @@ static struct int_queue *ehci_create_int_queue(struct usb_device *dev,
 			((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */
 			0x80); /* active */
 		td->qt_buffer[0] =
-		    cpu_to_hc32((unsigned long)buffer + i * elementsize);
+		  cpu_to_hc32(buffer_dma + i * elementsize);
 		td->qt_buffer[1] =
-		    cpu_to_hc32((td->qt_buffer[0] + 0x1000) & ~0xfff);
+		  cpu_to_hc32((buffer_dma + i * elementsize + 0x1000) & ~0xfff);
 		td->qt_buffer[2] =
-		    cpu_to_hc32((td->qt_buffer[0] + 0x2000) & ~0xfff);
+		  cpu_to_hc32((buffer_dma + i * elementsize + 0x2000) & ~0xfff);
 		td->qt_buffer[3] =
-		    cpu_to_hc32((td->qt_buffer[0] + 0x3000) & ~0xfff);
+		  cpu_to_hc32((buffer_dma + i * elementsize + 0x3000) & ~0xfff);
 		td->qt_buffer[4] =
-		    cpu_to_hc32((td->qt_buffer[0] + 0x4000) & ~0xfff);
+		  cpu_to_hc32((buffer_dma + i * elementsize + 0x4000) & ~0xfff);
 
 		*buf = buffer + i * elementsize;
 	}
@@ -1165,7 +1199,7 @@ static struct int_queue *ehci_create_int_queue(struct usb_device *dev,
 
 	/* hook up to periodic list */
 	result->last->qh_link = list->qh_link;
-	list->qh_link = cpu_to_hc32((unsigned long)result->first | QH_LINK_TYPE_QH);
+	list->qh_link = cpu_to_hc32(result->first_dma | QH_LINK_TYPE_QH);
 
 	if (enable_periodic(ehci) < 0) {
 		dev_err(&dev->dev,
@@ -1177,8 +1211,10 @@ static struct int_queue *ehci_create_int_queue(struct usb_device *dev,
 	dev_dbg(&dev->dev, "Exit create_int_queue\n");
 	return result;
 fail3:
-	dma_free_coherent(result->tds, 0, sizeof(struct qTD) * queuesize);
-	dma_free_coherent(result->first, 0, sizeof(struct QH) * queuesize);
+	dma_free_coherent(result->tds, result->tds_dma,
+			  sizeof(struct qTD) * queuesize);
+	dma_free_coherent(result->first, result->first_dma,
+			  sizeof(struct QH) * queuesize);
 	free(result);
 	return NULL;
 }
@@ -1235,14 +1271,14 @@ static int ehci_destroy_int_queue(struct usb_device *dev,
 		dev_dbg(&dev->dev,
 			"considering %p, with qh_link %x\n",
 			cur, cur->qh_link);
-		if (NEXT_QH(cur) == queue->first) {
+		if (NEXT_QH(queue, cur) == queue->first) {
 			dev_dbg(&dev->dev,
 				"found candidate. removing from chain\n");
 			cur->qh_link = queue->last->qh_link;
 			result = 0;
 			break;
 		}
-		cur = NEXT_QH(cur);
+		cur = NEXT_QH(queue, cur);
 	}
 
 	if (ehci->periodic_schedules > 0) {
@@ -1269,14 +1305,14 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
 	uint64_t start;
 	void *backbuffer;
 	int result = 0, ret;
+	dma_addr_t buffer_dma;
 
 	dev_dbg(ehci->dev, "dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",
 	      dev, pipe, buffer, length, interval);
 
-	dma_sync_single_for_device((unsigned long)buffer, length,
-				   DMA_BIDIRECTIONAL);
+	buffer_dma = dma_map_single(ehci->dev, buffer, length, DMA_BIDIRECTIONAL);
 
-	queue = ehci_create_int_queue(dev, pipe, 1, length, buffer, interval);
+	queue = ehci_create_int_queue(dev, pipe, 1, length, buffer, buffer_dma, interval);
 	if (!queue)
 		return -EINVAL;
 
@@ -1298,8 +1334,7 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
 			result = -EINVAL;
 	}
 
-	dma_sync_single_for_cpu((unsigned long)buffer, length,
-				DMA_BIDIRECTIONAL);
+	dma_unmap_single(ehci->dev, buffer_dma, length, DMA_BIDIRECTIONAL);
 
 	ret = ehci_destroy_int_queue(dev, queue);
 	if (!result)
@@ -1335,11 +1370,11 @@ struct ehci_host *ehci_register(struct device_d *dev, struct ehci_data *data)
 	ehci->post_init = data->post_init;
 
 	ehci->qh_list = dma_alloc_coherent(sizeof(struct QH) * NUM_QH,
-					   DMA_ADDRESS_BROKEN);
+					   &ehci->qh_list_dma);
 	ehci->periodic_queue = dma_alloc_coherent(sizeof(struct QH),
-						  DMA_ADDRESS_BROKEN);
+						  &ehci->periodic_queue_dma);
 	ehci->td = dma_alloc_coherent(sizeof(struct qTD) * NUM_TD,
-				      DMA_ADDRESS_BROKEN);
+				      &ehci->td_dma);
 
 	host->hw_dev = dev;
 	host->init = ehci_init;
-- 
2.24.0


_______________________________________________
barebox mailing list
barebox@xxxxxxxxxxxxxxxxxxx
http://lists.infradead.org/mailman/listinfo/barebox



[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux