[PATCH v3] EHCI: split ehci_qh structure into hw and sw parts

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

 



This patch is against latest linus git tree.


>From 7dc04f6d1feaa10f8e9c8d8edd4714f7dae8e92f Mon Sep 17 00:00:00 2001
From: Alek Du <alek.du@xxxxxxxxx>
Date: Thu, 18 Jun 2009 16:52:48 +0800
Subject: [PATCH] EHCI: split ehci_qh structure into hw and sw parts

The ehci_qh structure merged hw and sw together which is not good:
1. More and more items are being added into ehci_qh, the ehci_qh software
   part are unnecessary to be allocated in DMA qh_pool.
2. If HCD has local SRAM, the sw part will consume it too, and it won't
   bring any benefit.
3. For non-x86 system, the entire ehci_qh is uncachable, actually we
   only need the hw part to be uncacheable. Split them will give the sw
   part cacheable feature.

Signed-off-by: Alek Du <alek.du@xxxxxxxxx>
---
 drivers/usb/host/ehci-dbg.c   |   61 ++++++++++++++------------
 drivers/usb/host/ehci-hcd.c   |   21 +++++----
 drivers/usb/host/ehci-hub.c   |    2 +-
 drivers/usb/host/ehci-mem.c   |   26 ++++++++---
 drivers/usb/host/ehci-q.c     |   59 +++++++++++++------------
 drivers/usb/host/ehci-sched.c |   97 +++++++++++++++++++++++------------------
 drivers/usb/host/ehci.h       |   19 ++++++--
 7 files changed, 162 insertions(+), 123 deletions(-)

diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 7f4ace7..5b588f2 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -135,9 +135,9 @@ static void __maybe_unused
 dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
 	ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
-		qh, qh->hw_next, qh->hw_info1, qh->hw_info2,
-		qh->hw_current);
-	dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next);
+		qh, qh->hw->hw_next, qh->hw->hw_info1, qh->hw->hw_info2,
+		qh->hw->hw_current);
+	dbg_qtd("overlay", ehci, (struct ehci_qtd *) &qh->hw->hw_qtd_next);
 }
 
 static void __maybe_unused
@@ -401,30 +401,30 @@ static void qh_lines (
 	char			mark;
 	__le32			list_end = EHCI_LIST_END(ehci);
 
-	if (qh->hw_qtd_next == list_end)	/* NEC does this */
+	if (qh->hw->hw_qtd_next == list_end)	/* NEC does this */
 		mark = '@';
 	else
-		mark = token_mark(ehci, qh->hw_token);
+		mark = token_mark(ehci, qh->hw->hw_token);
 	if (mark == '/') {	/* qh_alt_next controls qh advance? */
-		if ((qh->hw_alt_next & QTD_MASK(ehci))
-				== ehci->async->hw_alt_next)
+		if ((qh->hw->hw_alt_next & QTD_MASK(ehci))
+				== ehci->async->hw->hw_alt_next)
 			mark = '#';	/* blocked */
-		else if (qh->hw_alt_next == list_end)
+		else if (qh->hw->hw_alt_next == list_end)
 			mark = '.';	/* use hw_qtd_next */
 		/* else alt_next points to some other qtd */
 	}
-	scratch = hc32_to_cpup(ehci, &qh->hw_info1);
-	hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &qh->hw_current) : 0;
+	scratch = hc32_to_cpup(ehci, &qh->hw->hw_info1);
+	hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &qh->hw->hw_current) : 0;
 	temp = scnprintf (next, size,
 			"qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
 			qh, scratch & 0x007f,
 			speed_char (scratch),
 			(scratch >> 8) & 0x000f,
-			scratch, hc32_to_cpup(ehci, &qh->hw_info2),
-			hc32_to_cpup(ehci, &qh->hw_token), mark,
-			(cpu_to_hc32(ehci, QTD_TOGGLE) & qh->hw_token)
+			scratch, hc32_to_cpup(ehci, &qh->hw->hw_info2),
+			hc32_to_cpup(ehci, &qh->hw->hw_token), mark,
+			(cpu_to_hc32(ehci, QTD_TOGGLE) & qh->hw->hw_token)
 				? "data1" : "data0",
-			(hc32_to_cpup(ehci, &qh->hw_alt_next) >> 1) & 0x0f);
+			(hc32_to_cpup(ehci, &qh->hw->hw_alt_next) >> 1) & 0x0f);
 	size -= temp;
 	next += temp;
 
@@ -435,10 +435,10 @@ static void qh_lines (
 		mark = ' ';
 		if (hw_curr == td->qtd_dma)
 			mark = '*';
-		else if (qh->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma))
+		else if (qh->hw->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma))
 			mark = '+';
 		else if (QTD_LENGTH (scratch)) {
-			if (td->hw_alt_next == ehci->async->hw_alt_next)
+			if (td->hw_alt_next == ehci->async->hw->hw_alt_next)
 				mark = '#';
 			else if (td->hw_alt_next != list_end)
 				mark = '/';
@@ -495,7 +495,8 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)
 	 * one QH per line, and TDs we know about
 	 */
 	spin_lock_irqsave (&ehci->lock, flags);
-	for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh)
+	for (qh = to_qh(&ehci->async->qh_next); size > 0 && qh;
+					qh = to_qh(&qh->qh_next))
 		qh_lines (ehci, qh, &next, &size);
 	if (ehci->reclaim && size > 0) {
 		temp = scnprintf (next, size, "\nreclaim =\n");
@@ -521,6 +522,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
 	char			*next;
 	unsigned		i;
 	__hc32			tag;
+	struct ehci_qh		*qh;
 
 	if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC)))
 		return 0;
@@ -552,20 +554,21 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
 		do {
 			switch (hc32_to_cpu(ehci, tag)) {
 			case Q_TYPE_QH:
+				qh = to_qh(&p);
 				temp = scnprintf (next, size, " qh%d-%04x/%p",
-						p.qh->period,
+						qh->period,
 						hc32_to_cpup(ehci,
-								&p.qh->hw_info2)
+							&p.qh_hw->hw_info2)
 							/* uframe masks */
 							& (QH_CMASK | QH_SMASK),
-						p.qh);
+						qh);
 				size -= temp;
 				next += temp;
 				/* don't repeat what follows this qh */
 				for (temp = 0; temp < seen_count; temp++) {
 					if (seen [temp].ptr != p.ptr)
 						continue;
-					if (p.qh->qh_next.ptr) {
+					if (qh->qh_next.ptr) {
 						temp = scnprintf (next, size,
 							" ...");
 						size -= temp;
@@ -576,14 +579,14 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
 				/* show more info the first time around */
 				if (temp == seen_count) {
 					u32	scratch = hc32_to_cpup(ehci,
-							&p.qh->hw_info1);
+							&p.qh_hw->hw_info1);
 					struct ehci_qtd	*qtd;
 					char		*type = "";
 
 					/* count tds, get ep direction */
 					temp = 0;
 					list_for_each_entry (qtd,
-							&p.qh->qtd_list,
+							&qh->qtd_list,
 							qtd_list) {
 						temp++;
 						switch (0x03 & (hc32_to_cpu(
@@ -600,17 +603,19 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
 						speed_char (scratch),
 						scratch & 0x007f,
 						(scratch >> 8) & 0x000f, type,
-						p.qh->usecs, p.qh->c_usecs,
+						qh->usecs, qh->c_usecs,
 						temp,
 						0x7ff & (scratch >> 16));
 
 					if (seen_count < DBG_SCHED_LIMIT)
-						seen [seen_count++].qh = p.qh;
+						seen[seen_count++].qh_hw =
+									p.qh_hw;
 				} else
 					temp = 0;
-				if (p.qh) {
-					tag = Q_NEXT_TYPE(ehci, p.qh->hw_next);
-					p = p.qh->qh_next;
+				if (qh) {
+					tag = Q_NEXT_TYPE(ehci,
+							p.qh_hw->hw_next);
+					p = qh->qh_next;
 				}
 				break;
 			case Q_TYPE_FSTN:
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 2b72473..27361ae 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -543,13 +543,14 @@ static int ehci_init(struct usb_hcd *hcd)
 	 * its dummy is used in hw_alt_next of many tds, to prevent the qh
 	 * from automatically advancing to the next td after short reads.
 	 */
-	ehci->async->qh_next.qh = NULL;
-	ehci->async->hw_next = QH_NEXT(ehci, ehci->async->qh_dma);
-	ehci->async->hw_info1 = cpu_to_hc32(ehci, QH_HEAD);
-	ehci->async->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
-	ehci->async->hw_qtd_next = EHCI_LIST_END(ehci);
+	ehci->async->qh_next.qh_hw = NULL;
+	ehci->async->hw->hw_next = QH_NEXT(ehci, ehci->async->qh_dma);
+	ehci->async->hw->hw_info1 = cpu_to_hc32(ehci, QH_HEAD);
+	ehci->async->hw->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
+	ehci->async->hw->hw_qtd_next = EHCI_LIST_END(ehci);
 	ehci->async->qh_state = QH_STATE_LINKED;
-	ehci->async->hw_alt_next = QTD_NEXT(ehci, ehci->async->dummy->qtd_dma);
+	ehci->async->hw->hw_alt_next = QTD_NEXT(ehci,
+				ehci->async->dummy->qtd_dma);
 
 	/* clear interrupt enables, set irq latency */
 	if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
@@ -978,7 +979,7 @@ rescan:
 	/* endpoints can be iso streams.  for now, we don't
 	 * accelerate iso completions ... so spin a while.
 	 */
-	if (qh->hw_info1 == 0) {
+	if (qh->hw->hw_info1 == 0) {
 		ehci_vdbg (ehci, "iso delay\n");
 		goto idle_timeout;
 	}
@@ -987,9 +988,9 @@ rescan:
 		qh->qh_state = QH_STATE_IDLE;
 	switch (qh->qh_state) {
 	case QH_STATE_LINKED:
-		for (tmp = ehci->async->qh_next.qh;
+		for (tmp = to_qh(&ehci->async->qh_next);
 				tmp && tmp != qh;
-				tmp = tmp->qh_next.qh)
+				tmp = to_qh(&tmp->qh_next))
 			continue;
 		/* periodic qh self-unlinks on empty */
 		if (!tmp)
@@ -1047,7 +1048,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
 		if (!list_empty(&qh->qtd_list)) {
 			WARN_ONCE(1, "clear_halt for a busy endpoint\n");
 		} else if (qh->qh_state == QH_STATE_IDLE) {
-			qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
+			qh->hw->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
 		} else {
 			/* It's not safe to write into the overlay area
 			 * while the QH is active.  Unlink it first and
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index f46ad27..e93b9c4 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -267,7 +267,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
 
 	/* maybe re-activate the schedule(s) */
 	temp = 0;
-	if (ehci->async->qh_next.qh)
+	if (ehci->async->qh_next.qh_hw)
 		temp |= CMD_ASE;
 	if (ehci->periodic_sched)
 		temp |= CMD_PSE;
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
index 10d5291..8fc0d3e 100644
--- a/drivers/usb/host/ehci-mem.c
+++ b/drivers/usb/host/ehci-mem.c
@@ -75,7 +75,8 @@ static void qh_destroy(struct ehci_qh *qh)
 	}
 	if (qh->dummy)
 		ehci_qtd_free (ehci, qh->dummy);
-	dma_pool_free (ehci->qh_pool, qh, qh->qh_dma);
+	dma_pool_free(ehci->qh_pool, qh->hw, qh->qh_dma);
+	kfree(qh);
 }
 
 static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
@@ -83,15 +84,19 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
 	struct ehci_qh		*qh;
 	dma_addr_t		dma;
 
-	qh = (struct ehci_qh *)
-		dma_pool_alloc (ehci->qh_pool, flags, &dma);
+	qh = kzalloc(sizeof *qh, GFP_ATOMIC);
 	if (!qh)
-		return qh;
+		goto done;
+	qh->hw = (struct ehci_qh_hw *)
+		dma_pool_alloc(ehci->qh_pool, flags, &dma);
+	if (!qh->hw)
+		goto fail;
 
-	memset (qh, 0, sizeof *qh);
+	memset(qh->hw, 0, sizeof *qh->hw);
 	qh->refcount = 1;
 	qh->ehci = ehci;
 	qh->qh_dma = dma;
+	qh->hw->sw = qh;
 	// INIT_LIST_HEAD (&qh->qh_list);
 	INIT_LIST_HEAD (&qh->qtd_list);
 
@@ -99,10 +104,15 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
 	qh->dummy = ehci_qtd_alloc (ehci, flags);
 	if (qh->dummy == NULL) {
 		ehci_dbg (ehci, "no dummy td\n");
-		dma_pool_free (ehci->qh_pool, qh, qh->qh_dma);
-		qh = NULL;
+		goto fail1;
 	}
+done:
 	return qh;
+fail1:
+	dma_pool_free(ehci->qh_pool, qh->hw, qh->qh_dma);
+fail:
+	kfree(qh);
+	return NULL;
 }
 
 /* to share a qh (cpu threads, or hc) */
@@ -180,7 +190,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
 	/* QHs for control/bulk/intr transfers */
 	ehci->qh_pool = dma_pool_create ("ehci_qh",
 			ehci_to_hcd(ehci)->self.controller,
-			sizeof (struct ehci_qh),
+			sizeof(struct ehci_qh_hw),
 			32 /* byte alignment (for hw parts) */,
 			4096 /* can't cross 4K */);
 	if (!ehci->qh_pool) {
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 3192f68..77d7d81 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -90,16 +90,16 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
 	/* writes to an active overlay are unsafe */
 	BUG_ON(qh->qh_state != QH_STATE_IDLE);
 
-	qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
-	qh->hw_alt_next = EHCI_LIST_END(ehci);
+	qh->hw->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
+	qh->hw->hw_alt_next = EHCI_LIST_END(ehci);
 
 	/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
 	wmb ();
-	qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
+	qh->hw->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
 }
 
 /* if it weren't for a common silicon quirk (writing the dummy into the qh
- * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault
+ * overlay, so qh->hw->hw_token wrongly becomes inactive/halted), only fault
  * recovery (including urb dequeue) would need software changes to a QH...
  */
 static void
@@ -113,7 +113,7 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
 		qtd = list_entry (qh->qtd_list.next,
 				struct ehci_qtd, qtd_list);
 		/* first qtd may already be partially processed */
-		if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw_current)
+		if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw->hw_current)
 			qtd = NULL;
 	}
 
@@ -215,7 +215,7 @@ __acquires(ehci->lock)
 		struct ehci_qh	*qh = (struct ehci_qh *) urb->hcpriv;
 
 		/* S-mask in a QH means it's an interrupt urb */
-		if ((qh->hw_info2 & cpu_to_hc32(ehci, QH_SMASK)) != 0) {
+		if ((qh->hw->hw_info2 & cpu_to_hc32(ehci, QH_SMASK)) != 0) {
 
 			/* ... update hc-wide periodic stats (for usbfs) */
 			ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
@@ -257,7 +257,7 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
 
 /*
  * Process and free completed qtds for a qh, returning URBs to drivers.
- * Chases up to qh->hw_current.  Returns number of completions called,
+ * Chases up to qh->hw->hw_current.  Returns number of completions called,
  * indicating how much "real" work we did.
  */
 static unsigned
@@ -348,7 +348,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
 					qtd->hw_token = cpu_to_hc32(ehci,
 							token);
 					wmb();
-					qh->hw_token = cpu_to_hc32(ehci, token);
+					qh->hw->hw_token =
+						cpu_to_hc32(ehci, token);
 					goto retry_xacterr;
 				}
 				stopped = 1;
@@ -391,16 +392,16 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
 			/* qh unlinked; token in overlay may be most current */
 			if (state == QH_STATE_IDLE
 					&& cpu_to_hc32(ehci, qtd->qtd_dma)
-						== qh->hw_current)
-				token = hc32_to_cpu(ehci, qh->hw_token);
+						== qh->hw->hw_current)
+				token = hc32_to_cpu(ehci, qh->hw->hw_token);
 
 			/* force halt for unlinked or blocked qh, so we'll
 			 * patch the qh later and so that completions can't
 			 * activate it while we "know" it's stopped.
 			 */
-			if ((halt & qh->hw_token) == 0) {
+			if ((halt & qh->hw->hw_token) == 0) {
 halt:
-				qh->hw_token |= halt;
+				qh->hw->hw_token |= halt;
 				wmb ();
 			}
 		}
@@ -452,7 +453,7 @@ halt:
 	 * it after fault cleanup, or recovering from silicon wrongly
 	 * overlaying the dummy qtd (which reduces DMA chatter).
 	 */
-	if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END(ehci)) {
+	if (stopped != 0 || qh->hw->hw_qtd_next == EHCI_LIST_END(ehci)) {
 		switch (state) {
 		case QH_STATE_IDLE:
 			qh_refresh(ehci, qh);
@@ -470,7 +471,7 @@ halt:
 			 * except maybe high bandwidth ...
 			 */
 			if ((cpu_to_hc32(ehci, QH_SMASK)
-					& qh->hw_info2) != 0) {
+					& qh->hw->hw_info2) != 0) {
 				intr_deschedule (ehci, qh);
 				(void) qh_schedule (ehci, qh);
 			} else
@@ -591,7 +592,7 @@ qh_urb_transaction (
 		 * (this will usually be overridden later.)
 		 */
 		if (is_input)
-			qtd->hw_alt_next = ehci->async->hw_alt_next;
+			qtd->hw_alt_next = ehci->async->hw->hw_alt_next;
 
 		/* qh makes control packets use qtd toggle; maybe switch it */
 		if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
@@ -832,8 +833,8 @@ done:
 
 	/* init as live, toggle clear, advance to dummy */
 	qh->qh_state = QH_STATE_IDLE;
-	qh->hw_info1 = cpu_to_hc32(ehci, info1);
-	qh->hw_info2 = cpu_to_hc32(ehci, info2);
+	qh->hw->hw_info1 = cpu_to_hc32(ehci, info1);
+	qh->hw->hw_info2 = cpu_to_hc32(ehci, info2);
 	qh_refresh (ehci, qh);
 	return qh;
 }
@@ -850,7 +851,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 	/* (re)start the async schedule? */
 	head = ehci->async;
 	timer_action_done (ehci, TIMER_ASYNC_OFF);
-	if (!head->qh_next.qh) {
+	if (!head->qh_next.qh_hw) {
 		u32	cmd = ehci_readl(ehci, &ehci->regs->command);
 
 		if (!(cmd & CMD_ASE)) {
@@ -870,11 +871,11 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
 	/* splice right after start */
 	qh->qh_next = head->qh_next;
-	qh->hw_next = head->hw_next;
+	qh->hw->hw_next = head->hw->hw_next;
 	wmb ();
 
-	head->qh_next.qh = qh;
-	head->hw_next = dma;
+	head->qh_next.qh_hw = qh->hw;
+	head->hw->hw_next = dma;
 
 	qh->xacterrs = QH_XACTERR_MAX;
 	qh->qh_state = QH_STATE_LINKED;
@@ -920,7 +921,7 @@ static struct ehci_qh *qh_append_tds (
 
                         /* usb_reset_device() briefly reverts to address 0 */
                         if (usb_pipedevice (urb->pipe) == 0)
-                                qh->hw_info1 &= ~qh_addr_mask;
+				qh->hw->hw_info1 &= ~qh_addr_mask;
 		}
 
 		/* just one way to queue requests: swap with the dummy qtd.
@@ -1037,7 +1038,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
 
 	// qh->hw_next = cpu_to_hc32(qh->qh_dma);
 	qh->qh_state = QH_STATE_IDLE;
-	qh->qh_next.qh = NULL;
+	qh->qh_next.qh_hw = NULL;
 	qh_put (qh);			// refcount from reclaim
 
 	/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
@@ -1057,7 +1058,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
 		 * active but idle for a while once it empties.
 		 */
 		if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state)
-				&& ehci->async->qh_next.qh == NULL)
+				&& ehci->async->qh_next.qh_hw == NULL)
 			timer_action (ehci, TIMER_ASYNC_OFF);
 	}
 
@@ -1103,10 +1104,10 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 	ehci->reclaim = qh = qh_get (qh);
 
 	prev = ehci->async;
-	while (prev->qh_next.qh != qh)
-		prev = prev->qh_next.qh;
+	while (prev->qh_next.qh_hw != qh->hw)
+		prev = to_qh(&prev->qh_next);
 
-	prev->hw_next = qh->hw_next;
+	prev->hw->hw_next = qh->hw->hw_next;
 	prev->qh_next = qh->qh_next;
 	wmb ();
 
@@ -1135,7 +1136,7 @@ static void scan_async (struct ehci_hcd *ehci)
 	ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index);
 	timer_action_done (ehci, TIMER_ASYNC_SHRINK);
 rescan:
-	qh = ehci->async->qh_next.qh;
+	qh = to_qh(&ehci->async->qh_next);
 	if (likely (qh != NULL)) {
 		do {
 			/* clean any finished work for this qh */
@@ -1173,7 +1174,7 @@ rescan:
 					action = TIMER_ASYNC_SHRINK;
 			}
 
-			qh = qh->qh_next.qh;
+			qh = to_qh(&qh->qh_next);
 		} while (qh);
 	}
 	if (action == TIMER_ASYNC_SHRINK)
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 9d1babc..5489618 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -49,7 +49,7 @@ periodic_next_shadow(struct ehci_hcd *ehci, union ehci_shadow *periodic,
 {
 	switch (hc32_to_cpu(ehci, tag)) {
 	case Q_TYPE_QH:
-		return &periodic->qh->qh_next;
+		return &to_qh(periodic)->qh_next;
 	case Q_TYPE_FSTN:
 		return &periodic->fstn->fstn_next;
 	case Q_TYPE_ITD:
@@ -93,19 +93,21 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
 	__hc32			*hw_p = &ehci->periodic [frame];
 	union ehci_shadow	*q = &ehci->pshadow [frame];
 	unsigned		usecs = 0;
+	struct ehci_qh		*qh;
 
 	while (q->ptr) {
 		switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) {
 		case Q_TYPE_QH:
+			qh = to_qh(q);
 			/* is it in the S-mask? */
-			if (q->qh->hw_info2 & cpu_to_hc32(ehci, 1 << uframe))
-				usecs += q->qh->usecs;
+			if (q->qh_hw->hw_info2 & cpu_to_hc32(ehci, 1 << uframe))
+				usecs += qh->usecs;
 			/* ... or C-mask? */
-			if (q->qh->hw_info2 & cpu_to_hc32(ehci,
+			if (q->qh_hw->hw_info2 & cpu_to_hc32(ehci,
 					1 << (8 + uframe)))
-				usecs += q->qh->c_usecs;
-			hw_p = &q->qh->hw_next;
-			q = &q->qh->qh_next;
+				usecs += qh->c_usecs;
+			hw_p = &q->qh_hw->hw_next;
+			q = &qh->qh_next;
 			break;
 		// case Q_TYPE_FSTN:
 		default:
@@ -226,6 +228,7 @@ periodic_tt_usecs (
 	__hc32			*hw_p = &ehci->periodic [frame];
 	union ehci_shadow	*q = &ehci->pshadow [frame];
 	unsigned char		uf;
+	struct ehci_qh		*qh;
 
 	memset(tt_usecs, 0, 16);
 
@@ -236,12 +239,13 @@ periodic_tt_usecs (
 			q = &q->itd->itd_next;
 			continue;
 		case Q_TYPE_QH:
-			if (same_tt(dev, q->qh->dev)) {
-				uf = tt_start_uframe(ehci, q->qh->hw_info2);
-				tt_usecs[uf] += q->qh->tt_usecs;
+			qh = to_qh(q);
+			if (same_tt(dev, qh->dev)) {
+				uf = tt_start_uframe(ehci, q->qh_hw->hw_info2);
+				tt_usecs[uf] += qh->tt_usecs;
 			}
-			hw_p = &q->qh->hw_next;
-			q = &q->qh->qh_next;
+			hw_p = &q->qh_hw->hw_next;
+			q = &qh->qh_next;
 			continue;
 		case Q_TYPE_SITD:
 			if (same_tt(dev, q->sitd->urb->dev)) {
@@ -375,6 +379,7 @@ static int tt_no_collision (
 	for (; frame < ehci->periodic_size; frame += period) {
 		union ehci_shadow	here;
 		__hc32			type;
+		struct ehci_qh		*qh;
 
 		here = ehci->pshadow [frame];
 		type = Q_NEXT_TYPE(ehci, ehci->periodic [frame]);
@@ -385,18 +390,19 @@ static int tt_no_collision (
 				here = here.itd->itd_next;
 				continue;
 			case Q_TYPE_QH:
-				if (same_tt (dev, here.qh->dev)) {
+				qh = to_qh(&here);
+				if (same_tt(dev, qh->dev)) {
 					u32		mask;
 
 					mask = hc32_to_cpu(ehci,
-							here.qh->hw_info2);
+							here.qh_hw->hw_info2);
 					/* "knows" no gap is needed */
 					mask |= mask >> 8;
 					if (mask & uf_mask)
 						break;
 				}
-				type = Q_NEXT_TYPE(ehci, here.qh->hw_next);
-				here = here.qh->qh_next;
+				type = Q_NEXT_TYPE(ehci, here.qh_hw->hw_next);
+				here = qh->qh_next;
 				continue;
 			case Q_TYPE_SITD:
 				if (same_tt (dev, here.sitd->urb->dev)) {
@@ -498,7 +504,8 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
 	dev_dbg (&qh->dev->dev,
 		"link qh%d-%04x/%p start %d [%d/%d us]\n",
-		period, hc32_to_cpup(ehci, &qh->hw_info2) & (QH_CMASK | QH_SMASK),
+		period, hc32_to_cpup(ehci,
+		&qh->hw->hw_info2) & (QH_CMASK | QH_SMASK),
 		qh, qh->start, qh->usecs, qh->c_usecs);
 
 	/* high bandwidth, or otherwise every microframe */
@@ -517,27 +524,29 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
 			if (type == cpu_to_hc32(ehci, Q_TYPE_QH))
 				break;
 			prev = periodic_next_shadow(ehci, prev, type);
-			hw_p = &here.qh->hw_next;
+			hw_p = &here.qh_hw->hw_next;
 			here = *prev;
 		}
 
 		/* sorting each branch by period (slow-->fast)
 		 * enables sharing interior tree nodes
 		 */
-		while (here.ptr && qh != here.qh) {
-			if (qh->period > here.qh->period)
+		while (here.ptr && qh->hw != here.qh_hw) {
+			struct ehci_qh	*qh = to_qh(&here);
+
+			if (qh->period > qh->period)
 				break;
-			prev = &here.qh->qh_next;
-			hw_p = &here.qh->hw_next;
+			prev = &qh->qh_next;
+			hw_p = &here.qh_hw->hw_next;
 			here = *prev;
 		}
 		/* link in this qh, unless some earlier pass did that */
-		if (qh != here.qh) {
+		if (qh->hw != here.qh_hw) {
 			qh->qh_next = here;
-			if (here.qh)
-				qh->hw_next = *hw_p;
+			if (here.qh_hw)
+				qh->hw->hw_next = *hw_p;
 			wmb ();
-			prev->qh = qh;
+			prev->qh_hw = qh->hw;
 			*hw_p = QH_NEXT (ehci, qh->qh_dma);
 		}
 	}
@@ -580,7 +589,7 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
 	dev_dbg (&qh->dev->dev,
 		"unlink qh%d-%04x/%p start %d [%d/%d us]\n",
 		qh->period,
-		hc32_to_cpup(ehci, &qh->hw_info2) & (QH_CMASK | QH_SMASK),
+		hc32_to_cpup(ehci, &qh->hw->hw_info2) & (QH_CMASK | QH_SMASK),
 		qh, qh->start, qh->usecs, qh->c_usecs);
 
 	/* qh->qh_next still "live" to HC */
@@ -599,20 +608,20 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
 	qh_unlink_periodic (ehci, qh);
 
 	/* simple/paranoid:  always delay, expecting the HC needs to read
-	 * qh->hw_next or finish a writeback after SPLIT/CSPLIT ... and
+	 * qh->hw->hw_next or finish a writeback after SPLIT/CSPLIT ... and
 	 * expect khubd to clean up after any CSPLITs we won't issue.
 	 * active high speed queues may need bigger delays...
 	 */
 	if (list_empty (&qh->qtd_list)
 			|| (cpu_to_hc32(ehci, QH_CMASK)
-					& qh->hw_info2) != 0)
+					& qh->hw->hw_info2) != 0)
 		wait = 2;
 	else
 		wait = 55;	/* worst case: 3 * 1024 */
 
 	udelay (wait);
 	qh->qh_state = QH_STATE_IDLE;
-	qh->hw_next = EHCI_LIST_END(ehci);
+	qh->hw->hw_next = EHCI_LIST_END(ehci);
 	wmb ();
 }
 
@@ -740,12 +749,12 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
 	unsigned	frame;		/* 0..(qh->period - 1), or NO_FRAME */
 
 	qh_refresh(ehci, qh);
-	qh->hw_next = EHCI_LIST_END(ehci);
+	qh->hw->hw_next = EHCI_LIST_END(ehci);
 	frame = qh->start;
 
 	/* reuse the previous schedule slots, if we can */
 	if (frame < qh->period) {
-		uframe = ffs(hc32_to_cpup(ehci, &qh->hw_info2) & QH_SMASK);
+		uframe = ffs(hc32_to_cpup(ehci, &qh->hw->hw_info2) & QH_SMASK);
 		status = check_intr_schedule (ehci, frame, --uframe,
 				qh, &c_mask);
 	} else {
@@ -783,11 +792,11 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
 		qh->start = frame;
 
 		/* reset S-frame and (maybe) C-frame masks */
-		qh->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK));
-		qh->hw_info2 |= qh->period
+		qh->hw->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK));
+		qh->hw->hw_info2 |= qh->period
 			? cpu_to_hc32(ehci, 1 << uframe)
 			: cpu_to_hc32(ehci, QH_SMASK);
-		qh->hw_info2 |= c_mask;
+		qh->hw->hw_info2 |= c_mask;
 	} else
 		ehci_dbg (ehci, "reused qh %p schedule\n", qh);
 
@@ -2175,20 +2184,22 @@ restart:
 
 		while (q.ptr != NULL) {
 			unsigned		uf;
-			union ehci_shadow	temp;
 			int			live;
+			struct ehci_qh		*qh;
+			struct ehci_qh		*qh_tmp;
 
 			live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state);
 			switch (hc32_to_cpu(ehci, type)) {
 			case Q_TYPE_QH:
+				qh = to_qh(&q);
 				/* handle any completions */
-				temp.qh = qh_get (q.qh);
-				type = Q_NEXT_TYPE(ehci, q.qh->hw_next);
-				q = q.qh->qh_next;
-				modified = qh_completions (ehci, temp.qh);
-				if (unlikely (list_empty (&temp.qh->qtd_list)))
-					intr_deschedule (ehci, temp.qh);
-				qh_put (temp.qh);
+				qh_tmp = qh_get(qh);
+				type = Q_NEXT_TYPE(ehci, q.qh_hw->hw_next);
+				q = qh->qh_next;
+				modified = qh_completions(ehci, qh_tmp);
+				if (unlikely(list_empty(&qh_tmp->qtd_list)))
+					intr_deschedule(ehci, qh_tmp);
+				qh_put(qh_tmp);
 				break;
 			case Q_TYPE_FSTN:
 				/* for "save place" FSTNs, look at QH entries
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 90ad339..a949d49 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -280,7 +280,7 @@ struct ehci_qtd {
  * For entries in the async schedule, the type tag always says "qh".
  */
 union ehci_shadow {
-	struct ehci_qh		*qh;		/* Q_TYPE_QH */
+	struct ehci_qh_hw	*qh_hw;		/* Q_TYPE_QH */
 	struct ehci_itd		*itd;		/* Q_TYPE_ITD */
 	struct ehci_sitd	*sitd;		/* Q_TYPE_SITD */
 	struct ehci_fstn	*fstn;		/* Q_TYPE_FSTN */
@@ -298,8 +298,8 @@ union ehci_shadow {
  * These appear in both the async and (for interrupt) periodic schedules.
  */
 
-struct ehci_qh {
-	/* first part defined by EHCI spec */
+/* first part defined by EHCI spec */
+struct ehci_qh_hw {
 	__hc32			hw_next;	/* see EHCI 3.6.1 */
 	__hc32			hw_info1;       /* see EHCI 3.6.2 */
 #define	QH_HEAD		0x00008000
@@ -318,6 +318,12 @@ struct ehci_qh {
 	__hc32			hw_buf [5];
 	__hc32			hw_buf_hi [5];
 
+	/* we need a back ptr to software part for some case */
+	struct ehci_qh		*sw;
+} __attribute__ ((aligned(32)));
+
+struct ehci_qh {
+	struct ehci_qh_hw	*hw;
 	/* the rest is HCD-private */
 	dma_addr_t		qh_dma;		/* address of qh */
 	union ehci_shadow	qh_next;	/* ptr to qh; or periodic */
@@ -355,7 +361,12 @@ struct ehci_qh {
 	unsigned short		start;		/* where polling starts */
 #define NO_FRAME ((unsigned short)~0)			/* pick new start */
 	struct usb_device	*dev;		/* access to TT */
-} __attribute__ ((aligned (32)));
+};
+
+static inline struct ehci_qh *to_qh(union ehci_shadow *shadow)
+{
+	return shadow->qh_hw ? shadow->qh_hw->sw : NULL;
+}
 
 /*-------------------------------------------------------------------------*/
 
-- 
1.6.0.4
--
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