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

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

 



>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