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